1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2010-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 /*
25  * This file contains implementations of the EVO HAL methods for display class
26  * 3.x (also known as "nvdisplay").
27  */
28 
29 #include "nvkms-dma.h"
30 #include "nvkms-types.h"
31 #include "nvkms-rmapi.h"
32 #include "nvkms-surface.h"
33 #include "nvkms-softfloat.h"
34 #include "nvkms-evo.h"
35 #include "nvkms-evo1.h"
36 #include "nvkms-prealloc.h"
37 #include "nv-float.h"
38 #include "nvkms-dpy.h"
39 #include "nvkms-vrr.h"
40 
41 #include <nvmisc.h>
42 
43 #include <class/clc372sw.h> // NVC372_DISPLAY_SW
44 #include <class/clc373.h> // NVC373_DISP_CAPABILITIES
45 #include <class/clc37b.h> // NVC37B_WINDOW_IMM_CHANNEL_DMA
46 #include <class/clc37d.h> // NVC37D_CORE_CHANNEL_DMA
47 #include <class/clc37dcrcnotif.h> // NVC37D_NOTIFIER_CRC
48 #include <class/clc37dswspare.h> // NVC37D_HEAD_SET_SW_SPARE_*
49 #include <class/clc37e.h> // NVC37E_WINDOW_CHANNEL_DMA
50 #include <class/clc573.h> // NVC573_DISP_CAPABILITIES
51 #include <class/clc57d.h> // NVC57D_CORE_CHANNEL_DMA
52 #include <class/clc57e.h> // NVC57E_WINDOW_CHANNEL_DMA
53 #include <class/clc57esw.h>
54 #include <class/clc673.h> // NVC673_DISP_CAPABILITIES
55 #include <class/clc67d.h> // NVC67D_CORE_CHANNEL_DMA
56 #include <class/clc67e.h> // NVC67E_WINDOW_CHANNEL_DMA
57 
58 #include <ctrl/ctrlc370/ctrlc370chnc.h>
59 #include <ctrl/ctrlc370/ctrlc370rg.h>
60 #include <ctrl/ctrlc372/ctrlc372chnc.h>
61 
62 #define NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C3        \
63     (NVBIT64(NvKmsSurfaceMemoryFormatRF16GF16BF16XF16))
64 
65 #define NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C5        \
66     (NVBIT64(NvKmsSurfaceMemoryFormatRF16GF16BF16XF16))
67 
68 #define NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C6        \
69     (NVBIT64(NvKmsSurfaceMemoryFormatRF16GF16BF16XF16) |    \
70      NVBIT64(NvKmsSurfaceMemoryFormatX2B10G10R10))
71 
72 /** Number of CRCs supported by hardware on NVC37D hardware (SF/SOR, Comp, RG) */
73 #define NV_EVO3_NUM_CRC_FIELDS 3
74 
75 /** Number of CRCs supported by hardware on NVC37D hardware SF/SOR, Comp, RG Ovf and Count */
76 #define NV_EVO3_NUM_CRC_FLAGS 4
77 
78 static NvBool EvoIsChannelIdleC3(NVDevEvoPtr pDevEvo,
79                                  NVEvoChannelPtr pChan,
80                                  NvU32 sd,
81                                  NvBool *result);
82 
83 static void SetCsc00MatrixC5(NVEvoChannelPtr pChannel,
84                              const struct NvKmsCscMatrix *matrix);
85 static void SetCsc11MatrixC5(NVEvoChannelPtr pChannel,
86                              const struct NvKmsCscMatrix *matrix);
87 static void
88 UpdateCompositionC3(NVDevEvoPtr pDevEvo,
89                     NVEvoChannelPtr pChannel,
90                     const struct NvKmsCompositionParams *pCompParams,
91                     NVEvoUpdateState *updateState,
92                     enum NvKmsSurfaceMemoryFormat format);
93 static void
94 UpdateCompositionC5(NVDevEvoPtr pDevEvo,
95                     NVEvoChannelPtr pChannel,
96                     const struct NvKmsCompositionParams *pCompParams,
97                     NVEvoUpdateState *updateState,
98                     NvBool bypassComposition,
99                     enum NvKmsSurfaceMemoryFormat format);
100 
101 static void
102 EvoSetupIdentityOutputLutC5(NVEvoLutDataRec *pData,
103                             enum NvKmsLUTState *lutState,
104                             NvU32 *lutSize,
105                             NvBool *isLutModeVss);
106 
107 static void
108 EvoSetupIdentityBaseLutC5(NVEvoLutDataRec *pData,
109                           enum NvKmsLUTState *lutState,
110                           NvU32 *lutSize,
111                           NvBool *isLutModeVss);
112 
113 ct_assert(NV_EVO_LOCK_PIN_0 >
114           NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_PIN_INTERNAL_SCAN_LOCK__SIZE_1);
115 
116 /* nvdisplay has a maximum of 2 eyes and 3 planes per surface */
117 ct_assert((NVKMS_MAX_EYES * NVKMS_MAX_PLANES_PER_SURFACE) == 6);
118 
119 #define NV_EVO3_SUPPORTED_DITHERING_MODES                               \
120     ((1 << NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE_AUTO)        | \
121      (1 << NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE_DYNAMIC_2X2) | \
122      (1 << NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE_STATIC_2X2)  | \
123      (1 << NV_KMS_DPY_ATTRIBUTE_REQUESTED_DITHERING_MODE_TEMPORAL))
124 
125 #define NV_EVO3_SUPPORTED_CURSOR_COMP_BLEND_MODES              \
126     ((1 << NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE)                    | \
127      (1 << NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA)         | \
128      (1 << NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA)             | \
129      (1 << NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA) | \
130      (1 << NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA))
131 
132 /* Windows support all composition modes. */
133 #define NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES              \
134     ((1 << NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE)                    | \
135      (1 << NVKMS_COMPOSITION_BLENDING_MODE_TRANSPARENT)               | \
136      (1 << NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA)         | \
137      (1 << NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA)             | \
138      (1 << NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA) | \
139      (1 << NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA))
140 
141 #define NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C3                                     \
142     (DRF_DEF(C37D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_LUT, _USAGE_1025)     | \
143      DRF_DEF(C37D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_2) | \
144      DRF_DEF(C37D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE))
145 
146 #define NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C5                                     \
147     (DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _ILUT_ALLOWED, _TRUE)        | \
148      DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _TMO_LUT_ALLOWED, _TRUE))
149 
150 static inline NvU8 EyeAndPlaneToCtxDmaIdx(const NvU8 eye, const NvU8 plane)
151 {
152     /*
153      * See the definition of the SetContextDmaIso and SetOffset methods in the
154      * relevant nvdClass_01.mfs file to see how these method array indices are
155      * mapped.
156      */
157     nvAssert((eye < NVKMS_MAX_EYES) && (plane < NVKMS_MAX_PLANES_PER_SURFACE));
158 
159     return eye + (plane << 1);
160 }
161 
162 static void InitChannelCapsC3(NVDevEvoPtr pDevEvo,
163                               NVEvoChannelPtr pChannel)
164 {
165     if ((pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL) != 0) {
166         static const NVEvoChannelCaps WindowCaps = {
167             /*
168              * Window classes always support timestamp flips, and allow full
169              * use of the 64-bit timestamp value.
170              */
171             .validTimeStampBits = 64,
172             /* Window classes always support tearing flips. */
173             .tearingFlips = TRUE,
174             .vrrTearingFlips = TRUE,
175             /* Window classes support per-eye stereo flips. */
176             .perEyeStereoFlips = TRUE,
177         };
178 
179         pChannel->caps = WindowCaps;
180     }
181 }
182 
183 // HW supports ratio = 1, 2 (downscaling), 4 (downscaling)
184 #define NUM_SCALER_RATIOS 3
185 
186 // There are 16 phases stored in matrix, but HW can derive the values of phase
187 // +16 and -16 from phase 0. Therefore, SW loads phase +16/-16 in phase 0 coeff
188 // values.
189 // coeff values in phase 0.
190 #define NUM_TAPS5_COEFF_PHASES 16
191 
192 // There are 5 coefficient values per phase (or matrix row), but SW doesn't need
193 // to upload c2. So, the value here is set to 4.
194 #define NUM_TAPS5_COEFF_VALUES 4
195 
196 // The coefficient values are obtained from bug 1953108 comment 10
197 // Per MFS: However since all 5 coefficients have to add up to 1.0, only 4 need to be specified, and
198 //          HW can derive the missing one. The center coefficient is the one that is left out, so
199 //          if the 5 taps need weights (c0, c1, c2, c3, c4) then only (c0, c1, c3, c4) are stored,
200 //          and c2 is calculated by HW.
201 //          Phase 0 is the center phase and the corresponding filter kernel is symmetrical:
202 //          c0=c4, c1=c3  --> only c0 and c1 need to be stored.
203 //          Phase 16 (and -16) is the edge phase and the corresponding filter kernels are:
204 //          (0, c0, c1, c1, c0) for phase +16
205 //          (c0, c1, c1, c0, 0) for phase -16
206 //          The difference between +16 and -16 is automatically handled by HW. The table only needs
207 //          to store c0 and c1 for either case.
208 // Therefore, based on MFS above, the matrix below contains the values loaded to HW.
209 // Real Phase 0 is commented for easy reference.
210 // Also, phase 16 values (last row) are commented, but its C0,C1 values are loaded in row 0/phase 0.
211 static const NvU32 scalerTaps5Coeff[NUM_SCALER_RATIOS][NUM_TAPS5_COEFF_PHASES][NUM_TAPS5_COEFF_VALUES] =
212 {
213  // ratio = 1
214  {{  0  ,   0 ,             -16 ,  144}, // real phase 0:{ 0, 0, /*256,*/ 0, 0 },
215   {  0  ,  -5 ,  /*255,*/     5 ,    0},
216   {  0  ,  -9 ,  /*254,*/    11 ,    0},
217   { -1  , -12 ,  /*251,*/    18 ,   -1},
218   { -1  , -15 ,  /*248,*/    25 ,   -1},
219   { -1  , -18 ,  /*243,*/    33 ,   -2},
220   { -2  , -20 ,  /*238,*/    42 ,   -3},
221   { -2  , -21 ,  /*232,*/    51 ,   -3},
222   { -3  , -22 ,  /*225,*/    60 ,   -5},
223   { -3  , -22 ,  /*217,*/    70 ,   -6},
224   { -4  , -22 ,  /*208,*/    81 ,   -7},
225   { -4  , -22 ,  /*199,*/    91 ,   -9},
226   { -5  , -21 ,  /*190,*/   102 ,  -10},
227   { -5  , -20 ,  /*180,*/   113 ,  -12},
228   { -5  , -19 ,  /*169,*/   125 ,  -13},
229   { -6  , -18 ,  /*158,*/   136 ,  -15}
230   // real phase 16: {  0  , -16 ,  144,   144 ,  -16        }
231  },
232  // ratio = 2
233  {{ 3,    60 ,               20 , 108 }, // real phase 0:  {3 ,   60 ,  130 ,   60  ,   3 },
234   { 3 ,   57 ,   /*130,*/    63 ,   4 },
235   { 2 ,   54 ,   /*130,*/    66 ,   4 },
236   { 2 ,   51 ,   /*129,*/    69 ,   5 },
237   { 2 ,   48 ,   /*128,*/    72 ,   6 },
238   { 1 ,   45 ,   /*128,*/    75 ,   7 },
239   { 1 ,   43 ,   /*127,*/    78 ,   7 },
240   { 1 ,   40 ,   /*125,*/    81 ,   8 },
241   { 1 ,   37 ,   /*124,*/    84 ,   9 },
242   { 0 ,   35 ,   /*122,*/    88 ,  10 },
243   { 0 ,   33 ,   /*121,*/    91 ,  12 },
244   { 0 ,   30 ,   /*119,*/    94 ,  13 },
245   { 0 ,   28 ,   /*117,*/    97 ,  14 },
246   { 0 ,   26 ,   /*115,*/    99 ,  16 },
247   { 0 ,   24 ,   /*112,*/   102 ,  17 },
248   { 0 ,   22 ,   /*110,*/   105 ,  19 },
249   // real phase 16:{0 ,   20 ,  108 ,  108  ,  20 },
250  },
251  // ratio = 4
252  {{ 4 ,  62 ,               23  , 105 }, // real phase 0: {4  ,  62 ,  124 ,   62  ,   4 ,
253   { 4 ,  59 ,    /*124,*/   64  ,   5 },
254   { 3 ,  56 ,    /*124,*/   67  ,   6 },
255   { 3 ,  53 ,    /*123,*/   70  ,   7 },
256   { 2 ,  51 ,    /*123,*/   73  ,   8 },
257   { 2 ,  48 ,    /*122,*/   76  ,   8 },
258   { 2 ,  45 ,    /*121,*/   79  ,   9 },
259   { 1 ,  43 ,    /*120,*/   81  ,  10 },
260   { 1 ,  40 ,    /*119,*/   84  ,  12 },
261   { 1 ,  38 ,    /*117,*/   87  ,  13 },
262   { 1 ,  36 ,    /*116,*/   90  ,  14 },
263   { 0 ,  34 ,    /*114,*/   92  ,  15 },
264   { 0 ,  31 ,    /*113,*/   95  ,  17 },
265   { 0 ,  29 ,    /*111,*/   97  ,  18 },
266   { 0 ,  27 ,    /*109,*/  100  ,  20 },
267   { 0 ,  25 ,    /*107,*/  102  ,  22 },
268   // real phase 16: {0  ,  23 ,  105 ,  105  ,  23 },
269  }
270 };
271 
272 static void InitScalerCoefficientsPrecomp5(NVEvoChannelPtr pChannel,
273                                            NvU32 coeff, NvU32 index)
274 {
275     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_INPUT_SCALER_COEFF_VALUE, 1);
276     nvDmaSetEvoMethodData(pChannel,
277         DRF_NUM(C57E, _SET_INPUT_SCALER_COEFF_VALUE, _DATA, coeff) |
278         DRF_NUM(C57E, _SET_INPUT_SCALER_COEFF_VALUE, _INDEX, index));
279 }
280 
281 static void InitScalerCoefficientsPostcomp5(NVDevEvoPtr pDevEvo,
282                                             NVEvoChannelPtr pChannel,
283                                             NvU32 coeff, NvU32 index)
284 {
285     NvU32 h;
286 
287     for (h = 0; h < pDevEvo->numHeads; h++) {
288         nvDmaSetStartEvoMethod(pChannel,
289             NVC57D_HEAD_SET_OUTPUT_SCALER_COEFF_VALUE(h), 1);
290         nvDmaSetEvoMethodData(pChannel,
291             DRF_NUM(C57D, _HEAD_SET_OUTPUT_SCALER_COEFF_VALUE, _DATA, coeff) |
292             DRF_NUM(C57D, _HEAD_SET_OUTPUT_SCALER_COEFF_VALUE, _INDEX, index));
293     }
294 }
295 
296 static void InitTaps5ScalerCoefficientsC5(NVDevEvoPtr pDevEvo,
297                                           NVEvoChannelPtr pChannel,
298                                           NvBool isPrecomp)
299 {
300     NvU8 ratio;
301 
302     if (isPrecomp) {
303         const NVEvoWindowCaps *pWinCaps =
304             &pDevEvo->gpus[0].capabilities.window[pChannel->instance];
305         const NVEvoScalerCaps *pScalerCaps = &pWinCaps->scalerCaps;
306 
307         if (!pScalerCaps->present) {
308             return;
309         }
310     }
311 
312     for (ratio = 0; ratio < NUM_SCALER_RATIOS; ratio++) {
313         NvU8 phase;
314         for (phase = 0; phase < NUM_TAPS5_COEFF_PHASES; phase++) {
315             NvU8 coeffIdx;
316             for (coeffIdx = 0; coeffIdx < NUM_TAPS5_COEFF_VALUES; coeffIdx++) {
317                 NvU32 coeff = scalerTaps5Coeff[ratio][phase][coeffIdx];
318                 NvU32 index = ratio << 6 | phase << 2 | coeffIdx;
319 
320                 if (isPrecomp) {
321                     InitScalerCoefficientsPrecomp5(pChannel, coeff, index);
322                 } else {
323                     InitScalerCoefficientsPostcomp5(pDevEvo,
324                                                     pChannel, coeff, index);
325                 }
326             }
327         }
328     }
329 }
330 
331 /*
332  * This is a 3x4 matrix with S5.14 coefficients (truncated from S5.16
333  * SW-specified values).
334  */
335 static const struct NvKmsCscMatrix Rec2020RGBToLMS = {{
336     { 0x697c, 0x8620, 0x1064, 0 },
337     { 0x2aa8, 0xb86c, 0x1ce8, 0 },
338     {  0x62c, 0x1354, 0xe684, 0 },
339 }};
340 
341 /*
342  * This is a 3x4 matrix with S5.14 coefficients (truncated from S5.16
343  * SW-specified values).
344  */
345 static const struct NvKmsCscMatrix Rec709RGBToLMS = {{
346     { 0x4bb8, 0x9f84, 0x14c8, 0 },
347     { 0x27fc, 0xba2c, 0x1dd4, 0 },
348     { 0x8fc,  0x2818, 0xcef0, 0 },
349 }};
350 
351 /*
352  * This is a 3x4 matrix with S5.14 coefficients (truncated from S5.16
353  * SW-specified values).
354  */
355 static const struct NvKmsCscMatrix LMSToRec709RGB = {{
356     { 0x62c48,  0x1aadf4, 0x25a8,   0 },
357     { 0x1ead18, 0x28f64,  0x1fc390, 0 },
358     { 0x1ffd00, 0x1fbc34, 0x146c4,  0 },
359 }};
360 
361 /*
362  * This is a 3x4 matrix with S5.14 coefficients (truncated from S5.16
363  * SW-specified values).
364  */
365 static const struct NvKmsCscMatrix LMSToRec2020RGB = {{
366     { 0x36fc0,  0x1d7e54, 0x11e0,   0 },
367     { 0x1f3584, 0x1fbc8,  0x1fcebc, 0 },
368     { 0x1ff964, 0x1fe6a4, 0x11ff4,  0 },
369 }};
370 
371 /*
372  * The two arrays below specify the PQ OETF transfer function that's used to
373  * convert from linear LMS FP16 to PQ encoded L'M'S' fixed-point.
374  */
375 static const NvU32 OetfPQ512SegSizesLog2[] = {
376     0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3,
377     3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5,
378     5,
379 };
380 
381 static const NvU16 OetfPQ512Entries[] = {
382     0x0000, 0x000C, 0x0014, 0x001C, 0x0028, 0x003C, 0x005C, 0x008C, 0x00D0, 0x0134, 0x0184, 0x01C8, 0x0238, 0x029C, 0x033C, 0x03C4,
383     0x043C, 0x04A4, 0x0504, 0x0560, 0x0600, 0x0690, 0x0714, 0x078C, 0x07FC, 0x0864, 0x08C8, 0x0924, 0x0980, 0x09D4, 0x0A24, 0x0A70,
384     0x0B04, 0x0B90, 0x0C10, 0x0C88, 0x0CFC, 0x0D68, 0x0DD4, 0x0E38, 0x0EF4, 0x0FA4, 0x1048, 0x10E4, 0x1174, 0x1200, 0x1284, 0x1304,
385     0x13F4, 0x14D0, 0x159C, 0x165C, 0x1714, 0x17C0, 0x1864, 0x1900, 0x1A28, 0x1B34, 0x1C30, 0x1D1C, 0x1DFC, 0x1ECC, 0x1F94, 0x2050,
386     0x2104, 0x21B0, 0x2258, 0x22F8, 0x2390, 0x2424, 0x24B4, 0x2540, 0x25C4, 0x2648, 0x26C4, 0x2740, 0x27B8, 0x282C, 0x289C, 0x290C,
387     0x29E0, 0x2AAC, 0x2B70, 0x2C2C, 0x2CE0, 0x2D90, 0x2E38, 0x2ED8, 0x2F74, 0x300C, 0x30A0, 0x3130, 0x31BC, 0x3244, 0x32C8, 0x3348,
388     0x3440, 0x352C, 0x360C, 0x36E4, 0x37B4, 0x387C, 0x393C, 0x39F8, 0x3AA8, 0x3B58, 0x3C00, 0x3CA4, 0x3D44, 0x3DDC, 0x3E74, 0x3F04,
389     0x401C, 0x4128, 0x4228, 0x431C, 0x4408, 0x44E8, 0x45C4, 0x4694, 0x475C, 0x4820, 0x48DC, 0x4994, 0x4A48, 0x4AF4, 0x4B9C, 0x4C3C,
390     0x4D78, 0x4EA0, 0x4FBC, 0x50CC, 0x51D0, 0x52CC, 0x53BC, 0x54A0, 0x5580, 0x5658, 0x5728, 0x57F0, 0x58B4, 0x5974, 0x5A2C, 0x5ADC,
391     0x5C34, 0x5D7C, 0x5EB4, 0x5FDC, 0x60F4, 0x6204, 0x630C, 0x6404, 0x64F8, 0x65E0, 0x66C4, 0x679C, 0x6870, 0x693C, 0x6A04, 0x6AC4,
392     0x6C38, 0x6D94, 0x6EE4, 0x7020, 0x7150, 0x7274, 0x738C, 0x7498, 0x7598, 0x7694, 0x7784, 0x786C, 0x794C, 0x7A24, 0x7AF8, 0x7BC4,
393     0x7D50, 0x7EC4, 0x8024, 0x8174, 0x82B4, 0x83E8, 0x850C, 0x8628, 0x8738, 0x883C, 0x8938, 0x8A2C, 0x8B18, 0x8BFC, 0x8CD8, 0x8DB0,
394     0x8F4C, 0x90D0, 0x9240, 0x939C, 0x94EC, 0x962C, 0x975C, 0x9880, 0x999C, 0x9AAC, 0x9BB0, 0x9CAC, 0x9DA0, 0x9E8C, 0x9F70, 0xA04C,
395     0xA1F4, 0xA384, 0xA500, 0xA664, 0xA7BC, 0xA904, 0xAA3C, 0xAB6C, 0xAC8C, 0xADA0, 0xAEAC, 0xAFAC, 0xB0A4, 0xB194, 0xB27C, 0xB360,
396     0xB510, 0xB6A4, 0xB824, 0xB994, 0xBAF0, 0xBC3C, 0xBD78, 0xBEA8, 0xBFCC, 0xC0E4, 0xC1F0, 0xC2F4, 0xC3F0, 0xC4E4, 0xC5CC, 0xC6B0,
397     0xC78C, 0xC860, 0xC930, 0xC9F8, 0xCABC, 0xCB7C, 0xCC38, 0xCCEC, 0xCD9C, 0xCE48, 0xCEF0, 0xCF94, 0xD034, 0xD0D4, 0xD16C, 0xD200,
398     0xD294, 0xD324, 0xD3B4, 0xD43C, 0xD4C4, 0xD54C, 0xD5CC, 0xD650, 0xD6CC, 0xD748, 0xD7C4, 0xD83C, 0xD8B0, 0xD924, 0xD994, 0xDA08,
399     0xDAE0, 0xDBB4, 0xDC84, 0xDD4C, 0xDE10, 0xDECC, 0xDF84, 0xE038, 0xE0E8, 0xE194, 0xE238, 0xE2DC, 0xE37C, 0xE418, 0xE4B0, 0xE544,
400     0xE5D4, 0xE664, 0xE6F0, 0xE778, 0xE800, 0xE884, 0xE904, 0xE984, 0xEA00, 0xEA7C, 0xEAF4, 0xEB68, 0xEBDC, 0xEC50, 0xECC0, 0xED30,
401     0xEE08, 0xEED8, 0xEFA4, 0xF068, 0xF128, 0xF1E4, 0xF298, 0xF348, 0xF3F4, 0xF49C, 0xF540, 0xF5E0, 0xF67C, 0xF714, 0xF7A8, 0xF83C,
402     0xF8CC, 0xF958, 0xF9E0, 0xFA68, 0xFAEC, 0xFB6C, 0xFBE8, 0xFC64, 0xFCE0, 0xFD58, 0xFDCC, 0xFE40, 0xFEB4, 0xFF24, 0xFF90, 0xFFFC,
403 };
404 
405 /*
406  * The two arrays below specify the PQ EOTF transfer function that's used to
407  * convert from PQ encoded L'M'S' fixed-point to linear LMS FP16. This transfer
408  * function is the inverse of the OETF curve.
409  */
410 static const NvU32 EotfPQ512SegSizesLog2[] = {
411     6, 6, 4, 4, 4, 3, 4, 3, 3, 3, 2, 2, 2, 3, 3, 2,
412     2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
413     6, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 1, 2,
414     2, 1, 1, 2, 2, 2, 2, 1, 2, 1, 1, 2, 1, 4, 2, 2,
415 };
416 
417 static const NvU16 EotfPQ512Entries[] = {
418     0x0000, 0x0001, 0x0003, 0x0005, 0x0008, 0x000C, 0x0011, 0x0016, 0x001B, 0x0022, 0x0028, 0x002F, 0x0037, 0x003F, 0x0048, 0x0051,
419     0x005A, 0x0064, 0x006F, 0x007A, 0x0085, 0x0091, 0x009E, 0x00AB, 0x00B8, 0x00C6, 0x00D4, 0x00E3, 0x00F3, 0x0102, 0x0113, 0x0123,
420     0x0135, 0x0146, 0x0158, 0x016B, 0x017E, 0x0192, 0x01A6, 0x01BB, 0x01D0, 0x01E5, 0x01FC, 0x0212, 0x0229, 0x0241, 0x0259, 0x0272,
421     0x028B, 0x02A4, 0x02BE, 0x02D9, 0x02F4, 0x0310, 0x032C, 0x0349, 0x0366, 0x0384, 0x03A2, 0x03C1, 0x03E0, 0x0400, 0x0421, 0x0442,
422     0x0463, 0x0485, 0x04A8, 0x04CB, 0x04EF, 0x0513, 0x0538, 0x055D, 0x0583, 0x05AA, 0x05D1, 0x05F9, 0x0621, 0x064A, 0x0673, 0x069D,
423     0x06C7, 0x06F3, 0x071E, 0x074B, 0x0777, 0x07A5, 0x07D3, 0x0801, 0x0819, 0x0830, 0x0849, 0x0861, 0x087A, 0x0893, 0x08AD, 0x08C7,
424     0x08E1, 0x08FB, 0x0916, 0x0931, 0x094C, 0x0968, 0x0984, 0x09A0, 0x09BD, 0x09DA, 0x09F7, 0x0A15, 0x0A33, 0x0A51, 0x0A70, 0x0A8F,
425     0x0AAE, 0x0ACE, 0x0AEE, 0x0B0E, 0x0B2F, 0x0B50, 0x0B71, 0x0B93, 0x0BB5, 0x0BD7, 0x0BFA, 0x0C0F, 0x0C20, 0x0C32, 0x0C44, 0x0C56,
426     0x0C69, 0x0CB5, 0x0D03, 0x0D55, 0x0DA9, 0x0E01, 0x0E5B, 0x0EB9, 0x0F1B, 0x0F7F, 0x0FE7, 0x1029, 0x1061, 0x109A, 0x10D5, 0x1111,
427     0x1150, 0x1190, 0x11D3, 0x1217, 0x125E, 0x12A6, 0x12F0, 0x133D, 0x138B, 0x13DC, 0x1417, 0x1442, 0x146D, 0x149A, 0x14C8, 0x14F7,
428     0x1527, 0x1558, 0x158B, 0x15BF, 0x15F4, 0x162A, 0x1662, 0x169B, 0x16D5, 0x1711, 0x174E, 0x178C, 0x17CC, 0x1806, 0x1828, 0x184A,
429     0x186D, 0x18B4, 0x18FF, 0x194D, 0x199E, 0x19F3, 0x1A4B, 0x1AA7, 0x1B06, 0x1B37, 0x1B69, 0x1B9B, 0x1BCF, 0x1C02, 0x1C1D, 0x1C38,
430     0x1C54, 0x1C70, 0x1C8D, 0x1CAB, 0x1CC9, 0x1CE7, 0x1D06, 0x1D26, 0x1D46, 0x1D88, 0x1DCC, 0x1E13, 0x1E5C, 0x1EA8, 0x1EF6, 0x1F47,
431     0x1F9A, 0x1FF1, 0x2025, 0x2053, 0x2082, 0x20B3, 0x20E6, 0x211A, 0x214F, 0x2187, 0x21C0, 0x21FA, 0x2237, 0x2275, 0x22B5, 0x22F7,
432     0x233B, 0x23C9, 0x2430, 0x247F, 0x24D3, 0x252B, 0x2589, 0x25EB, 0x2653, 0x26C1, 0x2734, 0x27AD, 0x2817, 0x2838, 0x285A, 0x287C,
433     0x28A0, 0x28C5, 0x28EA, 0x2911, 0x2938, 0x2960, 0x298A, 0x29B4, 0x29DF, 0x2A0C, 0x2A39, 0x2A68, 0x2A98, 0x2AFA, 0x2B62, 0x2BCE,
434     0x2C20, 0x2C5B, 0x2C99, 0x2CDA, 0x2D1E, 0x2D65, 0x2DB0, 0x2DFD, 0x2E4E, 0x2EA3, 0x2EFC, 0x2F58, 0x2FB8, 0x300E, 0x3043, 0x307A,
435     0x30B3, 0x30D0, 0x30EE, 0x310D, 0x312C, 0x314C, 0x316D, 0x318E, 0x31B0, 0x31D3, 0x31F6, 0x321A, 0x323F, 0x3265, 0x328B, 0x32B2,
436     0x32DA, 0x332D, 0x3383, 0x33DC, 0x341D, 0x344D, 0x347F, 0x34B4, 0x34EA, 0x3523, 0x355E, 0x359B, 0x35DB, 0x361D, 0x3662, 0x36A9,
437     0x36F3, 0x3740, 0x3791, 0x37E4, 0x381D, 0x384A, 0x3879, 0x38A9, 0x38DB, 0x3910, 0x3946, 0x397E, 0x39B8, 0x39F5, 0x3A34, 0x3A75,
438     0x3AB9, 0x3AFF, 0x3B48, 0x3B94, 0x3BE2, 0x3C1A, 0x3C44, 0x3C70, 0x3C9D, 0x3CA0, 0x3CA3, 0x3CA6, 0x3CA9, 0x3CAC, 0x3CAF, 0x3CB1,
439     0x3CB4, 0x3CB7, 0x3CBA, 0x3CBD, 0x3CC0, 0x3CC3, 0x3CC6, 0x3CC9, 0x3CCC, 0x3CCF, 0x3CD2, 0x3CD5, 0x3CD8, 0x3CDB, 0x3CDE, 0x3CE1,
440     0x3CE4, 0x3CE7, 0x3CEA, 0x3CEE, 0x3CF1, 0x3CF4, 0x3CF7, 0x3CFA, 0x3CFD, 0x3D00, 0x3D03, 0x3D06, 0x3D09, 0x3D0D, 0x3D10, 0x3D13,
441     0x3D16, 0x3D19, 0x3D1C, 0x3D20, 0x3D23, 0x3D26, 0x3D29, 0x3D2C, 0x3D30, 0x3D33, 0x3D36, 0x3D39, 0x3D3D, 0x3D40, 0x3D43, 0x3D46,
442     0x3D4A, 0x3D4D, 0x3D50, 0x3D54, 0x3D57, 0x3D5A, 0x3D5D, 0x3D61, 0x3D64, 0x3D9B, 0x3DD3, 0x3E0D, 0x3E4A, 0x3E89, 0x3ECA, 0x3F0E,
443     0x3F54, 0x3F9C, 0x3FE8, 0x401B, 0x4043, 0x406D, 0x4099, 0x40C6, 0x40F4, 0x4124, 0x4156, 0x418A, 0x41C0, 0x41F8, 0x4232, 0x426D,
444     0x42AB, 0x42EB, 0x432E, 0x4373, 0x43BA, 0x4428, 0x4479, 0x44D0, 0x452D, 0x4591, 0x45FC, 0x466F, 0x46EB, 0x472C, 0x476F, 0x47B5,
445     0x47FE, 0x4824, 0x484B, 0x4874, 0x489D, 0x48F5, 0x4954, 0x4986, 0x49B9, 0x49EF, 0x4A26, 0x4A5F, 0x4A9B, 0x4AD9, 0x4B19, 0x4B9F,
446     0x4C18, 0x4C66, 0x4CBA, 0x4CE6, 0x4D13, 0x4D43, 0x4D74, 0x4DA7, 0x4DDC, 0x4E12, 0x4E4B, 0x4E86, 0x4EC3, 0x4F02, 0x4F44, 0x4F88,
447     0x4FCE, 0x500C, 0x5032, 0x5082, 0x50D8, 0x5106, 0x5135, 0x5166, 0x5199, 0x5205, 0x5278, 0x52F5, 0x537C, 0x53C3, 0x5406, 0x542D,
448     0x5454, 0x54A9, 0x5503, 0x550F, 0x551B, 0x5527, 0x5533, 0x5540, 0x554C, 0x5559, 0x5565, 0x5572, 0x557F, 0x558C, 0x5599, 0x55A7,
449     0x55B4, 0x55C1, 0x55CF, 0x5607, 0x5641, 0x567E, 0x56BC, 0x56FE, 0x5741, 0x5788, 0x57D1,
450 };
451 
452 #define TMO_LUT_NUM_SEGMENTS 64
453 #define TMO_LUT_SEG_SIZE_LOG2 4
454 #define TMO_LUT_NUM_ENTRIES 1024
455 
456 struct TmoLutSettings
457 {
458     NvU32 satMode;
459     NvU32 lowIntensityZoneEnd;
460     NvU32 lowIntensityValueLinWeight;
461     NvU32 lowIntensityValueNonLinWeight;
462     NvU32 lowIntensityValueThreshold;
463 
464     NvU32 medIntensityZoneStart;
465     NvU32 medIntensityZoneEnd;
466     NvU32 medIntensityValueLinWeight;
467     NvU32 medIntensityValueNonLinWeight;
468     NvU32 medIntensityValueThreshold;
469 
470     NvU32 highIntensityZoneStart;
471     NvU32 highIntensityValueLinWeight;
472     NvU32 highIntensityValueNonLinWeight;
473     NvU32 highIntensityValueThreshold;
474 };
475 
476 // Default Rec.2020 weights, no color correction.
477 static const struct TmoLutSettings TMO_LUT_SETTINGS_REC2020 = { 2, 1280, 256, 256, 255, 4960,  4961, 256, 256, 255, 10640, 256, 256, 255 };
478 
479 static void InitCsc0LUT(NVEvoChannelPtr pChannel,
480                         const NvU32 *pSegmentSizes, NvU32 numSegmentSizes,
481                         const NvU16 *pLUTEntries, NvU32 numEntries)
482 {
483     NvU32 i;
484 
485     for (i = 0; i < numSegmentSizes; i++) {
486         nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC0LUT_SEGMENT_SIZE, 1);
487         nvDmaSetEvoMethodData(pChannel,
488             DRF_NUM(C57E, _SET_CSC0LUT_SEGMENT_SIZE, _IDX, i) |
489             DRF_NUM(C57E, _SET_CSC0LUT_SEGMENT_SIZE, _VALUE, pSegmentSizes[i]));
490     }
491 
492     for (i = 0; i < numEntries; i++) {
493         nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC0LUT_ENTRY, 1);
494         nvDmaSetEvoMethodData(pChannel,
495             DRF_NUM(C57E, _SET_CSC0LUT_ENTRY, _IDX, i) |
496             DRF_NUM(C57E, _SET_CSC0LUT_ENTRY, _VALUE, pLUTEntries[i]));
497     }
498 }
499 
500 static void InitCsc1LUT(NVEvoChannelPtr pChannel,
501                         const NvU32 *pSegmentSizes, NvU32 numSegmentSizes,
502                         const NvU16 *pLUTEntries, NvU32 numEntries)
503 {
504     NvU32 i;
505 
506     for (i = 0; i < numSegmentSizes; i++) {
507         nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC1LUT_SEGMENT_SIZE, 1);
508         nvDmaSetEvoMethodData(pChannel,
509             DRF_NUM(C57E, _SET_CSC1LUT_SEGMENT_SIZE, _IDX, i) |
510             DRF_NUM(C57E, _SET_CSC1LUT_SEGMENT_SIZE, _VALUE, pSegmentSizes[i]));
511     }
512 
513     for (i = 0; i < numEntries; i++) {
514         nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC1LUT_ENTRY, 1);
515         nvDmaSetEvoMethodData(pChannel,
516             DRF_NUM(C57E, _SET_CSC1LUT_ENTRY, _IDX, i) |
517             DRF_NUM(C57E, _SET_CSC1LUT_ENTRY, _VALUE, pLUTEntries[i]));
518     }
519 }
520 
521 static void ConfigureCsc0C5(NVDevEvoPtr pDevEvo,
522                             NVEvoChannelPtr pChannel,
523                             enum NvKmsInputColorSpace colorspace,
524                             NvBool enable)
525 {
526     NVEvoWindowCaps *pWinCaps =
527         &pDevEvo->gpus[0].capabilities.window[pChannel->instance];
528     struct NvKmsCscMatrix matrix = { };
529     NvU32 lutData = 0;
530     NvU32 csc01Data = 0;
531 
532     if (!pWinCaps->csc0MatricesPresent) {
533         return;
534     }
535 
536     if (enable) {
537         if (colorspace == NVKMS_INPUT_COLORSPACE_BT2100_PQ) {
538             matrix = Rec2020RGBToLMS;
539         } else {
540             matrix = Rec709RGBToLMS;
541         }
542 
543         lutData |= DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _INTERPOLATE, _ENABLE) |
544                    DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _MIRROR, _DISABLE) |
545                    DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _ENABLE, _ENABLE);
546 
547         csc01Data |= DRF_DEF(C57E, _SET_CSC01CONTROL, _ENABLE, _ENABLE);
548     } else {
549         matrix = NVKMS_IDENTITY_CSC_MATRIX;
550 
551         lutData |= DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _INTERPOLATE, _DISABLE) |
552                    DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _MIRROR, _DISABLE) |
553                    DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _ENABLE, _DISABLE);
554 
555         csc01Data |= DRF_DEF(C57E, _SET_CSC01CONTROL, _ENABLE, _DISABLE);
556     }
557 
558     /* Linear RGB FP16 -> Linear LMS FP16 */
559     SetCsc00MatrixC5(pChannel, &matrix);
560 
561     /* Linear LMS FP16 -> PQ encoded L'M'S' fixed-point */
562     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC0LUT_CONTROL, 1);
563     nvDmaSetEvoMethodData(pChannel, lutData);
564 
565     /*
566      * PQ encoded L'M'S' fixed-point -> ICtCp
567      *
568      * Note that we're converting between fixed colorspaces, so the default HW
569      * coefficients are sufficient.
570      */
571     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC01CONTROL, 1);
572     nvDmaSetEvoMethodData(pChannel, csc01Data);
573 }
574 
575 static inline float64_t maxF(float64_t a, float64_t b)
576 {
577     return f64_lt(a, b) ? b : a;
578 }
579 
580 static inline float64_t clampF(float64_t value, float64_t min, float64_t max)
581 {
582     value = maxF(value, min);
583     value = f64_lt(max, value) ? max : value;
584     return value;
585 }
586 
587 static float64_t PQEotf(float64_t colorValue, NvBool inverse)
588 {
589     const float64_t zero  = {0x0000000000000000}; // 0.0
590     const float64_t one   = {0x3FF0000000000000}; // 1.0
591     const float64_t m1    = {0x3FC463FFFFFFB9A2}; // 0.1593017578125
592     const float64_t m2    = {0x4053B60000000000}; // 78.84375
593     const float64_t c1    = {0x3FEAC00000000000}; // 0.8359375
594     const float64_t c2    = {0x4032DA0000000000}; // 18.8515625
595     const float64_t c3    = {0x4032B00000000000}; // 18.6875
596 
597     const float64_t invm1 = {0x40191C0D56E72ABA}; // 1/m1 = 6.27739463602
598     const float64_t invm2 = {0x3F89F9B585D7C997}; // 1/m2 = 0.01268331351
599 
600     if (inverse) {
601         // Convert from linear to PQ-encoded values.
602         float64_t L = clampF(colorValue, zero, one);
603         float64_t powLm1 = nvKmsPow(L, m1);
604         float64_t N = nvKmsPow(f64_div(f64_add(c1,  f64_mul(c2, powLm1)),
605                                        f64_add(one, f64_mul(c3, powLm1))), m2);
606 
607         return clampF(N, zero, one);
608     } else {
609         // Convert from PQ-encoded values to linear values.
610         float64_t N = clampF(colorValue, zero, one);
611         float64_t powNinvM2 = nvKmsPow(N, invm2);
612         float64_t L = nvKmsPow(f64_div(maxF(f64_sub(powNinvM2, c1), zero),
613                                        f64_sub(c2, f64_mul(c3, powNinvM2))),
614                                invm1);
615 
616         return clampF(L, zero, one);
617     }
618 }
619 
620 // Hermite spline
621 static float64_t P(float64_t B, float64_t KS, float64_t maxLum)
622 {
623     const float64_t one    = {0x3FF0000000000000}; // 1.0
624     const float64_t two    = {0x4000000000000000}; // 2.0
625     const float64_t negtwo = {0xC000000000000000}; // -2.0
626     const float64_t three  = {0x4008000000000000}; // 3.0
627 
628     float64_t t  = f64_div(f64_sub(B, KS), f64_sub(one, KS));
629     float64_t t2 = f64_mul(t, t);
630     float64_t t3 = f64_mul(t2, t);
631 
632     return
633         f64_add(f64_add(
634             f64_mul(f64_add(f64_sub(f64_mul(two, t3), f64_mul(three, t2)), one), KS),
635             f64_mul(f64_add(f64_sub(t3, f64_mul(two, t2)), t), f64_sub(one, KS))),
636             f64_mul(f64_add(f64_mul(negtwo, t3), f64_mul(three, t2)), maxLum));
637 }
638 
639 /*
640  * PQ tone mapping operator with no remapping of blacks or "toe" section of
641  * curve. Messing with nonlinearity and remapping in the SDR portion of the
642  * curve results in bad looking PC desktop and game content.
643  *
644  * XXX HDR TODO: Remap blacks and implement toe section for video content?
645  */
646 static NvU16 TmoLutEntry(NvU32 i, NvU32 targetMaxLum, NvU32 srcMaxLum)
647 {
648     const float64_t zero         = {0x0000000000000000}; // 0.0
649     const float64_t half         = {0x3FE0000000000000}; // 0.5
650     const float64_t one          = {0x3FF0000000000000}; // 1.0
651     const float64_t oneHalf      = {0x3FF8000000000000}; // 1.5
652     const float64_t tenThousand  = {0x40C3880000000000}; // 10000.0
653     const float64_t maxIntensity = {0x40CFFF8000000000}; // 16383.0
654 
655     float64_t outputF;
656     float64_t inputF =
657         f64_div(ui32_to_f64(i), ui32_to_f64(TMO_LUT_NUM_ENTRIES - 1));
658 
659     float64_t targetMaxLumF = ui32_to_f64(targetMaxLum);
660     float64_t srcMaxLumF    = ui32_to_f64(srcMaxLum);
661 
662     // Normalize luminance to 10,000 nits
663     float64_t Lw   = f64_div(srcMaxLumF, tenThousand);
664     float64_t Lmax = f64_div(targetMaxLumF, tenThousand);
665 
666     float64_t maxLumRatio;
667     float64_t KS;
668     float64_t E1;
669     float64_t E2;
670 
671     nvAssert(srcMaxLum >= targetMaxLum);
672 
673     Lw   = PQEotf(Lw, TRUE);
674     Lmax = PQEotf(Lmax, TRUE);
675 
676     maxLumRatio = f64_div(Lmax, Lw);
677 
678     KS = f64_sub(f64_mul(oneHalf, maxLumRatio), half);
679 
680     E1 = f64_div(inputF, Lw);
681 
682     if (f64_lt(E1, KS) || f64_eq(KS, one)) {
683         E2 = E1;
684     } else {
685         E2 = P(E1, KS, maxLumRatio);
686     }
687 
688     outputF = clampF(f64_mul(E2, Lw), zero, Lmax);
689 
690     return (NvU16) f64_to_ui32(clampF(f64_mul(outputF, maxIntensity),
691                                       zero, maxIntensity),
692                                softfloat_round_near_even, FALSE) << 2;
693 }
694 
695 static void InitializeTmoLut(const NVEvoChannelPtr pChannel,
696                              NVLutSurfaceEvoPtr pLutSurfaceEvo,
697                              NvU32 sd)
698 {
699     NVEvoLutDataRec *pData = pLutSurfaceEvo->subDeviceAddress[sd];
700     NvU64 vssHead = 0;
701     NvU32 lutEntryCounter = 0, i;
702 
703     // VSS Header
704     for (lutEntryCounter = 0; lutEntryCounter < NV_LUT_VSS_HEADER_SIZE; lutEntryCounter++) {
705         vssHead = 0;
706         for (i = 0; ((i < 16) && (((lutEntryCounter * 16) + i) < TMO_LUT_NUM_SEGMENTS)); i++) {
707             NvU64 temp = TMO_LUT_SEG_SIZE_LOG2;
708             vssHead |= temp << (i * 3);
709         }
710         nvkms_memcpy(&(pData->base[lutEntryCounter]), &vssHead, sizeof(NVEvoLutEntryRec));
711     }
712 
713     for (i = 0; i < TMO_LUT_NUM_ENTRIES; i++) {
714         pData->base[i + NV_LUT_VSS_HEADER_SIZE].Red =
715         pData->base[i + NV_LUT_VSS_HEADER_SIZE].Green =
716         pData->base[i + NV_LUT_VSS_HEADER_SIZE].Blue =
717             TmoLutEntry(i,
718                         pChannel->tmoParams.targetMaxLums[sd],
719                         pChannel->tmoParams.srcMaxLum);
720     }
721 
722     // Copy the last entry for interpolation
723     pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE].Red =
724         pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE - 1].Red;
725     pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE].Blue =
726         pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE - 1].Blue;
727     pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE].Green =
728         pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE - 1].Green;
729 }
730 
731 static NvBool UpdateTmoParams(NVEvoChannelPtr pChannel,
732                               NvBool enabled,
733                               NvU32 srcMaxLum,
734                               const NvU32 targetMaxLums[NVKMS_MAX_SUBDEVICES])
735 {
736     NvU16 sd;
737     NvBool dirty = FALSE;
738 
739     if (pChannel->tmoParams.enabled != enabled) {
740         pChannel->tmoParams.enabled = enabled;
741         dirty = TRUE;
742     }
743 
744     if (pChannel->tmoParams.srcMaxLum != srcMaxLum) {
745         pChannel->tmoParams.srcMaxLum = srcMaxLum;
746         dirty = TRUE;
747     }
748 
749     for (sd = 0; sd < NVKMS_MAX_SUBDEVICES; sd++) {
750         if (pChannel->tmoParams.targetMaxLums[sd] != targetMaxLums[sd]) {
751             pChannel->tmoParams.targetMaxLums[sd] = targetMaxLums[sd];
752             dirty = TRUE;
753         }
754     }
755 
756     return dirty;
757 }
758 
759 static void ConfigureTmoLut(NVDevEvoPtr pDevEvo,
760                             const NVFlipChannelEvoHwState *pHwState,
761                             NVEvoChannelPtr pChannel)
762 {
763     NvU16 sd;
764     const NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
765     const NvU32 head = pDevEvo->headForWindow[win];
766     NvU32 ctxDma;
767     const NvU32 offset = offsetof(NVEvoLutDataRec, base);
768     const struct TmoLutSettings *tmoLutSettings;
769     const NvU32 srcMaxLum = nvGetHDRSrcMaxLum(pHwState);
770 
771     NvBool needsTmoLut = FALSE;
772     NvU32 targetMaxLums[NVKMS_MAX_SUBDEVICES] = {0};
773     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
774         const NVDispHeadStateEvoRec *pHeadState =
775             &pDevEvo->pDispEvo[sd]->headState[head];
776 
777         targetMaxLums[sd] = pHeadState->hdr.staticMetadata.maxCLL;
778 
779         // If any head needs tone mapping, enable TMO for channel
780         if (nvNeedsTmoLut(pDevEvo, pChannel, pHwState,
781                           srcMaxLum, targetMaxLums[sd])) {
782             needsTmoLut = TRUE;
783         }
784     }
785 
786     if (!UpdateTmoParams(pChannel, needsTmoLut, srcMaxLum, targetMaxLums)) {
787         // No change in parameters, no need to reconfigure.
788         return;
789     }
790 
791     if (!pChannel->tmoParams.enabled) {
792         nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CONTEXT_DMA_TMO_LUT, 1);
793         nvDmaSetEvoMethodData(pChannel,
794             DRF_NUM(C57E, _SET_CONTEXT_DMA_TMO_LUT, _HANDLE, 0));
795 
796         return;
797     }
798 
799     // Initialize TMO LUT on all subdevices
800     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
801         nvAssert(pHwState->tmoLut.pLutSurfaceEvo != NULL);
802         InitializeTmoLut(pChannel, pHwState->tmoLut.pLutSurfaceEvo, sd);
803     }
804 
805     // Program TMO LUT
806 
807     // XXX HDR TODO: Support other transfer functions
808     tmoLutSettings = &TMO_LUT_SETTINGS_REC2020;
809 
810     ctxDma = pHwState->tmoLut.pLutSurfaceEvo->dispCtxDma;
811 
812     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_CONTROL, 1);
813     nvDmaSetEvoMethodData(pChannel,
814         DRF_NUM(C57E, _SET_TMO_CONTROL, _SIZE,
815                 NV_LUT_VSS_HEADER_SIZE + TMO_LUT_NUM_ENTRIES + 1) |
816         DRF_DEF(C57E, _SET_TMO_CONTROL, _INTERPOLATE, _ENABLE)    |
817         DRF_NUM(C57E, _SET_TMO_CONTROL, _SAT_MODE, tmoLutSettings->satMode));
818 
819     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_OFFSET_TMO_LUT, 1);
820     nvDmaSetEvoMethodData(pChannel,
821         DRF_NUM(C57E, _SET_OFFSET_TMO_LUT, _ORIGIN, offset >> 8));
822 
823     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CONTEXT_DMA_TMO_LUT, 1);
824     nvDmaSetEvoMethodData(pChannel,
825         DRF_NUM(C57E, _SET_CONTEXT_DMA_TMO_LUT, _HANDLE, ctxDma));
826 
827 
828     // Low Intensity
829 
830     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_LOW_INTENSITY_ZONE, 1);
831     nvDmaSetEvoMethodData(pChannel,
832         DRF_NUM(C57E, _SET_TMO_LOW_INTENSITY_ZONE, _END,
833                 tmoLutSettings->lowIntensityZoneEnd));
834 
835     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_LOW_INTENSITY_VALUE, 1);
836     nvDmaSetEvoMethodData(pChannel,
837         DRF_NUM(C57E, _SET_TMO_LOW_INTENSITY_VALUE, _LIN_WEIGHT,
838                 tmoLutSettings->lowIntensityValueLinWeight)           |
839         DRF_NUM(C57E, _SET_TMO_LOW_INTENSITY_VALUE, _NON_LIN_WEIGHT,
840                 tmoLutSettings->lowIntensityValueNonLinWeight)        |
841         DRF_NUM(C57E, _SET_TMO_LOW_INTENSITY_VALUE, _THRESHOLD,
842                 tmoLutSettings->lowIntensityValueThreshold));
843 
844     // Medium Intensity
845 
846     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_MEDIUM_INTENSITY_ZONE, 1);
847     nvDmaSetEvoMethodData(pChannel,
848         DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_ZONE, _START,
849                 tmoLutSettings->medIntensityZoneStart)         |
850         DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_ZONE, _END,
851                 tmoLutSettings->medIntensityZoneEnd));
852 
853     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_MEDIUM_INTENSITY_VALUE, 1);
854     nvDmaSetEvoMethodData(pChannel,
855         DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_VALUE, _LIN_WEIGHT,
856                 tmoLutSettings->medIntensityValueLinWeight)              |
857         DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_VALUE, _NON_LIN_WEIGHT,
858                 tmoLutSettings->medIntensityValueNonLinWeight)           |
859         DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_VALUE, _THRESHOLD,
860                 tmoLutSettings->medIntensityValueThreshold));
861 
862     // High Intensity
863 
864     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_HIGH_INTENSITY_ZONE, 1);
865     nvDmaSetEvoMethodData(pChannel,
866         DRF_NUM(C57E, _SET_TMO_HIGH_INTENSITY_ZONE, _START,
867                 tmoLutSettings->highIntensityZoneStart));
868 
869     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_HIGH_INTENSITY_VALUE, 1);
870     nvDmaSetEvoMethodData(pChannel,
871         DRF_NUM(C57E, _SET_TMO_HIGH_INTENSITY_VALUE, _LIN_WEIGHT,
872                 tmoLutSettings->highIntensityValueLinWeight)           |
873         DRF_NUM(C57E, _SET_TMO_HIGH_INTENSITY_VALUE, _NON_LIN_WEIGHT,
874                 tmoLutSettings->highIntensityValueNonLinWeight)        |
875         DRF_NUM(C57E, _SET_TMO_HIGH_INTENSITY_VALUE, _THRESHOLD,
876                 tmoLutSettings->highIntensityValueThreshold));
877 }
878 
879 static void ConfigureCsc1C5(NVDevEvoPtr pDevEvo,
880                             NVEvoChannelPtr pChannel,
881                             NvBool enable)
882 {
883     NVEvoWindowCaps *pWinCaps =
884         &pDevEvo->gpus[0].capabilities.window[pChannel->instance];
885     struct NvKmsCscMatrix matrix = { };
886     NvU32 lutData = 0;
887     NvU32 csc10Data = 0;
888     const NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
889     const NvU32 head = pDevEvo->headForWindow[win];
890 
891     if (!pWinCaps->csc1MatricesPresent || (head == NV_INVALID_HEAD)) {
892         return;
893     }
894 
895     if (enable) {
896         const NvU32 sdMask = nvPeekEvoSubDevMask(pDevEvo);
897         const NvU32 sd = (sdMask == 0) ? 0 : __builtin_ffs(sdMask) - 1;
898         const NVDispHeadStateEvoRec *pHeadState;
899 
900         /*
901          * All callers of this path should push a single sd on the stack,
902          * so that ffs(sdMask) is safe.
903          */
904         nvAssert(nvPopCount32(sdMask) == 1);
905 
906         pHeadState = &pDevEvo->pDispEvo[sd]->headState[head];
907 
908         if (pHeadState->hdr.outputState == NVKMS_HDR_OUTPUT_STATE_HDR) {
909             // XXX HDR TODO: Support other transfer functions
910             nvAssert(pHeadState->tf == NVKMS_OUTPUT_TF_PQ);
911             matrix = LMSToRec2020RGB;
912         } else {
913             matrix = LMSToRec709RGB;
914         }
915 
916         lutData |= DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _INTERPOLATE, _ENABLE) |
917                    DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _MIRROR, _DISABLE) |
918                    DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _ENABLE, _ENABLE);
919 
920         csc10Data |= DRF_DEF(C57E, _SET_CSC10CONTROL, _ENABLE, _ENABLE);
921     } else {
922         matrix = NVKMS_IDENTITY_CSC_MATRIX;
923 
924         lutData |= DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _INTERPOLATE, _DISABLE) |
925                    DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _MIRROR, _DISABLE) |
926                    DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _ENABLE, _DISABLE);
927 
928         csc10Data |= DRF_DEF(C57E, _SET_CSC10CONTROL, _ENABLE, _DISABLE);
929     }
930 
931     /*
932      * ICtCp -> PQ encoded L'M'S' fixed-point
933      *
934      * Note that we're converting between fixed colorspaces, so the default HW
935      * coefficients are sufficient.
936      */
937     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC10CONTROL, 1);
938     nvDmaSetEvoMethodData(pChannel, csc10Data);
939 
940     /* PQ encoded L'M'S' fixed-point -> Linear LMS FP16 */
941     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC1LUT_CONTROL, 1);
942     nvDmaSetEvoMethodData(pChannel, lutData);
943 
944     /* Linear LMS FP16 -> Linear RGB FP16 */
945     SetCsc11MatrixC5(pChannel, &matrix);
946 }
947 
948 static void InitDesktopColorC3(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
949 {
950     NvU32 head;
951 
952     for (head = 0; head < pDevEvo->numHeads; head++) {
953         nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DESKTOP_COLOR(head), 1);
954         nvDmaSetEvoMethodData(pChannel,
955             DRF_NUM(C37D, _HEAD_SET_DESKTOP_COLOR, _RED,   0) |
956             DRF_NUM(C37D, _HEAD_SET_DESKTOP_COLOR, _GREEN, 0) |
957             DRF_NUM(C37D, _HEAD_SET_DESKTOP_COLOR, _BLUE,  0) |
958             DRF_NUM(C37D, _HEAD_SET_DESKTOP_COLOR, _ALPHA, 255));
959     }
960 }
961 
962 static void InitDesktopColorC5(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
963 {
964     NvU32 head;
965 
966     for (head = 0; head < pDevEvo->numHeads; head++) {
967         nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DESKTOP_COLOR_ALPHA_RED(head), 1);
968         nvDmaSetEvoMethodData(pChannel,
969             DRF_NUM(C57D, _HEAD_SET_DESKTOP_COLOR_ALPHA_RED, _ALPHA, 255) |
970             DRF_NUM(C57D, _HEAD_SET_DESKTOP_COLOR_ALPHA_RED, _RED, 0));
971 
972         nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DESKTOP_COLOR_GREEN_BLUE(head), 1);
973         nvDmaSetEvoMethodData(pChannel,
974             DRF_NUM(C57D, _HEAD_SET_DESKTOP_COLOR_GREEN_BLUE, _GREEN, 0) |
975             DRF_NUM(C57D, _HEAD_SET_DESKTOP_COLOR_GREEN_BLUE, _BLUE, 0));
976     }
977 }
978 
979 static void EvoInitChannel3(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
980 {
981     InitChannelCapsC3(pDevEvo, pChannel);
982 }
983 
984 static void EvoInitChannelC3(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
985 {
986     const NvBool isCore =
987             FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
988                            pChannel->channelMask);
989 
990     EvoInitChannel3(pDevEvo, pChannel);
991 
992     if (isCore) {
993         InitDesktopColorC3(pDevEvo, pChannel);
994     }
995 }
996 
997 static void EvoInitChannelC5(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
998 {
999     const NvBool isCore =
1000             FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
1001                            pChannel->channelMask);
1002     const NvBool isWindow =
1003         ((pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL) != 0);
1004 
1005     EvoInitChannel3(pDevEvo, pChannel);
1006 
1007     if (isCore) {
1008         InitTaps5ScalerCoefficientsC5(pDevEvo, pChannel, FALSE);
1009         InitDesktopColorC5(pDevEvo, pChannel);
1010     } else if (isWindow) {
1011         NVEvoWindowCaps *pWinCaps =
1012             &pDevEvo->gpus[0].capabilities.window[pChannel->instance];
1013         NvU32 csc0SizesLen = ARRAY_LEN(OetfPQ512SegSizesLog2);
1014         NvU32 csc0EntriesLen = ARRAY_LEN(OetfPQ512Entries);
1015         NvU32 csc1SizesLen = ARRAY_LEN(EotfPQ512SegSizesLog2);
1016         NvU32 csc1EntriesLen = ARRAY_LEN(EotfPQ512Entries);
1017 
1018         InitTaps5ScalerCoefficientsC5(pDevEvo, pChannel, TRUE);
1019 
1020         if (pWinCaps->cscLUTsPresent) {
1021             InitCsc0LUT(pChannel,
1022                         OetfPQ512SegSizesLog2, csc0SizesLen,
1023                         OetfPQ512Entries, csc0EntriesLen);
1024             InitCsc1LUT(pChannel,
1025                         EotfPQ512SegSizesLog2, csc1SizesLen,
1026                         EotfPQ512Entries, csc1EntriesLen);
1027         }
1028     }
1029 }
1030 
1031 static const NvU32 IdentityFMTMatrix[12] = {
1032     0x00010000, 0x00000000, 0x00000000, 0x00000000,
1033     0x00000000, 0x00010000, 0x00000000, 0x00000000,
1034     0x00000000, 0x00000000, 0x00010000, 0x00000000
1035 };
1036 
1037 /*
1038  * TODO: The full set of FMT matrices needs to be generated for each RGB and YUV
1039  * encoding. For now, I'm using the matrix below to convert all YUV input
1040  * formats to pipe native.
1041  */
1042 static const NvU32 YCbCrRec709_8bpcFMTMatrix[12] = {
1043     0x0001ccb7, 0x00012b3c, 0x00000000, 0x001f06f1,
1044     0x001f770c, 0x00012b3c, 0x001fc933, 0x00004d2d,
1045     0x00000000, 0x00012b3c, 0x00021edd, 0x001eddde
1046 };
1047 
1048 static const NvU32* EvoGetFMTMatrixC5(
1049     const enum NvKmsSurfaceMemoryFormat format)
1050 {
1051     const NvKmsSurfaceMemoryFormatInfo *pFormatInfo =
1052         nvKmsGetSurfaceMemoryFormatInfo(format);
1053 
1054     if (pFormatInfo->isYUV) {
1055         return YCbCrRec709_8bpcFMTMatrix;
1056     } else {
1057         return IdentityFMTMatrix;
1058     }
1059 }
1060 
1061 static void EvoSetFMTMatrixC5(
1062     NVEvoChannelPtr pChannel, const enum NvKmsSurfaceMemoryFormat format)
1063 {
1064     const NvU32 *matrix = EvoGetFMTMatrixC5(format);
1065     NvU32 method = NVC57E_SET_FMT_COEFFICIENT_C00;
1066     int i;
1067 
1068     for (i = 0; i < 12; i++) {
1069         nvDmaSetStartEvoMethod(pChannel, method, 1);
1070         nvDmaSetEvoMethodData(pChannel, matrix[i]);
1071 
1072         method += 4;
1073     }
1074 }
1075 
1076 static void EvoInitDefaultLutC5(NVDevEvoPtr pDevEvo)
1077 {
1078     NVLutSurfaceEvoPtr pLut = pDevEvo->lut.defaultLut;
1079     NvU16 sd;
1080 
1081     nvAssert(pLut);
1082 
1083     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
1084         NvU32 lutSize;
1085         NvBool isLutModeVss;
1086         NVEvoLutDataRec *pData = pLut->subDeviceAddress[sd];
1087 
1088         EvoSetupIdentityBaseLutC5(pData,
1089                                   &pDevEvo->lut.defaultBaseLUTState[sd],
1090                                   &lutSize, &isLutModeVss);
1091 
1092         EvoSetupIdentityOutputLutC5(pData,
1093                                     &pDevEvo->lut.defaultOutputLUTState[sd],
1094                                     &lutSize, &isLutModeVss);
1095     }
1096 }
1097 
1098 static void EvoInitWindowMapping3(NVDevEvoPtr pDevEvo,
1099                                   NVEvoModesetUpdateState *pModesetUpdateState)
1100 {
1101     NVEvoUpdateState *updateState = &pModesetUpdateState->updateState;
1102     NVEvoChannelPtr pChannel = pDevEvo->core;
1103     NvU32 win, sd;
1104 
1105     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
1106 
1107     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
1108 
1109     /* Bind each window to a head.  On GV100, there is a fixed mapping. */
1110     for (win = 0; win < pDevEvo->numWindows; win++) {
1111         NvU32 head = pDevEvo->headForWindow[win];
1112 
1113         nvDmaSetStartEvoMethod(pChannel, NVC37D_WINDOW_SET_CONTROL(win), 1);
1114         if ((head == NV_INVALID_HEAD) || (head >= pDevEvo->numHeads)) {
1115             nvDmaSetEvoMethodData(pChannel,
1116                                   DRF_NUM(C37D, _WINDOW_SET_CONTROL, _OWNER,
1117                                           NVC37D_WINDOW_SET_CONTROL_OWNER_NONE));
1118         } else {
1119             nvDmaSetEvoMethodData(pChannel, DRF_NUM(C37D, _WINDOW_SET_CONTROL, _OWNER, head));
1120         }
1121     }
1122 
1123     pModesetUpdateState->windowMappingChanged = FALSE;
1124 
1125     for (sd = 0;  sd < pDevEvo->numSubDevices; sd++) {
1126         void *pCoreDma = pDevEvo->pSubDevices[sd]->pCoreDma;
1127         /*
1128          * Short timeout (100ms) because we don't expect display to be very
1129          * busy at this point (it should at most be processing methods from
1130          * InitChannel()).
1131          */
1132         const NvU32 timeout = 100000;
1133         NvU64 startTime = 0;
1134 
1135         if (!((nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)))) {
1136             continue;
1137         }
1138 
1139         /* This core channel must be idle before reading state cache */
1140         do {
1141             NvBool isIdle = NV_FALSE;
1142             if (!EvoIsChannelIdleC3(pDevEvo, pChannel, sd, &isIdle)) {
1143                 nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR, "EvoIsChannelIdleC3() failed!");
1144             }
1145             if (isIdle) {
1146                 break;
1147             }
1148             if (nvExceedsTimeoutUSec(&startTime, timeout)) {
1149                 nvEvoLogDev(pDevEvo, EVO_LOG_ERROR,
1150                             "Timed out waiting for core channel idle.");
1151                 break;
1152             }
1153         } while (TRUE);
1154 
1155         for (win = 0; win < pDevEvo->numWindows; win++) {
1156             NvU32 data = nvDmaLoadPioMethod(pCoreDma, NVC37D_WINDOW_SET_CONTROL(win));
1157 
1158             if (DRF_VAL(C37D,
1159                         _WINDOW_SET_CONTROL, _OWNER, data) !=
1160                 pDevEvo->headForWindow[win]) {
1161 
1162                 pModesetUpdateState->windowMappingChanged = TRUE;
1163 
1164                 nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
1165                 nvDisableCoreInterlockUpdateState(pDevEvo,
1166                                                   updateState,
1167                                                   pDevEvo->window[win]);
1168                 nvPopEvoSubDevMask(pDevEvo);
1169             }
1170         }
1171     }
1172 }
1173 
1174 static void EvoInitWindowMappingC3(const NVDispEvoRec *pDispEvo,
1175                                    NVEvoModesetUpdateState *pModesetUpdateState)
1176 {
1177     NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
1178     NVEvoUpdateState *updateState = &pModesetUpdateState->updateState;
1179     NVEvoChannelPtr pChannel = pDevEvo->core;
1180     NvU32 win;
1181 
1182     nvPushEvoSubDevMaskDisp(pDispEvo);
1183 
1184     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
1185 
1186     EvoInitWindowMapping3(pDevEvo,
1187                           pModesetUpdateState);
1188 
1189     // Set window usage bounds
1190     for (win = 0; win < pDevEvo->numWindows; win++) {
1191         nvDmaSetStartEvoMethod(pChannel, NVC37D_WINDOW_SET_WINDOW_USAGE_BOUNDS(win), 1);
1192         /* XXXnvdisplay: window scaling */
1193         nvDmaSetEvoMethodData(pChannel, NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C3);
1194     }
1195     nvPopEvoSubDevMask(pDevEvo);
1196 }
1197 
1198 static void EvoInitWindowMappingC5(const NVDispEvoRec *pDispEvo,
1199                                    NVEvoModesetUpdateState *pModesetUpdateState)
1200 {
1201     NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
1202     NVEvoUpdateState *updateState = &pModesetUpdateState->updateState;
1203     NVEvoChannelPtr pChannel = pDevEvo->core;
1204     NvU32 win;
1205 
1206     nvPushEvoSubDevMaskDisp(pDispEvo);
1207 
1208     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
1209 
1210     EvoInitWindowMapping3(pDevEvo,
1211                           pModesetUpdateState);
1212 
1213     // Set window usage bounds
1214     for (win = 0; win < pDevEvo->numWindows; win++) {
1215         NvU32 bounds = NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C5;
1216 
1217         bounds |=
1218             DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_2) |
1219             DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE);
1220 
1221         nvDmaSetStartEvoMethod(pChannel, NVC57D_WINDOW_SET_WINDOW_USAGE_BOUNDS(win), 1);
1222         nvDmaSetEvoMethodData(pChannel, bounds);
1223     }
1224     nvPopEvoSubDevMask(pDevEvo);
1225 }
1226 
1227 static NvBool ComputeMinFrameIdle(
1228     const NVHwModeTimingsEvo *pTimings,
1229     NvU16 *pLeadingRasterLines,
1230     NvU16 *pTrailingRasterLines)
1231 {
1232     const NVHwModeViewPortEvo *pViewPort = &pTimings->viewPort;
1233 
1234     /*
1235      * leadingRasterLines defines the number of lines between the start of the
1236      * frame (vsync) and the start of the active region.  This includes Vsync,
1237      * Vertical Back Porch, and the top part of the overscan border.  The
1238      * minimum value is 2 because vsync and VBP must be at least 1 line each.
1239      *
1240      * trailingRasterLines defines the number of lines between the end of the
1241      * active region and the end of the frame.  This includes the bottom part
1242      * of the overscan border and the Vertical Front Porch.
1243      */
1244     const NvU32 activeHeight = (pTimings->rasterBlankStart.y -
1245                                 pTimings->rasterBlankEnd.y);
1246     /* This is how it's done in dispClassNVD20CoreUpdateErrorChecks_hls.c */
1247     const NvU32 overscan = (activeHeight / 2) - (pViewPort->out.height / 2);
1248 
1249     /*
1250      * The +1 is justified by this comment in the error check:
1251      *
1252      * If the value is 1, that means there are 2 lines of vblank (lines 0 and
1253      * 1) before active.  That is why the uLeadingBorder equation needs +1;
1254      */
1255     const NvU32 leadingRasterLines =
1256         pTimings->rasterBlankEnd.y + overscan + pViewPort->out.yAdjust + 1;
1257     const NvU32 trailingRasterLines =
1258         pTimings->rasterSize.y - (leadingRasterLines + pViewPort->out.height);
1259 
1260     /* nvdClass_01.mfs says: "The minimum value is 2 because vsync and VBP must
1261      * be at least 1 line each." */
1262     if (leadingRasterLines < 2) {
1263         return FALSE;
1264     }
1265 
1266     *pLeadingRasterLines = leadingRasterLines;
1267     *pTrailingRasterLines = trailingRasterLines;
1268 
1269     return TRUE;
1270 }
1271 
1272 static void EvoSetRasterParams3(NVDevEvoPtr pDevEvo, int head,
1273                                 const NVHwModeTimingsEvo *pTimings,
1274                                 const NVEvoColorRec *pOverscanColor,
1275                                 NVEvoUpdateState *updateState)
1276 {
1277     NVEvoChannelPtr pChannel = pDevEvo->core;
1278     /* XXXnvdisplay: Convert these for YCbCr, as necessary */
1279     NvU32 overscanColor =
1280         DRF_NUM(C37D, _HEAD_SET_OVERSCAN_COLOR, _RED_CR, pOverscanColor->red) |
1281         DRF_NUM(C37D, _HEAD_SET_OVERSCAN_COLOR, _GREEN_Y, pOverscanColor->green) |
1282         DRF_NUM(C37D, _HEAD_SET_OVERSCAN_COLOR, _BLUE_CB, pOverscanColor->blue);
1283     NvU32 hdmiStereoCtrl;
1284     NvU16 minFrameIdleLeadingRasterLines, minFrameIdleTrailingRasterLines;
1285     NvBool ret;
1286 
1287     /* These methods should only apply to a single pDpy */
1288     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
1289 
1290     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
1291 
1292     // XXX[AGP]: These methods are sequential and could use an incrementing
1293     // method, but it's not clear if there's a bug in EVO that causes corruption
1294     // sometimes.  Play it safe and send methods with count=1.
1295 
1296     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_OVERSCAN_COLOR(head), 1);
1297     nvDmaSetEvoMethodData(pChannel, overscanColor);
1298 
1299     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_RASTER_SIZE(head), 1);
1300     nvDmaSetEvoMethodData(pChannel,
1301         DRF_NUM(C37D, _HEAD_SET_RASTER_SIZE, _WIDTH, pTimings->rasterSize.x) |
1302         DRF_NUM(C37D, _HEAD_SET_RASTER_SIZE, _HEIGHT, pTimings->rasterSize.y));
1303 
1304     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_RASTER_SYNC_END(head), 1);
1305     nvDmaSetEvoMethodData(pChannel,
1306         DRF_NUM(C37D, _HEAD_SET_RASTER_SYNC_END, _X, pTimings->rasterSyncEnd.x) |
1307         DRF_NUM(C37D, _HEAD_SET_RASTER_SYNC_END, _Y, pTimings->rasterSyncEnd.y));
1308 
1309     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_RASTER_BLANK_END(head), 1);
1310     nvDmaSetEvoMethodData(pChannel,
1311         DRF_NUM(C37D, _HEAD_SET_RASTER_BLANK_END, _X, pTimings->rasterBlankEnd.x) |
1312         DRF_NUM(C37D, _HEAD_SET_RASTER_BLANK_END, _Y, pTimings->rasterBlankEnd.y));
1313 
1314     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_RASTER_BLANK_START(head), 1);
1315     nvDmaSetEvoMethodData(pChannel,
1316         DRF_NUM(C37D, _HEAD_SET_RASTER_BLANK_START, _X, pTimings->rasterBlankStart.x) |
1317         DRF_NUM(C37D, _HEAD_SET_RASTER_BLANK_START, _Y, pTimings->rasterBlankStart.y));
1318 
1319     ret = ComputeMinFrameIdle(pTimings,
1320                               &minFrameIdleLeadingRasterLines,
1321                               &minFrameIdleTrailingRasterLines);
1322     if (!ret) {
1323         /* This should have been ensured by IMP in AssignPerHeadImpParams. */
1324         nvAssert(ret);
1325         /* In case a mode validation override was used to skip IMP, program the
1326          * default values.  This may still cause a hardware exception. */
1327         minFrameIdleLeadingRasterLines = 2;
1328         minFrameIdleTrailingRasterLines = 1;
1329     }
1330 
1331     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_MIN_FRAME_IDLE(head), 1);
1332     nvDmaSetEvoMethodData(pChannel,
1333         DRF_NUM(C37D, _HEAD_SET_MIN_FRAME_IDLE, _LEADING_RASTER_LINES,
1334                 minFrameIdleLeadingRasterLines) |
1335         DRF_NUM(C37D, _HEAD_SET_MIN_FRAME_IDLE, _TRAILING_RASTER_LINES,
1336                 minFrameIdleTrailingRasterLines));
1337 
1338     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PIXEL_CLOCK_FREQUENCY(head), 1);
1339     nvDmaSetEvoMethodData(pChannel,
1340         DRF_NUM(C37D, _HEAD_SET_PIXEL_CLOCK_FREQUENCY, _HERTZ,
1341                 pTimings->pixelClock * 1000) |
1342         DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_FREQUENCY, _ADJ1000DIV1001,_FALSE));
1343 
1344     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PIXEL_CLOCK_CONFIGURATION(head), 1);
1345     nvDmaSetEvoMethodData(pChannel,
1346         DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_CONFIGURATION, _NOT_DRIVER, _FALSE) |
1347         DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_CONFIGURATION, _HOPPING, _DISABLE) |
1348         DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_CONFIGURATION, _HOPPING_MODE, _VBLANK));
1349 
1350     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX(head), 1);
1351     nvDmaSetEvoMethodData(pChannel,
1352         DRF_NUM(C37D, _HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, _HERTZ,
1353                 pTimings->pixelClock * 1000) |
1354         DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, _ADJ1000DIV1001,_FALSE));
1355 
1356     nvDmaSetStartEvoMethod(pChannel,
1357         NVC37D_HEAD_SET_FRAME_PACKED_VACTIVE_COLOR(head), 1);
1358     nvDmaSetEvoMethodData(pChannel,
1359         DRF_NUM(C37D, _HEAD_SET_FRAME_PACKED_VACTIVE_COLOR, _RED_CR, 0) |
1360 #if defined(DEBUG)
1361         DRF_NUM(C37D, _HEAD_SET_FRAME_PACKED_VACTIVE_COLOR, _GREEN_Y,  512) |
1362 #else
1363         DRF_NUM(C37D, _HEAD_SET_FRAME_PACKED_VACTIVE_COLOR, _GREEN_Y,  0) |
1364 #endif
1365         DRF_NUM(C37D, _HEAD_SET_FRAME_PACKED_VACTIVE_COLOR, _BLUE_CB, 0));
1366 
1367     hdmiStereoCtrl = DRF_NUM(C37D, _HEAD_SET_HDMI_CTRL, _HDMI_VIC, 0);
1368     if (pTimings->hdmi3D) {
1369         hdmiStereoCtrl =
1370             FLD_SET_DRF(C37D, _HEAD_SET_HDMI_CTRL, _VIDEO_FORMAT, _STEREO3D, hdmiStereoCtrl);
1371     } else {
1372         hdmiStereoCtrl =
1373             FLD_SET_DRF(C37D, _HEAD_SET_HDMI_CTRL, _VIDEO_FORMAT, _NORMAL, hdmiStereoCtrl);
1374     }
1375     nvDmaSetStartEvoMethod(pChannel,
1376         NVC37D_HEAD_SET_HDMI_CTRL(head), 1);
1377     nvDmaSetEvoMethodData(pChannel, hdmiStereoCtrl);
1378 }
1379 
1380 static void EvoSetRasterParamsC3(NVDevEvoPtr pDevEvo, int head,
1381                                  const NVHwModeTimingsEvo *pTimings,
1382                                  const NvU8 tilePosition,
1383                                  const NVDscInfoEvoRec *pDscInfo,
1384                                  const NVEvoColorRec *pOverscanColor,
1385                                  NVEvoUpdateState *updateState)
1386 {
1387     nvAssert(tilePosition == 0);
1388     EvoSetRasterParams3(pDevEvo, head, pTimings, pOverscanColor, updateState);
1389 }
1390 
1391 static void EvoSetRasterParams5(NVDevEvoPtr pDevEvo, int head,
1392                                 const NVHwModeTimingsEvo *pTimings,
1393                                 const NvU8 tilePosition,
1394                                 const NVEvoColorRec *pOverscanColor,
1395                                 NVEvoUpdateState *updateState)
1396 {
1397     NVEvoChannelPtr pChannel = pDevEvo->core;
1398 
1399     /* These methods should only apply to a single pDpy */
1400     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
1401 
1402     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
1403 
1404     EvoSetRasterParams3(pDevEvo, head, pTimings, pOverscanColor, updateState);
1405 
1406     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_TILE_POSITION(head), 1);
1407     nvDmaSetEvoMethodData(pChannel,
1408         DRF_NUM(C57D, _HEAD_SET_TILE_POSITION, _X, tilePosition) |
1409         DRF_NUM(C57D, _HEAD_SET_TILE_POSITION, _Y, 0));
1410 }
1411 
1412 static void EvoSetRasterParamsC5(NVDevEvoPtr pDevEvo, int head,
1413                                  const NVHwModeTimingsEvo *pTimings,
1414                                  const NvU8 tilePosition,
1415                                  const NVDscInfoEvoRec *pDscInfo,
1416                                  const NVEvoColorRec *pOverscanColor,
1417                                  NVEvoUpdateState *updateState)
1418 {
1419     nvAssert(pDscInfo->type != NV_DSC_INFO_EVO_TYPE_HDMI);
1420     EvoSetRasterParams5(pDevEvo, head, pTimings, tilePosition, pOverscanColor,
1421                         updateState);
1422 }
1423 
1424 static NvU32 GetHdmiDscHBlankPixelTarget(const NVHwModeTimingsEvo *pTimings,
1425                                          const NVDscInfoEvoRec *pDscInfo)
1426 {
1427     nvAssert((pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) ||
1428                  (pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_SINGLE));
1429 
1430     const NvU32 hblankMin =
1431         (pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) ?
1432             ((pDscInfo->hdmi.hblankMin + 1) / 2) :
1433             pDscInfo->hdmi.hblankMin;
1434 
1435     NvU32 hBlankPixelTarget =
1436         NV_UNSIGNED_DIV_CEIL((pTimings->rasterSize.x *
1437                               pDscInfo->hdmi.dscTBlankToTTotalRatioX1k),
1438                               1000);
1439 
1440     hBlankPixelTarget = NV_MAX(hblankMin, hBlankPixelTarget);
1441 
1442     if (pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) {
1443         hBlankPixelTarget += (hBlankPixelTarget % 2);
1444     }
1445 
1446     return hBlankPixelTarget;
1447 }
1448 
1449 static void EvoSetRasterParamsC6(NVDevEvoPtr pDevEvo, int head,
1450                                  const NVHwModeTimingsEvo *pTimings,
1451                                  const NvU8 tilePosition,
1452                                  const NVDscInfoEvoRec *pDscInfo,
1453                                  const NVEvoColorRec *pOverscanColor,
1454                                  NVEvoUpdateState *updateState)
1455 {
1456     NvU32 rasterHBlankDelay;
1457     NVEvoChannelPtr pChannel = pDevEvo->core;
1458 
1459     /* These methods should only apply to a single pDpy */
1460     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
1461 
1462     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
1463 
1464     EvoSetRasterParams5(pDevEvo, head, pTimings, tilePosition, pOverscanColor,
1465                         updateState);
1466 
1467     if (pDscInfo->type == NV_DSC_INFO_EVO_TYPE_HDMI) {
1468         const NvU32 hBlank = pTimings->rasterSize.x -
1469             pTimings->rasterBlankStart.x + pTimings->rasterBlankEnd.x;
1470         const NvU32 hBlankPixelTarget =
1471             GetHdmiDscHBlankPixelTarget(pTimings, pDscInfo);
1472 
1473         const NvU32 headSetRasterHBlankDelayStart =
1474             pTimings->rasterSize.x - pTimings->rasterBlankStart.x - 2;
1475         const NvU32 headSetRasterHBlankDelayEnd =
1476             hBlankPixelTarget - hBlank + headSetRasterHBlankDelayStart;
1477 
1478         rasterHBlankDelay =
1479             DRF_NUM(C67D, _HEAD_SET_RASTER_HBLANK_DELAY, _BLANK_START,
1480                     headSetRasterHBlankDelayStart);
1481         rasterHBlankDelay |=
1482             DRF_NUM(C67D, _HEAD_SET_RASTER_HBLANK_DELAY, _BLANK_END,
1483                     headSetRasterHBlankDelayEnd);
1484     } else {
1485         rasterHBlankDelay = 0;
1486     }
1487 
1488     nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_RASTER_HBLANK_DELAY(head), 1);
1489     nvDmaSetEvoMethodData(pChannel, rasterHBlankDelay);
1490 }
1491 
1492 static void EvoSetProcAmpC3(NVDispEvoPtr pDispEvo, const NvU32 head,
1493                             NVEvoUpdateState *updateState)
1494 {
1495     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
1496     NVEvoChannelPtr pChannel = pDevEvo->core;
1497     const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
1498     NvU32 dynRange;
1499 
1500     /* These methods should only apply to a single pDpyEvo */
1501     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
1502 
1503     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
1504 
1505     // These NVT defines match the HEAD_SET_PROCAMP ones.
1506     ct_assert(NVT_COLORIMETRY_RGB == NVC37D_HEAD_SET_PROCAMP_COLOR_SPACE_RGB);
1507     ct_assert(NVT_COLORIMETRY_YUV_601 == NVC37D_HEAD_SET_PROCAMP_COLOR_SPACE_YUV_601);
1508     ct_assert(NVT_COLORIMETRY_YUV_709 == NVC37D_HEAD_SET_PROCAMP_COLOR_SPACE_YUV_709);
1509     /* XXXnvdisplay add REC2020 */
1510     ct_assert(NVT_COLOR_RANGE_FULL == NVC37D_HEAD_SET_PROCAMP_RANGE_COMPRESSION_DISABLE);
1511     ct_assert(NVT_COLOR_RANGE_LIMITED == NVC37D_HEAD_SET_PROCAMP_RANGE_COMPRESSION_ENABLE);
1512 
1513     if (pHeadState->procAmp.colorRange == NVT_COLOR_RANGE_FULL) {
1514         dynRange = DRF_DEF(C37D, _HEAD_SET_PROCAMP, _DYNAMIC_RANGE, _VESA);
1515     } else {
1516         nvAssert(pHeadState->procAmp.colorRange == NVT_COLOR_RANGE_LIMITED);
1517         dynRange = DRF_DEF(C37D, _HEAD_SET_PROCAMP, _DYNAMIC_RANGE, _CEA);
1518     }
1519 
1520     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PROCAMP(head), 1);
1521     nvDmaSetEvoMethodData(pChannel,
1522         DRF_NUM(C37D, _HEAD_SET_PROCAMP, _COLOR_SPACE,
1523                 pHeadState->procAmp.colorimetry) |
1524         DRF_DEF(C37D, _HEAD_SET_PROCAMP, _CHROMA_LPF, _DISABLE) |
1525         DRF_NUM(C37D, _HEAD_SET_PROCAMP, _SAT_COS,
1526                 pHeadState->procAmp.satCos) |
1527         DRF_NUM(C37D, _HEAD_SET_PROCAMP, _SAT_SINE, 0) |
1528         dynRange |
1529         DRF_NUM(C37D, _HEAD_SET_PROCAMP, _RANGE_COMPRESSION,
1530                 pHeadState->procAmp.colorRange) |
1531         DRF_DEF(C37D, _HEAD_SET_PROCAMP, _BLACK_LEVEL, _GRAPHICS));
1532 }
1533 
1534 static const struct NvKmsCscMatrix RGBToFullRangeYCbCrRec709Matrix = {{
1535     {   0x8000, 0x1f8bbc, 0x1ff444, 0x8000 },
1536     {   0x366c,   0xb718,   0x127c,      0 },
1537     { 0x1fe2ac, 0x1f9d54,   0x8000, 0x8000 },
1538 }};
1539 static const struct NvKmsCscMatrix RGBToFullRangeYCbCrRec601Matrix = {{
1540     {   0x8000, 0x1f94d0, 0x1feb30, 0x8000 },
1541     {   0x4c8c,   0x9644,   0x1d30,      0 },
1542     { 0x1fd4cc, 0x1fab34,   0x8000, 0x8000 },
1543 }};
1544 static const struct NvKmsCscMatrix RGBToLimitedRangeYCbCrRec2020Matrix = {{
1545     {   0x7000, 0x1f9900, 0x1ff700, 0x8000 },
1546     {   0x3988,   0x947c,    0xcfc, 0x1000 },
1547     { 0x1fe0b8, 0x1faf44,   0x7000, 0x8000 },
1548 }};
1549 static const struct NvKmsCscMatrix RGBToLimitedRangeYCbCrRec709Matrix = {{
1550     {   0x7000, 0x1f9a44, 0x1ff5bc, 0x8000 },
1551     {   0x2e90,   0x9ca4,    0xfd0, 0x1000 },
1552     { 0x1fe654, 0x1fa9a8,   0x7000, 0x8000 },
1553 }};
1554 static const struct NvKmsCscMatrix RGBToLimitedRangeYCbCrRec601Matrix = {{
1555     {   0x7000, 0x1fa234, 0x1fedc8, 0x8000 },
1556     {   0x417c,   0x8090,   0x18f8, 0x1000 },
1557     { 0x1fda34, 0x1fb5cc,   0x7000, 0x8000 },
1558 }};
1559 static const struct NvKmsCscMatrix RGBToLimitedRangeRGB = {{
1560     { 0xdb04,      0,       0, 0x1000 },
1561     {      0, 0xdb04,       0, 0x1000 },
1562     {      0,      0,  0xdb04, 0x1000 },
1563 }};
1564 
1565 /*!
1566  * Return the appropriate OCSC1 matrix for the requested color range and
1567  * colorimetry, or NULL if the OCSC1 should be disabled.
1568  */
1569 static const struct NvKmsCscMatrix* EvoGetOCsc1MatrixC5(const NVDispHeadStateEvoRec *pHeadState)
1570 {
1571     if (pHeadState->procAmp.colorRange == NVT_COLOR_RANGE_FULL) {
1572         switch (pHeadState->procAmp.colorimetry) {
1573             case NVT_COLORIMETRY_BT2020RGB:
1574                 // fall through
1575             case NVT_COLORIMETRY_RGB:
1576                 // No OCSC1 needed.
1577                 return NULL;
1578             case NVT_COLORIMETRY_YUV_601:
1579                 return &RGBToFullRangeYCbCrRec601Matrix;
1580             case NVT_COLORIMETRY_YUV_709:
1581                 return &RGBToFullRangeYCbCrRec709Matrix;
1582             default:
1583                 nvAssert(!"Unexpected colorimetry");
1584                 return NULL;
1585         }
1586     } else {
1587         switch (pHeadState->procAmp.colorimetry) {
1588             case NVT_COLORIMETRY_BT2020RGB:
1589                 // fall through
1590             case NVT_COLORIMETRY_RGB:
1591                 return &RGBToLimitedRangeRGB;
1592             case NVT_COLORIMETRY_YUV_601:
1593                 return &RGBToLimitedRangeYCbCrRec601Matrix;
1594             case NVT_COLORIMETRY_YUV_709:
1595                 return &RGBToLimitedRangeYCbCrRec709Matrix;
1596             case NVT_COLORIMETRY_BT2020YCC:
1597                 return &RGBToLimitedRangeYCbCrRec2020Matrix;
1598             default:
1599                 nvAssert(!"Unexpected colorimetry");
1600                 return NULL;
1601         }
1602     }
1603 }
1604 
1605 struct EvoClampRangeC5 {
1606     NvU32 green, red_blue;
1607 };
1608 
1609 /*!
1610  * Return the output clamping ranges for the requested color range and
1611  * colorimetry.
1612  */
1613 static struct EvoClampRangeC5
1614 EvoGetOCsc1ClampRange(const NVDispHeadStateEvoRec *pHeadState)
1615 {
1616     if (pHeadState->procAmp.colorRange == NVT_COLOR_RANGE_FULL) {
1617         return (struct EvoClampRangeC5) {
1618             .green    = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _LOW,  0x0) |
1619                         DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _HIGH, 0xFFF),
1620             .red_blue = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _LOW,  0x0) |
1621                         DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _HIGH, 0xFFF),
1622         };
1623     } else {
1624         switch (pHeadState->procAmp.colorimetry) {
1625             default:
1626                 nvAssert(!"Unexpected colorimetry");
1627                 // fall through
1628             case NVT_COLORIMETRY_BT2020RGB:
1629                 // fall through
1630             case NVT_COLORIMETRY_RGB:
1631                 return (struct EvoClampRangeC5) {
1632                     .green    = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _LOW,  0x100) |
1633                                 DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _HIGH, 0xEB0),
1634                     .red_blue = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _LOW,  0x100) |
1635                                 DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _HIGH, 0xEB0),
1636                 };
1637             case NVT_COLORIMETRY_YUV_601:
1638             case NVT_COLORIMETRY_YUV_709:
1639             case NVT_COLORIMETRY_BT2020YCC:
1640                 return (struct EvoClampRangeC5) {
1641                     .green    = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _LOW,  0x100) |
1642                                 DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _HIGH, 0xEB0),
1643                     .red_blue = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _LOW,  0x100) |
1644                                 DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _HIGH, 0xF00),
1645                 };
1646         }
1647     }
1648 }
1649 
1650 
1651 static void EvoSetOCsc1C5(NVDispEvoPtr pDispEvo, const NvU32 head)
1652 {
1653     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
1654     NVEvoChannelPtr pChannel = pDevEvo->core;
1655     const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
1656     const struct NvKmsCscMatrix *matrix = EvoGetOCsc1MatrixC5(pHeadState);
1657     struct EvoClampRangeC5 clamp = EvoGetOCsc1ClampRange(pHeadState);
1658 
1659     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_CLAMP_RANGE_GREEN(head), 1);
1660     nvDmaSetEvoMethodData(pChannel, clamp.green);
1661     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_CLAMP_RANGE_RED_BLUE(head), 1);
1662     nvDmaSetEvoMethodData(pChannel, clamp.red_blue);
1663 
1664     if (matrix) {
1665         int x, y;
1666         NvU32 method = NVC57D_HEAD_SET_OCSC1COEFFICIENT_C00(head);
1667 
1668         nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC1CONTROL(head), 1);
1669         nvDmaSetEvoMethodData(pChannel,
1670             DRF_DEF(C57D, _HEAD_SET_OCSC1CONTROL, _ENABLE, _ENABLE));
1671 
1672         for (y = 0; y < 3; y++) {
1673             for (x = 0; x < 4; x++) {
1674                 nvDmaSetStartEvoMethod(pChannel, method, 1);
1675                 nvDmaSetEvoMethodData(pChannel, matrix->m[y][x]);
1676 
1677                 method += 4;
1678             }
1679         }
1680     } else {
1681         nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC1CONTROL(head), 1);
1682         nvDmaSetEvoMethodData(pChannel,
1683             DRF_DEF(C57D, _HEAD_SET_OCSC1CONTROL, _ENABLE, _DISABLE));
1684     }
1685 }
1686 
1687 /*
1688  *     1.402       1.0       0.0
1689  * -0.714136       1.0 -0.344136
1690  *       0.0       1.0     1.772
1691  */
1692 static const struct NvKmsMatrix CrYCb601toRGBMatrix = { {
1693     { 0x3fb374bc, 0x3f800000, 0x00000000 },
1694     { 0xbf36d19e, 0x3f800000, 0xbeb03298 },
1695     { 0x00000000, 0x3f800000, 0x3fe2d0e5 }
1696 } };
1697 
1698 /*
1699  *    1.5748       1.0       0.0
1700  * -0.468124       1.0 -0.187324
1701  *       0.0       1.0    1.8556
1702  */
1703 static const struct NvKmsMatrix CrYCb709toRGBMatrix = { {
1704     { 0x3fc9930c, 0x3f800000, 0x00000000 },
1705     { 0xbeefadf3, 0x3f800000, 0xbe3fd1dd },
1706     { 0x00000000, 0x3f800000, 0x3fed844d }
1707 } };
1708 
1709 /*
1710  *       0.5 -0.418688 -0.081312
1711  *     0.299     0.587     0.114
1712  * -0.168736 -0.331264       0.5
1713  */
1714 static const struct NvKmsMatrix RGBtoCrYCb601Matrix = { {
1715     { 0x3f000000, 0xbed65e46, 0xbda686e8 },
1716     { 0x3e991687, 0x3f1645a2, 0x3de978d5 },
1717     { 0xbe2cc921, 0xbea99b6f, 0x3f000000 }
1718 } };
1719 
1720 /*
1721  *       0.5  -0.45415  -0.04585
1722  *   0.21260   0.71520   0.07220
1723  *  -0.11457  -0.38543       0.5
1724  */
1725 static const struct NvKmsMatrix RGBtoCrYCb709Matrix = { {
1726     { 0x3f000000, 0xbee88659, 0xbd3bcd36 },
1727     { 0x3e59b3d0, 0x3f371759, 0x3d93dd98 },
1728     { 0xbdeaa3ad, 0xbec55715, 0x3f000000 }
1729 } };
1730 
1731 /*
1732  * Converts FP32 to fixed point S5.14 coefficient format
1733  */
1734 static inline NvU32 cscCoefConvertS514(float32_t x)
1735 {
1736     /* more concisely, (NvS32)floor(x * 65536.0 + 2.0) */
1737     const NvS32 y = f32_to_i32(f32_mulAdd(x,
1738                                           NvU32viewAsF32(NV_FLOAT_65536),
1739                                           NvU32viewAsF32(NV_FLOAT_TWO)),
1740                                softfloat_round_min, FALSE);
1741     return (NvU32)(0x001ffffc & clamp_S32(y, -0x100000, 0xfffff));
1742 }
1743 
1744 /*
1745  * Sets up the OCSC0 matrix coefficients, used to perform saturation
1746  * adjustment.
1747  *
1748  * The pipeline operates in FP16 RGB, however this adjustment must be
1749  * performed in CrYCb. Therefore, we multiply the saturation
1750  * adjustment matrix by the appropriate color space conversion
1751  * matrix. The specific color space used depends on the colorimetry of
1752  * the final output. Then we multiply by its inverse to convert back
1753  * to RGB. Finally, we convert the coefficients to S5.14 fixed point
1754  * format.
1755  *
1756  * The OCSC0 matrix will be enabled later in EvoSetLUTContextDmaC5 if
1757  * and only if we also enable the OLUT as required by the
1758  * specification.
1759  */
1760 static void EvoSetOCsc0C5(NVDispEvoPtr pDispEvo, const NvU32 head)
1761 {
1762     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
1763     NVEvoChannelPtr pChannel = pDevEvo->core;
1764     const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
1765 
1766     const float32_t zeroF32 = NvU32viewAsF32(NV_FLOAT_ZERO);
1767     const float32_t oneF32 = NvU32viewAsF32(NV_FLOAT_ONE);
1768     const float32_t inv2048F32 = f32_div(NvU32viewAsF32(NV_FLOAT_HALF),
1769                                          NvU32viewAsF32(NV_FLOAT_1024));
1770     /* divide satCos by the default setting of 1024 */
1771     const float32_t satCos = f32_div(i32_to_f32(pHeadState->procAmp.satCos),
1772                                      NvU32viewAsF32(NV_FLOAT_1024));
1773     const struct NvKmsMatrixF32 satHueMatrix = { {
1774         {  satCos, zeroF32, zeroF32 },
1775         { zeroF32,  oneF32, zeroF32 },
1776         { zeroF32, zeroF32,  satCos }
1777     } };
1778     struct NvKms3x4MatrixF32 ocsc0Matrix = { {
1779         {  oneF32, zeroF32, zeroF32, zeroF32 },
1780         { zeroF32,  oneF32, zeroF32, zeroF32 },
1781         { zeroF32, zeroF32,  oneF32, zeroF32 }
1782     } };
1783 
1784     struct NvKmsMatrixF32 CrYCbtoRGBMatrix;
1785     struct NvKmsMatrixF32 RGBtoCrYCbMatrix;
1786     switch (pHeadState->procAmp.colorimetry) {
1787     default:
1788         nvAssert(!"Unexpected colorimetry");
1789         /* fallthrough */
1790     case NVT_COLORIMETRY_RGB:
1791     case NVT_COLORIMETRY_BT2020RGB:
1792         /* fallthrough; for RGB output, perform saturation adjustment in YUV709 */
1793     case NVT_COLORIMETRY_YUV_709:
1794         CrYCbtoRGBMatrix = NvKmsMatrixToNvKmsMatrixF32(CrYCb709toRGBMatrix);
1795         RGBtoCrYCbMatrix = NvKmsMatrixToNvKmsMatrixF32(RGBtoCrYCb709Matrix);
1796         break;
1797     case NVT_COLORIMETRY_YUV_601:
1798         CrYCbtoRGBMatrix = NvKmsMatrixToNvKmsMatrixF32(CrYCb601toRGBMatrix);
1799         RGBtoCrYCbMatrix = NvKmsMatrixToNvKmsMatrixF32(RGBtoCrYCb601Matrix);
1800         break;
1801     }
1802 
1803     ocsc0Matrix = nvMultiply3x4Matrix(&RGBtoCrYCbMatrix, &ocsc0Matrix);
1804     ocsc0Matrix = nvMultiply3x4Matrix(&satHueMatrix, &ocsc0Matrix);
1805     ocsc0Matrix = nvMultiply3x4Matrix(&CrYCbtoRGBMatrix, &ocsc0Matrix);
1806 
1807     if (nvkms_output_rounding_fix()) {
1808         /*
1809          * XXX Only apply WAR for bug 2267663 for linear output TFs. Non-linear
1810          * TFs could amplify the 1/2048 factor to the point of being
1811          * perceptible.
1812          */
1813         if (pHeadState->tf == NVKMS_OUTPUT_TF_NONE) {
1814             ocsc0Matrix.m[0][3] = f32_add(ocsc0Matrix.m[0][3], inv2048F32);
1815             ocsc0Matrix.m[1][3] = f32_add(ocsc0Matrix.m[1][3], inv2048F32);
1816             ocsc0Matrix.m[2][3] = f32_add(ocsc0Matrix.m[2][3], inv2048F32);
1817         }
1818     }
1819 
1820     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC0COEFFICIENT_C00(head), 12);
1821     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C00, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[0][0])));
1822     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C01, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[0][1])));
1823     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C02, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[0][2])));
1824     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C03, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[0][3])));
1825 
1826     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C10, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[1][0])));
1827     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C11, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[1][1])));
1828     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C12, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[1][2])));
1829     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C13, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[1][3])));
1830 
1831     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C20, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[2][0])));
1832     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C21, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[2][1])));
1833     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C22, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[2][2])));
1834     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C23, _VALUE, cscCoefConvertS514(ocsc0Matrix.m[2][3])));
1835 }
1836 
1837 static void EvoSetProcAmpC5(NVDispEvoPtr pDispEvo, const NvU32 head,
1838                             NVEvoUpdateState *updateState)
1839 {
1840     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
1841     NVEvoChannelPtr pChannel = pDevEvo->core;
1842     const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
1843     NvU32 dynRange, chromaLpf, chromaDownV;
1844     NvU32 colorimetry;
1845 
1846     NVT_COLORIMETRY nvtColorimetry = pHeadState->procAmp.colorimetry;
1847     NVT_COLOR_RANGE nvtColorRange = pHeadState->procAmp.colorRange;
1848 
1849     /* These methods should only apply to a single pDpyEvo */
1850     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
1851 
1852     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
1853 
1854     switch (nvtColorimetry) {
1855         default:
1856             nvAssert(!"Unrecognized colorimetry");
1857             // fall through
1858         case NVT_COLORIMETRY_BT2020RGB:
1859             // fall through
1860         case NVT_COLORIMETRY_RGB:
1861             colorimetry = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _COLOR_SPACE, _RGB);
1862             break;
1863         case NVT_COLORIMETRY_YUV_601:
1864             colorimetry = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _COLOR_SPACE, _YUV_601);
1865             break;
1866         case NVT_COLORIMETRY_YUV_709:
1867             colorimetry = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _COLOR_SPACE, _YUV_709);
1868             break;
1869         case NVT_COLORIMETRY_BT2020YCC:
1870             colorimetry = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _COLOR_SPACE, _YUV_2020);
1871             break;
1872     }
1873 
1874     if (nvtColorRange == NVT_COLOR_RANGE_FULL) {
1875         dynRange = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _DYNAMIC_RANGE, _VESA);
1876     } else {
1877         nvAssert(nvtColorRange == NVT_COLOR_RANGE_LIMITED);
1878         dynRange = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _DYNAMIC_RANGE, _CEA);
1879     }
1880 
1881     /*
1882      * NVC67D_HEAD_SET_PROCAMP_CHROMA_DOWN_V is only defined in NVC67D, but
1883      * it is an unused bit in NVC57D_HEAD_SET_PROCAMP, and YUV420 should only
1884      * be set on >=nvdisplay 4.0, so it's okay to set it here.
1885      */
1886     if (pHeadState->procAmp.colorFormat == NVT_COLOR_FORMAT_YCbCr420) {
1887         chromaLpf = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _CHROMA_LPF, _ENABLE);
1888         chromaDownV = DRF_DEF(C67D, _HEAD_SET_PROCAMP, _CHROMA_DOWN_V, _ENABLE);
1889     } else {
1890         chromaLpf = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _CHROMA_LPF, _DISABLE);
1891         chromaDownV = DRF_DEF(C67D, _HEAD_SET_PROCAMP, _CHROMA_DOWN_V, _DISABLE);
1892     }
1893 
1894     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_PROCAMP(head), 1);
1895     nvDmaSetEvoMethodData(pChannel,
1896                           colorimetry | dynRange | chromaLpf | chromaDownV);
1897 
1898     EvoSetOCsc0C5(pDispEvo, head);
1899     EvoSetOCsc1C5(pDispEvo, head);
1900 }
1901 
1902 /*
1903  * With nvdisplay, external fliplock pins are controlled via a headless
1904  * SetControl method, unlike previous EVO display implementations which
1905  * specified this information in the per-head HeadSetControl method.  This
1906  * function loops over all of the core nvkms HeadControl data structures to
1907  * determine which pins should be enabled in the SetControl method.  It should
1908  * be called any time the HeadControl data structures are updated.
1909  */
1910 static void SetControl(NVDevEvoPtr pDevEvo, int sd)
1911 {
1912     NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[sd];
1913     NVEvoChannelPtr pChannel = pDevEvo->core;
1914     NvU32 data = 0;
1915     NvU32 head;
1916 
1917     for (head = 0; head < pDevEvo->numHeads; head++) {
1918         NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
1919         if (pHC->flipLock && !NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->flipLockPin)) {
1920             NvU32 pin = pHC->flipLockPin - NV_EVO_LOCK_PIN_0;
1921             data = FLD_IDX_SET_DRF(C37D, _SET_CONTROL, _FLIP_LOCK_PIN,
1922                                    pin, _ENABLE, data);
1923         }
1924     }
1925 
1926     /*
1927      * GV100 HW bug 2062029 WAR
1928      *
1929      * GV100 always holds the external fliplock line low as if
1930      * NVC37D_SET_CONTROL_FLIP_LOCK_PIN was enabled.  To work around this,
1931      * the GV100 VBIOS initializes the fliplock GPIOs to be software
1932      * controlled (forced off).  The following rmctrl needs to be called to
1933      * switch HW control of the fliplock GPIOs back on whenever external
1934      * fliplock is enabled.
1935      */
1936     {
1937         NVC370_CTRL_SET_SWAPRDY_GPIO_WAR_PARAMS params = { };
1938 
1939         params.base.subdeviceIndex = pEvoSubDev->subDeviceInstance;
1940         params.bEnable = (data != 0);
1941 
1942         if (nvRmApiControl(
1943                 nvEvoGlobal.clientHandle,
1944                 pDevEvo->displayHandle,
1945                 NVC370_CTRL_CMD_SET_SWAPRDY_GPIO_WAR,
1946                 &params, sizeof(params)) != NVOS_STATUS_SUCCESS) {
1947             nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR, "Failed to override fliplock GPIO");
1948         }
1949     }
1950 
1951     nvDmaSetStartEvoMethod(pChannel, NVC37D_SET_CONTROL, 1);
1952     nvDmaSetEvoMethodData(pChannel, data);
1953 }
1954 
1955 static void EvoSetHeadControlC3(NVDevEvoPtr pDevEvo, int sd, int head,
1956                                 NVEvoUpdateState *updateState)
1957 {
1958     NVEvoChannelPtr pChannel = pDevEvo->core;
1959     NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[sd];
1960     /*
1961      * NOTE: This function should only push state to the hardware based on data
1962      * in the pHC.  If not, then we may miss updates due to the memcmp of the
1963      * HeadControl structure in UpdateEvoLockState().
1964      */
1965     NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
1966     NvU32 data = 0, pin;
1967     NvU32 serverLockMode, clientLockMode;
1968 
1969     /* These methods should only apply to a single subdevice */
1970     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
1971 
1972     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
1973 
1974     switch (pHC->serverLock) {
1975     case NV_EVO_NO_LOCK:
1976         serverLockMode = NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_MODE_NO_LOCK;
1977         break;
1978     case NV_EVO_FRAME_LOCK:
1979         serverLockMode = NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_MODE_FRAME_LOCK;
1980         break;
1981     case NV_EVO_RASTER_LOCK:
1982         serverLockMode = NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_MODE_RASTER_LOCK;
1983         break;
1984     default:
1985         nvAssert(!"Invalid server lock mode");
1986         return;
1987     }
1988 
1989     switch (pHC->clientLock) {
1990     case NV_EVO_NO_LOCK:
1991         clientLockMode = NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_MODE_NO_LOCK;
1992         break;
1993     case NV_EVO_FRAME_LOCK:
1994         clientLockMode = NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_MODE_FRAME_LOCK;
1995         break;
1996     case NV_EVO_RASTER_LOCK:
1997         clientLockMode = NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_MODE_RASTER_LOCK;
1998         break;
1999     default:
2000         nvAssert(!"Invalid client lock mode");
2001         return;
2002     }
2003 
2004     // Convert head control state to EVO method values.
2005     nvAssert(!pHC->interlaced);
2006     data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _STRUCTURE, _PROGRESSIVE);
2007 
2008     nvAssert(pHC->serverLockPin != NV_EVO_LOCK_PIN_ERROR);
2009     nvAssert(pHC->clientLockPin != NV_EVO_LOCK_PIN_ERROR);
2010 
2011     if (serverLockMode == NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_MODE_NO_LOCK) {
2012         data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _MASTER_LOCK_PIN, _LOCK_PIN_NONE);
2013     } else if (NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->serverLockPin)) {
2014         pin = pHC->serverLockPin - NV_EVO_LOCK_PIN_INTERNAL_0;
2015         /*
2016          * nvdClass_01.mfs says:
2017          * "master lock pin, if internal, must be set to the corresponding
2018          * internal pin for that head" (error check #12)
2019          */
2020         nvAssert(pin == head);
2021         data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _MASTER_LOCK_PIN,
2022                         NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_PIN_INTERNAL_SCAN_LOCK(pin));
2023     } else {
2024         pin = pHC->serverLockPin - NV_EVO_LOCK_PIN_0;
2025         data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _MASTER_LOCK_PIN,
2026                         NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_PIN_LOCK_PIN(pin));
2027     }
2028     data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _MASTER_LOCK_MODE, serverLockMode);
2029 
2030     if (clientLockMode == NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_MODE_NO_LOCK) {
2031         data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCK_PIN, _LOCK_PIN_NONE);
2032     } else if (NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->clientLockPin)) {
2033         pin = pHC->clientLockPin - NV_EVO_LOCK_PIN_INTERNAL_0;
2034         data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCK_PIN,
2035                         NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_PIN_INTERNAL_SCAN_LOCK(pin));
2036     } else {
2037         pin = pHC->clientLockPin - NV_EVO_LOCK_PIN_0;
2038         data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCK_PIN,
2039                         NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_PIN_LOCK_PIN(pin));
2040     }
2041     data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCK_MODE, clientLockMode);
2042     data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCKOUT_WINDOW,
2043                     pHC->clientLockoutWindow);
2044 
2045     /*
2046      * We always enable stereo lock when it's available and either framelock
2047      * or rasterlock is in use.
2048      */
2049     if (pHC->stereoLocked) {
2050         if (pHC->serverLock != NV_EVO_NO_LOCK) {
2051             data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _MASTER_STEREO_LOCK_MODE,
2052                             NVC37D_HEAD_SET_CONTROL_MASTER_STEREO_LOCK_MODE_ENABLE);
2053         }
2054         if (pHC->clientLock != NV_EVO_NO_LOCK) {
2055             data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_STEREO_LOCK_MODE,
2056                             NVC37D_HEAD_SET_CONTROL_SLAVE_STEREO_LOCK_MODE_ENABLE);
2057         }
2058     }
2059 
2060     nvAssert(pHC->stereoPin != NV_EVO_LOCK_PIN_ERROR);
2061     if (NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->stereoPin)) {
2062         data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _STEREO_PIN, _LOCK_PIN_NONE);
2063     } else {
2064         pin = pHC->stereoPin - NV_EVO_LOCK_PIN_0;
2065         data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _STEREO_PIN,
2066                         NVC37D_HEAD_SET_CONTROL_STEREO_PIN_LOCK_PIN(pin));
2067     }
2068 
2069     if (pHC->hdmi3D) {
2070         data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _STEREO3D_STRUCTURE, _FRAME_PACKED);
2071     } else {
2072         data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _STEREO3D_STRUCTURE, _NORMAL);
2073     }
2074 
2075     /*
2076      * NVC67D_HEAD_SET_CONTROL_YUV420PACKER is only defined in NVC67D, but
2077      * it is an unused bit in NVC37D_HEAD_SET_CONTROL, and YUV420 should only
2078      * be set on >=nvdisplay 4.0, so it's okay to set it here.
2079      */
2080     if (pHC->hwYuv420) {
2081         data |= DRF_DEF(C67D, _HEAD_SET_CONTROL, _YUV420PACKER, _ENABLE);
2082     } else {
2083         data |= DRF_DEF(C67D, _HEAD_SET_CONTROL, _YUV420PACKER, _DISABLE);
2084     }
2085 
2086     // Send the HeadSetControl method.
2087     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL(head), 1);
2088     nvDmaSetEvoMethodData(pChannel, data);
2089 
2090     SetControl(pDevEvo, sd);
2091 
2092     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_LOCK_CHAIN(head), 1);
2093     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C37D, _HEAD_SET_LOCK_CHAIN, _POSITION,
2094                                      pHC->lockChainPosition));
2095 
2096 /* XXX temporary WAR; see bug 4028718 */
2097 #if !defined(NVC37D_HEAD_SET_LOCK_OFFSET)
2098 #define NVC37D_HEAD_SET_LOCK_OFFSET(a)                                          (0x00002040 + (a)*0x00000400)
2099 #define NVC37D_HEAD_SET_LOCK_OFFSET_X                                           14:0
2100 #define NVC37D_HEAD_SET_LOCK_OFFSET_Y                                           30:16
2101 #endif
2102 
2103     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_LOCK_OFFSET(head), 1);
2104     nvDmaSetEvoMethodData(pChannel, pHC->setLockOffsetX ?
2105                           DRF_NUM(C37D, _HEAD_SET_LOCK_OFFSET, _X,
2106                                      27) : 0);
2107 }
2108 
2109 static void EvoSetHeadRefClkC3(NVDevEvoPtr pDevEvo, int head, NvBool external,
2110                                NVEvoUpdateState *updateState)
2111 {
2112     NVEvoChannelPtr pChannel = pDevEvo->core;
2113     NvU32 sd;
2114 
2115     /* These methods should only apply to a single subdevice */
2116     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
2117 
2118     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
2119 
2120     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
2121         if (nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)) {
2122             if (external) {
2123                 pDevEvo->gpus[sd].setSwSpareA[head] =
2124                     FLD_SET_DRF(C37D,
2125                                 _HEAD_SET_SW_SPARE_A_CODE,
2126                                 _VPLL_REF,
2127                                 _QSYNC,
2128                                 pDevEvo->gpus[sd].setSwSpareA[head]);
2129             } else {
2130                 pDevEvo->gpus[sd].setSwSpareA[head] =
2131                     FLD_SET_DRF(C37D,
2132                                 _HEAD_SET_SW_SPARE_A_CODE,
2133                                 _VPLL_REF,
2134                                 _NO_PREF,
2135                                 pDevEvo->gpus[sd].setSwSpareA[head]);
2136             }
2137 
2138             nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
2139             nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_SW_SPARE_A(head), 1);
2140             nvDmaSetEvoMethodData(pChannel, pDevEvo->gpus[sd].setSwSpareA[head]);
2141             nvPopEvoSubDevMask(pDevEvo);
2142         }
2143     }
2144 }
2145 
2146 static void EvoSORSetControlC3(const NVConnectorEvoRec *pConnectorEvo,
2147                                const enum nvKmsTimingsProtocol protocol,
2148                                const NvU32 orIndex,
2149                                const NvU32 headMask)
2150 {
2151     NVDevEvoPtr pDevEvo = pConnectorEvo->pDispEvo->pDevEvo;
2152     NVEvoChannelPtr pChannel = pDevEvo->core;
2153     NvU32 hwProtocol = 0;
2154 
2155     /* These methods should only apply to a single pDpy */
2156     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
2157     nvAssert(orIndex != NV_INVALID_OR);
2158 
2159     if (headMask != 0) {
2160         switch (protocol) {
2161         default:
2162             nvAssert(!"Unknown SOR protocol");
2163             /* Fall through */
2164         case NVKMS_PROTOCOL_SOR_SINGLE_TMDS_A:
2165             hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_A;
2166             break;
2167         case NVKMS_PROTOCOL_SOR_SINGLE_TMDS_B:
2168             hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_B;
2169             break;
2170         case NVKMS_PROTOCOL_SOR_DUAL_TMDS:
2171             hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_DUAL_TMDS;
2172             break;
2173         case NVKMS_PROTOCOL_SOR_DP_A:
2174             hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_DP_A;
2175             break;
2176         case NVKMS_PROTOCOL_SOR_DP_B:
2177             hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_DP_B;
2178             break;
2179         case NVKMS_PROTOCOL_SOR_LVDS_CUSTOM:
2180             hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_LVDS_CUSTOM;
2181             break;
2182         case NVKMS_PROTOCOL_SOR_HDMI_FRL:
2183             hwProtocol = NVC67D_SOR_SET_CONTROL_PROTOCOL_HDMI_FRL;
2184             break;
2185         }
2186     }
2187 
2188     nvDmaSetStartEvoMethod(pChannel, NVC37D_SOR_SET_CONTROL(orIndex), 1);
2189     nvDmaSetEvoMethodData(pChannel,
2190         DRF_NUM(C37D, _SOR_SET_CONTROL, _OWNER_MASK, headMask) |
2191         DRF_NUM(C37D, _SOR_SET_CONTROL, _PROTOCOL, hwProtocol) |
2192         DRF_DEF(C37D, _SOR_SET_CONTROL, _DE_SYNC_POLARITY, _POSITIVE_TRUE) |
2193         DRF_DEF(C37D, _SOR_SET_CONTROL, _PIXEL_REPLICATE_MODE, _OFF));
2194 }
2195 
2196 static NvU32 EvoGetPixelDepthC3(const enum nvKmsPixelDepth pixelDepth)
2197 {
2198     switch (pixelDepth) {
2199     case NVKMS_PIXEL_DEPTH_18_444:
2200         return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_18_444;
2201     case NVKMS_PIXEL_DEPTH_24_444:
2202         return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_24_444;
2203     case NVKMS_PIXEL_DEPTH_30_444:
2204         return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_30_444;
2205     case NVKMS_PIXEL_DEPTH_16_422:
2206         return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_16_422;
2207     case NVKMS_PIXEL_DEPTH_20_422:
2208         return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_20_422;
2209 
2210     }
2211     nvAssert(!"Unexpected pixel depth");
2212     return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_24_444;
2213 }
2214 
2215 static void EvoPIORSetControlC3(const NVConnectorEvoRec *pConnectorEvo,
2216                                 const enum nvKmsTimingsProtocol protocol,
2217                                 const NvU32 orIndex,
2218                                 const NvU32 headMask)
2219 {
2220     NVDevEvoPtr pDevEvo = pConnectorEvo->pDispEvo->pDevEvo;
2221     NVEvoChannelPtr pChannel = pDevEvo->core;
2222 
2223     /* These methods should only apply to a single pDpy */
2224     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
2225 
2226     if (headMask != 0) {
2227         nvAssert(protocol == NVKMS_PROTOCOL_PIOR_EXT_TMDS_ENC);
2228     }
2229 
2230     nvDmaSetStartEvoMethod(pChannel, NVC37D_PIOR_SET_CONTROL(orIndex), 1);
2231     nvDmaSetEvoMethodData(pChannel,
2232         DRF_NUM(C37D, _PIOR_SET_CONTROL, _OWNER_MASK, headMask) |
2233         DRF_DEF(C37D, _PIOR_SET_CONTROL, _PROTOCOL, _EXT_TMDS_ENC) |
2234         DRF_DEF(C37D, _PIOR_SET_CONTROL, _DE_SYNC_POLARITY, _POSITIVE_TRUE));
2235 }
2236 
2237 static void EvoDSISetControlC6(const NVConnectorEvoRec *pConnectorEvo,
2238                                const enum nvKmsTimingsProtocol protocol,
2239                                const NvU32 orIndex,
2240                                const NvU32 headMask)
2241 {
2242     NVDevEvoPtr pDevEvo = pConnectorEvo->pDispEvo->pDevEvo;
2243     NVEvoChannelPtr pChannel = pDevEvo->core;
2244 
2245     /* These methods should only apply to a single pDpy */
2246     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
2247     /* Only Head 0 can be used to drive DSI output on Orin */
2248     nvAssert((headMask == 0x0) || (headMask == 0x1));
2249     /* Only one DSI engine exists on Orin */
2250     nvAssert(orIndex == 0);
2251 
2252     if (headMask != 0) {
2253         nvAssert(protocol == NVKMS_PROTOCOL_DSI);
2254     }
2255 
2256     nvDmaSetStartEvoMethod(pChannel, NVC67D_DSI_SET_CONTROL(orIndex), 1);
2257     nvDmaSetEvoMethodData(pChannel,
2258         DRF_NUM(C67D, _DSI_SET_CONTROL, _OWNER_MASK, headMask));
2259 }
2260 
2261 static void EvoORSetControlC3Helper(const NVConnectorEvoRec *pConnectorEvo,
2262                                     const enum nvKmsTimingsProtocol protocol,
2263                                     const NvU32 orIndex,
2264                                     const NvU32 headMask)
2265 {
2266     switch (pConnectorEvo->or.type) {
2267     case NV0073_CTRL_SPECIFIC_OR_TYPE_SOR:
2268         EvoSORSetControlC3(pConnectorEvo, protocol, orIndex, headMask);
2269         break;
2270     case NV0073_CTRL_SPECIFIC_OR_TYPE_PIOR:
2271         EvoPIORSetControlC3(pConnectorEvo, protocol, orIndex, headMask);
2272         break;
2273     case NV0073_CTRL_SPECIFIC_OR_TYPE_DAC:
2274         /* No DAC support on nvdisplay.  Fall through. */
2275     default:
2276         nvAssert(!"Invalid pConnectorEvo->or.type");
2277         break;
2278     }
2279 }
2280 
2281 static void EvoORSetControlC3(NVDevEvoPtr pDevEvo,
2282                               const NVConnectorEvoRec *pConnectorEvo,
2283                               const enum nvKmsTimingsProtocol protocol,
2284                               const NvU32 orIndex,
2285                               const NvU32 headMask,
2286                               NVEvoUpdateState *updateState)
2287 {
2288     /* These methods should only apply to a single pDpy */
2289     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
2290 
2291     nvUpdateUpdateState(pDevEvo, updateState, pDevEvo->core);
2292 
2293     EvoORSetControlC3Helper(pConnectorEvo, protocol, orIndex, headMask);
2294 }
2295 
2296 static void EvoORSetControlC6(NVDevEvoPtr pDevEvo,
2297                               const NVConnectorEvoRec *pConnectorEvo,
2298                               const enum nvKmsTimingsProtocol protocol,
2299                               const NvU32 orIndex,
2300                               const NvU32 headMask,
2301                               NVEvoUpdateState *updateState)
2302 {
2303     /* These methods should only apply to a single pDpy */
2304     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
2305 
2306     nvUpdateUpdateState(pDevEvo, updateState, pDevEvo->core);
2307 
2308     switch (pConnectorEvo->or.type) {
2309     case NV0073_CTRL_SPECIFIC_OR_TYPE_DSI:
2310         EvoDSISetControlC6(pConnectorEvo, protocol, orIndex, headMask);
2311         break;
2312     default:
2313         EvoORSetControlC3Helper(pConnectorEvo, protocol, orIndex, headMask);
2314         break;
2315     }
2316 }
2317 
2318 static void EvoHeadSetControlORC3(NVDevEvoPtr pDevEvo,
2319                                   const int head,
2320                                   const NVHwModeTimingsEvo *pTimings,
2321                                   const enum nvKmsPixelDepth pixelDepth,
2322                                   const NvBool colorSpaceOverride,
2323                                   NVEvoUpdateState *updateState)
2324 {
2325     NVEvoChannelPtr pChannel = pDevEvo->core;
2326     const NvU32 hwPixelDepth = EvoGetPixelDepthC3(pixelDepth);
2327     const NvU16 colorSpaceFlag = nvEvo1GetColorSpaceFlag(pDevEvo,
2328                                                          colorSpaceOverride);
2329 
2330     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE(head), 1);
2331     nvDmaSetEvoMethodData(pChannel,
2332         DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _CRC_MODE, _COMPLETE_RASTER) |
2333         (pTimings->hSyncPol ?
2334             DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _HSYNC_POLARITY, _NEGATIVE_TRUE) :
2335             DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _HSYNC_POLARITY, _POSITIVE_TRUE)) |
2336         (pTimings->vSyncPol ?
2337             DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _VSYNC_POLARITY, _NEGATIVE_TRUE) :
2338             DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _VSYNC_POLARITY, _POSITIVE_TRUE)) |
2339          DRF_NUM(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _PIXEL_DEPTH, hwPixelDepth) |
2340         (colorSpaceOverride ?
2341             (DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_OVERRIDE, _ENABLE) |
2342              DRF_NUM(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_FLAG, colorSpaceFlag)) :
2343             DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_OVERRIDE, _DISABLE)));
2344 }
2345 
2346 static void EvoHeadSetControlORC5(NVDevEvoPtr pDevEvo,
2347                                   const int head,
2348                                   const NVHwModeTimingsEvo *pTimings,
2349                                   const enum nvKmsPixelDepth pixelDepth,
2350                                   const NvBool colorSpaceOverride,
2351                                   NVEvoUpdateState *updateState)
2352 {
2353     NVEvoChannelPtr pChannel = pDevEvo->core;
2354     const NvU32 hwPixelDepth = EvoGetPixelDepthC3(pixelDepth);
2355     const NvU16 colorSpaceFlag = nvEvo1GetColorSpaceFlag(pDevEvo,
2356                                                          colorSpaceOverride);
2357 
2358     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_CONTROL_OUTPUT_RESOURCE(head), 1);
2359     nvDmaSetEvoMethodData(pChannel,
2360         DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _CRC_MODE, _COMPLETE_RASTER) |
2361         (pTimings->hSyncPol ?
2362             DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _HSYNC_POLARITY, _NEGATIVE_TRUE) :
2363             DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _HSYNC_POLARITY, _POSITIVE_TRUE)) |
2364         (pTimings->vSyncPol ?
2365             DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _VSYNC_POLARITY, _NEGATIVE_TRUE) :
2366             DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _VSYNC_POLARITY, _POSITIVE_TRUE)) |
2367          DRF_NUM(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _PIXEL_DEPTH, hwPixelDepth) |
2368         (colorSpaceOverride ?
2369             (DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_OVERRIDE, _ENABLE) |
2370              DRF_NUM(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_FLAG, colorSpaceFlag)) :
2371             DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_OVERRIDE, _DISABLE)) |
2372         DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _EXT_PACKET_WIN, _NONE));
2373 }
2374 
2375 static void EvoHeadSetDisplayIdC3(NVDevEvoPtr pDevEvo,
2376                                   const NvU32 head, const NvU32 displayId,
2377                                   NVEvoUpdateState *updateState)
2378 {
2379     NVEvoChannelPtr pChannel = pDevEvo->core;
2380 
2381     /* These methods should only apply to a single pDpy */
2382     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
2383 
2384     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
2385 
2386     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DISPLAY_ID(head, 0), 1);
2387     nvDmaSetEvoMethodData(pChannel, displayId);
2388 }
2389 
2390 static void SetFormatUsageBoundsOneWindow3(NVDevEvoPtr pDevEvo, NvU32 window,
2391                                            const NvU64 supportedFormats,
2392                                            NVEvoUpdateState *updateState)
2393 {
2394     NVEvoChannelPtr pChannel = pDevEvo->core;
2395     NvU32 value = 0;
2396 
2397     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
2398 
2399     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED1BPP) {
2400         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2401                             _RGB_PACKED1BPP, _TRUE, value);
2402     }
2403     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED2BPP) {
2404         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2405                             _RGB_PACKED2BPP, _TRUE, value);
2406     }
2407     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED4BPP) {
2408         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2409                             _RGB_PACKED4BPP, _TRUE, value);
2410     }
2411     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED8BPP) {
2412         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2413                             _RGB_PACKED8BPP, _TRUE, value);
2414     }
2415     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PACKED422) {
2416         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2417                             _YUV_PACKED422, _TRUE, value);
2418     }
2419     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP420) {
2420         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2421                             _YUV_SEMI_PLANAR420, _TRUE, value);
2422     }
2423     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP422) {
2424         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2425                             _YUV_SEMI_PLANAR422, _TRUE, value);
2426     }
2427     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP444) {
2428         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2429                             _YUV_SEMI_PLANAR444, _TRUE, value);
2430     }
2431     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP420) {
2432         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2433                             _EXT_YUV_SEMI_PLANAR420, _TRUE, value);
2434     }
2435     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP422) {
2436         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2437                             _EXT_YUV_SEMI_PLANAR422, _TRUE, value);
2438     }
2439     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP444) {
2440         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2441                             _EXT_YUV_SEMI_PLANAR444, _TRUE, value);
2442     }
2443     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PLANAR444) {
2444         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2445                             _YUV_PLANAR444, _TRUE, value);
2446     }
2447     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PLANAR420) {
2448         value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
2449                             _YUV_PLANAR420, _TRUE, value);
2450     }
2451 
2452     if (supportedFormats != 0 && value == 0) {
2453         nvAssert(!"Unknown depth in SetFormatUsageBoundsOneWindow");
2454     }
2455 
2456     nvDmaSetStartEvoMethod(pChannel, NVC37D_WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS(window), 1);
2457     nvDmaSetEvoMethodData(pChannel, value);
2458 }
2459 
2460 static inline NvU32 GetMaxPixelsFetchedPerLine(NvU16 inWidth,
2461                                                NvU16 maxHDownscaleFactor)
2462 {
2463     /*
2464      * Volta should be:
2465      * (((SetViewportSizeIn.Width + 6) * SetMaxInputScaleFactor.Horizontal + 1023 ) >> 10 ) + 6
2466      *
2467      * Turing should be:
2468      * (((SetViewportSizeIn.Width + 6) * SetMaxInputScaleFactor.Horizontal + 1023 ) >> 10 ) + 8
2469      *
2470      * Ampere, which adds "overfetch" to have tiled displays / 2-head-1-OR use cases without
2471      * visual artefacts at head boundaries:
2472      * (((SetViewportSizeIn.Width + 14) * SetMaxInputScaleFactor.Horizontal + 1023) >> 10) + 8
2473      *
2474      * We don't have to be super-precise when programming maxPixelsFetchedPerLine,
2475      * so return realistic worst-case value.
2476      */
2477     return (((inWidth + 14) * maxHDownscaleFactor + 1023) >> 10) + 8;
2478 }
2479 
2480 static void SetScalingUsageBoundsOneWindow5(
2481                                 NVDevEvoPtr pDevEvo, NvU32 window,
2482                                 const struct NvKmsScalingUsageBounds *pScaling,
2483                                 NvBool layerUsable,
2484                                 const NVHwModeViewPortEvo *pViewPort,
2485                                 NVEvoUpdateState *updateState)
2486 {
2487     NVEvoChannelPtr pChannel = pDevEvo->core;
2488     NvU32 setWindowUsageBounds = NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C5;
2489     NvU32 maxPixelsFetchedPerLine;
2490 
2491     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
2492 
2493     nvDmaSetStartEvoMethod(pChannel,
2494         NVC57D_WINDOW_SET_MAX_INPUT_SCALE_FACTOR(window), 1);
2495     nvDmaSetEvoMethodData(pChannel,
2496         DRF_NUM(C57D, _WINDOW_SET_MAX_INPUT_SCALE_FACTOR, _HORIZONTAL,
2497                 pScaling->maxHDownscaleFactor) |
2498         DRF_NUM(C57D, _WINDOW_SET_MAX_INPUT_SCALE_FACTOR, _VERTICAL,
2499                 pScaling->maxVDownscaleFactor));
2500 
2501     if (layerUsable) {
2502         maxPixelsFetchedPerLine = GetMaxPixelsFetchedPerLine(pViewPort->in.width,
2503                                                    pScaling->maxHDownscaleFactor);
2504     } else {
2505         maxPixelsFetchedPerLine = 0;
2506     }
2507 
2508     setWindowUsageBounds |=
2509         (DRF_NUM(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _MAX_PIXELS_FETCHED_PER_LINE,maxPixelsFetchedPerLine)) |
2510         (pScaling->vTaps >= NV_EVO_SCALER_5TAPS ?
2511             DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_5) :
2512             DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_2)) |
2513         (pScaling->vUpscalingAllowed ?
2514             DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _TRUE) :
2515             DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE));
2516     nvDmaSetStartEvoMethod(pChannel,
2517         NVC57D_WINDOW_SET_WINDOW_USAGE_BOUNDS(window), 1);
2518     nvDmaSetEvoMethodData(pChannel, setWindowUsageBounds);
2519 }
2520 
2521 static NvBool EvoSetUsageBounds3(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
2522                                  const struct NvKmsUsageBounds *pUsage,
2523                                  NVEvoUpdateState *updateState)
2524 {
2525     const struct NvKmsUsageBounds *pCurrentUsage =
2526         &pDevEvo->gpus[sd].headState[head].usage;
2527     /* Return FALSE if a core channel UPDATE isn't actually needed. */
2528     NvBool needCoreUpdate = FALSE;
2529     NvU32 layer;
2530 
2531     /* These methods should only apply to a single pDpy */
2532     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
2533 
2534     for (layer = 0; layer < pDevEvo->head[head].numLayers; layer++) {
2535         NvU64 currentFormats = 0;
2536         NvU64 targetFormats = 0;
2537 
2538         if (pCurrentUsage->layer[layer].usable) {
2539             currentFormats =
2540                 pCurrentUsage->layer[layer].supportedSurfaceMemoryFormats;
2541         }
2542 
2543         if (pUsage->layer[layer].usable) {
2544             targetFormats = pUsage->layer[layer].supportedSurfaceMemoryFormats;
2545         }
2546 
2547         if (targetFormats == currentFormats) {
2548             continue;
2549         }
2550 
2551         SetFormatUsageBoundsOneWindow3(pDevEvo,
2552                                        NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(
2553                                         pDevEvo->head[head].layer[layer]->channelMask),
2554                                        targetFormats,
2555                                        updateState);
2556         needCoreUpdate = TRUE;
2557     }
2558 
2559     return needCoreUpdate;
2560 }
2561 
2562 static NvBool EvoSetUsageBoundsC3(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
2563                                   const struct NvKmsUsageBounds *pUsage,
2564                                   NVEvoUpdateState *updateState)
2565 {
2566     return EvoSetUsageBounds3(pDevEvo, sd, head, pUsage, updateState);
2567 }
2568 
2569 static NvBool EvoSetUsageBoundsC5(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
2570                                   const struct NvKmsUsageBounds *pUsage,
2571                                   NVEvoUpdateState *updateState)
2572 {
2573     const struct NvKmsUsageBounds *pCurrentUsage =
2574         &pDevEvo->gpus[sd].headState[head].usage;
2575     NvBool needCoreUpdate;
2576     NvU32 layer;
2577 
2578     needCoreUpdate = EvoSetUsageBounds3(pDevEvo, sd, head, pUsage, updateState);
2579 
2580     for (layer = 0; layer < pDevEvo->head[head].numLayers; layer++) {
2581         if ((pCurrentUsage->layer[layer].usable != pUsage->layer[layer].usable) ||
2582             (!nvEvoScalingUsageBoundsEqual(&pCurrentUsage->layer[layer].scaling,
2583                                            &pUsage->layer[layer].scaling))) {
2584             const NVHwModeViewPortEvo *pViewPort =
2585                 &pDevEvo->gpus[sd].pDispEvo->headState[head].timings.viewPort;
2586 
2587             SetScalingUsageBoundsOneWindow5(
2588                 pDevEvo,
2589                 NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(
2590                     pDevEvo->head[head].layer[layer]->channelMask),
2591                 &pUsage->layer[layer].scaling,
2592                 pUsage->layer[layer].usable,
2593                 pViewPort,
2594                 updateState);
2595             needCoreUpdate = TRUE;
2596         }
2597     }
2598 
2599     return needCoreUpdate;
2600 }
2601 
2602 static void EvoSetNotifierC3(NVDevEvoRec *pDevEvo,
2603                              const NvBool notify,
2604                              const NvBool awaken,
2605                              const NvU32 notifier,
2606                              NVEvoUpdateState *updateState)
2607 {
2608     NVEvoChannelPtr pChannel = pDevEvo->core;
2609 
2610     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
2611 
2612     // To work around HW BUG 1945716, set the core channel completion notifier
2613     // context DMA to 0 when notification is not requested.
2614     if (notify) {
2615         NvU32 sd;
2616         for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
2617             if (nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)) {
2618                 nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
2619                 nvDmaSetStartEvoMethod(pChannel,
2620                     NVC37D_SET_CONTEXT_DMA_NOTIFIER, 1);
2621                 nvDmaSetEvoMethodData(pChannel,
2622                     DRF_NUM(C37D,
2623                             _SET_CONTEXT_DMA_NOTIFIER,
2624                             _HANDLE,
2625                             pDevEvo->core->notifiersDma[sd].ctxHandle));
2626                 nvPopEvoSubDevMask(pDevEvo);
2627             }
2628         }
2629     } else {
2630         nvDmaSetStartEvoMethod(pChannel,
2631             NVC37D_SET_CONTEXT_DMA_NOTIFIER, 1);
2632         nvDmaSetEvoMethodData(pChannel,
2633             DRF_NUM(C37D, _SET_CONTEXT_DMA_NOTIFIER, _HANDLE, 0));
2634     }
2635 
2636     /*
2637      * XXXnvdisplay: Note that nvdClass_01.mfs says:
2638      * "The units of the offset are 16 bytes.", while dispClass_02.mfs says:
2639      * "The units of the offset are 32 bit words."
2640      * The "legacy" 32-bit notifier format is no longer supported.  This will
2641      * have to be exposed to upper layers.
2642      */
2643     ASSERT_DRF_NUM(C37D, _SET_NOTIFIER_CONTROL, _OFFSET, notifier);
2644 
2645     nvDmaSetStartEvoMethod(pChannel, NVC37D_SET_NOTIFIER_CONTROL, 1);
2646     nvDmaSetEvoMethodData(pChannel,
2647         DRF_NUM(C37D, _SET_NOTIFIER_CONTROL, _OFFSET, notifier) |
2648         (awaken ?
2649             DRF_DEF(C37D, _SET_NOTIFIER_CONTROL, _MODE, _WRITE_AWAKEN) :
2650             DRF_DEF(C37D, _SET_NOTIFIER_CONTROL, _MODE, _WRITE)) |
2651         (notify ?
2652             DRF_DEF(C37D, _SET_NOTIFIER_CONTROL, _NOTIFY, _ENABLE) :
2653             DRF_DEF(C37D, _SET_NOTIFIER_CONTROL, _NOTIFY, _DISABLE)));
2654 }
2655 
2656 static void UpdateCoreC3(NVEvoChannelPtr pChannel,
2657                          NVEvoChannelMask interlockChannelMask,
2658                          NvU32 flipLockPin,
2659                          NvBool releaseElv)
2660 {
2661     NvU32 head, interlockFlags = 0;
2662     NvU32 window, windowInterlockFlags = 0;
2663     NvU32 update = DRF_NUM(C37D, _UPDATE, _FLIP_LOCK_PIN, flipLockPin);
2664 
2665     update |= releaseElv ? DRF_DEF(C37D, _UPDATE, _RELEASE_ELV, _TRUE) : 0;
2666 
2667     for (head = 0; head < NV_EVO_CHANNEL_MASK_CURSOR__SIZE; head++) {
2668         if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _CURSOR, head, _ENABLE,
2669                                interlockChannelMask)) {
2670             interlockFlags |=
2671                 DRF_IDX_DEF(C37D, _SET_INTERLOCK_FLAGS,
2672                             _INTERLOCK_WITH_CURSOR, head, _ENABLE);
2673         }
2674     }
2675 
2676     for (window = 0; window < NV_EVO_CHANNEL_MASK_WINDOW__SIZE; window++) {
2677         if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
2678                                interlockChannelMask)) {
2679             windowInterlockFlags |=
2680                 DRF_IDX_DEF(C37D, _SET_WINDOW_INTERLOCK_FLAGS,
2681                             _INTERLOCK_WITH_WINDOW, window, _ENABLE);
2682         }
2683     }
2684 
2685     nvDmaSetStartEvoMethod(pChannel, NVC37D_SET_INTERLOCK_FLAGS, 1);
2686     nvDmaSetEvoMethodData(pChannel, interlockFlags);
2687 
2688     nvDmaSetStartEvoMethod(pChannel, NVC37D_SET_WINDOW_INTERLOCK_FLAGS, 1);
2689     nvDmaSetEvoMethodData(pChannel, windowInterlockFlags);
2690 
2691     nvDmaSetStartEvoMethod(pChannel, NVC37D_UPDATE, 1);
2692     nvDmaSetEvoMethodData(pChannel, update);
2693 
2694     nvDmaKickoffEvo(pChannel);
2695 }
2696 
2697 static void UpdateWindowIMM(NVEvoChannelPtr pChannel,
2698                             NVEvoChannelMask winImmChannelMask,
2699                             NVEvoChannelMask winImmInterlockMask,
2700                             NvBool releaseElv)
2701 {
2702     nvAssert((winImmChannelMask & ~NV_EVO_CHANNEL_MASK_WINDOW_ALL) == 0);
2703     nvAssert((winImmInterlockMask & ~NV_EVO_CHANNEL_MASK_WINDOW_ALL) == 0);
2704 
2705     if ((winImmChannelMask & pChannel->channelMask) != 0) {
2706         NvU32 updateImm = 0;
2707 
2708         if ((winImmInterlockMask & pChannel->channelMask) != 0) {
2709             updateImm |= DRF_DEF(C37B, _UPDATE, _INTERLOCK_WITH_WINDOW, _ENABLE);
2710         } else {
2711             updateImm |= DRF_DEF(C37B, _UPDATE, _INTERLOCK_WITH_WINDOW, _DISABLE);
2712         }
2713         updateImm |= releaseElv ? DRF_DEF(C37B, _UPDATE, _RELEASE_ELV, _TRUE) : 0;
2714 
2715         nvDmaSetStartEvoMethod(pChannel->imm.u.dma, NVC37B_UPDATE, 1);
2716         nvDmaSetEvoMethodData(pChannel->imm.u.dma, updateImm);
2717         nvDmaKickoffEvo(pChannel->imm.u.dma);
2718     }
2719 }
2720 
2721 static void UpdateWindowC3(NVEvoChannelPtr pChannel,
2722                            NVEvoChannelMask interlockChannelMask,
2723                            NVEvoChannelMask winImmChannelMask,
2724                            NVEvoChannelMask winImmInterlockMask,
2725                            NvBool transitionWAR,
2726                            NvU32 flipLockPin,
2727                            NvBool releaseElv)
2728 {
2729     NvU32 head, interlockFlags = 0;
2730     NvU32 window, windowInterlockFlags = 0;
2731     NvU32 update = DRF_NUM(C37E, _UPDATE, _FLIP_LOCK_PIN, flipLockPin);
2732 
2733     update |= releaseElv ? DRF_DEF(C37E, _UPDATE, _RELEASE_ELV, _TRUE) : 0;
2734 
2735     if ((winImmInterlockMask & pChannel->channelMask) != 0) {
2736         /*
2737          * We expect winImmChannelMask to always be a superset of
2738          * winImmInterlockMask. We should never interlock with a window
2739          * immediate channel if we're not also going to kick off that
2740          * window immediate channel.
2741          */
2742         nvAssert((winImmChannelMask & pChannel->channelMask) != 0);
2743 
2744         update |= DRF_DEF(C37E, _UPDATE, _INTERLOCK_WITH_WIN_IMM, _ENABLE);
2745     } else {
2746         update |= DRF_DEF(C37E, _UPDATE, _INTERLOCK_WITH_WIN_IMM, _DISABLE);
2747     }
2748 
2749     // Nothing currently requires updating a window channel without releasing
2750     // ELV.
2751     nvAssert(releaseElv);
2752 
2753     if (FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
2754                        interlockChannelMask)) {
2755         interlockFlags |=
2756             DRF_DEF(C37E, _SET_INTERLOCK_FLAGS, _INTERLOCK_WITH_CORE, _ENABLE);
2757     }
2758 
2759     for (head = 0; head < NV_EVO_CHANNEL_MASK_CURSOR__SIZE; head++) {
2760         if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _CURSOR, head, _ENABLE,
2761                                interlockChannelMask)) {
2762             interlockFlags |=
2763                 DRF_IDX_DEF(C37E, _SET_INTERLOCK_FLAGS,
2764                             _INTERLOCK_WITH_CURSOR, head, _ENABLE);
2765         }
2766     }
2767 
2768     for (window = 0; window < NV_EVO_CHANNEL_MASK_WINDOW__SIZE; window++) {
2769         if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
2770                                interlockChannelMask)) {
2771             windowInterlockFlags |=
2772                 DRF_IDX_DEF(C37E, _SET_WINDOW_INTERLOCK_FLAGS,
2773                             _INTERLOCK_WITH_WINDOW, window, _ENABLE);
2774         }
2775     }
2776 
2777     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_INTERLOCK_FLAGS, 1);
2778     nvDmaSetEvoMethodData(pChannel, interlockFlags);
2779 
2780     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_WINDOW_INTERLOCK_FLAGS, 1);
2781     nvDmaSetEvoMethodData(pChannel, windowInterlockFlags);
2782 
2783     /*
2784      * If we determined that this update will transition from NULL to non-NULL
2785      * ctxdma or vice-versa, bookend this update method with software methods
2786      * to notify RM to apply a workaround for hardware bug 2193096.
2787      */
2788     if (transitionWAR) {
2789         nvDmaSetStartEvoMethod(pChannel, NVC57E_SW_SET_MCLK_SWITCH, 1);
2790         nvDmaSetEvoMethodData(pChannel,
2791             DRF_DEF(C57E, _SW_SET_MCLK_SWITCH, _ENABLE, _FALSE));
2792     }
2793 
2794     nvDmaSetStartEvoMethod(pChannel, NVC37E_UPDATE, 1);
2795     nvDmaSetEvoMethodData(pChannel, update);
2796 
2797     if (transitionWAR) {
2798         nvDmaSetStartEvoMethod(pChannel, NVC57E_SW_SET_MCLK_SWITCH, 1);
2799         nvDmaSetEvoMethodData(pChannel,
2800             DRF_DEF(C57E, _SW_SET_MCLK_SWITCH, _ENABLE, _TRUE));
2801     }
2802 
2803     UpdateWindowIMM(pChannel, winImmChannelMask,
2804                     winImmInterlockMask, releaseElv);
2805 
2806     nvDmaKickoffEvo(pChannel);
2807 }
2808 
2809 /*!
2810  * This function finds any fliplocked channels in the current update and pushes
2811  * flips for them setting the appropriate fliplock pin and interlock masks.
2812  *
2813  * All of this complexity is here to support the case where multiple heads on a
2814  * single GPU are fliplocked together, but flip requests come in for only a
2815  * subset of those heads at a time (e.g., separate X screens on a single GPU).
2816  * Unlike previous hardware, we're required to interlock all channels which are
2817  * part of a fliplock update, instead of just using fliplock across heads.
2818  */
2819 /*
2820  * There are two scenarios:
2821  * a) All fliplocked channels on this GPU are already part of this update.  In
2822  *    that case we just need to set the appropriate fliplock pin for each, and
2823  *    we're done -- they're already interlocked.
2824  * b) Some fliplocked channels are not part of this update.  We still need to
2825  *    set them in the interlock mask, but it's dangerous to interlock with any
2826  *    channels *not* in the fliplock group; as an example:
2827  *    With two separate X screens on a single GPU, each driving one monitor,
2828  *    fliplocked together, if we get a flip request for screen 0/head 0 that
2829  *    interlocks core and base, then a second flip request for screen 1/head1
2830  *    that interlocks core and base, we would end up programming one flip on
2831  *    the window on head 0, one flip on the window on head 1, and two flips in
2832  *    the core channel.  The second core channel flip would never complete
2833  *    since it would be waiting for an interlock with the other window
2834  *    channels.
2835  *
2836  *    To handle this case we pull the fliplocked channels out of this update
2837  *    and update them interlocked with all fliplocked channels (including those
2838  *    that aren't in this update), then proceed with a normal interlocked
2839  *    update excluding the fliplocked channels.
2840  *
2841  * \return Channel mask of channels which were handled by this function.
2842  *              Channels in this mask should be considered done and have no
2843  *              further updates pushed.  No other channels should be
2844  *              interlocked with them.
2845  */
2846 static NVEvoChannelMask ProcessFlipLockUpdates(
2847     NVDevEvoPtr pDevEvo,
2848     NvU32 sd,
2849     NvU32 *pFlipLockPin,
2850     const NVEvoUpdateState *updateState)
2851 {
2852     NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[sd];
2853     NvU32 head, window;
2854     /* Channels that are part of this update which need to be fliplocked. */
2855     NVEvoChannelMask flipLockUpdateMask = 0;
2856     /* All channels on this subdevice which are fliplocked. */
2857     NVEvoChannelMask flipLockAllMask = 0;
2858     /* Channels which this function has handled and do not need further
2859      * processing. */
2860     NVEvoChannelMask handledMask = 0;
2861     NVEvoLockPin pin = NV_EVO_LOCK_PIN_ERROR;
2862     NvU32 hwPin = NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN_NONE;
2863 
2864     /* First check if any of the fliplock-qualifying channels are actually
2865      * fliplocked, and determine which pin they're using. */
2866     for (head = 0; head < pDevEvo->numHeads; head++) {
2867         NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
2868 
2869         if (pHC->flipLock) {
2870             /* Convert the head index to a window index (two windows per head,
2871              * one "base" and one "overlay"; we only fliplock "base") */
2872             NVEvoChannelMask windowMask =
2873                 DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, head * 2, _ENABLE);
2874             if (updateState->subdev[sd].flipLockQualifyingMask & windowMask) {
2875                 if (flipLockUpdateMask == 0) {
2876                     pin = pHC->flipLockPin;
2877                 } else {
2878                     /* For now, we only support kicking off a single fliplock
2879                      * group as part of a single update call. */
2880                     nvAssert(pin == pHC->flipLockPin);
2881                 }
2882                 flipLockUpdateMask |= windowMask;
2883             }
2884         }
2885     }
2886 
2887     /* If we don't have any fliplocked updates, then we're done. */
2888     if (flipLockUpdateMask == 0) {
2889         goto done;
2890     }
2891 
2892     /*
2893      * Gather all of the channels on this GPU which are part of this fliplock
2894      * group (some of which may not be part of this update).
2895      */
2896     for (head = 0; head < pDevEvo->numHeads; head++) {
2897         NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
2898 
2899         if (pHC->flipLock && (pHC->flipLockPin == pin)) {
2900             NVEvoChannelMask windowMask =
2901                 DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, head * 2, _ENABLE);
2902             flipLockAllMask |= windowMask;
2903         }
2904     }
2905 
2906     /* Convert the pin to a hardware enum. */
2907     if (NV_EVO_LOCK_PIN_IS_INTERNAL(pin)) {
2908         hwPin = NVC37E_UPDATE_FLIP_LOCK_PIN_INTERNAL_FLIP_LOCK_0 +
2909                 (pin - NV_EVO_LOCK_PIN_INTERNAL_0);
2910     } else {
2911         hwPin = NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN(pin - NV_EVO_LOCK_PIN_0);
2912     }
2913 
2914     /* If we're updating all of the fliplocked channels in this update, we can
2915      * interlock with other channels as normal. */
2916     if (flipLockUpdateMask == flipLockAllMask) {
2917         goto done;
2918     }
2919 
2920     /*
2921      * Kick off each of our update channels, using the full fliplock mask and
2922      * hwPin calculated above.
2923      */
2924     nvAssert((flipLockUpdateMask & ~NV_EVO_CHANNEL_MASK_WINDOW_ALL) == 0);
2925     for (window = 0; window < pDevEvo->numWindows; window++) {
2926         const NVEvoChannelMask windowMask =
2927             DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE);
2928         NVEvoChannelMask winImmChannelMask =
2929             updateState->subdev[sd].winImmChannelMask;
2930         NVEvoChannelMask winImmInterlockMask =
2931             updateState->subdev[sd].winImmInterlockMask;
2932         if (flipLockUpdateMask & windowMask) {
2933             const NvBool transitionWAR =
2934                 (updateState->subdev[sd].flipTransitionWAR & windowMask) != 0;
2935             UpdateWindowC3(pDevEvo->window[window],
2936                            flipLockAllMask,
2937                            winImmChannelMask,
2938                            winImmInterlockMask,
2939                            transitionWAR,
2940                            hwPin, TRUE /* releaseElv */);
2941         } else {
2942             UpdateWindowIMM(pDevEvo->window[window], winImmChannelMask,
2943                             winImmInterlockMask, TRUE /* releaseElv */);
2944         }
2945     }
2946     handledMask = flipLockUpdateMask;
2947     hwPin = NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN_NONE;
2948 
2949 done:
2950     *pFlipLockPin = hwPin;
2951     return handledMask;
2952 }
2953 
2954 static void EvoUpdateC3(NVDevEvoPtr pDevEvo,
2955                         const NVEvoUpdateState *updateState,
2956                         NvBool releaseElv)
2957 {
2958     NvU32 sd, window;
2959 
2960     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
2961         NVEvoChannelMask updateChannelMask =
2962             updateState->subdev[sd].channelMask;
2963         const NVEvoChannelMask noCoreInterlockMask =
2964             updateState->subdev[sd].noCoreInterlockMask;
2965         NVEvoChannelMask coreInterlockMask =
2966             updateChannelMask & ~noCoreInterlockMask;
2967         const NvU32 subDeviceMask = (1 << sd);
2968         NvU32 flipLockPin = NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN_NONE;
2969 
2970         nvPushEvoSubDevMask(pDevEvo, subDeviceMask);
2971 
2972         if (updateState->subdev[sd].flipLockQualifyingMask) {
2973             NVEvoChannelMask handledChannels = 0;
2974 
2975             nvAssert((updateState->subdev[sd].flipLockQualifyingMask &
2976                       ~updateChannelMask) == 0);
2977             nvAssert((updateState->subdev[sd].flipLockQualifyingMask &
2978                       updateState->subdev[sd].noCoreInterlockMask) == 0);
2979 
2980             handledChannels =
2981                 ProcessFlipLockUpdates(pDevEvo, sd, &flipLockPin, updateState);
2982 
2983             updateChannelMask &= ~handledChannels;
2984             coreInterlockMask &= ~handledChannels;
2985         }
2986 
2987         if (FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
2988                            updateChannelMask)) {
2989             const NVEvoChannelMask thisInterlockMask =
2990                 FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
2991                                coreInterlockMask) ? coreInterlockMask : 0;
2992             UpdateCoreC3(pDevEvo->core, thisInterlockMask, flipLockPin,
2993                          releaseElv);
2994         }
2995 
2996         for (window = 0; window < pDevEvo->numWindows; window++) {
2997             const NVEvoChannelMask windowMask =
2998                 DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE);
2999             NVEvoChannelMask winImmChannelMask =
3000                 updateState->subdev[sd].winImmChannelMask;
3001             NVEvoChannelMask winImmInterlockMask =
3002                 updateState->subdev[sd].winImmInterlockMask;
3003             if (updateChannelMask & windowMask) {
3004                 const NvBool transitionWAR =
3005                     (updateState->subdev[sd].flipTransitionWAR & windowMask) != 0;
3006                 NVEvoChannelMask thisInterlockMask =
3007                     FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
3008                                        coreInterlockMask) ? coreInterlockMask : 0;
3009                 UpdateWindowC3(pDevEvo->window[window],
3010                                thisInterlockMask,
3011                                winImmChannelMask,
3012                                winImmInterlockMask,
3013                                transitionWAR,
3014                                flipLockPin,
3015                                releaseElv);
3016             } else {
3017                 UpdateWindowIMM(pDevEvo->window[window], winImmChannelMask,
3018                                 winImmInterlockMask, releaseElv);
3019             }
3020         }
3021 
3022         nvPopEvoSubDevMask(pDevEvo);
3023     }
3024 }
3025 
3026 /*!
3027  * Initialize head-specific IMP param fields.
3028  *
3029  * Initialize the NVC372_CTRL_IMP_HEAD for the specific head.
3030  *
3031  * \param[out]  pImpHead   The param structure to initialize.
3032  * \param[in]   pTimings   The rastering timings and viewport configuration.
3033  * \param[in]   head       The number of the head that will be driven.
3034  *
3035  * \return      FALSE iff the parameters aren't even legal for HW.
3036  */
3037 static NvBool AssignPerHeadImpParams(NVC372_CTRL_IMP_HEAD *pImpHead,
3038                                      const NVHwModeTimingsEvo *pTimings,
3039                                      const NvBool enableDsc,
3040                                      const NvBool b2Heads1Or,
3041                                      const int head,
3042                                      const NVEvoScalerCaps *pScalerCaps)
3043 {
3044     const NVHwModeViewPortEvo *pViewPort = &pTimings->viewPort;
3045     struct NvKmsScalingUsageBounds scalingUsageBounds = { };
3046 
3047     pImpHead->headIndex = head;
3048 
3049     /* raster timings */
3050 
3051     pImpHead->maxPixelClkKHz = pTimings->pixelClock;
3052 
3053     pImpHead->rasterSize.width           = pTimings->rasterSize.x;
3054     pImpHead->rasterSize.height          = pTimings->rasterSize.y;
3055     pImpHead->rasterBlankStart.X         = pTimings->rasterBlankStart.x;
3056     pImpHead->rasterBlankStart.Y         = pTimings->rasterBlankStart.y;
3057     pImpHead->rasterBlankEnd.X           = pTimings->rasterBlankEnd.x;
3058     pImpHead->rasterBlankEnd.Y           = pTimings->rasterBlankEnd.y;
3059     pImpHead->rasterVertBlank2.yStart    = pTimings->rasterVertBlank2Start;
3060     pImpHead->rasterVertBlank2.yEnd      = pTimings->rasterVertBlank2End;
3061 
3062     /* XXX TODO: Fill in correct scanlock information (only needed for
3063      * MIN_VPSTATE). */
3064     pImpHead->control.masterLockMode = NV_DISP_LOCK_MODE_NO_LOCK;
3065     pImpHead->control.masterLockPin = NV_DISP_LOCK_PIN_UNSPECIFIED;
3066     pImpHead->control.slaveLockMode = NV_DISP_LOCK_MODE_NO_LOCK;
3067     pImpHead->control.slaveLockPin = NV_DISP_LOCK_PIN_UNSPECIFIED;
3068 
3069     if (!nvComputeScalingUsageBounds(pScalerCaps,
3070                                     pViewPort->in.width, pViewPort->in.height,
3071                                     pViewPort->out.width, pViewPort->out.height,
3072                                     pViewPort->hTaps, pViewPort->vTaps,
3073                                     &scalingUsageBounds)) {
3074         return FALSE;
3075     }
3076     pImpHead->bUpscalingAllowedV = scalingUsageBounds.vUpscalingAllowed;
3077     pImpHead->maxDownscaleFactorV = scalingUsageBounds.maxVDownscaleFactor;
3078     pImpHead->maxDownscaleFactorH = scalingUsageBounds.maxHDownscaleFactor;
3079     pImpHead->outputScalerVerticalTaps =
3080         NVEvoScalerTapsToNum(scalingUsageBounds.vTaps);
3081 
3082     if (!ComputeMinFrameIdle(pTimings,
3083                              &pImpHead->minFrameIdle.leadingRasterLines,
3084                              &pImpHead->minFrameIdle.trailingRasterLines)) {
3085         return FALSE;
3086     }
3087 
3088     /* Assume we'll need the full 1025-entry output LUT. */
3089     pImpHead->lut = NVC372_CTRL_IMP_LUT_USAGE_1025;
3090 
3091     /* Cursor width, in units of 32 pixels.  Assume we use the maximum size. */
3092     pImpHead->cursorSize32p = 256 / 32;
3093 
3094     pImpHead->bEnableDsc = enableDsc;
3095 
3096     pImpHead->bIs2Head1Or = b2Heads1Or;
3097 
3098     pImpHead->bYUV420Format =
3099         (pTimings->yuv420Mode == NV_YUV420_MODE_HW);
3100 
3101     return TRUE;
3102 }
3103 
3104 /*!
3105  * Initialize window-specific IMP param fields.
3106  *
3107  * Initialize the NVC372_CTRL_IMP_WINDOW for the specific window.
3108  *
3109  * \param[out]  pImpWindow          The param structure to initialize.
3110  * \param[in]   pViewPort           The viewport configuration for the head that
3111  *                                  the window is bound to.
3112  * \param[in]   supportedFormats    The surface memory formats that can be
3113  *                                  supported on this window.
3114  * \param[in]   window              The number of the window.
3115  * \param[in]   head                The number of the head that the window is
3116  *                                  bound to.
3117  */
3118 static void AssignPerWindowImpParams(NVC372_CTRL_IMP_WINDOW *pImpWindow,
3119                                 const NVHwModeViewPortEvo *pViewPort,
3120                                 const NvU64 supportedFormats,
3121                                 const struct NvKmsScalingUsageBounds *pScaling,
3122                                 const int window,
3123                                 const int head)
3124 {
3125     pImpWindow->windowIndex = window;
3126     pImpWindow->owningHead = head;
3127 
3128     pImpWindow->formatUsageBound = 0;
3129     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED1BPP) {
3130         pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_RGB_PACKED_1_BPP;
3131     }
3132     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED2BPP) {
3133         pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_RGB_PACKED_2_BPP;
3134     }
3135     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED4BPP) {
3136         pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_RGB_PACKED_4_BPP;
3137     }
3138     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED8BPP) {
3139         pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_RGB_PACKED_8_BPP;
3140     }
3141     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PACKED422) {
3142         pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_YUV_PACKED_422;
3143     }
3144     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP420) {
3145         pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_YUV_SEMI_PLANAR_420;
3146     }
3147     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP422) {
3148         pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_YUV_SEMI_PLANAR_422;
3149     }
3150     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP444) {
3151         pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_YUV_SEMI_PLANAR_444;
3152     }
3153     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP420) {
3154         pImpWindow->formatUsageBound |=
3155             NVC372_CTRL_FORMAT_EXT_YUV_SEMI_PLANAR_420;
3156     }
3157     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP422) {
3158         pImpWindow->formatUsageBound |=
3159             NVC372_CTRL_FORMAT_EXT_YUV_SEMI_PLANAR_422;
3160     }
3161     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP444) {
3162         pImpWindow->formatUsageBound |=
3163             NVC372_CTRL_FORMAT_EXT_YUV_SEMI_PLANAR_444;
3164     }
3165     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PLANAR444) {
3166         pImpWindow->formatUsageBound |=
3167             NVC372_CTRL_FORMAT_YUV_PLANAR_444;
3168     }
3169     if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PLANAR420) {
3170         pImpWindow->formatUsageBound |=
3171             NVC372_CTRL_FORMAT_YUV_PLANAR_420;
3172     }
3173 
3174     if (pImpWindow->formatUsageBound == 0) {
3175         nvAssert(!"Unknown format in AssignPerWindowImpParams");
3176     }
3177 
3178     pImpWindow->maxPixelsFetchedPerLine =
3179         GetMaxPixelsFetchedPerLine(pViewPort->in.width,
3180                                    pScaling->maxHDownscaleFactor);
3181 
3182     pImpWindow->maxDownscaleFactorH = pScaling->maxHDownscaleFactor;
3183     pImpWindow->maxDownscaleFactorV = pScaling->maxVDownscaleFactor;
3184     pImpWindow->bUpscalingAllowedV = pScaling->vUpscalingAllowed;
3185     pImpWindow->inputScalerVerticalTaps =
3186         NVEvoScalerTapsToNum(pScaling->vTaps);
3187 
3188     /* Assume we need a full 1025-entry window (input) and tone-mapping
3189      * output (TMO) LUT. */
3190     pImpWindow->lut = NVC372_CTRL_IMP_LUT_USAGE_1025;
3191     pImpWindow->tmoLut = NVC372_CTRL_IMP_LUT_USAGE_1025;
3192 }
3193 
3194 static void
3195 EvoIsModePossibleC3(NVDispEvoPtr pDispEvo,
3196                     const NVEvoIsModePossibleDispInput *pInput,
3197                     NVEvoIsModePossibleDispOutput *pOutput)
3198 {
3199     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
3200     const NVEvoCapabilitiesPtr pEvoCaps = &pDevEvo->gpus[0].capabilities;
3201     NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS *pImp =
3202         nvPreallocGet(pDevEvo, PREALLOC_TYPE_IMP_PARAMS, sizeof(*pImp));
3203     NvBool result = FALSE;
3204     NvU32 head;
3205     NvU32 ret;
3206 
3207     nvkms_memset(pImp, 0, sizeof(*pImp));
3208 
3209     for (head = 0; head < NVKMS_MAX_HEADS_PER_DISP; head++) {
3210         const NVHwModeTimingsEvo *pTimings = pInput->head[head].pTimings;
3211         const NvU32 enableDsc = pInput->head[head].enableDsc;
3212         const NvBool b2Heads1Or = pInput->head[head].b2Heads1Or;
3213         const struct NvKmsUsageBounds *pUsage = pInput->head[head].pUsage;
3214         const NVHwModeViewPortEvo *pViewPort;
3215         NvU8 impHeadIndex;
3216         NvU32 layer;
3217 
3218         if (pTimings == NULL) {
3219             continue;
3220         }
3221 
3222         pViewPort = &pTimings->viewPort;
3223 
3224         impHeadIndex = pImp->numHeads;
3225         pImp->numHeads++;
3226         nvAssert(impHeadIndex < NVC372_CTRL_MAX_POSSIBLE_HEADS);
3227 
3228         if (!AssignPerHeadImpParams(&pImp->head[impHeadIndex],
3229                                     pTimings,
3230                                     enableDsc,
3231                                     b2Heads1Or,
3232                                     head,
3233                                     &pEvoCaps->head[head].scalerCaps)) {
3234             goto done;
3235         }
3236 
3237         /* XXXnvdisplay: This assumes a fixed window<->head mapping */
3238         for (layer = 0; layer < pDevEvo->head[head].numLayers; layer++) {
3239             if (!pUsage->layer[layer].usable) {
3240                 continue;
3241             }
3242 
3243             nvAssert(pImp->numWindows < NVC372_CTRL_MAX_POSSIBLE_WINDOWS);
3244 
3245             AssignPerWindowImpParams(
3246                 &pImp->window[pImp->numWindows],
3247                 pViewPort,
3248                 pUsage->layer[layer].supportedSurfaceMemoryFormats,
3249                 &pUsage->layer[layer].scaling,
3250                 NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(
3251                     pDevEvo->head[head].layer[layer]->channelMask),
3252                 head);
3253 
3254             pImp->numWindows++;
3255         }
3256     }
3257 
3258     pImp->base.subdeviceIndex = pDispEvo->displayOwner;
3259 
3260     /* XXXnvdisplay: Set bUseCachedPerfState? */
3261 
3262     /*
3263      * Set NEED_MIN_VPSTATE if reallocBandwidth != NONE. RM-IMP will only
3264      * output the min required display bandwidth values if NEED_MIN_VPSTATE
3265      * is set.
3266      */
3267     if (pInput->requireBootClocks ||
3268         (pInput->reallocBandwidth != NV_EVO_REALLOCATE_BANDWIDTH_MODE_NONE)) {
3269         // XXX TODO: IMP requires lock pin information if pstate information is
3270         // requested. For now, just assume no locking.
3271         pImp->options = NVC372_CTRL_IS_MODE_POSSIBLE_OPTIONS_NEED_MIN_VPSTATE;
3272     }
3273 
3274     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
3275                          pDevEvo->rmCtrlHandle,
3276                          NVC372_CTRL_CMD_IS_MODE_POSSIBLE,
3277                          pImp, sizeof(*pImp));
3278 
3279     // XXXnvdisplay TODO: check pImp->minImpVPState if
3280     // pInput->requireBootClocks is true?
3281     if (ret != NV_OK || !pImp->bIsPossible) {
3282         goto done;
3283     }
3284 
3285     result = TRUE;
3286 
3287 done:
3288     pOutput->possible = result;
3289     if (pOutput->possible) {
3290         pOutput->minRequiredBandwidthKBPS = pImp->minRequiredBandwidthKBPS;
3291         pOutput->floorBandwidthKBPS = pImp->floorBandwidthKBPS;
3292     }
3293 
3294     nvPreallocRelease(pDevEvo, PREALLOC_TYPE_IMP_PARAMS);
3295 }
3296 
3297 static void EvoPrePostIMPC3(NVDispEvoPtr pDispEvo, NvBool isPre)
3298 {
3299     /* Nothing to do on nvdisplay -- pre/post IMP calls are not required. */
3300 }
3301 
3302 static void
3303 EvoFlipC3(NVDevEvoPtr pDevEvo,
3304           NVEvoChannelPtr pChannel,
3305           const NVFlipChannelEvoHwState *pHwState,
3306           NVEvoUpdateState *updateState,
3307           NvBool bypassComposition);
3308 
3309 /*
3310  * Returns TRUE iff the CSC should be enabled (i.e., the matrix is not the
3311  * identity matrix).
3312  */
3313 static NvBool SetCscMatrixC3(NVEvoChannelPtr pChannel,
3314                              const struct NvKmsCscMatrix *matrix)
3315 {
3316     NvU32 method = NVC37E_SET_CSC_RED2RED;
3317     int y;
3318 
3319     if (nvIsCscMatrixIdentity(matrix)) {
3320         return FALSE;
3321     }
3322 
3323     for (y = 0; y < 3; y++) {
3324         int x;
3325 
3326         for (x = 0; x < 4; x++) {
3327             // Use DRF_NUM to truncate client-supplied values that are out of
3328             // range.
3329             NvU32 val = DRF_NUM(C37E, _SET_CSC_RED2RED, _COEFF,
3330                                 matrix->m[y][x]);
3331 
3332             nvDmaSetStartEvoMethod(pChannel, method, 1);
3333             nvDmaSetEvoMethodData(pChannel, val);
3334 
3335             method += 4;
3336         }
3337     }
3338 
3339     return TRUE;
3340 }
3341 
3342 static void SetCscMatrixC5Wrapper(NVEvoChannelPtr pChannel,
3343                                   const struct NvKmsCscMatrix *matrix,
3344                                   NvU32 coeffMethod, NvU32 controlMethod,
3345                                   NvU32 enableMethodData,
3346                                   NvU32 disableMethodData)
3347 {
3348     int y;
3349 
3350     if (nvIsCscMatrixIdentity(matrix)) {
3351         nvDmaSetStartEvoMethod(pChannel, controlMethod, 1);
3352         nvDmaSetEvoMethodData(pChannel, disableMethodData);
3353         return;
3354     }
3355 
3356     nvDmaSetStartEvoMethod(pChannel, controlMethod, 1);
3357     nvDmaSetEvoMethodData(pChannel, enableMethodData);
3358 
3359     for (y = 0; y < 3; y++) {
3360         int x;
3361 
3362         for (x = 0; x < 4; x++) {
3363             // Use DRF_NUM to truncate client-supplied values that are out of
3364             // range.
3365             //
3366             // Note that it doesn't matter whether we use the CSC00 or CSC11
3367             // methods to truncate since they're identical.
3368             NvU32 val = DRF_NUM(C57E, _SET_CSC00COEFFICIENT_C00, _VALUE,
3369                                 matrix->m[y][x]);
3370 
3371             nvDmaSetStartEvoMethod(pChannel, coeffMethod, 1);
3372             nvDmaSetEvoMethodData(pChannel, val);
3373 
3374             coeffMethod += 4;
3375         }
3376     }
3377 }
3378 
3379 static void SetCsc00MatrixC5(NVEvoChannelPtr pChannel,
3380                              const struct NvKmsCscMatrix *matrix)
3381 {
3382     SetCscMatrixC5Wrapper(pChannel,
3383                     matrix,
3384                     NVC57E_SET_CSC00COEFFICIENT_C00, NVC57E_SET_CSC00CONTROL,
3385                     DRF_DEF(C57E, _SET_CSC00CONTROL, _ENABLE, _ENABLE),
3386                     DRF_DEF(C57E, _SET_CSC00CONTROL, _ENABLE, _DISABLE));
3387 }
3388 
3389 static void SetCsc11MatrixC5(NVEvoChannelPtr pChannel,
3390                              const struct NvKmsCscMatrix *matrix)
3391 {
3392     SetCscMatrixC5Wrapper(pChannel,
3393                     matrix,
3394                     NVC57E_SET_CSC11COEFFICIENT_C00, NVC57E_SET_CSC11CONTROL,
3395                     DRF_DEF(C57E, _SET_CSC11CONTROL, _ENABLE, _ENABLE),
3396                     DRF_DEF(C57E, _SET_CSC11CONTROL, _ENABLE, _DISABLE));
3397 }
3398 
3399 /*
3400  * WAR for GV100 HW bug 1978592:
3401  *
3402  * Timestamped flips allow SW to specify the earliest time that the next UPDATE
3403  * will complete.  Due to a HW bug, GV100 waits for the timestamp in the ARMED
3404  * state (i.e. the timestamps that were pushed in the previous UPDATE) instead
3405  * of the timestamp in the ASSEMBLY state (the time we want to postpone this
3406  * flip until).
3407  *
3408  * This WAR inserts an additional UPDATE to push the timestamp from ASSEMBLY to
3409  * ARMED while changing no other state, so the following normal UPDATE can
3410  * wait for the correct timestamp.
3411  *
3412  * This update needs to have the following characteristics:
3413  *
3414  * - MIN_PRESENT_INTERVAL 0
3415  * - TIMESTAMP_MODE       _ENABLE
3416  * - All other SET_PRESENT_CONTROL fields unmodified from previous UPDATE
3417  * - SET_UPDATE_TIMESTAMP (target timestamp)
3418  * - RELEASE_ELV _FALSE
3419  * - Non-interlocked
3420  * - Non-fliplocked
3421  */
3422 static void
3423 InsertAdditionalTimestampFlip(NVDevEvoPtr pDevEvo,
3424                               NVEvoChannelPtr pChannel,
3425                               const NVFlipChannelEvoHwState *pHwState,
3426                               NVEvoUpdateState *updateState)
3427 {
3428     NvU32 presentControl = pChannel->oldPresentControl;
3429 
3430     /* This hardware bug is only present on GV100 which uses window
3431      * class C37E. */
3432     nvAssert(pChannel->hwclass == NVC37E_WINDOW_CHANNEL_DMA);
3433 
3434     nvAssert(pHwState->timeStamp != 0);
3435 
3436     /*
3437      * Update the necessary fields in SET_PRESENT_CONTROL without modifying
3438      * the existing values by using the cached SET_PRESENT_CONTROL values
3439      * from the previous update.
3440      *
3441      * Note that BEGIN_MODE must not be changed here; even though BEGIN_MODE
3442      * may currently be NON_TEARING, a NON_TEARING + MIN_PRESENT_INTERVAL 0
3443      * flip will be correctly collapsed with the surrounding
3444      * MIN_PRESENT_INTERVAL 1 flips.  If we were to change BEGIN_MODE to
3445      * IMMEDIATE, this would cause an additional delay due to the transition
3446      * from NON_TEARING to IMMEDIATE.
3447      */
3448     presentControl = FLD_SET_DRF_NUM(C37E, _SET_PRESENT_CONTROL,
3449                                      _MIN_PRESENT_INTERVAL,
3450                                      0, presentControl);
3451     presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL,
3452                                  _TIMESTAMP_MODE,
3453                                  _ENABLE, presentControl);
3454 
3455     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_PRESENT_CONTROL, 1);
3456     nvDmaSetEvoMethodData(pChannel, presentControl);
3457 
3458     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_UPDATE_TIMESTAMP_LO, 2);
3459     nvDmaSetEvoMethodData(pChannel, NvU64_LO32(pHwState->timeStamp));
3460     nvDmaSetEvoMethodData(pChannel, NvU64_HI32(pHwState->timeStamp));
3461 
3462     // Issue non-interlocked, non-fliplocked, non-ReleaseElv UPDATE
3463     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_INTERLOCK_FLAGS, 1);
3464     nvDmaSetEvoMethodData(pChannel, 0);
3465 
3466     nvDmaSetStartEvoMethod(pChannel,
3467                            NVC37E_SET_WINDOW_INTERLOCK_FLAGS,
3468                            1);
3469     nvDmaSetEvoMethodData(pChannel, 0);
3470 
3471     nvDmaSetStartEvoMethod(pChannel, NVC37E_UPDATE, 1);
3472     nvDmaSetEvoMethodData(pChannel,
3473         DRF_DEF(C37E, _UPDATE, _RELEASE_ELV, _FALSE) |
3474         DRF_NUM(C37E, _UPDATE, _FLIP_LOCK_PIN,
3475             NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN_NONE) |
3476         DRF_DEF(C37E, _UPDATE, _INTERLOCK_WITH_WIN_IMM,
3477             _DISABLE));
3478 }
3479 
3480 static void
3481 EvoProgramSemaphore3(NVDevEvoPtr pDevEvo,
3482                      NVEvoChannelPtr pChannel,
3483                      const NVFlipChannelEvoHwState *pHwState)
3484 {
3485     nvAssertSameSemaphoreSurface(pHwState);
3486 
3487     if (pHwState->syncObject.u.semaphores.acquireSurface.pSurfaceEvo == NULL) {
3488         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_SEMAPHORE, 1);
3489         nvDmaSetEvoMethodData(pChannel, 0);
3490         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_CONTROL, 1);
3491         nvDmaSetEvoMethodData(pChannel, 0);
3492         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_ACQUIRE, 1);
3493         nvDmaSetEvoMethodData(pChannel, 0);
3494         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_RELEASE, 1);
3495         nvDmaSetEvoMethodData(pChannel, 0);
3496     } else {
3497         const NVFlipNIsoSurfaceEvoHwState *pNIso =
3498             &pHwState->syncObject.u.semaphores.acquireSurface;
3499 
3500         nvAssert(pNIso->format == NVKMS_NISO_FORMAT_FOUR_WORD_NVDISPLAY);
3501         /* XXX nvdisplay: enforce this at a higher level */
3502         nvAssert((pNIso->offsetInWords % 4) == 0);
3503 
3504         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_SEMAPHORE, 1);
3505         nvDmaSetEvoMethodData(pChannel, pNIso->pSurfaceEvo->planes[0].ctxDma);
3506 
3507         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_ACQUIRE, 1);
3508         nvDmaSetEvoMethodData(pChannel,
3509             pHwState->syncObject.u.semaphores.acquireValue);
3510 
3511         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_RELEASE, 1);
3512         nvDmaSetEvoMethodData(pChannel,
3513             pHwState->syncObject.u.semaphores.releaseValue);
3514 
3515         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_CONTROL, 1);
3516         nvDmaSetEvoMethodData(pChannel, DRF_NUM(C37E, _SET_SEMAPHORE_CONTROL, _OFFSET,
3517                                          pNIso->offsetInWords / 4));
3518     }
3519 }
3520 
3521 /*!
3522  * On Tegra, syncpts are used for synchronization between SW and HW,
3523  * and also across HW engines. Since NvDisplay 4.0 only natively
3524  * understands semaphores, there's a SHIM layer in the memory subsystem
3525  * that will convert semaphore acquires/releases into corresponding
3526  * syncpoint reads/writes. As such, each syncpoint is mapped to an
3527  * underlying 'dummy' semaphore surface, and the methods for these surfaces
3528  * need to be programmed as if they were real memory-backed semaphores.
3529  */
3530 
3531 static void
3532 EvoProgramSemaphore6(NVDevEvoPtr pDevEvo,
3533                      NVEvoChannelPtr pChannel,
3534                      const NVFlipChannelEvoHwState *pHwState)
3535 {
3536     NvU32 hCtxDma, offset, acqMode, relMode, value;
3537     const NVFlipNIsoSurfaceEvoHwState *pNIso;
3538 
3539     /*! Program Acq-only semaphore */
3540     hCtxDma = offset = acqMode = relMode = value = 0;
3541     if (pHwState->syncObject.usingSyncpt) {
3542         if (pHwState->syncObject.u.syncpts.isPreSyncptSpecified) {
3543             NvU32 id = pHwState->syncObject.u.syncpts.preSyncpt;
3544             hCtxDma = pDevEvo->preSyncptTable[id].hCtxDma;
3545         } else {
3546             hCtxDma = 0;
3547         }
3548         offset = 0;
3549         acqMode = DRF_DEF(C67E, _SET_ACQ_SEMAPHORE_CONTROL, _ACQ_MODE, _CGEQ);
3550         value = pHwState->syncObject.u.syncpts.preValue;
3551     } else {
3552         if (pHwState->syncObject.u.semaphores.acquireSurface.pSurfaceEvo != NULL) {
3553             pNIso = &pHwState->syncObject.u.semaphores.acquireSurface;
3554             hCtxDma = pNIso->pSurfaceEvo->planes[0].ctxDma;
3555             offset = pNIso->offsetInWords / 4;
3556             acqMode = DRF_DEF(C67E, _SET_ACQ_SEMAPHORE_CONTROL, _ACQ_MODE, _EQ);
3557             value = pHwState->syncObject.u.semaphores.acquireValue;
3558         }
3559     }
3560 
3561     /*! set ctx dma handle */
3562     nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_CONTEXT_DMA_ACQ_SEMAPHORE, 1);
3563     nvDmaSetEvoMethodData(pChannel,
3564         DRF_NUM(C67E, _SET_CONTEXT_DMA_ACQ, _SEMAPHORE_HANDLE, hCtxDma));
3565     /*! set semaphore control and acq mode */
3566     nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_ACQ_SEMAPHORE_CONTROL, 1);
3567     nvDmaSetEvoMethodData(pChannel, offset | acqMode);
3568     /*! set semaphore value */
3569     nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_ACQ_SEMAPHORE_VALUE, 1);
3570     nvDmaSetEvoMethodData(pChannel,
3571         DRF_NUM(C67E, _SET_ACQ_SEMAPHORE_VALUE, _VALUE, value));
3572 
3573     /*! Program Rel-only semaphore */
3574     hCtxDma = offset = acqMode = relMode = value = 0;
3575     if (pHwState->syncObject.usingSyncpt) {
3576         hCtxDma = pHwState->syncObject.u.syncpts.postCtxDma;
3577         offset = 0;
3578         acqMode = DRF_DEF(C67E, _SET_SEMAPHORE_CONTROL, _SKIP_ACQ, _TRUE);
3579         relMode = DRF_DEF(C67E, _SET_SEMAPHORE_CONTROL, _REL_MODE, _WRITE);
3580         value = pHwState->syncObject.u.syncpts.postValue;
3581         /*! increase local max val as well */
3582         if (hCtxDma != 0) {
3583             pChannel->postSyncpt.syncptMaxVal++;
3584         }
3585     } else {
3586         if (pHwState->syncObject.u.semaphores.releaseSurface.pSurfaceEvo != NULL) {
3587             pNIso = &pHwState->syncObject.u.semaphores.releaseSurface;
3588             hCtxDma = pNIso->pSurfaceEvo->planes[0].ctxDma;
3589             offset = pNIso->offsetInWords / 4;
3590             acqMode = DRF_DEF(C67E, _SET_SEMAPHORE_CONTROL, _SKIP_ACQ, _TRUE);
3591             relMode = DRF_DEF(C67E, _SET_SEMAPHORE_CONTROL, _REL_MODE, _WRITE);
3592             value = pHwState->syncObject.u.semaphores.releaseValue;
3593         }
3594     }
3595 
3596     /*! set ctx dma handle */
3597     nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_CONTEXT_DMA_SEMAPHORE, 1);
3598     nvDmaSetEvoMethodData(pChannel,
3599         DRF_NUM(C67E, _SET_CONTEXT_DMA_SEMAPHORE, _HANDLE, hCtxDma));
3600     /*! set semaphore control and acq-rel mode */
3601     nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_SEMAPHORE_CONTROL, 1);
3602     nvDmaSetEvoMethodData(pChannel, offset | acqMode | relMode);
3603     /*! set semaphore value */
3604     nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_SEMAPHORE_RELEASE, 1);
3605     nvDmaSetEvoMethodData(pChannel,
3606         DRF_NUM(C67E, _SET_SEMAPHORE_RELEASE, _VALUE, value));
3607 }
3608 
3609 static NvBool
3610 EvoFlipC3Common(NVDevEvoPtr pDevEvo,
3611          NVEvoChannelPtr pChannel,
3612          const NVFlipChannelEvoHwState *pHwState,
3613          NVEvoUpdateState *updateState,
3614          NvU32 head)
3615 {
3616     const NvKmsSurfaceMemoryFormatInfo *pFormatInfo;
3617     NvU32 presentControl, eye;
3618     NvU32 storage;
3619     NvU8 planeIndex;
3620 
3621     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
3622 
3623     /* program notifier */
3624 
3625     if (pHwState->completionNotifier.surface.pSurfaceEvo == NULL) {
3626         /*
3627          * if no notifier surface is attached but vrr RGLine calculations
3628          * for frame pacing are enabled then we need to provide our own
3629          * surface and keep getting flip completion updates.
3630          */
3631         NvU32 offset;
3632         const NvU32 sdMask = nvPeekEvoSubDevMask(pDevEvo);
3633         const NvU32 sd = (sdMask == 0) ? 0 : __builtin_ffs(sdMask) - 1;
3634         NVDispHeadStateEvoRec *pHeadState = &pDevEvo->pDispEvo[sd]->headState[head];
3635         struct NvKmsVrrFramePacingInfo *pVrrFramePacingInfo = &pHeadState->vrrFramePacingInfo;
3636 
3637         if (pVrrFramePacingInfo->framePacingActive) {
3638             nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_NOTIFIER, 1);
3639             nvDmaSetEvoMethodData(pChannel,
3640                 DRF_NUM(C37E,
3641                         _SET_CONTEXT_DMA_NOTIFIER,
3642                         _HANDLE,
3643                         pChannel->notifiersDma[sd].ctxHandle));
3644 
3645             offset = nvPrepareNextVrrNotifier(pChannel, sd, head);
3646             nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_NOTIFIER_CONTROL, 1);
3647             nvDmaSetEvoMethodData(pChannel,
3648                 DRF_NUM(C37E, _SET_NOTIFIER_CONTROL, _OFFSET, offset) |
3649                 (DRF_DEF(C37E, _SET_NOTIFIER_CONTROL, _MODE, _WRITE_AWAKEN)));
3650         } else {
3651             nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_NOTIFIER, 1);
3652             nvDmaSetEvoMethodData(pChannel, 0);
3653             nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_NOTIFIER_CONTROL, 1);
3654             nvDmaSetEvoMethodData(pChannel, 0);
3655         }
3656     } else {
3657         const NVFlipNIsoSurfaceEvoHwState *pNIso =
3658             &pHwState->completionNotifier.surface;
3659         NvU32 value = 0;
3660 
3661         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_NOTIFIER, 1);
3662         nvDmaSetEvoMethodData(pChannel, pNIso->pSurfaceEvo->planes[0].ctxDma);
3663 
3664         nvAssert(pNIso->format == NVKMS_NISO_FORMAT_FOUR_WORD_NVDISPLAY);
3665         /* XXX nvdisplay: enforce this at a higher level */
3666         nvAssert((pNIso->offsetInWords % 4) == 0);
3667 
3668         value = FLD_SET_DRF_NUM(C37E, _SET_NOTIFIER_CONTROL, _OFFSET,
3669                                 pNIso->offsetInWords / 4, value);
3670 
3671         if (pHwState->completionNotifier.awaken) {
3672             value = FLD_SET_DRF(C37E, _SET_NOTIFIER_CONTROL, _MODE,
3673                                 _WRITE_AWAKEN, value);
3674         } else {
3675             value = FLD_SET_DRF(C37E, _SET_NOTIFIER_CONTROL, _MODE,
3676                                 _WRITE, value);
3677         }
3678 
3679         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_NOTIFIER_CONTROL, 1);
3680         nvDmaSetEvoMethodData(pChannel, value);
3681     }
3682 
3683     if (!pHwState->pSurfaceEvo[NVKMS_LEFT]) {
3684         // Disable this window, and set all its ctxdma entries to NULL.
3685         for (eye = 0; eye < NVKMS_MAX_EYES; eye++) {
3686             for (planeIndex = 0;
3687                  planeIndex < NVKMS_MAX_PLANES_PER_SURFACE;
3688                  planeIndex++) {
3689                 const NvU8 ctxDmaIdx = EyeAndPlaneToCtxDmaIdx(eye, planeIndex);
3690                 nvDmaSetStartEvoMethod(pChannel,
3691                                        NVC37E_SET_CONTEXT_DMA_ISO(ctxDmaIdx),
3692                                        1);
3693                 nvDmaSetEvoMethodData(pChannel, 0);
3694             }
3695         }
3696 
3697         return FALSE;
3698     }
3699 
3700     presentControl = DRF_NUM(C37E, _SET_PRESENT_CONTROL, _MIN_PRESENT_INTERVAL,
3701                              pHwState->minPresentInterval);
3702 
3703     if (pHwState->timeStamp != 0) {
3704         presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _TIMESTAMP_MODE,
3705                                      _ENABLE, presentControl);
3706     } else {
3707         presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _TIMESTAMP_MODE,
3708                                      _DISABLE, presentControl);
3709     }
3710 
3711     if (pHwState->tearing) {
3712         presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _BEGIN_MODE,
3713                                      _IMMEDIATE, presentControl);
3714     } else {
3715         presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _BEGIN_MODE,
3716                                      _NON_TEARING, presentControl);
3717     }
3718 
3719     if (pHwState->pSurfaceEvo[NVKMS_RIGHT]) {
3720         if (pHwState->perEyeStereoFlip) {
3721             presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _STEREO_MODE,
3722                                          _AT_ANY_FRAME, presentControl);
3723         } else {
3724             presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _STEREO_MODE,
3725                                          _PAIR_FLIP, presentControl);
3726         }
3727     } else {
3728         presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _STEREO_MODE,
3729                                      _MONO, presentControl);
3730     }
3731     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_PRESENT_CONTROL, 1);
3732     nvDmaSetEvoMethodData(pChannel, presentControl);
3733 
3734     /*
3735      * GV100 timestamped flips need a duplicate update which only changes
3736      * TIMESTAMP_MODE and MIN_PRESENT_INTERVAL fields in SET_PRESENT_CONTROL;
3737      * to allow updating these fields without changing anything else in
3738      * SET_PRESENT_CONTROL, cache the values we sent in previous flips here.
3739      * (bug 1990958)
3740      */
3741     pChannel->oldPresentControl = presentControl;
3742 
3743     /* Set the surface parameters. */
3744     FOR_ALL_EYES(eye) {
3745         const NVSurfaceEvoRec *pSurfaceEvoPerEye = pHwState->pSurfaceEvo[eye];
3746         NvU8 numSurfacePlanes = 0;
3747 
3748         if (pSurfaceEvoPerEye != NULL) {
3749             pFormatInfo =
3750                 nvKmsGetSurfaceMemoryFormatInfo(pSurfaceEvoPerEye->format);
3751             numSurfacePlanes = pFormatInfo->numPlanes;
3752         }
3753 
3754         for (planeIndex = 0;
3755              planeIndex < NVKMS_MAX_PLANES_PER_SURFACE;
3756              planeIndex++) {
3757             NvU32 ctxdma = 0;
3758             NvU64 offset = 0;
3759             const NvU8 ctxDmaIdx = EyeAndPlaneToCtxDmaIdx(eye, planeIndex);
3760 
3761             if (planeIndex < numSurfacePlanes) {
3762                 ctxdma = pSurfaceEvoPerEye->planes[planeIndex].ctxDma;
3763                 offset = pSurfaceEvoPerEye->planes[planeIndex].offset;
3764             }
3765 
3766             nvDmaSetStartEvoMethod(pChannel,
3767                                    NVC37E_SET_CONTEXT_DMA_ISO(ctxDmaIdx), 1);
3768             nvDmaSetEvoMethodData(pChannel, ctxdma);
3769             nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_OFFSET(ctxDmaIdx), 1);
3770             nvDmaSetEvoMethodData(pChannel, nvCtxDmaOffsetFromBytes(offset));
3771         }
3772     }
3773 
3774     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SIZE, 1);
3775     nvDmaSetEvoMethodData(pChannel,
3776         DRF_NUM(C37E, _SET_SIZE, _WIDTH, pHwState->pSurfaceEvo[NVKMS_LEFT]->widthInPixels) |
3777         DRF_NUM(C37E, _SET_SIZE, _HEIGHT, pHwState->pSurfaceEvo[NVKMS_LEFT]->heightInPixels));
3778 
3779     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SIZE_IN, 1);
3780     nvDmaSetEvoMethodData(pChannel,
3781         DRF_NUM(C37E, _SET_SIZE_IN, _WIDTH, pHwState->sizeIn.width) |
3782         DRF_NUM(C37E, _SET_SIZE_IN, _HEIGHT, pHwState->sizeIn.height));
3783 
3784     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SIZE_OUT, 1);
3785     nvDmaSetEvoMethodData(pChannel,
3786         DRF_NUM(C37E, _SET_SIZE_OUT, _WIDTH, pHwState->sizeOut.width) |
3787         DRF_NUM(C37E, _SET_SIZE_OUT, _HEIGHT, pHwState->sizeOut.height));
3788 
3789     /* XXX nvdisplay: enforce pitch/BL layout are consistent between eyes at a
3790      * higher level */
3791 
3792     storage = 0;
3793     if (pHwState->pSurfaceEvo[NVKMS_LEFT]->layout ==
3794         NvKmsSurfaceMemoryLayoutBlockLinear) {
3795         const NvU32 blockHeight = pHwState->pSurfaceEvo[NVKMS_LEFT]->log2GobsPerBlockY;
3796         storage |= DRF_NUM(C37E, _SET_STORAGE, _BLOCK_HEIGHT, blockHeight);
3797         if (pDevEvo->hal->caps.supportsSetStorageMemoryLayout) {
3798             storage |= DRF_DEF(C37E, _SET_STORAGE, _MEMORY_LAYOUT, _BLOCKLINEAR);
3799         }
3800     } else if (pDevEvo->hal->caps.supportsSetStorageMemoryLayout) {
3801         storage |= DRF_DEF(C37E, _SET_STORAGE, _MEMORY_LAYOUT, _PITCH);
3802     }
3803     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_STORAGE, 1);
3804     nvDmaSetEvoMethodData(pChannel, storage);
3805 
3806     pFormatInfo = nvKmsGetSurfaceMemoryFormatInfo(
3807                     pHwState->pSurfaceEvo[NVKMS_LEFT]->format);
3808 
3809     for (planeIndex = 0;
3810          planeIndex < NVKMS_MAX_PLANES_PER_SURFACE;
3811          planeIndex++) {
3812         NvU32 pitch;
3813 
3814         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_PLANAR_STORAGE(planeIndex),
3815                                1);
3816 
3817         if (planeIndex >= pFormatInfo->numPlanes) {
3818             nvDmaSetEvoMethodData(pChannel,
3819                 DRF_NUM(C37E, _SET_PLANAR_STORAGE, _PITCH, 0));
3820             continue;
3821         }
3822 
3823         /*
3824          * Per nvdClass_01.mfs, the HEAD_SET_STORAGE_PITCH "units are blocks
3825          * if the layout is BLOCKLINEAR, the units are multiples of 64 bytes
3826          * if the layout is PITCH."
3827          */
3828         pitch = pHwState->pSurfaceEvo[NVKMS_LEFT]->planes[planeIndex].pitch;
3829         if (pHwState->pSurfaceEvo[NVKMS_LEFT]->layout ==
3830             NvKmsSurfaceMemoryLayoutBlockLinear) {
3831             /* pitch is already in units of blocks; no conversion needed. */
3832             nvDmaSetEvoMethodData(pChannel,
3833                 DRF_NUM(C37E, _SET_PLANAR_STORAGE, _PITCH, pitch));
3834         } else {
3835             /* XXX nvdisplay: enforce this at a higher level */
3836             nvAssert((pitch & 63) == 0);
3837             nvDmaSetEvoMethodData(pChannel,
3838                 DRF_NUM(C37E, _SET_PLANAR_STORAGE, _PITCH, pitch >> 6));
3839         }
3840     }
3841 
3842     ASSERT_EYES_MATCH(pHwState->pSurfaceEvo, format);
3843 
3844     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_UPDATE_TIMESTAMP_LO, 2);
3845     nvDmaSetEvoMethodData(pChannel, NvU64_LO32(pHwState->timeStamp));
3846     nvDmaSetEvoMethodData(pChannel, NvU64_HI32(pHwState->timeStamp));
3847 
3848     return TRUE;
3849 }
3850 
3851 /*
3852  * This function returns TRUE if precomp needs to swap the U and V components to
3853  * support the given input surface format. For all such formats,
3854  * SetParams.SwapUV needs to be enabled.
3855  *
3856  * Due to the "feature" described in bug 1640117, there's a mismatch in the
3857  * ihub<->precomp interface:
3858  * - For all Yx___UxVx_N444 and Yx___UxVx_N422 formats, ihub will fetch and send
3859  *   the V sample as the first chroma byte, and the U sample as the second byte.
3860  *   However, precomp expects the U sample as the first byte, and the V sample
3861  *   as the second byte.
3862  * - For all Yx___VxUx_N420 formats, ihub will fetch and send the U sample as
3863  *   the first chroma byte, and the V sample as the second byte.
3864  *   However, precomp expects the V sample as the first byte, and the U sample
3865  *   as the second byte.
3866  *
3867  * In the above explanation, note that ihub simply fetches and sends the chroma
3868  * bytes in the same order that they're packed in memory.
3869  */
3870 static NvBool IsSurfaceFormatUVSwapped(
3871     const enum NvKmsSurfaceMemoryFormat format)
3872 {
3873     switch (format) {
3874     case NvKmsSurfaceMemoryFormatY8___U8V8_N444:
3875     case NvKmsSurfaceMemoryFormatY8___U8V8_N422:
3876     case NvKmsSurfaceMemoryFormatY8___V8U8_N420:
3877     case NvKmsSurfaceMemoryFormatY10___U10V10_N444:
3878     case NvKmsSurfaceMemoryFormatY10___U10V10_N422:
3879     case NvKmsSurfaceMemoryFormatY10___V10U10_N420:
3880     case NvKmsSurfaceMemoryFormatY12___U12V12_N444:
3881     case NvKmsSurfaceMemoryFormatY12___U12V12_N422:
3882     case NvKmsSurfaceMemoryFormatY12___V12U12_N420:
3883         return TRUE;
3884     case NvKmsSurfaceMemoryFormatY8_U8__Y8_V8_N422:
3885     case NvKmsSurfaceMemoryFormatU8_Y8__V8_Y8_N422:
3886     case NvKmsSurfaceMemoryFormatY8___V8U8_N444:
3887     case NvKmsSurfaceMemoryFormatY8___V8U8_N422:
3888     case NvKmsSurfaceMemoryFormatY8___U8V8_N420:
3889     case NvKmsSurfaceMemoryFormatY10___V10U10_N444:
3890     case NvKmsSurfaceMemoryFormatY10___V10U10_N422:
3891     case NvKmsSurfaceMemoryFormatY10___U10V10_N420:
3892     case NvKmsSurfaceMemoryFormatY12___V12U12_N444:
3893     case NvKmsSurfaceMemoryFormatY12___V12U12_N422:
3894     case NvKmsSurfaceMemoryFormatY12___U12V12_N420:
3895     case NvKmsSurfaceMemoryFormatY8___U8___V8_N444:
3896     case NvKmsSurfaceMemoryFormatY8___U8___V8_N420:
3897         return FALSE;
3898     case NvKmsSurfaceMemoryFormatI8:
3899     case NvKmsSurfaceMemoryFormatA1R5G5B5:
3900     case NvKmsSurfaceMemoryFormatX1R5G5B5:
3901     case NvKmsSurfaceMemoryFormatR5G6B5:
3902     case NvKmsSurfaceMemoryFormatA8R8G8B8:
3903     case NvKmsSurfaceMemoryFormatX8R8G8B8:
3904     case NvKmsSurfaceMemoryFormatA8B8G8R8:
3905     case NvKmsSurfaceMemoryFormatX8B8G8R8:
3906     case NvKmsSurfaceMemoryFormatA2B10G10R10:
3907     case NvKmsSurfaceMemoryFormatX2B10G10R10:
3908     case NvKmsSurfaceMemoryFormatRF16GF16BF16AF16:
3909     case NvKmsSurfaceMemoryFormatRF16GF16BF16XF16:
3910     case NvKmsSurfaceMemoryFormatR16G16B16A16:
3911     case NvKmsSurfaceMemoryFormatRF32GF32BF32AF32:
3912         return FALSE;
3913     }
3914 
3915     return FALSE;
3916 }
3917 
3918 /*
3919  * Map the given NvKmsSurfaceMemoryFormat to its corresponding HW format for the
3920  * C370 (Volta) NVDISPLAY class.
3921  *
3922  * Volta supports YUV422 packed, but this function excludes the corresponding
3923  * mappings because the required programming support hasn't been added to NVKMS
3924  * yet.
3925  *
3926  * Return 0 in the case of an unrecognized NvKmsSurfaceMemoryFormat.
3927  */
3928 static NvU32 nvHwFormatFromKmsFormatC3(
3929     const enum NvKmsSurfaceMemoryFormat format)
3930 {
3931     switch (format) {
3932     case NvKmsSurfaceMemoryFormatI8:
3933         return NVC37E_SET_PARAMS_FORMAT_I8;
3934     case NvKmsSurfaceMemoryFormatA1R5G5B5:
3935     case NvKmsSurfaceMemoryFormatX1R5G5B5:
3936         return NVC37E_SET_PARAMS_FORMAT_A1R5G5B5;
3937     case NvKmsSurfaceMemoryFormatR5G6B5:
3938         return NVC37E_SET_PARAMS_FORMAT_R5G6B5;
3939     case NvKmsSurfaceMemoryFormatA8R8G8B8:
3940         return NVC37E_SET_PARAMS_FORMAT_A8R8G8B8;
3941     case NvKmsSurfaceMemoryFormatX8R8G8B8:
3942         return NVC37E_SET_PARAMS_FORMAT_X8R8G8B8;
3943     case NvKmsSurfaceMemoryFormatA8B8G8R8:
3944         return NVC37E_SET_PARAMS_FORMAT_A8B8G8R8;
3945     case NvKmsSurfaceMemoryFormatX8B8G8R8:
3946         return NVC37E_SET_PARAMS_FORMAT_X8B8G8R8;
3947     case NvKmsSurfaceMemoryFormatA2B10G10R10:
3948         return NVC37E_SET_PARAMS_FORMAT_A2B10G10R10;
3949     case NvKmsSurfaceMemoryFormatX2B10G10R10:
3950         return NVC37E_SET_PARAMS_FORMAT_X2BL10GL10RL10_XRBIAS;
3951     case NvKmsSurfaceMemoryFormatRF16GF16BF16AF16:
3952     case NvKmsSurfaceMemoryFormatRF16GF16BF16XF16:
3953         return NVC37E_SET_PARAMS_FORMAT_RF16_GF16_BF16_AF16;
3954     case NvKmsSurfaceMemoryFormatR16G16B16A16:
3955         return NVC37E_SET_PARAMS_FORMAT_R16_G16_B16_A16;
3956     case NvKmsSurfaceMemoryFormatRF32GF32BF32AF32:
3957     case NvKmsSurfaceMemoryFormatY8_U8__Y8_V8_N422:
3958     case NvKmsSurfaceMemoryFormatU8_Y8__V8_Y8_N422:
3959     case NvKmsSurfaceMemoryFormatY8___U8V8_N444:
3960     case NvKmsSurfaceMemoryFormatY8___V8U8_N444:
3961     case NvKmsSurfaceMemoryFormatY8___U8V8_N422:
3962     case NvKmsSurfaceMemoryFormatY8___V8U8_N422:
3963     case NvKmsSurfaceMemoryFormatY8___U8V8_N420:
3964     case NvKmsSurfaceMemoryFormatY8___V8U8_N420:
3965     case NvKmsSurfaceMemoryFormatY10___U10V10_N444:
3966     case NvKmsSurfaceMemoryFormatY10___V10U10_N444:
3967     case NvKmsSurfaceMemoryFormatY10___U10V10_N422:
3968     case NvKmsSurfaceMemoryFormatY10___V10U10_N422:
3969     case NvKmsSurfaceMemoryFormatY10___U10V10_N420:
3970     case NvKmsSurfaceMemoryFormatY10___V10U10_N420:
3971     case NvKmsSurfaceMemoryFormatY12___U12V12_N444:
3972     case NvKmsSurfaceMemoryFormatY12___V12U12_N444:
3973     case NvKmsSurfaceMemoryFormatY12___U12V12_N422:
3974     case NvKmsSurfaceMemoryFormatY12___V12U12_N422:
3975     case NvKmsSurfaceMemoryFormatY12___U12V12_N420:
3976     case NvKmsSurfaceMemoryFormatY12___V12U12_N420:
3977     case NvKmsSurfaceMemoryFormatY8___U8___V8_N444:
3978     case NvKmsSurfaceMemoryFormatY8___U8___V8_N420:
3979         return 0;
3980     }
3981 
3982     return 0;
3983 }
3984 
3985 /*
3986  * Map the given NvKmsSurfaceMemoryFormat to its corresponding HW format for the
3987  * C570 (Turing) NVDISPLAY class.
3988  *
3989  * Return 0 in the case of an unrecognized NvKmsSurfaceMemoryFormat.
3990  */
3991 static NvU32 nvHwFormatFromKmsFormatC5(
3992     const enum NvKmsSurfaceMemoryFormat format)
3993 {
3994     switch (format) {
3995     case NvKmsSurfaceMemoryFormatY8_U8__Y8_V8_N422:
3996         return NVC57E_SET_PARAMS_FORMAT_Y8_U8__Y8_V8_N422;
3997     case NvKmsSurfaceMemoryFormatU8_Y8__V8_Y8_N422:
3998         return NVC57E_SET_PARAMS_FORMAT_U8_Y8__V8_Y8_N422;
3999     case NvKmsSurfaceMemoryFormatY8___U8V8_N444:
4000     case NvKmsSurfaceMemoryFormatY8___V8U8_N444:
4001         return NVC57E_SET_PARAMS_FORMAT_Y8___U8V8_N444;
4002     case NvKmsSurfaceMemoryFormatY8___U8V8_N422:
4003     case NvKmsSurfaceMemoryFormatY8___V8U8_N422:
4004         return NVC57E_SET_PARAMS_FORMAT_Y8___U8V8_N422;
4005     case NvKmsSurfaceMemoryFormatY8___U8V8_N420:
4006     case NvKmsSurfaceMemoryFormatY8___V8U8_N420:
4007         return NVC57E_SET_PARAMS_FORMAT_Y8___V8U8_N420;
4008     case NvKmsSurfaceMemoryFormatY10___U10V10_N444:
4009     case NvKmsSurfaceMemoryFormatY10___V10U10_N444:
4010         return NVC57E_SET_PARAMS_FORMAT_Y10___U10V10_N444;
4011     case NvKmsSurfaceMemoryFormatY10___U10V10_N422:
4012     case NvKmsSurfaceMemoryFormatY10___V10U10_N422:
4013         return NVC57E_SET_PARAMS_FORMAT_Y10___U10V10_N422;
4014     case NvKmsSurfaceMemoryFormatY10___U10V10_N420:
4015     case NvKmsSurfaceMemoryFormatY10___V10U10_N420:
4016         return NVC57E_SET_PARAMS_FORMAT_Y10___V10U10_N420;
4017     case NvKmsSurfaceMemoryFormatY12___U12V12_N444:
4018     case NvKmsSurfaceMemoryFormatY12___V12U12_N444:
4019         return NVC57E_SET_PARAMS_FORMAT_Y12___U12V12_N444;
4020     case NvKmsSurfaceMemoryFormatY12___U12V12_N422:
4021     case NvKmsSurfaceMemoryFormatY12___V12U12_N422:
4022         return NVC57E_SET_PARAMS_FORMAT_Y12___U12V12_N422;
4023     case NvKmsSurfaceMemoryFormatY12___U12V12_N420:
4024     case NvKmsSurfaceMemoryFormatY12___V12U12_N420:
4025         return NVC57E_SET_PARAMS_FORMAT_Y12___V12U12_N420;
4026     case NvKmsSurfaceMemoryFormatY8___U8___V8_N444:
4027     case NvKmsSurfaceMemoryFormatY8___U8___V8_N420:
4028     case NvKmsSurfaceMemoryFormatI8:
4029     case NvKmsSurfaceMemoryFormatA1R5G5B5:
4030     case NvKmsSurfaceMemoryFormatX1R5G5B5:
4031     case NvKmsSurfaceMemoryFormatR5G6B5:
4032     case NvKmsSurfaceMemoryFormatA8R8G8B8:
4033     case NvKmsSurfaceMemoryFormatX8R8G8B8:
4034     case NvKmsSurfaceMemoryFormatA8B8G8R8:
4035     case NvKmsSurfaceMemoryFormatX8B8G8R8:
4036     case NvKmsSurfaceMemoryFormatA2B10G10R10:
4037     case NvKmsSurfaceMemoryFormatX2B10G10R10:
4038     case NvKmsSurfaceMemoryFormatRF16GF16BF16AF16:
4039     case NvKmsSurfaceMemoryFormatRF16GF16BF16XF16:
4040     case NvKmsSurfaceMemoryFormatR16G16B16A16:
4041     case NvKmsSurfaceMemoryFormatRF32GF32BF32AF32:
4042         return nvHwFormatFromKmsFormatC3(format);
4043     }
4044 
4045     return 0;
4046 }
4047 
4048 /*
4049  * Map the given NvKmsSurfaceMemoryFormat to its corresponding HW format for the
4050  * C670 (Orin and Ampere) NVDISPLAY class.
4051  *
4052  * Return 0 in the case of an unrecognized NvKmsSurfaceMemoryFormat.
4053  */
4054 static NvU32 nvHwFormatFromKmsFormatC6(
4055     const enum NvKmsSurfaceMemoryFormat format)
4056 {
4057     switch (format) {
4058     case NvKmsSurfaceMemoryFormatY8___U8___V8_N444:
4059         return NVC67E_SET_PARAMS_FORMAT_Y8___U8___V8_N444;
4060     case NvKmsSurfaceMemoryFormatY8___U8___V8_N420:
4061         return NVC67E_SET_PARAMS_FORMAT_Y8___U8___V8_N420;
4062     case NvKmsSurfaceMemoryFormatX2B10G10R10:
4063         return NVC67E_SET_PARAMS_FORMAT_A2B10G10R10;
4064     case NvKmsSurfaceMemoryFormatY8_U8__Y8_V8_N422:
4065     case NvKmsSurfaceMemoryFormatU8_Y8__V8_Y8_N422:
4066     case NvKmsSurfaceMemoryFormatY8___U8V8_N444:
4067     case NvKmsSurfaceMemoryFormatY8___V8U8_N444:
4068     case NvKmsSurfaceMemoryFormatY8___U8V8_N422:
4069     case NvKmsSurfaceMemoryFormatY8___V8U8_N422:
4070     case NvKmsSurfaceMemoryFormatY8___U8V8_N420:
4071     case NvKmsSurfaceMemoryFormatY8___V8U8_N420:
4072     case NvKmsSurfaceMemoryFormatY10___U10V10_N444:
4073     case NvKmsSurfaceMemoryFormatY10___V10U10_N444:
4074     case NvKmsSurfaceMemoryFormatY10___U10V10_N422:
4075     case NvKmsSurfaceMemoryFormatY10___V10U10_N422:
4076     case NvKmsSurfaceMemoryFormatY10___U10V10_N420:
4077     case NvKmsSurfaceMemoryFormatY10___V10U10_N420:
4078     case NvKmsSurfaceMemoryFormatY12___U12V12_N444:
4079     case NvKmsSurfaceMemoryFormatY12___V12U12_N444:
4080     case NvKmsSurfaceMemoryFormatY12___U12V12_N422:
4081     case NvKmsSurfaceMemoryFormatY12___V12U12_N422:
4082     case NvKmsSurfaceMemoryFormatY12___U12V12_N420:
4083     case NvKmsSurfaceMemoryFormatY12___V12U12_N420:
4084     case NvKmsSurfaceMemoryFormatI8:
4085     case NvKmsSurfaceMemoryFormatA1R5G5B5:
4086     case NvKmsSurfaceMemoryFormatX1R5G5B5:
4087     case NvKmsSurfaceMemoryFormatR5G6B5:
4088     case NvKmsSurfaceMemoryFormatA8R8G8B8:
4089     case NvKmsSurfaceMemoryFormatX8R8G8B8:
4090     case NvKmsSurfaceMemoryFormatA8B8G8R8:
4091     case NvKmsSurfaceMemoryFormatX8B8G8R8:
4092     case NvKmsSurfaceMemoryFormatA2B10G10R10:
4093     case NvKmsSurfaceMemoryFormatRF16GF16BF16AF16:
4094     case NvKmsSurfaceMemoryFormatRF16GF16BF16XF16:
4095     case NvKmsSurfaceMemoryFormatR16G16B16A16:
4096     case NvKmsSurfaceMemoryFormatRF32GF32BF32AF32:
4097         return nvHwFormatFromKmsFormatC5(format);
4098     }
4099 
4100     return 0;
4101 }
4102 
4103 static
4104 NVLutSurfaceEvoPtr EvoGetLutSurface3(NVDevEvoPtr pDevEvo,
4105                                      NVEvoChannelPtr pChannel,
4106                                      const NVFlipChannelEvoHwState *pHwState)
4107 {
4108     NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
4109     NvU32 head = pDevEvo->headForWindow[win];
4110     NvBool found = FALSE;
4111     const NVDispEvoRec *pDispEvo = NULL;
4112     NvU32 sd;
4113 
4114     if ((pHwState->pSurfaceEvo[NVKMS_LEFT] == NULL) ||
4115         (head == NV_INVALID_HEAD)) {
4116         return NULL;
4117     }
4118 
4119     /* Input Lut is explicitly enabled by client */
4120     if (pHwState->inputLut.pLutSurfaceEvo != NULL) {
4121         return pHwState->inputLut.pLutSurfaceEvo;
4122     }
4123 
4124     /*
4125      * For everything but I8 surfaces, we can just use the specified
4126      * LUT, even if it's NULL.
4127      * For I8 surfaces, we can only use the specified surface if it's
4128      * non-NULL (an input LUT is required).
4129      */
4130     if (pHwState->pSurfaceEvo[NVKMS_LEFT]->format !=
4131         NvKmsSurfaceMemoryFormatI8) {
4132         return NULL;
4133     }
4134 
4135     /*
4136      * The rest of the function is to handle the I8 case where no input
4137      * LUT was specified: look up the LUT to use from the device.
4138      */
4139 
4140     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
4141         if (nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)) {
4142             if (found) {
4143                 nvAssert(pDispEvo == pDevEvo->gpus[sd].pDispEvo);
4144             } else {
4145                 pDispEvo = pDevEvo->gpus[sd].pDispEvo;
4146                 found = TRUE;
4147             }
4148         }
4149     }
4150 
4151     nvAssert(found);
4152 
4153     /*
4154      * It is not allowed to change the input LUT on immediate flips. The
4155      * higher-level code should makes sure to disable tearing if there is change
4156      * in the surface format and curLUTIndex does not change until next
4157      * EvoSetLUTContextDma3() call which also makes sure to disable tearing.
4158      */
4159     return pDispEvo->headState[head].lut.pCurrSurface;
4160 }
4161 
4162 static void
4163 EvoFlipC3(NVDevEvoPtr pDevEvo,
4164           NVEvoChannelPtr pChannel,
4165           const NVFlipChannelEvoHwState *pHwState,
4166           NVEvoUpdateState *updateState,
4167           NvBool bypassComposition)
4168 {
4169     NvBool enableCSC, swapUV, flip3Return;
4170     enum NvKmsSurfaceMemoryFormat format;
4171     NVLutSurfaceEvoPtr pLutSurfaceEvo =
4172         EvoGetLutSurface3(pDevEvo, pChannel, pHwState);
4173     NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
4174     NvU32 head = pDevEvo->headForWindow[win];
4175 
4176     if (pHwState->timeStamp != 0) {
4177         InsertAdditionalTimestampFlip(pDevEvo, pChannel, pHwState,
4178                                       updateState);
4179     }
4180 
4181     flip3Return = EvoFlipC3Common(pDevEvo, pChannel, pHwState, updateState, head);
4182 
4183     /* program semaphore */
4184     EvoProgramSemaphore3(pDevEvo, pChannel, pHwState);
4185 
4186     if (!flip3Return) {
4187         return;
4188     }
4189 
4190     format = pHwState->pSurfaceEvo[NVKMS_LEFT]->format;
4191 
4192     enableCSC = SetCscMatrixC3(pChannel, &pHwState->cscMatrix);
4193     swapUV = IsSurfaceFormatUVSwapped(format);
4194     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_PARAMS, 1);
4195     nvDmaSetEvoMethodData(pChannel,
4196         (enableCSC ? DRF_DEF(C37E, _SET_PARAMS, _CSC, _ENABLE) :
4197                      DRF_DEF(C37E, _SET_PARAMS, _CSC, _DISABLE)) |
4198         DRF_NUM(C37E, _SET_PARAMS, _FORMAT, nvHwFormatFromKmsFormatC3(format)) |
4199         (swapUV ? DRF_DEF(C37E, _SET_PARAMS, _SWAP_UV, _ENABLE) :
4200                   DRF_DEF(C37E, _SET_PARAMS, _SWAP_UV, _DISABLE)) |
4201         DRF_DEF(C37E, _SET_PARAMS, _UNDERREPLICATE, _DISABLE));
4202 
4203     if (pLutSurfaceEvo) {
4204         const NvU32 ctxDma = pLutSurfaceEvo->dispCtxDma;
4205         const NvU32 origin = offsetof(NVEvoLutDataRec, base);
4206 
4207         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTROL_INPUT_LUT, 1);
4208         nvDmaSetEvoMethodData(pChannel,
4209             DRF_DEF(C37E, _SET_CONTROL_INPUT_LUT, _SIZE, _SIZE_1025) |
4210             DRF_DEF(C37E, _SET_CONTROL_INPUT_LUT, _RANGE, _UNITY) |
4211             DRF_DEF(C37E, _SET_CONTROL_INPUT_LUT, _OUTPUT_MODE, _INDEX));
4212 
4213         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_OFFSET_INPUT_LUT, 1);
4214         nvDmaSetEvoMethodData(pChannel,
4215             DRF_NUM(C37E, _SET_OFFSET_INPUT_LUT, _ORIGIN, origin));
4216 
4217         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_INPUT_LUT, 1);
4218         nvDmaSetEvoMethodData(pChannel,
4219             DRF_NUM(C37E, _SET_CONTEXT_DMA_INPUT_LUT, _HANDLE, ctxDma));
4220     } else {
4221         nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_INPUT_LUT, 1);
4222         nvDmaSetEvoMethodData(pChannel, 0);
4223     }
4224 
4225     UpdateCompositionC3(pDevEvo, pChannel,
4226                         &pHwState->composition, updateState,
4227                         format);
4228 }
4229 
4230 static void EvoSetupPQEotfBaseLutC5(NVEvoLutDataRec *pData,
4231                                     enum NvKmsLUTState *lutState,
4232                                     NvU32 *lutSize,
4233                                     NvBool *isLutModeVss)
4234 {
4235     NvU32 lutDataStartingIndex = NV_LUT_VSS_HEADER_SIZE;
4236     NvU32 numEotfPQ512Entries = ARRAY_LEN(EotfPQ512Entries);
4237     NvU32 eotfTableIdx;
4238     NvU64 vssHead = 0;
4239     NvU32 lutEntryCounter = 0, i;
4240 
4241     // Skip LUT data init if already done
4242     if (*lutState == NvKmsLUTStatePQ) {
4243         goto skipInit;
4244     }
4245 
4246     // VSS Header
4247     for (lutEntryCounter = 0; lutEntryCounter < NV_LUT_VSS_HEADER_SIZE; lutEntryCounter++) {
4248         vssHead = 0;
4249         for (i = 0; ((i < 16) && (((lutEntryCounter * 16) + i) < ARRAY_LEN(EotfPQ512SegSizesLog2))); i++) {
4250             NvU64 temp = EotfPQ512SegSizesLog2[(lutEntryCounter * 16) + i];
4251             temp = temp << (i * 3);
4252             vssHead |= temp;
4253         }
4254         nvkms_memcpy(&(pData->base[lutEntryCounter]), &vssHead, sizeof(NVEvoLutEntryRec));
4255     }
4256 
4257     for (eotfTableIdx = 0; eotfTableIdx < numEotfPQ512Entries; eotfTableIdx++) {
4258         /*
4259          * Values are in range [0.0, 125.0], will be scaled back by OLUT.
4260          * XXX HDR TODO: Divide by 125.0 if output mode is not HDR?
4261          */
4262         pData->base[eotfTableIdx + lutDataStartingIndex].Red =
4263         pData->base[eotfTableIdx + lutDataStartingIndex].Green =
4264         pData->base[eotfTableIdx + lutDataStartingIndex].Blue =
4265             EotfPQ512Entries[eotfTableIdx];
4266     }
4267 
4268     // Copy the last entry for interpolation
4269     pData->base[numEotfPQ512Entries + lutDataStartingIndex].Red =
4270         pData->base[numEotfPQ512Entries + lutDataStartingIndex - 1].Red;
4271     pData->base[numEotfPQ512Entries + lutDataStartingIndex].Green =
4272         pData->base[numEotfPQ512Entries + lutDataStartingIndex - 1].Green;
4273     pData->base[numEotfPQ512Entries + lutDataStartingIndex].Blue =
4274         pData->base[numEotfPQ512Entries + lutDataStartingIndex - 1].Blue;
4275 
4276 skipInit:
4277     *lutState = NvKmsLUTStatePQ;
4278     *lutSize = NV_LUT_VSS_HEADER_SIZE + numEotfPQ512Entries + 1;
4279     *isLutModeVss = TRUE;
4280 }
4281 
4282 static void
4283 EvoSetupIdentityBaseLutC5(NVEvoLutDataRec *pData,
4284                           enum NvKmsLUTState *lutState,
4285                           NvU32 *lutSize,
4286                           NvBool *isLutModeVss)
4287 {
4288     int i;
4289 
4290     // Skip LUT data init if already done
4291     if (*lutState == NvKmsLUTStateIdentity) {
4292         goto skipInit;
4293     }
4294 
4295     ct_assert(NV_NUM_EVO_LUT_ENTRIES == 1025);
4296 
4297     // nvdisplay 3 uses FP16 entries in the ILUT.
4298     for (i = 0; i < 1024; i++) {
4299         pData->base[NV_LUT_VSS_HEADER_SIZE + i].Red =
4300         pData->base[NV_LUT_VSS_HEADER_SIZE + i].Green =
4301         pData->base[NV_LUT_VSS_HEADER_SIZE + i].Blue = nvUnorm10ToFp16(i).v;
4302     }
4303     pData->base[NV_LUT_VSS_HEADER_SIZE + 1024] =
4304         pData->base[NV_LUT_VSS_HEADER_SIZE + 1023];
4305 
4306 skipInit:
4307     *lutState = NvKmsLUTStateIdentity;
4308     *lutSize = NV_LUT_VSS_HEADER_SIZE + NV_NUM_EVO_LUT_ENTRIES;
4309     *isLutModeVss = FALSE;
4310 }
4311 
4312 static void
4313 EvoFlipC5Common(NVDevEvoPtr pDevEvo,
4314          NVEvoChannelPtr pChannel,
4315          const NVFlipChannelEvoHwState *pHwState,
4316          NVEvoUpdateState *updateState,
4317          NvBool bypassComposition)
4318 {
4319     enum NvKmsSurfaceMemoryFormat format;
4320     NvBool swapUV;
4321     NvU32 hTaps, vTaps;
4322     NvBool scaling = FALSE;
4323     NVLutSurfaceEvoPtr pLutSurfaceEvo = NULL;
4324     NvU32 lutSize = NV_NUM_EVO_LUT_ENTRIES;
4325     NvBool isLutModeVss = FALSE;
4326 
4327     NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
4328     NvU32 head = pDevEvo->headForWindow[win];
4329 
4330     const NvU32 sdMask = nvPeekEvoSubDevMask(pDevEvo);
4331     const NvU32 sd = (sdMask == 0) ? 0 : __builtin_ffs(sdMask) - 1;
4332     const NVDispHeadStateEvoRec *pHeadState = &pDevEvo->pDispEvo[sd]->headState[head];
4333 
4334     // XXX HDR TODO: Handle other colorspaces
4335     // XXX HDR TODO: Enable custom input LUTs with HDR
4336     if (pHwState->colorspace != NVKMS_INPUT_COLORSPACE_BT2100_PQ) {
4337         pLutSurfaceEvo = EvoGetLutSurface3(pDevEvo, pChannel, pHwState);
4338     }
4339 
4340     if (!EvoFlipC3Common(pDevEvo, pChannel, pHwState, updateState, head)) {
4341         ConfigureTmoLut(pDevEvo, pHwState, pChannel);
4342         return;
4343     }
4344 
4345     format = pHwState->pSurfaceEvo[NVKMS_LEFT]->format;
4346 
4347     swapUV = IsSurfaceFormatUVSwapped(format);
4348     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_PARAMS, 1);
4349     nvDmaSetEvoMethodData(pChannel,
4350         DRF_NUM(C57E, _SET_PARAMS, _FORMAT, nvHwFormatFromKmsFormatC6(format)) |
4351         (swapUV ? DRF_DEF(C57E, _SET_PARAMS, _SWAP_UV, _ENABLE) :
4352                   DRF_DEF(C57E, _SET_PARAMS, _SWAP_UV, _DISABLE)));
4353 
4354     /*
4355      * In nvdisplay 2, there was a fixed-function block in the precomp FMT
4356      * module that was responsible for YUV->RGB conversion.
4357      *
4358      * In nvdisplay 3, that fixed-function block no longer exists.
4359      * In its place, there's a generic 3x4 S5.16 coefficient matrix that SW must
4360      * explicitly configure to convert the input surface format to the internal
4361      * RGB pipe native format.
4362      */
4363     EvoSetFMTMatrixC5(pChannel, format);
4364 
4365     vTaps = (pHwState->vTaps >= NV_EVO_SCALER_5TAPS) ?
4366             NVC57E_SET_CONTROL_INPUT_SCALER_VERTICAL_TAPS_TAPS_5 :
4367             NVC57E_SET_CONTROL_INPUT_SCALER_VERTICAL_TAPS_TAPS_2;
4368     hTaps = (pHwState->hTaps >= NV_EVO_SCALER_5TAPS) ?
4369             NVC57E_SET_CONTROL_INPUT_SCALER_HORIZONTAL_TAPS_TAPS_5 :
4370             NVC57E_SET_CONTROL_INPUT_SCALER_HORIZONTAL_TAPS_TAPS_2;
4371 
4372     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CONTROL_INPUT_SCALER, 1);
4373     nvDmaSetEvoMethodData(pChannel,
4374         DRF_NUM(C57E, _SET_CONTROL_INPUT_SCALER, _VERTICAL_TAPS, vTaps) |
4375         DRF_NUM(C57E, _SET_CONTROL_INPUT_SCALER, _HORIZONTAL_TAPS, hTaps));
4376 
4377     scaling = (pHwState->sizeIn.width != pHwState->sizeOut.width) ||
4378               (pHwState->sizeIn.height != pHwState->sizeOut.height);
4379     nvAssert(!(scaling && bypassComposition));
4380 
4381     /*
4382      * If scaling or tonemapping, we must enable the CSC0 and CSC1 pipelines.
4383      *
4384      * If no scaling or tonemapping, just use CSC11 to convert from the input
4385      * gamut to the output (panel) gamut, and disable everything else.
4386      */
4387     if (scaling ||
4388         nvNeedsTmoLut(pDevEvo, pChannel, pHwState,
4389                       nvGetHDRSrcMaxLum(pHwState),
4390                       pHeadState->hdr.staticMetadata.maxCLL)) {
4391         ConfigureCsc0C5(pDevEvo, pChannel, pHwState->colorspace, TRUE);
4392         ConfigureCsc1C5(pDevEvo, pChannel, TRUE);
4393     } else {
4394         ConfigureCsc0C5(pDevEvo, pChannel, pHwState->colorspace, FALSE);
4395         ConfigureCsc1C5(pDevEvo, pChannel, FALSE);
4396 
4397         SetCsc11MatrixC5(pChannel, &pHwState->cscMatrix);
4398     }
4399 
4400     // In nvdisplay 3, an ILUT is required to convert the input surface to FP16,
4401     // unless the surface being displayed is already FP16 to begin with.
4402     if ((format == NvKmsSurfaceMemoryFormatRF16GF16BF16AF16) ||
4403         (format == NvKmsSurfaceMemoryFormatRF16GF16BF16XF16) || bypassComposition) {
4404         nvAssert((pHwState->colorspace == NVKMS_INPUT_COLORSPACE_SCRGB_LINEAR) ||
4405                  (pHwState->colorspace == NVKMS_INPUT_COLORSPACE_NONE));
4406         pLutSurfaceEvo = NULL;
4407     } else if (!pLutSurfaceEvo) {
4408         NVEvoLutDataRec *pData = NULL;
4409 
4410         pLutSurfaceEvo = pDevEvo->lut.defaultLut;
4411         pData = pLutSurfaceEvo->subDeviceAddress[sd];
4412 
4413         nvAssert(pData);
4414 
4415         switch (pHwState->colorspace) {
4416             case NVKMS_INPUT_COLORSPACE_BT2100_PQ:
4417                 EvoSetupPQEotfBaseLutC5(pData,
4418                                         &pDevEvo->lut.defaultBaseLUTState[sd],
4419                                         &lutSize, &isLutModeVss);
4420                 break;
4421             case NVKMS_INPUT_COLORSPACE_NONE:
4422                 EvoSetupIdentityBaseLutC5(pData,
4423                                           &pDevEvo->lut.defaultBaseLUTState[sd],
4424                                           &lutSize, &isLutModeVss);
4425                 break;
4426             default: // XXX HDR TODO: Handle other colorspaces
4427                 nvAssert(FALSE);
4428                 EvoSetupIdentityBaseLutC5(pData,
4429                                           &pDevEvo->lut.defaultBaseLUTState[sd],
4430                                           &lutSize, &isLutModeVss);
4431                 break;
4432         }
4433     }
4434 
4435     if (pLutSurfaceEvo) {
4436         const NvU32 ctxDma = pLutSurfaceEvo->dispCtxDma;
4437         const NvU32 origin = offsetof(NVEvoLutDataRec, base);
4438 
4439         nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_ILUT_CONTROL, 1);
4440         nvDmaSetEvoMethodData(pChannel,
4441             (isLutModeVss ? DRF_DEF(C57E, _SET_ILUT_CONTROL, _INTERPOLATE, _ENABLE) :
4442                             DRF_DEF(C57E, _SET_ILUT_CONTROL, _INTERPOLATE, _DISABLE)) |
4443             DRF_DEF(C57E, _SET_ILUT_CONTROL, _MIRROR, _DISABLE) |
4444             (isLutModeVss ? DRF_DEF(C57E, _SET_ILUT_CONTROL, _MODE, _SEGMENTED) :
4445                             DRF_DEF(C57E, _SET_ILUT_CONTROL, _MODE, _DIRECT10)) |
4446             DRF_NUM(C57E, _SET_ILUT_CONTROL, _SIZE, lutSize));
4447 
4448         nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_OFFSET_ILUT, 1);
4449         nvDmaSetEvoMethodData(pChannel,
4450             DRF_NUM(C57E, _SET_OFFSET_ILUT, _ORIGIN, origin));
4451 
4452         nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CONTEXT_DMA_ILUT, 1);
4453         nvDmaSetEvoMethodData(pChannel,
4454             DRF_NUM(C57E, _SET_CONTEXT_DMA_ILUT, _HANDLE, ctxDma));
4455     } else {
4456         nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CONTEXT_DMA_ILUT, 1);
4457         nvDmaSetEvoMethodData(pChannel, 0);
4458     }
4459 
4460     ConfigureTmoLut(pDevEvo, pHwState, pChannel);
4461 
4462     UpdateCompositionC5(pDevEvo, pChannel,
4463                         &pHwState->composition, updateState,
4464                         bypassComposition,
4465                         format);
4466 }
4467 
4468 static void
4469 EvoFlipC5(NVDevEvoPtr pDevEvo,
4470           NVEvoChannelPtr pChannel,
4471           const NVFlipChannelEvoHwState *pHwState,
4472           NVEvoUpdateState *updateState,
4473           NvBool bypassComposition)
4474 {
4475     EvoFlipC5Common(pDevEvo, pChannel, pHwState, updateState, bypassComposition);
4476 
4477     /* Work around bug 2117571: whenever the tearing mode is changing, send a
4478      * software method to notify RM. */
4479     if (pHwState->tearing != pChannel->oldTearingMode) {
4480         NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
4481         NvU32 head = pDevEvo->headForWindow[win];
4482 
4483         if (head != NV_INVALID_HEAD) {
4484             nvDmaSetStartEvoMethod(pChannel, NVC57E_WINDOWS_NOTIFY_RM, 1);
4485             nvDmaSetEvoMethodData(pChannel,
4486                 DRF_DEF(C57E, _WINDOWS_NOTIFY_RM, _VSYNC_STATE_CHANGE, _TRUE) |
4487                 DRF_NUM(C57E, _WINDOWS_NOTIFY_RM, _ASSOCIATED_HEAD, head) |
4488                 (pHwState->tearing ?
4489                     DRF_DEF(C57E, _WINDOWS_NOTIFY_RM, _VSYNC_STATE, _OFF) :
4490                     DRF_DEF(C57E, _WINDOWS_NOTIFY_RM, _VSYNC_STATE, _ON)));
4491         }
4492 
4493         pChannel->oldTearingMode = pHwState->tearing;
4494     }
4495 
4496     /* program semaphore */
4497     EvoProgramSemaphore3(pDevEvo, pChannel, pHwState);
4498 }
4499 
4500 static void
4501 EvoFlipC6(NVDevEvoPtr pDevEvo,
4502           NVEvoChannelPtr pChannel,
4503           const NVFlipChannelEvoHwState *pHwState,
4504           NVEvoUpdateState *updateState,
4505           NvBool bypassComposition)
4506 {
4507     NvBool fromTop = TRUE;
4508     NvBool fromLeft = TRUE;
4509 
4510     NvU32 vDirVal = 0;
4511     NvU32 hDirVal = 0;
4512 
4513     switch (pHwState->rrParams.rotation) {
4514         case NVKMS_ROTATION_90:
4515         case NVKMS_ROTATION_270:
4516             nvAssert(!"Invalid rotation requested.");
4517             /* Fall-through */
4518         case NVKMS_ROTATION_0:
4519             break;
4520         case NVKMS_ROTATION_180:
4521             fromTop = FALSE;
4522             fromLeft = FALSE;
4523             break;
4524     }
4525 
4526     if (pHwState->rrParams.reflectionX) {
4527         fromLeft = !fromLeft;
4528     }
4529     if (pHwState->rrParams.reflectionY) {
4530         fromTop = !fromTop;
4531     }
4532 
4533     vDirVal = (fromTop ?
4534                DRF_DEF(C67E, _SET_SCAN_DIRECTION, _VERTICAL_DIRECTION, _FROM_TOP) :
4535                DRF_DEF(C67E, _SET_SCAN_DIRECTION, _VERTICAL_DIRECTION, _FROM_BOTTOM));
4536     hDirVal = (fromLeft ?
4537                DRF_DEF(C67E, _SET_SCAN_DIRECTION, _HORIZONTAL_DIRECTION, _FROM_LEFT) :
4538                DRF_DEF(C67E, _SET_SCAN_DIRECTION, _HORIZONTAL_DIRECTION, _FROM_RIGHT));
4539 
4540     nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_SCAN_DIRECTION, 1);
4541     nvDmaSetEvoMethodData(pChannel, vDirVal | hDirVal);
4542 
4543     EvoFlipC5Common(pDevEvo, pChannel, pHwState, updateState, bypassComposition);
4544 
4545     /* program semaphore */
4546     EvoProgramSemaphore6(pDevEvo, pChannel, pHwState);
4547 }
4548 
4549 static void UpdateComposition(NVDevEvoPtr pDevEvo,
4550                               NVEvoChannelPtr pChannel,
4551                               /* smaller => closer to front */
4552                               NvU32 depth,
4553                               NvU32 colorKeySelect,
4554                               NvU32 constantAlpha,
4555                               NvU32 compositionFactorSelect,
4556                               const NVColorKey key,
4557                               NVEvoUpdateState *updateState)
4558 {
4559     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
4560 
4561     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_COMPOSITION_CONTROL, 1);
4562     nvDmaSetEvoMethodData(pChannel,
4563         DRF_NUM(C37E, _SET_COMPOSITION_CONTROL, _COLOR_KEY_SELECT, colorKeySelect) |
4564         DRF_NUM(C37E, _SET_COMPOSITION_CONTROL, _DEPTH, depth));
4565 
4566     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_COMPOSITION_CONSTANT_ALPHA, 1);
4567     nvDmaSetEvoMethodData(pChannel,
4568         DRF_NUM(C37E, _SET_COMPOSITION_CONSTANT_ALPHA, _K1, constantAlpha));
4569 
4570     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_COMPOSITION_FACTOR_SELECT, 1);
4571     nvDmaSetEvoMethodData(pChannel, compositionFactorSelect);
4572 
4573 #define UPDATE_COMPONENT(_COMP, _C, _c) \
4574     nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_KEY_##_COMP, 1); \
4575     if (key.match##_C) { \
4576         nvDmaSetEvoMethodData(pChannel, \
4577             DRF_NUM(C37E, _SET_KEY_##_COMP, _MIN, key._c) | \
4578             DRF_NUM(C37E, _SET_KEY_##_COMP, _MAX, key._c)); \
4579     } else { \
4580         nvDmaSetEvoMethodData(pChannel, \
4581             DRF_NUM(C37E, _SET_KEY_##_COMP, _MIN, 0) | \
4582             DRF_SHIFTMASK(NVC37E_SET_KEY_##_COMP##_MAX)); \
4583     }
4584 
4585     if (colorKeySelect !=
4586         NVC37E_SET_COMPOSITION_CONTROL_COLOR_KEY_SELECT_DISABLE) {
4587         UPDATE_COMPONENT(ALPHA, A, a);
4588         UPDATE_COMPONENT(RED_CR, R, r);
4589         UPDATE_COMPONENT(GREEN_Y, G, g);
4590         UPDATE_COMPONENT(BLUE_CB, B, b);
4591     }
4592 
4593 #undef UPDATE_COMPONENT
4594 }
4595 
4596 static void EvoFlipTransitionWARC3(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
4597                                    const NVEvoSubDevHeadStateRec *pSdHeadState,
4598                                    const NVFlipEvoHwState *pFlipState,
4599                                    NVEvoUpdateState *updateState)
4600 {
4601     /* Nothing to do for Volta */
4602 }
4603 
4604 /*
4605  * Hardware bug 2193096 requires that we send special software methods around
4606  * a window channel update that transitions from NULL ctxdma to non-NULL or
4607  * vice versa.  Below we compare the current hardware state in pSdHeadState
4608  * against the state to be pushed in this update in pFlipState, and add any
4609  * window(s) that qualify to the 'flipTransitionWAR' mask in the updateState.
4610  */
4611 static void EvoFlipTransitionWARC5(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
4612                                    const NVEvoSubDevHeadStateRec *pSdHeadState,
4613                                    const NVFlipEvoHwState *pFlipState,
4614                                    NVEvoUpdateState *updateState)
4615 {
4616     NvU32 layer;
4617 
4618     for (layer = 0; layer < pDevEvo->head[head].numLayers; layer++) {
4619         const NvBool enabledPrev =
4620             pSdHeadState->layer[layer].pSurfaceEvo[NVKMS_LEFT] != NULL;
4621         const NvBool enabledNext =
4622             pFlipState->layer[layer].pSurfaceEvo[NVKMS_LEFT] != NULL;
4623 
4624         if (enabledPrev != enabledNext) {
4625             /* XXX TODO: dynamic window assignment */
4626             const NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(
4627                 pDevEvo->head[head].layer[layer]->channelMask);
4628             updateState->subdev[sd].flipTransitionWAR |=
4629                 DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, win, _ENABLE);
4630 
4631             nvAssert(pFlipState->dirty.layer[layer]);
4632         }
4633     }
4634 }
4635 
4636 static void EvoFlipTransitionWARC6(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
4637                                    const NVEvoSubDevHeadStateRec *pSdHeadState,
4638                                    const NVFlipEvoHwState *pFlipState,
4639                                    NVEvoUpdateState *updateState)
4640 {
4641     /* Nothing to do for Orin/Ampere for now */
4642 }
4643 
4644 static void
4645 UpdateCompositionC3(NVDevEvoPtr pDevEvo,
4646                     NVEvoChannelPtr pChannel,
4647                     const struct NvKmsCompositionParams *pCompParams,
4648                     NVEvoUpdateState *updateState,
4649                     enum NvKmsSurfaceMemoryFormat format)
4650 {
4651     NvU32 colorKeySelect;
4652     NvU32 compositionFactorSelect = 0;
4653     NvU32 constantAlpha = 0;
4654     NvU32 match;
4655 
4656     switch (pCompParams->colorKeySelect) {
4657         case NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE:
4658             colorKeySelect =
4659                 NVC37E_SET_COMPOSITION_CONTROL_COLOR_KEY_SELECT_DISABLE;
4660             break;
4661         case NVKMS_COMPOSITION_COLOR_KEY_SELECT_SRC:
4662             colorKeySelect =
4663                 NVC37E_SET_COMPOSITION_CONTROL_COLOR_KEY_SELECT_SRC;
4664 
4665             break;
4666         case NVKMS_COMPOSITION_COLOR_KEY_SELECT_DST:
4667             colorKeySelect =
4668                 NVC37E_SET_COMPOSITION_CONTROL_COLOR_KEY_SELECT_DST;
4669 
4670            break;
4671         default:
4672             nvAssert(!"Invalid color key select");
4673             return;
4674     }
4675 
4676     /* Match and nomatch pixels should not use alpha blending mode at once. */
4677     nvAssert((colorKeySelect == NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE) ||
4678              (!NvKmsIsCompositionModeUseAlpha(pCompParams->blendingMode[0])) ||
4679              (!NvKmsIsCompositionModeUseAlpha(pCompParams->blendingMode[1])));
4680 
4681     /*
4682      * Match and nomatch pixels should not use blending mode PREMULT_ALPHA,
4683      * NON_PREMULT_ALPHA, PREMULT_SURFACE_ALPHA, and NON_PREMULT_SURFACE_ALPHA
4684      * at once.
4685      */
4686     nvAssert(pCompParams->blendingMode[0] == NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE ||
4687              pCompParams->blendingMode[0] == NVKMS_COMPOSITION_BLENDING_MODE_TRANSPARENT ||
4688              pCompParams->blendingMode[1] == NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE ||
4689              pCompParams->blendingMode[1] == NVKMS_COMPOSITION_BLENDING_MODE_TRANSPARENT);
4690 
4691     for (match = 0; match <= 1; match++) {
4692         switch (pCompParams->blendingMode[match]) {
4693             case NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE:
4694                 if (match == 1) {
4695                     compositionFactorSelect |=
4696                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _ONE) |
4697                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _ZERO);
4698                 } else {
4699                     compositionFactorSelect |=
4700                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _ONE) |
4701                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _ZERO);
4702                 }
4703                 break;
4704             case NVKMS_COMPOSITION_BLENDING_MODE_TRANSPARENT:
4705                 if (match == 1) {
4706                     compositionFactorSelect |=
4707                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _ZERO) |
4708                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _ONE);
4709                 } else {
4710                     compositionFactorSelect |=
4711                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _ZERO) |
4712                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _ONE);
4713                 }
4714                 break;
4715             case NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA:
4716                 constantAlpha = 255;
4717                 if (match == 1) {
4718                     compositionFactorSelect |=
4719                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1_TIMES_SRC) |
4720                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1_TIMES_SRC);
4721                 } else {
4722                     compositionFactorSelect |=
4723                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1_TIMES_SRC) |
4724                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1_TIMES_SRC);
4725                 }
4726                 break;
4727             case NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA:
4728                 constantAlpha = 255;
4729                 if (match == 1) {
4730                     compositionFactorSelect |=
4731                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1) |
4732                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1_TIMES_SRC);
4733                 } else {
4734                     compositionFactorSelect |=
4735                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1) |
4736                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1_TIMES_SRC);
4737                 }
4738                 break;
4739             case NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA:
4740                 constantAlpha = pCompParams->surfaceAlpha;
4741                 if (match == 1) {
4742                     compositionFactorSelect |=
4743                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1_TIMES_SRC) |
4744                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1_TIMES_SRC);
4745                 } else {
4746                     compositionFactorSelect |=
4747                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1_TIMES_SRC) |
4748                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1_TIMES_SRC);
4749                 }
4750                 break;
4751             case NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA:
4752                 constantAlpha = pCompParams->surfaceAlpha;
4753                 if (match == 1) {
4754                     compositionFactorSelect |=
4755                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1) |
4756                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1_TIMES_SRC);
4757                 } else {
4758                     compositionFactorSelect |=
4759                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1) |
4760                         DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1_TIMES_SRC);
4761                 }
4762                 break;
4763              default:
4764                 nvAssert(!"Invalid blend mode");
4765                 return;
4766         }
4767 
4768         /* Override the  composition factors for X channel emulated surface format. */
4769         if (NvKmsIsCompositionModeUseAlpha(pCompParams->blendingMode[match]) &&
4770             ((pDevEvo->hal->caps.xEmulatedSurfaceMemoryFormats & NVBIT64(format)) != 0U)) {
4771             if (match == 1) {
4772                 /* Clear the previously selected composition factors for match pixels. */
4773                 compositionFactorSelect &= ~(DRF_MASK(NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_MATCH_SELECT) <<
4774                                              DRF_SHIFT(NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_MATCH_SELECT));
4775                 compositionFactorSelect &= ~(DRF_MASK(NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_MATCH_SELECT) <<
4776                                              DRF_SHIFT(NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_MATCH_SELECT));
4777 
4778                 compositionFactorSelect |=
4779                     DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1) |
4780                     DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1);
4781             } else {
4782                 /* Clear the previously selected composition factors for no-match pixels. */
4783                 compositionFactorSelect &= ~(DRF_MASK(NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_NO_MATCH_SELECT) <<
4784                                              DRF_SHIFT(NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_NO_MATCH_SELECT));
4785                 compositionFactorSelect &= ~(DRF_MASK(NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_NO_MATCH_SELECT) <<
4786                                              DRF_SHIFT(NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_NO_MATCH_SELECT));
4787 
4788                 compositionFactorSelect |=
4789                     DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1) |
4790                     DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1);
4791             }
4792         }
4793     }
4794 
4795     UpdateComposition(pDevEvo,
4796                       pChannel,
4797                       pCompParams->depth,
4798                       colorKeySelect,
4799                       constantAlpha,
4800                       compositionFactorSelect,
4801                       pCompParams->colorKey,
4802                       updateState);
4803 }
4804 
4805 static void EvoBypassCompositionC5(NVDevEvoPtr pDevEvo,
4806                                    NVEvoChannelPtr pChannel,
4807                                    NVEvoUpdateState *updateState)
4808 {
4809     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
4810 
4811     nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_COMPOSITION_CONTROL, 1);
4812     nvDmaSetEvoMethodData(pChannel,
4813         DRF_DEF(C57E, _SET_COMPOSITION_CONTROL, _BYPASS, _ENABLE));
4814 }
4815 
4816 static void
4817 UpdateCompositionC5(NVDevEvoPtr pDevEvo,
4818                     NVEvoChannelPtr pChannel,
4819                     const struct NvKmsCompositionParams *pCompParams,
4820                     NVEvoUpdateState *updateState,
4821                     NvBool bypassComposition,
4822                     enum NvKmsSurfaceMemoryFormat format)
4823 {
4824     if (bypassComposition) {
4825         EvoBypassCompositionC5(pDevEvo, pChannel, updateState);
4826     } else {
4827         UpdateCompositionC3(pDevEvo, pChannel, pCompParams,
4828                             updateState, format);
4829     }
4830 }
4831 
4832 /*
4833  * The LUT entries in INDEX_1025_UNITY_RANGE have 16 bits, with the
4834  * black value at 24576, and the white at 49151. Since the effective
4835  * range is 16384, we treat this as a 14-bit LUT.  However, we need to
4836  * clear the low 3 bits to WAR hardware bug 813188.  This gives us
4837  * 14-bit LUT values, but only 11 bits of precision.
4838  * XXXnvdisplay: Bug 813188 is supposed to be fixed on NVDisplay; can we expose
4839  * more precision?
4840  */
4841 static inline NvU16 ColorToLUTEntry(NvU16 val)
4842 {
4843     const NvU16 val14bit = val >> 2;
4844     return (val14bit & ~7) + 24576;
4845 }
4846 
4847 /*
4848  * Unlike earlier EVO implementations, the INDEX mode of the input LUT on
4849  * NVDisplay is straightforward: the value of the input component is expanded
4850  * to the LUT size by simply shifting left by the difference between the LUT
4851  * index width and the component width.  We do the same, here, to select the
4852  * right LUT entry to fill.
4853  */
4854 static inline NvU32 GetLUTIndex(int i, int componentSize)
4855 {
4856     return i << (10 - componentSize);
4857 }
4858 
4859 static void
4860 EvoFillLUTSurfaceC3(NVEvoLutEntryRec *pLUTBuffer,
4861                     const NvU16 *red,
4862                     const NvU16 *green,
4863                     const NvU16 *blue,
4864                     int nColorMapEntries, int depth)
4865 {
4866     int i;
4867     NvU32 rSize, gSize, bSize;
4868 
4869     switch (depth) {
4870     case 15:
4871         rSize = gSize = bSize = 5;
4872         break;
4873     case 16:
4874         rSize = bSize = 5;
4875         gSize = 6;
4876         break;
4877     case 8:
4878     case 24:
4879         rSize = gSize = bSize = 8;
4880         break;
4881     case 30:
4882         rSize = gSize = bSize = 10;
4883         break;
4884     default:
4885         nvAssert(!"invalid depth");
4886         return;
4887     }
4888 
4889     for (i = 0; i < nColorMapEntries; i++) {
4890         if (i < (1 << rSize)) {
4891             pLUTBuffer[GetLUTIndex(i, rSize)].Red = ColorToLUTEntry(red[i]);
4892         }
4893         if (i < (1 << gSize)) {
4894             pLUTBuffer[GetLUTIndex(i, gSize)].Green = ColorToLUTEntry(green[i]);
4895         }
4896         if (i < (1 << bSize)) {
4897             pLUTBuffer[GetLUTIndex(i, bSize)].Blue = ColorToLUTEntry(blue[i]);
4898         }
4899     }
4900 }
4901 
4902 static inline float16_t ColorToFp16(NvU16 val, float32_t maxf)
4903 {
4904     return nvUnormToFp16(val, maxf);
4905 }
4906 
4907 static void
4908 EvoFillLUTSurfaceC5(NVEvoLutEntryRec *pLUTBuffer,
4909                     const NvU16 *red,
4910                     const NvU16 *green,
4911                     const NvU16 *blue,
4912                     int nColorMapEntries, int depth)
4913 {
4914     int i;
4915     NvU32 rSize, gSize, bSize;
4916     const float32_t maxf = ui32_to_f32(0xffff);
4917 
4918     switch (depth) {
4919     case 15:
4920         rSize = gSize = bSize = 5;
4921         break;
4922     case 16:
4923         rSize = bSize = 5;
4924         gSize = 6;
4925         break;
4926     case 8:
4927     case 24:
4928         rSize = gSize = bSize = 8;
4929         break;
4930     case 30:
4931         rSize = gSize = bSize = 10;
4932         break;
4933     default:
4934         nvAssert(!"invalid depth");
4935         return;
4936     }
4937 
4938     // Skip the VSS header
4939     pLUTBuffer += NV_LUT_VSS_HEADER_SIZE;
4940 
4941     for (i = 0; i < nColorMapEntries; i++) {
4942         if (i < (1 << rSize)) {
4943             pLUTBuffer[GetLUTIndex(i, rSize)].Red =
4944                 ColorToFp16(red[i], maxf).v;
4945         }
4946         if (i < (1 << gSize)) {
4947             pLUTBuffer[GetLUTIndex(i, gSize)].Green =
4948                 ColorToFp16(green[i], maxf).v;
4949         }
4950         if (i < (1 << bSize)) {
4951             pLUTBuffer[GetLUTIndex(i, bSize)].Blue =
4952                 ColorToFp16(blue[i], maxf).v;
4953         }
4954     }
4955 }
4956 
4957 static void EvoSetLUTContextDma3(NVDevEvoPtr pDevEvo,
4958                                  const int head,
4959                                  NVLutSurfaceEvoPtr pLutSurfEvo,
4960                                  NvBool enableBaseLut,
4961                                  NVEvoUpdateState *updateState,
4962                                  NvBool bypassComposition)
4963 {
4964     NVEvoChannelPtr pChannel = pDevEvo->core;
4965     NvU32 sd;
4966 
4967     /* These methods should only apply to a single pDpy */
4968     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
4969 
4970     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
4971 
4972     /*
4973      * For Window semaphores and Notifiers, the general rule of thumb is that
4974      * the current semaphore/notifier will be released if the address for the
4975      * semaphore/notifier changes (via context DMA change or offset change).
4976      * This allows SW to push updates in the window channel that change other
4977      * methods, but do not cause the semaphore or notifier to be released. This
4978      * make it possible to reprogram the window channel with new input Lut
4979      * without releasing semaphore.
4980      *
4981      * Note that higher-level code will use core channel notifiers to
4982      * synchronize these LUT updates, but that's okay because EvoUpdateC3()
4983      * will interlock the core and window channel(s) updates together.
4984     */
4985     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
4986         if (nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)) {
4987             NVEvoChannelPtr pChannel = pDevEvo->window[head << 1];
4988             NVEvoSubDevHeadStateRec *pSdHeadState =
4989                 &pDevEvo->gpus[sd].headState[head];
4990             NVFlipChannelEvoHwState *pMainFlipState =
4991                 &pSdHeadState->layer[NVKMS_MAIN_LAYER];
4992             NVLutSurfaceEvoPtr pInputLutSurfEvo = enableBaseLut ?
4993                 pLutSurfEvo : NULL;
4994 
4995             if (pMainFlipState->inputLut.pLutSurfaceEvo == pInputLutSurfEvo) {
4996                 continue;
4997             }
4998 
4999             pMainFlipState->inputLut.pLutSurfaceEvo = pInputLutSurfEvo;
5000             /* It is not allowed to change the input LUT on immediate flips. */
5001             pMainFlipState->tearing = FALSE;
5002 
5003             nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
5004 
5005             pDevEvo->hal->Flip(pDevEvo, pChannel, pMainFlipState, updateState,
5006                                bypassComposition);
5007 
5008             nvPopEvoSubDevMask(pDevEvo);
5009         }
5010     }
5011 }
5012 
5013 static void EvoSetLUTContextDmaC3(const NVDispEvoRec *pDispEvo,
5014                                   const int head,
5015                                   NVLutSurfaceEvoPtr pLutSurfEvo,
5016                                   NvBool enableBaseLut,
5017                                   NvBool enableOutputLut,
5018                                   NVEvoUpdateState *updateState,
5019                                   NvBool bypassComposition)
5020 {
5021     NvU32 ctxdma = (pLutSurfEvo != NULL) ? pLutSurfEvo->dispCtxDma : 0;
5022     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
5023     NVEvoChannelPtr pChannel = pDevEvo->core;
5024     NvU64 offset;
5025 
5026     nvAssert(ctxdma || (!enableBaseLut && !enableOutputLut));
5027 
5028     nvPushEvoSubDevMaskDisp(pDispEvo);
5029 
5030     EvoSetLUTContextDma3(pDevEvo,
5031                          head,
5032                          pLutSurfEvo,
5033                          enableBaseLut,
5034                          updateState,
5035                          bypassComposition);
5036 
5037     /* Program the output LUT */
5038 
5039     offset = offsetof(NVEvoLutDataRec, output);
5040     nvAssert((offset & 0xff) == 0);
5041 
5042     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL_OUTPUT_LUT(head), 1);
5043     nvDmaSetEvoMethodData(pChannel,
5044         DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_LUT, _SIZE, _SIZE_1025) |
5045         DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_LUT, _RANGE, _UNITY) |
5046         DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_LUT, _OUTPUT_MODE, _INTERPOLATE));
5047 
5048     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_OFFSET_OUTPUT_LUT(head), 1);
5049     nvDmaSetEvoMethodData(pChannel,
5050         DRF_NUM(C37D, _HEAD_SET_OFFSET_OUTPUT_LUT, _ORIGIN, offset >> 8));
5051 
5052     /* Set the ctxdma for the output LUT */
5053 
5054     if (!enableOutputLut) {
5055         /* Class C37D has no separate enable flag. */
5056         ctxdma = 0;
5057     }
5058     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTEXT_DMA_OUTPUT_LUT(head), 1);
5059     nvDmaSetEvoMethodData(pChannel,
5060             DRF_NUM(C37D, _HEAD_SET_CONTEXT_DMA_OUTPUT_LUT, _HANDLE, ctxdma));
5061 
5062     nvPopEvoSubDevMask(pDevEvo);
5063 }
5064 
5065 static void EvoSetupPQOetfOutputLutC5(NVEvoLutDataRec *pData,
5066                                       enum NvKmsLUTState *lutState,
5067                                       NvU32 *lutSize,
5068                                       NvBool *isLutModeVss)
5069 {
5070     NvU32 lutDataStartingIndex = NV_LUT_VSS_HEADER_SIZE;
5071     NvU32 numOetfPQ512Entries = ARRAY_LEN(OetfPQ512Entries);
5072     NvU32 oetfTableIdx;
5073     NvU64 vssHead = 0;
5074     NvU32 lutEntryCounter = 0, i;
5075 
5076     // Skip LUT data init if already done
5077     if (*lutState == NvKmsLUTStatePQ) {
5078         goto skipInit;
5079     }
5080 
5081     // VSS Header
5082     for (lutEntryCounter = 0; lutEntryCounter < NV_LUT_VSS_HEADER_SIZE; lutEntryCounter++) {
5083         vssHead = 0;
5084         for (i = 0; ((i < 16) && (((lutEntryCounter * 16) + i) < ARRAY_LEN(OetfPQ512SegSizesLog2))); i++) {
5085             NvU64 temp = OetfPQ512SegSizesLog2[(lutEntryCounter * 16) + i];
5086             temp = temp << (i * 3);
5087             vssHead |= temp;
5088         }
5089         nvkms_memcpy(&(pData->output[lutEntryCounter]), &vssHead, sizeof(NVEvoLutEntryRec));
5090     }
5091 
5092     for (oetfTableIdx = 0; oetfTableIdx < numOetfPQ512Entries; oetfTableIdx++) {
5093         pData->output[oetfTableIdx + lutDataStartingIndex].Red =
5094         pData->output[oetfTableIdx + lutDataStartingIndex].Green =
5095         pData->output[oetfTableIdx + lutDataStartingIndex].Blue =
5096             OetfPQ512Entries[oetfTableIdx];
5097     }
5098 
5099     // Copy the last entry for interpolation
5100     pData->output[numOetfPQ512Entries + lutDataStartingIndex].Red =
5101         pData->output[numOetfPQ512Entries + lutDataStartingIndex - 1].Red;
5102     pData->output[numOetfPQ512Entries + lutDataStartingIndex].Green =
5103         pData->output[numOetfPQ512Entries + lutDataStartingIndex - 1].Green;
5104     pData->output[numOetfPQ512Entries + lutDataStartingIndex].Blue =
5105         pData->output[numOetfPQ512Entries + lutDataStartingIndex - 1].Blue;
5106 
5107 skipInit:
5108     *lutState = NvKmsLUTStatePQ;
5109     *lutSize = numOetfPQ512Entries + 1;
5110     *isLutModeVss = TRUE;
5111 }
5112 
5113 static void EvoSetupIdentityOutputLutC5(NVEvoLutDataRec *pData,
5114                                         enum NvKmsLUTState *lutState,
5115                                         NvU32 *lutSize,
5116                                         NvBool *isLutModeVss)
5117 {
5118     NvU32 i;
5119 
5120     // Skip LUT data init if already done
5121     if (*lutState == NvKmsLUTStateIdentity) {
5122         goto skipInit;
5123     }
5124 
5125     ct_assert(NV_NUM_EVO_LUT_ENTRIES == 1025);
5126 
5127     // nvdisplay 3 uses 16-bit fixed-point entries in the OLUT.
5128     for (i = 0; i < 1024; i++) {
5129         pData->output[NV_LUT_VSS_HEADER_SIZE + i].Red =
5130         pData->output[NV_LUT_VSS_HEADER_SIZE + i].Green =
5131         pData->output[NV_LUT_VSS_HEADER_SIZE + i].Blue = (i << (16 - 10));
5132     }
5133     pData->output[NV_LUT_VSS_HEADER_SIZE + 1024] =
5134         pData->output[NV_LUT_VSS_HEADER_SIZE + 1023];
5135 
5136 skipInit:
5137     *lutState = NvKmsLUTStateIdentity;
5138     *lutSize = 1025;
5139     *isLutModeVss = FALSE;
5140 }
5141 
5142 static void SetupHDROutputLUT(NVDevEvoPtr pDevEvo,
5143                               const NVDispHeadStateEvoRec *pHeadState,
5144                               NvU32 sd,
5145                               enum NvKmsLUTState *lutState,
5146                               NvU32 *lutSize,
5147                               NvBool *isLutModeVss)
5148 {
5149     NVLutSurfaceEvoPtr pLut = pDevEvo->lut.defaultLut;
5150     NVEvoLutDataRec *pData = pLut->subDeviceAddress[sd];
5151 
5152     // XXX HDR TODO: Support other transfer functions
5153     nvAssert(pHeadState->tf == NVKMS_OUTPUT_TF_PQ);
5154 
5155     EvoSetupPQOetfOutputLutC5(pData, lutState, lutSize, isLutModeVss);
5156 }
5157 
5158 static void EvoSetLUTContextDmaC5(const NVDispEvoRec *pDispEvo,
5159                                   const int head,
5160                                   NVLutSurfaceEvoPtr pLutSurfEvo,
5161                                   NvBool enableBaseLut,
5162                                   NvBool enableOutputLut,
5163                                   NVEvoUpdateState *updateState,
5164                                   NvBool bypassComposition)
5165 {
5166     NvU32 ctxdma = (pLutSurfEvo != NULL) ? pLutSurfEvo->dispCtxDma : 0;
5167     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
5168     NVEvoChannelPtr pChannel = pDevEvo->core;
5169     NvU64 offset;
5170     NvU32 sd, lutSize = NV_NUM_EVO_LUT_ENTRIES;
5171     NvBool isLutModeVss = FALSE;
5172     const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
5173     NvBool disableOcsc0 = FALSE;
5174     NvU32 fpNormScale = 0xFFFFFFFF;
5175 
5176     // XXX HDR TODO: Enable custom output LUTs with HDR
5177     if (pHeadState->hdr.outputState == NVKMS_HDR_OUTPUT_STATE_HDR) {
5178         enableOutputLut = FALSE;
5179     }
5180 
5181     nvAssert(ctxdma || (!enableBaseLut && !enableOutputLut));
5182 
5183     nvPushEvoSubDevMaskDisp(pDispEvo);
5184 
5185     EvoSetLUTContextDma3(pDevEvo,
5186                          head,
5187                          pLutSurfEvo,
5188                          enableBaseLut,
5189                          updateState,
5190                          bypassComposition);
5191 
5192     /* Set the ctxdma for the output LUT */
5193 
5194     if (bypassComposition) {
5195         ctxdma = 0;
5196 
5197         /* if we're not enabling the OLUT, OCSC0 also needs to be disabled */
5198         disableOcsc0 = TRUE;
5199     } else if (!enableOutputLut) {
5200         /* Use the default OLUT if the client didn't provide one */
5201         ctxdma = pDevEvo->lut.defaultLut->dispCtxDma;
5202 
5203         // Setup default OLUT
5204         for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
5205             if (pHeadState->hdr.outputState == NVKMS_HDR_OUTPUT_STATE_HDR) {
5206                 SetupHDROutputLUT(pDevEvo, pHeadState, sd,
5207                                   &pDevEvo->lut.defaultOutputLUTState[sd],
5208                                   &lutSize, &isLutModeVss);
5209 
5210                 disableOcsc0 = TRUE;
5211 
5212                 /*
5213                  * Scale from [0.0, 125.0] to [0.0, 1.0]
5214                  * XXX HDR TODO: Assumes input is in this range, SDR is not.
5215                  */
5216                 fpNormScale = 0xFFFFFFFF / 125;
5217             } else {
5218                 NVLutSurfaceEvoPtr pLut = pDevEvo->lut.defaultLut;
5219                 NVEvoLutDataRec *pData = pLut->subDeviceAddress[sd];
5220 
5221                 EvoSetupIdentityOutputLutC5(
5222                     pData,
5223                     &pDevEvo->lut.defaultOutputLUTState[sd],
5224                     &lutSize, &isLutModeVss);
5225             }
5226         }
5227     }
5228 
5229     if (disableOcsc0) {
5230         nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC0CONTROL(head), 1);
5231         nvDmaSetEvoMethodData(pChannel, DRF_DEF(C57D, _HEAD_SET_OCSC0CONTROL, _ENABLE, _DISABLE));
5232     }
5233 
5234     /* Program the output LUT */
5235 
5236     offset = offsetof(NVEvoLutDataRec, output);
5237     nvAssert((offset & 0xff) == 0);
5238 
5239     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OLUT_CONTROL(head), 1);
5240     nvDmaSetEvoMethodData(pChannel,
5241         ((isLutModeVss || !nvkms_output_rounding_fix()) ?
5242             DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _INTERPOLATE, _ENABLE) :
5243             DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _INTERPOLATE, _DISABLE)) |
5244         DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _MIRROR, _DISABLE) |
5245         (isLutModeVss ? DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _MODE, _SEGMENTED) :
5246                         DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _MODE, _DIRECT10)) |
5247         DRF_NUM(C57D, _HEAD_SET_OLUT_CONTROL, _SIZE, NV_LUT_VSS_HEADER_SIZE +
5248                                                      lutSize));
5249 
5250     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OFFSET_OLUT(head), 1);
5251     nvDmaSetEvoMethodData(pChannel,
5252         DRF_NUM(C57D, _HEAD_SET_OFFSET_OLUT, _ORIGIN, offset >> 8));
5253 
5254     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OLUT_FP_NORM_SCALE(head), 1);
5255     nvDmaSetEvoMethodData(pChannel, fpNormScale);
5256 
5257     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_CONTEXT_DMA_OLUT(head), 1);
5258     nvDmaSetEvoMethodData(pChannel,
5259             DRF_NUM(C57D, _HEAD_SET_CONTEXT_DMA_OLUT, _HANDLE, ctxdma));
5260 
5261     if (!disableOcsc0) {
5262         /* only enable OCSC0 after enabling the OLUT */
5263         nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC0CONTROL(head), 1);
5264         nvDmaSetEvoMethodData(pChannel, DRF_DEF(C57D, _HEAD_SET_OCSC0CONTROL, _ENABLE, _ENABLE));
5265     }
5266 
5267     nvPopEvoSubDevMask(pDevEvo);
5268 }
5269 
5270 static inline NvU32 ReadCapReg(volatile const NvU32 *pCaps, NvU32 offset)
5271 {
5272     /* Offsets are in bytes, but the array has dword-sized elements. */
5273     return pCaps[offset / sizeof(NvU32)];
5274 }
5275 
5276 static NvBool QueryStereoPinC3(NVDevEvoPtr pDevEvo,
5277                                NVEvoSubDevPtr pEvoSubDev,
5278                                NvU32 *pStereoPin)
5279 {
5280     NVC370_CTRL_GET_LOCKPINS_CAPS_PARAMS params = { };
5281 
5282     params.base.subdeviceIndex = pEvoSubDev->subDeviceInstance;
5283 
5284     if (nvRmApiControl(nvEvoGlobal.clientHandle,
5285                        pDevEvo->displayHandle,
5286                        NVC370_CTRL_CMD_GET_LOCKPINS_CAPS,
5287                        &params, sizeof(params)) != NVOS_STATUS_SUCCESS) {
5288         nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
5289                          "Failed to query stereo pin");
5290         return FALSE;
5291     }
5292 
5293     if ((params.stereoPin >= NV_EVO_NUM_LOCK_PIN_CAPS) ||
5294         (params.stereoPin == NVC370_CTRL_GET_LOCKPINS_CAPS_STEREO_PIN_NONE)) {
5295         return FALSE;
5296     } else {
5297         *pStereoPin = params.stereoPin;
5298         return TRUE;
5299     }
5300 }
5301 
5302 static void EvoParseCapabilityNotifier3(NVDevEvoPtr pDevEvo,
5303                                         NVEvoSubDevPtr pEvoSubDev,
5304                                         volatile const NvU32 *pCaps)
5305 {
5306     NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
5307     const NvU32 sysCap = ReadCapReg(pCaps, NVC373_SYS_CAP);
5308     const NvU32 sysCapB = ReadCapReg(pCaps, NVC373_SYS_CAPB);
5309     NvU32 i, stereoPin;
5310     NvU32 layer;
5311 
5312     pDevEvo->caps.cursorCompositionCaps =
5313         (struct NvKmsCompositionCapabilities) {
5314             .supportedColorKeySelects =
5315                 NVBIT(NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE),
5316 
5317             .colorKeySelect = {
5318                 [NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE] = {
5319                     .supportedBlendModes = {
5320                         [1] = NV_EVO3_SUPPORTED_CURSOR_COMP_BLEND_MODES,
5321                     },
5322                 },
5323             }
5324         };
5325 
5326     for (layer = 0;
5327          layer < ARRAY_LEN(pDevEvo->caps.layerCaps); layer++) {
5328         pDevEvo->caps.layerCaps[layer].composition =
5329             (struct NvKmsCompositionCapabilities) {
5330                 .supportedColorKeySelects =
5331                     NVBIT(NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE) |
5332                     NVBIT(NVKMS_COMPOSITION_COLOR_KEY_SELECT_SRC) |
5333                     NVBIT(NVKMS_COMPOSITION_COLOR_KEY_SELECT_DST),
5334 
5335                 .colorKeySelect = {
5336                     [NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE] = {
5337                         .supportedBlendModes = {
5338                             [1] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
5339                         },
5340                     },
5341 
5342                     [NVKMS_COMPOSITION_COLOR_KEY_SELECT_SRC] = {
5343                         .supportedBlendModes = {
5344                             [0] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
5345                             [1] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
5346                         },
5347                     },
5348 
5349                     [NVKMS_COMPOSITION_COLOR_KEY_SELECT_DST] = {
5350                         .supportedBlendModes = {
5351                             [0] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
5352                             [1] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
5353                         },
5354                     },
5355                 },
5356             };
5357     }
5358 
5359     /*
5360      * Previous EVO display implementations exposed capabilities for lock pins,
5361      * detailing which pin(s) could be used for which functions. The idea was
5362      * that it didn't make sense to try to drive a stereo pin with a fliplock
5363      * signal (for example), so the pin associated with the stereo function was
5364      * marked as stereo-capable but not any other function; attempting to use a
5365      * non-stereo-capable pin for stereo or vice-versa would result in an error.
5366      *
5367      * With nvdisplay, the meaning of lock pins was changed such that they no
5368      * longer have a shared namespace.  So stereo lockpin 0 is not the same as
5369      * fliplock lockpin 0 and neither is the same as scanlock lockpin 0.  With
5370      * this scheme, there is no way to specify a pin that is incapable of a
5371      * given function, so the entire capabilities mechanism was removed.
5372      *
5373      * However, the pins chosen for HEAD_SET_CONTROL still need to match the
5374      * pins selected for each function in the VBIOS DCB.  Fliplock and scanlock
5375      * query this information through
5376      * NV5070_CTRL_CMD_GET_FRAMELOCK_HEADER_LOCKPINS.  Stereo is handled
5377      * here, using NVC370_CTRL_CMD_GET_LOCKPINS_CAPS.
5378      */
5379 
5380     for (i = 0; i < NV_EVO_NUM_LOCK_PIN_CAPS; i++) {
5381         pEvoCaps->pin[i].flipLock = TRUE;
5382         pEvoCaps->pin[i].scanLock = TRUE;
5383     }
5384 
5385     if (QueryStereoPinC3(pDevEvo, pEvoSubDev, &stereoPin)) {
5386         pEvoCaps->pin[stereoPin].stereo = TRUE;
5387     }
5388 
5389     // Miscellaneous capabilities
5390     // NVDisplay does not support interlaced modes.
5391     pEvoCaps->misc.supportsInterlaced = FALSE;
5392 
5393 /* XXX temporary WAR; see bug 4028718 */
5394 #if !defined(NVC373_HEAD_CLK_CAP)
5395 #define NVC373_HEAD_CLK_CAP(i)                                     (0x5e8+(i)*4) /* RW-4A */
5396 #define NVC373_HEAD_CLK_CAP__SIZE_1                                            8 /*       */
5397 #define NVC373_HEAD_CLK_CAP_PCLK_MAX                                         7:0 /* RWIUF */
5398 #define NVC373_HEAD_CLK_CAP_PCLK_MAX_INIT                             0x00000085 /* RWI-V */
5399 #endif
5400 
5401     // Heads
5402     ct_assert(ARRAY_LEN(pEvoCaps->head) >= NVC373_HEAD_CAPA__SIZE_1);
5403     for (i = 0; i < NVC373_HEAD_CAPA__SIZE_1; i++) {
5404         NVEvoHeadCaps *pHeadCaps = &pEvoCaps->head[i];
5405 
5406         pHeadCaps->usable =
5407             FLD_IDX_TEST_DRF(C373, _SYS_CAP, _HEAD_EXISTS, i, _YES, sysCap);
5408         if (pHeadCaps->usable) {
5409             pHeadCaps->maxPClkKHz =
5410                 DRF_VAL(C373, _HEAD_CLK_CAP, _PCLK_MAX,
5411                         ReadCapReg(pCaps, NVC373_HEAD_CLK_CAP(i))) * 10000;
5412         }
5413 
5414     }
5415 
5416     // SORs
5417     ct_assert(ARRAY_LEN(pEvoCaps->sor) >= NVC373_SOR_CAP__SIZE_1);
5418     for (i = 0; i < NVC373_SOR_CAP__SIZE_1; i++) {
5419         NVEvoSorCaps *pSorCaps = &pEvoCaps->sor[i];
5420 
5421         NvBool sorUsable =
5422             FLD_IDX_TEST_DRF(C373, _SYS_CAP, _SOR_EXISTS, i, _YES, sysCap);
5423 
5424         /* XXXnvdisplay: add SOR caps: max DP clk, ... */
5425         if (sorUsable) {
5426             const NvU32 sorCap = ReadCapReg(pCaps, NVC373_SOR_CAP(i));
5427             pSorCaps->dualTMDS =
5428                 FLD_TEST_DRF(C373, _SOR_CAP, _DUAL_TMDS, _TRUE, sorCap);
5429 
5430             /*
5431              * Assume that all SORs are equally capable, and that all SORs
5432              * support HDMI FRL if the display class supports it.  (If this
5433              * assert fires, we may need to rework SOR assignment for such HDMI
5434              * sinks.)
5435              *
5436              * Although HDMI_FRL is only defined for class C6, classes C3 and
5437              * C5 don't use that bit in the SOR_CAP register so it should
5438              * always be 0 on those chips.
5439              */
5440             nvAssert(!!FLD_TEST_DRF(C673, _SOR_CAP, _HDMI_FRL, _TRUE, sorCap) ==
5441                      !!pDevEvo->hal->caps.supportsHDMIFRL);
5442 
5443             pSorCaps->maxTMDSClkKHz =
5444                 DRF_VAL(C373, _SOR_CLK_CAP, _TMDS_MAX,
5445                         ReadCapReg(pCaps, NVC373_SOR_CLK_CAP(i))) * 10000;
5446         }
5447     }
5448 
5449     // Don't need any PIOR caps currently.
5450 
5451     // Windows
5452     ct_assert(ARRAY_LEN(pEvoCaps->window) >= NVC373_SYS_CAPB_WINDOW_EXISTS__SIZE_1);
5453     for (i = 0; i < NVC373_SYS_CAPB_WINDOW_EXISTS__SIZE_1; i++) {
5454         NVEvoWindowCaps *pWinCaps = &pEvoCaps->window[i];
5455 
5456         pWinCaps->usable =
5457             FLD_IDX_TEST_DRF(C373, _SYS_CAPB, _WINDOW_EXISTS, i, _YES, sysCapB);
5458     }
5459 }
5460 
5461 static void EvoParseCapabilityNotifierC3(NVDevEvoPtr pDevEvo,
5462                                          NVEvoSubDevPtr pEvoSubDev,
5463                                          volatile const NvU32 *pCaps)
5464 {
5465     NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
5466     NvU32 i;
5467 
5468     // Miscellaneous capabilities
5469     pEvoCaps->misc.supportsSemiPlanar = FALSE;
5470     pEvoCaps->misc.supportsPlanar = FALSE;
5471     pEvoCaps->misc.supportsDSI = FALSE;
5472 
5473     // Heads
5474     ct_assert(ARRAY_LEN(pEvoCaps->head) >= NVC373_HEAD_CAPA__SIZE_1);
5475     for (i = 0; i < NVC373_HEAD_CAPA__SIZE_1; i++) {
5476         NVEvoHeadCaps *pHeadCaps = &pEvoCaps->head[i];
5477 
5478         /* XXXnvdisplay: add caps for hsat, ocsc, lut */
5479         if (pHeadCaps->usable) {
5480             NVEvoScalerCaps *pScalerCaps = &pHeadCaps->scalerCaps;
5481 
5482             pScalerCaps->present =
5483                 FLD_TEST_DRF(C373, _HEAD_CAPA, _SCALER, _TRUE,
5484                              ReadCapReg(pCaps, NVC373_HEAD_CAPA(i)));
5485             if (pScalerCaps->present) {
5486                 NVEvoScalerTapsCaps *pTapsCaps;
5487                 NvU32 tmp;
5488 
5489                 /*
5490                  * Note that some of these may be zero (e.g., only 2-tap 444
5491                  * mode is supported on GV100, so the rest are all zero.
5492                  *
5493                  * Downscaling by more than 2x in either direction is not
5494                  * allowed by state error check for both horizontal and
5495                  * vertical 2-tap scaling.
5496                  *
5497                  * Downscaling by more than 4x in either direction is not
5498                  * allowed by argument error check (and state error check) for
5499                  * 5-tap scaling.
5500                  *
5501                  * 5-tap scaling is not implemented on GV100, though, so we
5502                  * should never see numTaps == 5 on GV100, and we can just use a
5503                  * max of 2 here all the time.
5504                  */
5505 
5506                 /* 2-tap capabilities */
5507                 tmp = ReadCapReg(pCaps, NVC373_HEAD_CAPD(i));
5508                 pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_2TAPS];
5509                 pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5510                 pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5511                 pTapsCaps->maxPixelsVTaps =
5512                     NV_MAX(DRF_VAL(C373, _HEAD_CAPD, _MAX_PIXELS_2TAP422, tmp),
5513                            DRF_VAL(C373, _HEAD_CAPD, _MAX_PIXELS_2TAP444, tmp));
5514 
5515                 /*
5516                  * Note that there is a capability register for 1TAP, but there
5517                  * doesn't appear to be a way to select 1-tap scaling in the
5518                  * channel methods, so don't bother reading it for now.
5519                  */
5520             }
5521         }
5522     }
5523 }
5524 
5525 static void EvoParsePrecompScalerCaps5(NVEvoCapabilitiesPtr pEvoCaps,
5526                                        volatile const NvU32 *pCaps)
5527 {
5528     int i;
5529 
5530     for (i = 0; i < NVC573_SYS_CAPB_WINDOW_EXISTS__SIZE_1; i++) {
5531         NVEvoWindowCaps *pWinCaps = &pEvoCaps->window[i];
5532         NVEvoScalerCaps *pScalerCaps = &pWinCaps->scalerCaps;
5533         NVEvoScalerTapsCaps *pTapsCaps;
5534         NvU32 capA = ReadCapReg(pCaps, NVC573_PRECOMP_WIN_PIPE_HDR_CAPA(i));
5535         NvU32 capD, capF;
5536 
5537         pScalerCaps->present =
5538             FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA, _SCLR_PRESENT,
5539                          _TRUE, capA);
5540         if (pScalerCaps->present) {
5541             capD = ReadCapReg(pCaps, NVC573_PRECOMP_WIN_PIPE_HDR_CAPD(i));
5542             capF = ReadCapReg(pCaps, NVC573_PRECOMP_WIN_PIPE_HDR_CAPF(i));
5543 
5544             /* 5-tap capabilities */
5545             pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_5TAPS];
5546             if (FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPD,
5547                              _SCLR_VS_MAX_SCALE_FACTOR, _4X, capD)) {
5548                 pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_4X;
5549             } else {
5550                 pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5551             }
5552 
5553             if (FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPD,
5554                              _SCLR_HS_MAX_SCALE_FACTOR, _4X, capD)) {
5555                 pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_4X;
5556             } else {
5557                 pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5558             }
5559 
5560             pTapsCaps->maxPixelsVTaps =
5561                 DRF_VAL(C573, _PRECOMP_WIN_PIPE_HDR_CAPF,
5562                         _VSCLR_MAX_PIXELS_5TAP, capF);
5563 
5564             /* 2-tap capabilities */
5565             pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_2TAPS];
5566             pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5567             pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5568             pTapsCaps->maxPixelsVTaps =
5569             DRF_VAL(C573, _PRECOMP_WIN_PIPE_HDR_CAPF, _VSCLR_MAX_PIXELS_2TAP,
5570                     capF);
5571         }
5572     }
5573 }
5574 
5575 static void EvoParseCapabilityNotifierC5C6Common(NVEvoCapabilitiesPtr pEvoCaps,
5576                                                  volatile const NvU32 *pCaps)
5577 {
5578     NvU32 i;
5579     NvBool postcompScalingSupported = FALSE;
5580 
5581     // Heads
5582     ct_assert(ARRAY_LEN(pEvoCaps->head) >= NVC573_SYS_CAP_HEAD_EXISTS__SIZE_1);
5583     for (i = 0; i < NVC573_SYS_CAP_HEAD_EXISTS__SIZE_1; i++) {
5584         NVEvoHeadCaps *pHeadCaps = &pEvoCaps->head[i];
5585 
5586         if (pHeadCaps->usable) {
5587             NVEvoScalerCaps *pScalerCaps = &pHeadCaps->scalerCaps;
5588             NVEvoScalerTapsCaps *pTapsCaps;
5589             NvU32 capA = ReadCapReg(pCaps, NVC573_POSTCOMP_HEAD_HDR_CAPA(i));
5590             NvU32 capC, capD;
5591 
5592             pScalerCaps->present =
5593                 FLD_TEST_DRF(C573, _POSTCOMP_HEAD_HDR_CAPA, _SCLR_PRESENT,
5594                              _TRUE, capA);
5595             if (pScalerCaps->present) {
5596                 postcompScalingSupported = TRUE;
5597 
5598                 capC = ReadCapReg(pCaps, NVC573_POSTCOMP_HEAD_HDR_CAPC(i));
5599                 capD = ReadCapReg(pCaps, NVC573_POSTCOMP_HEAD_HDR_CAPD(i));
5600 
5601                 /*
5602                  * Note that some of these may be zero.
5603                  *
5604                  * XXXnvdisplay: what about POSTCOMP_HEAD_HDR_CAPC_SCLR_*?
5605                  */
5606 
5607                 /* 5-tap capabilities */
5608                 pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_5TAPS];
5609                 if (FLD_TEST_DRF(C573, _POSTCOMP_HEAD_HDR_CAPC,
5610                                  _SCLR_VS_MAX_SCALE_FACTOR, _4X, capC)) {
5611                     pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_4X;
5612                 } else {
5613                     pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5614                 }
5615 
5616                 if (FLD_TEST_DRF(C573, _POSTCOMP_HEAD_HDR_CAPC,
5617                                  _SCLR_HS_MAX_SCALE_FACTOR, _4X, capC)) {
5618                     pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_4X;
5619                 } else {
5620                     pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5621                 }
5622 
5623                 pTapsCaps->maxPixelsVTaps =
5624                     DRF_VAL(C573, _POSTCOMP_HEAD_HDR_CAPD,
5625                     _VSCLR_MAX_PIXELS_5TAP, capD);
5626 
5627                 /* 2-tap capabilities */
5628                 pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_2TAPS];
5629                 pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5630                 pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
5631                 pTapsCaps->maxPixelsVTaps =
5632                     DRF_VAL(C573, _POSTCOMP_HEAD_HDR_CAPD,
5633                             _VSCLR_MAX_PIXELS_2TAP, capD);
5634             }
5635 
5636 #if defined(NV_DEBUG)
5637             NvU32 capA = ReadCapReg(pCaps, NVC573_POSTCOMP_HEAD_HDR_CAPA(i));
5638             NvU32 unitWidth = DRF_VAL(C573, _POSTCOMP_HEAD_HDR_CAPA, _UNIT_WIDTH, capA);
5639 
5640             // EvoInitChannelC5 assumes 16-bit fixed-point.
5641             nvAssert(unitWidth == 16);
5642 #endif
5643         }
5644     }
5645 
5646     for (i = 0; i < NVC573_SYS_CAPB_WINDOW_EXISTS__SIZE_1; i++) {
5647         NVEvoWindowCaps *pWinCaps = &pEvoCaps->window[i];
5648         NvU32 capA = ReadCapReg(pCaps, NVC573_PRECOMP_WIN_PIPE_HDR_CAPA(i));
5649 
5650         pWinCaps->tmoPresent = FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
5651                                             _TMO_PRESENT, _TRUE, capA);
5652 
5653         pWinCaps->csc0MatricesPresent =
5654             FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
5655                          _CSC00_PRESENT, _TRUE, capA) &&
5656             FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
5657                          _CSC01_PRESENT, _TRUE, capA);
5658 
5659         pWinCaps->csc1MatricesPresent =
5660             (FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
5661                           _CSC10_PRESENT, _TRUE, capA) &&
5662              FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
5663                           _CSC11_PRESENT, _TRUE, capA));
5664 
5665         pWinCaps->cscLUTsPresent =
5666             FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
5667                          _CSC0LUT_PRESENT, _TRUE, capA) &&
5668             FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
5669                          _CSC1LUT_PRESENT, _TRUE, capA);
5670 
5671         nvAssert(!pWinCaps->tmoPresent ||
5672                  (pWinCaps->csc0MatricesPresent &&
5673                   pWinCaps->csc1MatricesPresent &&
5674                   pWinCaps->cscLUTsPresent));
5675     }
5676 
5677     /*
5678      * To keep the design simple, NVKMS will expose support for precomp scaling
5679      * iff postcomp scaling isn't supported. This means that on chips which have
5680      * both precomp and postcomp scalers (e.g., Turing), NVKMS will only report
5681      * that postcomp scaling is supported.
5682      */
5683     if (!postcompScalingSupported) {
5684         EvoParsePrecompScalerCaps5(pEvoCaps, pCaps);
5685     }
5686 
5687     // XXXnvdisplay3: add SOR caps for DP over USB
5688 }
5689 
5690 static void EvoParseCapabilityNotifierC5(NVDevEvoPtr pDevEvo,
5691                                          NVEvoSubDevPtr pEvoSubDev,
5692                                          volatile const NvU32 *pCaps)
5693 {
5694     NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
5695 
5696     // Miscellaneous capabilities
5697 
5698     /*
5699      * On Turing, the NVC573_IHUB_COMMON_CAPA_SUPPORT_PLANAR bit actually
5700      * reports whether IHUB supports YUV _semi-planar_ formats.
5701      */
5702     pEvoCaps->misc.supportsSemiPlanar =
5703         FLD_TEST_DRF(C573, _IHUB_COMMON_CAPA, _SUPPORT_PLANAR, _TRUE,
5704                      ReadCapReg(pCaps, NVC573_IHUB_COMMON_CAPA));
5705     pEvoCaps->misc.supportsDSI = FALSE;
5706 
5707     EvoParseCapabilityNotifierC5C6Common(pEvoCaps, pCaps);
5708 }
5709 
5710 static void EvoParseCapabilityNotifierC6(NVDevEvoPtr pDevEvo,
5711                                          NVEvoSubDevPtr pEvoSubDev,
5712                                          volatile const NvU32 *pCaps)
5713 {
5714     NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
5715     NvU32 capC = ReadCapReg(pCaps, NVC673_IHUB_COMMON_CAPC);
5716     NvU32 i;
5717 
5718     // Miscellaneous capabilities
5719 
5720     pEvoCaps->misc.supportsPlanar =
5721         FLD_TEST_DRF(C673, _IHUB_COMMON_CAPA, _SUPPORT_PLANAR, _TRUE,
5722                      ReadCapReg(pCaps, NVC673_IHUB_COMMON_CAPA));
5723 
5724     pEvoCaps->misc.supportsSemiPlanar =
5725         FLD_TEST_DRF(C673, _IHUB_COMMON_CAPC, _SUPPORT_SEMI_PLANAR, _TRUE, capC);
5726 
5727     pEvoCaps->misc.supportsHVFlip =
5728         FLD_TEST_DRF(C673, _IHUB_COMMON_CAPC, _SUPPORT_HOR_VER_FLIP, _TRUE, capC);
5729 
5730     ct_assert(ARRAY_LEN(pEvoCaps->head) >= NVC673_SYS_CAP_HEAD_EXISTS__SIZE_1);
5731 
5732     // DSI is currently supported on just Orin, which has only 1 DSI engine (DSI0).
5733     pEvoCaps->misc.supportsDSI =
5734         FLD_TEST_DRF(C673, _SYS_CAP, _DSI0_EXISTS, _YES,
5735                      ReadCapReg(pCaps, NVC673_SYS_CAP));
5736 
5737     for (i = 0; i < NVC673_SYS_CAP_HEAD_EXISTS__SIZE_1; i++) {
5738         NVEvoHeadCaps *pHeadCaps = &pEvoCaps->head[i];
5739 
5740         if (pHeadCaps->usable) {
5741             NvU32 capA = ReadCapReg(pCaps, NVC673_POSTCOMP_HEAD_HDR_CAPA(i));
5742             NvBool hclpfPresent =
5743                 FLD_TEST_DRF(C673, _POSTCOMP_HEAD_HDR_CAPA, _HCLPF_PRESENT,
5744                              _TRUE, capA);
5745             NvBool vfilterPresent =
5746                 FLD_TEST_DRF(C673, _POSTCOMP_HEAD_HDR_CAPA, _VFILTER_PRESENT,
5747                              _TRUE, capA);
5748 
5749             pHeadCaps->supportsHDMIYUV420HW = hclpfPresent && vfilterPresent;
5750         }
5751     }
5752 
5753     EvoParseCapabilityNotifierC5C6Common(pEvoCaps, pCaps);
5754 }
5755 
5756 static NvU32 UsableWindowCount(const NVEvoCapabilities *pEvoCaps)
5757 {
5758     NvU32 i, count = 0;
5759     NvBool foundUnusable = FALSE;
5760 
5761     for (i = 0; i < ARRAY_LEN(pEvoCaps->window); i++) {
5762         if (pEvoCaps->window[i].usable) {
5763             count++;
5764             /* Assert that usable windows are contiguous starting from 0. */
5765             if (foundUnusable) {
5766                 nvAssert(!foundUnusable);
5767             }
5768         } else {
5769             foundUnusable = TRUE;
5770         }
5771     }
5772 
5773     return count;
5774 }
5775 
5776 typedef typeof(EvoParseCapabilityNotifierC3) parse_caps_t;
5777 typedef typeof(nvHwFormatFromKmsFormatC3) get_hw_fmt_t;
5778 
5779 static void SetHDRLayerCaps(NVDevEvoPtr pDevEvo)
5780 {
5781     NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[0];
5782     NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
5783     NvU32 win;
5784 #if defined(DEBUG)
5785     NvBool hdrLayerCapSet[NVKMS_MAX_LAYERS_PER_HEAD] = {FALSE};
5786 #endif
5787     NvU32 numLayers[NVKMS_MAX_HEADS_PER_DISP] = {0};
5788 
5789     /*
5790      * XXX HDR: This assumes the window => layer mapping from
5791      * nvAllocCoreChannelEvo().
5792      *
5793      * TODO: Rework API to explicitly factor in window => layer mapping.
5794      */
5795     for (win = 0; win < pDevEvo->numWindows; win++) {
5796         NVEvoWindowCaps *pWinCaps = &pEvoCaps->window[win];
5797         const NvU32 head = pDevEvo->headForWindow[win];
5798 
5799         /*
5800          * XXX HDR: layerCaps assumes that every head has layers with the
5801          * same capabilities.
5802          *
5803          * TODO: Rework API for per-head layerCaps if this assert trips.
5804          */
5805         nvAssert(!hdrLayerCapSet[numLayers[head]] ||
5806                  (pDevEvo->caps.layerCaps[numLayers[head]].supportsHDR ==
5807                   pWinCaps->tmoPresent));
5808 
5809         pDevEvo->caps.layerCaps[numLayers[head]].supportsHDR = pWinCaps->tmoPresent;
5810 
5811 #if defined(DEBUG)
5812         hdrLayerCapSet[numLayers[head]] = TRUE;
5813 #endif
5814 
5815         numLayers[head]++;
5816     }
5817 }
5818 
5819 static NvBool EvoGetCapabilities3(NVDevEvoPtr pDevEvo,
5820                                   parse_caps_t *pParse,
5821                                   get_hw_fmt_t *pGetHwFmt,
5822                                   NvU32 hwclass,
5823                                   size_t length)
5824 {
5825     NvU32 capsHandle = nvGenerateUnixRmHandle(&pDevEvo->handleAllocator);
5826     NVDispEvoPtr pDispEvo;
5827     NvU32 sd;
5828     NvU32 status;
5829     NvBool ret = FALSE;
5830     NvBool first = TRUE;
5831     NvBool supportsSemiPlanar = TRUE;
5832     NvBool supportsPlanar = TRUE;
5833     NvBool supportsHVFlip = TRUE;
5834     unsigned int i;
5835     enum NvKmsRotation curRotation;
5836     NvBool reflectionX;
5837     NvBool reflectionY;
5838     NvU8 layer;
5839 
5840     /* With nvdisplay, capabilities are exposed in a separate object. */
5841     status = nvRmApiAlloc(nvEvoGlobal.clientHandle,
5842                           pDevEvo->displayHandle,
5843                           capsHandle,
5844                           hwclass, NULL);
5845     if (status != NVOS_STATUS_SUCCESS) {
5846         nvAssert(!"Failed to allocate caps object");
5847         goto free_handle;
5848     }
5849 
5850     for (layer = 0;
5851          layer < ARRAY_LEN(pDevEvo->caps.layerCaps);
5852          layer++) {
5853         pDevEvo->caps.layerCaps[layer].supportsWindowMode = TRUE;
5854     }
5855 
5856     FOR_ALL_EVO_DISPLAYS(pDispEvo, sd, pDevEvo) {
5857 
5858         NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[sd];
5859         void *ptr;
5860 
5861         status = nvRmApiMapMemory(nvEvoGlobal.clientHandle,
5862                                   pDevEvo->pSubDevices[sd]->handle,
5863                                   capsHandle,
5864                                   0,
5865                                   length,
5866                                   &ptr,
5867                                   0);
5868         if (status != NVOS_STATUS_SUCCESS) {
5869             nvAssert(!"Failed to map caps memory");
5870             goto free_object;
5871         }
5872 
5873         nvkms_memset(&pEvoSubDev->capabilities, 0,
5874                      sizeof(pEvoSubDev->capabilities));
5875 
5876         EvoParseCapabilityNotifier3(pDevEvo, pEvoSubDev, ptr);
5877         pParse(pDevEvo, pEvoSubDev, ptr);
5878 
5879         status = nvRmApiUnmapMemory(nvEvoGlobal.clientHandle,
5880                                     pDevEvo->pSubDevices[sd]->handle,
5881                                     capsHandle, ptr, 0);
5882         if (status != NVOS_STATUS_SUCCESS) {
5883             nvAssert(!"Failed to unmap caps memory");
5884         }
5885 
5886         if (first) {
5887             pDevEvo->numWindows =
5888                 UsableWindowCount(&pEvoSubDev->capabilities);
5889             first = FALSE;
5890         } else {
5891             /* Assert that each subdevice has the same number of windows. */
5892             nvAssert(pDevEvo->numWindows ==
5893                      UsableWindowCount(&pEvoSubDev->capabilities));
5894         }
5895 
5896         /*
5897          * Expose YUV semi-planar iff all of the disps belonging to pDevEvo
5898          * support it.
5899          */
5900         supportsSemiPlanar &=
5901             pEvoSubDev->capabilities.misc.supportsSemiPlanar;
5902 
5903         /*
5904          * Expose YUV planar iff all of the disps belonging to pDevEvo
5905          * support it.
5906          */
5907         supportsPlanar &=
5908             pEvoSubDev->capabilities.misc.supportsPlanar;
5909 
5910         supportsHVFlip &=
5911             pEvoSubDev->capabilities.misc.supportsHVFlip;
5912     }
5913 
5914     SetHDRLayerCaps(pDevEvo);
5915 
5916     for (i = NvKmsSurfaceMemoryFormatMin;
5917          i <= NvKmsSurfaceMemoryFormatMax;
5918          i++) {
5919         const NvKmsSurfaceMemoryFormatInfo *pFormatInfo =
5920             nvKmsGetSurfaceMemoryFormatInfo(i);
5921 
5922         if ((pFormatInfo->numPlanes == 2 && !supportsSemiPlanar) ||
5923             (pFormatInfo->numPlanes == 3 && !supportsPlanar)) {
5924             continue;
5925         }
5926 
5927         if (pGetHwFmt(i) != 0) {
5928             NvU8 layer;
5929 
5930             for (layer = 0;
5931                  layer < ARRAY_LEN(pDevEvo->caps.layerCaps);
5932                  layer++) {
5933                 pDevEvo->caps.layerCaps[layer].supportedSurfaceMemoryFormats |=
5934                     NVBIT64(i);
5935             }
5936         }
5937     }
5938 
5939     for (reflectionX = FALSE;
5940          reflectionX <= TRUE;
5941          reflectionX++) {
5942 
5943         for (reflectionY = FALSE;
5944              reflectionY <= TRUE;
5945              reflectionY++) {
5946 
5947             for (curRotation = NVKMS_ROTATION_MIN;
5948                  curRotation <= NVKMS_ROTATION_MAX;
5949                  curRotation++) {
5950                 struct NvKmsRRParams rrParams = { curRotation,
5951                                                   reflectionX,
5952                                                   reflectionY };
5953                 NvU8 bitPosition;
5954 
5955                 if ((reflectionX || reflectionY) && !supportsHVFlip) {
5956                     continue;
5957                 }
5958 
5959                 if (curRotation == NVKMS_ROTATION_180 && !supportsHVFlip) {
5960                     continue;
5961                 }
5962 
5963                 /*
5964                  * Skipping over rotations by 90 and 270 degrees
5965                  * because these rotations require support for
5966                  * SCAN_COLUMN rotation, which hasn't been added
5967                  * to NVKMS yet.
5968                  */
5969                 if (curRotation == NVKMS_ROTATION_90 ||
5970                     curRotation == NVKMS_ROTATION_270) {
5971                     continue;
5972                 }
5973 
5974                 bitPosition = NvKmsRRParamsToCapBit(&rrParams);
5975                 pDevEvo->caps.validLayerRRTransforms |= NVBIT(bitPosition);
5976             }
5977         }
5978     }
5979 
5980     ret = TRUE;
5981 
5982 free_object:
5983     status = nvRmApiFree(nvEvoGlobal.clientHandle,
5984                          pDevEvo->displayHandle,
5985                          capsHandle);
5986     if (status != NVOS_STATUS_SUCCESS) {
5987         nvAssert(!"Failed to free caps object");
5988     }
5989 
5990 free_handle:
5991     nvFreeUnixRmHandle(&pDevEvo->handleAllocator, capsHandle);
5992 
5993     return ret;
5994 }
5995 
5996 static NvBool EvoGetCapabilitiesC3(NVDevEvoPtr pDevEvo)
5997 {
5998     return EvoGetCapabilities3(pDevEvo, EvoParseCapabilityNotifierC3,
5999                                nvHwFormatFromKmsFormatC3,
6000                                NVC373_DISP_CAPABILITIES,
6001                                sizeof(_NvC373DispCapabilities));
6002 }
6003 
6004 static NvBool EvoGetCapabilitiesC5(NVDevEvoPtr pDevEvo)
6005 {
6006     return EvoGetCapabilities3(pDevEvo, EvoParseCapabilityNotifierC5,
6007                                nvHwFormatFromKmsFormatC5,
6008                                NVC573_DISP_CAPABILITIES,
6009                                sizeof(_NvC573DispCapabilities));
6010 }
6011 
6012 static NvBool EvoGetCapabilitiesC6(NVDevEvoPtr pDevEvo)
6013 {
6014     return EvoGetCapabilities3(pDevEvo, EvoParseCapabilityNotifierC6,
6015                                nvHwFormatFromKmsFormatC6,
6016                                NVC673_DISP_CAPABILITIES,
6017                                sizeof(_NvC673DispCapabilities));
6018 }
6019 
6020 static void EvoSetViewportPointInC3(NVDevEvoPtr pDevEvo, const int head,
6021                                     NvU16 x, NvU16 y,
6022                                     NVEvoUpdateState *updateState)
6023 {
6024     NVEvoChannelPtr pChannel = pDevEvo->core;
6025 
6026     /* These methods should only apply to a single pDpy */
6027     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
6028 
6029     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
6030 
6031     /* Set the input viewport point */
6032     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_VIEWPORT_POINT_IN(head), 1);
6033     nvDmaSetEvoMethodData(pChannel, DRF_NUM(C37D, _HEAD_SET_VIEWPORT_POINT_IN, _X, x) |
6034                              DRF_NUM(C37D, _HEAD_SET_VIEWPORT_POINT_IN, _Y, y));
6035     /* XXXnvdisplay set ViewportValidPointIn to configure overfetch */
6036 }
6037 
6038 static void EvoSetOutputScalerC3(const NVDispEvoRec *pDispEvo, const NvU32 head,
6039                                  const NvU32 imageSharpeningValue,
6040                                  NVEvoUpdateState *updateState)
6041 {
6042     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
6043     NVEvoChannelPtr pChannel = pDevEvo->core;
6044     const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
6045     const NVHwModeViewPortEvo *pViewPort = &pHeadState->timings.viewPort;
6046 
6047     /* These methods should only apply to a single pDpyEvo */
6048     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
6049 
6050     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
6051 
6052     NvU32 vTaps = pViewPort->vTaps > NV_EVO_SCALER_2TAPS ?
6053                     NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER_VERTICAL_TAPS_TAPS_5 :
6054                     NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER_VERTICAL_TAPS_TAPS_2;
6055     NvU32 hTaps = pViewPort->hTaps > NV_EVO_SCALER_2TAPS ?
6056                     NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER_HORIZONTAL_TAPS_TAPS_5 :
6057                     NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER_HORIZONTAL_TAPS_TAPS_2;
6058 
6059     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER(head), 1);
6060     nvDmaSetEvoMethodData(pChannel,
6061         DRF_NUM(C37D, _HEAD_SET_CONTROL_OUTPUT_SCALER, _VERTICAL_TAPS, vTaps) |
6062         DRF_NUM(C37D, _HEAD_SET_CONTROL_OUTPUT_SCALER, _HORIZONTAL_TAPS, hTaps));
6063 }
6064 
6065 static NvBool EvoSetViewportInOut3(NVDevEvoPtr pDevEvo, const int head,
6066                                    const NVHwModeViewPortEvo *pViewPortMin,
6067                                    const NVHwModeViewPortEvo *pViewPort,
6068                                    const NVHwModeViewPortEvo *pViewPortMax,
6069                                    NVEvoUpdateState *updateState,
6070                                    NvU32 setWindowUsageBounds)
6071 {
6072     const NVEvoCapabilitiesPtr pEvoCaps = &pDevEvo->gpus[0].capabilities;
6073     NVEvoChannelPtr pChannel = pDevEvo->core;
6074     struct NvKmsScalingUsageBounds scalingUsageBounds = { };
6075     NvU32 win;
6076 
6077     /* These methods should only apply to a single pDpy */
6078     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
6079 
6080     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
6081 
6082     /* The input viewport shouldn't vary. */
6083     nvAssert(pViewPortMin->in.width == pViewPort->in.width);
6084     nvAssert(pViewPortMax->in.width == pViewPort->in.width);
6085     nvAssert(pViewPortMin->in.height == pViewPort->in.height);
6086     nvAssert(pViewPortMax->in.height == pViewPort->in.height);
6087     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_VIEWPORT_SIZE_IN(head), 1);
6088     nvDmaSetEvoMethodData(pChannel,
6089             DRF_NUM(C37D, _HEAD_SET_VIEWPORT_SIZE_IN, _WIDTH, pViewPort->in.width) |
6090             DRF_NUM(C37D, _HEAD_SET_VIEWPORT_SIZE_IN, _HEIGHT, pViewPort->in.height));
6091     /* XXXnvdisplay set ViewportValidSizeIn to configure overfetch */
6092 
6093     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_VIEWPORT_POINT_OUT_ADJUST(head), 1);
6094     nvDmaSetEvoMethodData(pChannel,
6095             DRF_NUM(C37D, _HEAD_SET_VIEWPORT_POINT_OUT, _ADJUST_X, pViewPort->out.xAdjust) |
6096             DRF_NUM(C37D, _HEAD_SET_VIEWPORT_POINT_OUT, _ADJUST_Y, pViewPort->out.yAdjust));
6097 
6098     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_VIEWPORT_SIZE_OUT(head), 1);
6099     nvDmaSetEvoMethodData(pChannel,
6100             DRF_NUM(C37D, _HEAD_SET_VIEWPORT_SIZE_OUT, _WIDTH, pViewPort->out.width) |
6101             DRF_NUM(C37D, _HEAD_SET_VIEWPORT_SIZE_OUT, _HEIGHT, pViewPort->out.height));
6102 
6103     /* XXXnvdisplay deal with pViewPortMin, pViewPortMax */
6104 
6105     if (!nvComputeScalingUsageBounds(&pEvoCaps->head[head].scalerCaps,
6106                                    pViewPort->in.width, pViewPort->in.height,
6107                                    pViewPort->out.width, pViewPort->out.height,
6108                                    pViewPort->hTaps, pViewPort->vTaps,
6109                                    &scalingUsageBounds)) {
6110         /* Should have been rejected by validation */
6111         nvAssert(!"Attempt to program invalid viewport");
6112     }
6113 
6114     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_MAX_OUTPUT_SCALE_FACTOR(head), 1);
6115     nvDmaSetEvoMethodData(pChannel,
6116         DRF_NUM(C37D, _HEAD_SET_MAX_OUTPUT_SCALE_FACTOR, _HORIZONTAL,
6117                 scalingUsageBounds.maxHDownscaleFactor) |
6118         DRF_NUM(C37D, _HEAD_SET_MAX_OUTPUT_SCALE_FACTOR, _VERTICAL,
6119                 scalingUsageBounds.maxVDownscaleFactor));
6120 
6121     /*
6122      * Program MAX_PIXELS_FETCHED_PER_LINE window usage bounds
6123      * for each window that is attached to the head.
6124      *
6125      * Precomp will clip the post-scaled window to the input viewport, reverse-scale
6126      * this cropped size back to the input surface domain, and isohub will fetch
6127      * this cropped size. This function assumes that there's no window scaling yet,
6128      * so the MAX_PIXELS_FETCHED_PER_LINE will be bounded by the input viewport
6129      * width. SetScalingUsageBoundsOneWindow5() will take care of updating
6130      * MAX_PIXELS_FETCHED_PER_LINE, if window scaling is enabled later.
6131      *
6132      * Program MAX_PIXELS_FETCHED_PER_LINE for each window that is attached to
6133      * head. For Turing+, SetScalingUsageBoundsOneWindow5() will take care of
6134      * programming window usage bounds only for the layers/windows in use.
6135      */
6136     setWindowUsageBounds |=
6137         DRF_NUM(C37D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _MAX_PIXELS_FETCHED_PER_LINE,
6138                 GetMaxPixelsFetchedPerLine(pViewPort->in.width,
6139                 NV_EVO_SCALE_FACTOR_1X));
6140 
6141     for (win = 0; win < pDevEvo->numWindows; win++) {
6142         if (head != pDevEvo->headForWindow[win]) {
6143             continue;
6144         }
6145 
6146         nvDmaSetStartEvoMethod(pChannel, NVC37D_WINDOW_SET_WINDOW_USAGE_BOUNDS(win), 1);
6147         nvDmaSetEvoMethodData(pChannel, setWindowUsageBounds);
6148     }
6149 
6150     return scalingUsageBounds.vUpscalingAllowed;
6151 }
6152 
6153 static void EvoSetViewportInOutC3(NVDevEvoPtr pDevEvo, const int head,
6154                                   const NVHwModeViewPortEvo *pViewPortMin,
6155                                   const NVHwModeViewPortEvo *pViewPort,
6156                                   const NVHwModeViewPortEvo *pViewPortMax,
6157                                   NVEvoUpdateState *updateState)
6158 {
6159     NVEvoChannelPtr pChannel = pDevEvo->core;
6160     NvBool verticalUpscalingAllowed =
6161         EvoSetViewportInOut3(pDevEvo, head, pViewPortMin, pViewPort,
6162                              pViewPortMax, updateState,
6163                              NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C3);
6164 
6165     nvDmaSetStartEvoMethod(pChannel,
6166         NVC37D_HEAD_SET_HEAD_USAGE_BOUNDS(head), 1);
6167     nvDmaSetEvoMethodData(pChannel,
6168         DRF_DEF(C37D, _HEAD_SET_HEAD_USAGE_BOUNDS, _CURSOR, _USAGE_W256_H256) |
6169         DRF_DEF(C37D, _HEAD_SET_HEAD_USAGE_BOUNDS, _OUTPUT_LUT, _USAGE_1025) |
6170         (verticalUpscalingAllowed ?
6171             DRF_DEF(C37D, _HEAD_SET_HEAD_USAGE_BOUNDS, _UPSCALING_ALLOWED, _TRUE) :
6172             DRF_DEF(C37D, _HEAD_SET_HEAD_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE)));
6173 }
6174 
6175 static void EvoSetViewportInOutC5(NVDevEvoPtr pDevEvo, const int head,
6176                                   const NVHwModeViewPortEvo *pViewPortMin,
6177                                   const NVHwModeViewPortEvo *pViewPort,
6178                                   const NVHwModeViewPortEvo *pViewPortMax,
6179                                   NVEvoUpdateState *updateState)
6180 {
6181     NVEvoChannelPtr pChannel = pDevEvo->core;
6182     NvU32 setWindowUsageBounds =
6183         (NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C5 |
6184          DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_2) |
6185          DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE));
6186     NvU32 verticalUpscalingAllowed =
6187         EvoSetViewportInOut3(pDevEvo, head, pViewPortMin, pViewPort,
6188                              pViewPortMax, updateState, setWindowUsageBounds);
6189 
6190     nvDmaSetStartEvoMethod(pChannel,
6191         NVC57D_HEAD_SET_HEAD_USAGE_BOUNDS(head), 1);
6192     nvDmaSetEvoMethodData(pChannel,
6193         DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _CURSOR, _USAGE_W256_H256) |
6194         DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _OLUT_ALLOWED, _TRUE) |
6195         /* Despite the generic name of this field, it's specific to vertical taps. */
6196         (pViewPort->vTaps > NV_EVO_SCALER_2TAPS ?
6197             DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _OUTPUT_SCALER_TAPS, _TAPS_5) :
6198             DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _OUTPUT_SCALER_TAPS, _TAPS_2)) |
6199         (verticalUpscalingAllowed ?
6200             DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _UPSCALING_ALLOWED, _TRUE) :
6201             DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE)));
6202 }
6203 
6204 /*!
6205  * Compute the C37D_HEAD_SET_CONTROL_CURSOR method value.
6206  *
6207  * This function also validates that the given NVSurfaceEvoRec can be
6208  * used as a cursor image.
6209 
6210  *
6211  * \param[in]  pDevEvo      The device on which the cursor will be programmed.
6212  * \param[in]  pSurfaceEvo  The surface to be used as the cursor image.
6213  * \param[out] pValue       The C37D_HEAD_SET_CONTROL_CURSOR method value.
6214 
6215  * \return  If TRUE, the surface can be used as a cursor image, and
6216  *          pValue contains the method value.  If FALSE, the surface
6217  *          cannot be used as a cursor image.
6218  */
6219 static NvBool EvoGetHeadSetControlCursorValueC3(const NVDevEvoRec *pDevEvo,
6220                                                 const NVSurfaceEvoRec *pSurfaceEvo,
6221                                                 NvU32 *pValue)
6222 {
6223     NvU32 value = 0;
6224 
6225     if (pSurfaceEvo == NULL) {
6226         value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _ENABLE, _DISABLE);
6227         value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _FORMAT, _A8R8G8B8);
6228         goto done;
6229     } else {
6230         value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _ENABLE, _ENABLE);
6231     }
6232 
6233     /* The cursor must always be pitch. */
6234 
6235     if (pSurfaceEvo->layout != NvKmsSurfaceMemoryLayoutPitch) {
6236         return FALSE;
6237     }
6238 
6239     /*
6240      * The only supported cursor image memory format is A8R8G8B8.
6241      */
6242     if (pSurfaceEvo->format == NvKmsSurfaceMemoryFormatA8R8G8B8) {
6243         value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _FORMAT, _A8R8G8B8);
6244     } else {
6245         return FALSE;
6246     }
6247 
6248     /*
6249      * The cursor only supports a few image sizes.
6250      */
6251     if ((pSurfaceEvo->widthInPixels == 32) &&
6252         (pSurfaceEvo->heightInPixels == 32)) {
6253         value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _SIZE, _W32_H32);
6254     } else if ((pSurfaceEvo->widthInPixels == 64) &&
6255                (pSurfaceEvo->heightInPixels == 64)) {
6256         value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _SIZE, _W64_H64);
6257     } else if ((pDevEvo->cursorHal->caps.maxSize >= 128) &&
6258                (pSurfaceEvo->widthInPixels == 128) &&
6259                (pSurfaceEvo->heightInPixels == 128)) {
6260         value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _SIZE, _W128_H128);
6261     } else if ((pDevEvo->cursorHal->caps.maxSize >= 256) &&
6262                (pSurfaceEvo->widthInPixels == 256) &&
6263                (pSurfaceEvo->heightInPixels == 256)) {
6264         value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _SIZE, _W256_H256);
6265     } else {
6266         return FALSE;
6267     }
6268 
6269     /*
6270      * Hard code the cursor hotspot.
6271      */
6272     value |= DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR, _HOT_SPOT_Y, 0);
6273     value |= DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR, _HOT_SPOT_X, 0);
6274 
6275     // XXXnvdisplay: Add support for cursor de-gamma.
6276 
6277 done:
6278 
6279     if (pValue != NULL) {
6280         *pValue = value;
6281     }
6282 
6283     return TRUE;
6284 }
6285 
6286 static void EvoSetCursorImageC3(NVDevEvoPtr pDevEvo, const int head,
6287                                 const NVSurfaceEvoRec *pSurfaceEvo,
6288                                 NVEvoUpdateState *updateState,
6289                                 const struct NvKmsCompositionParams *pCursorCompParams)
6290 {
6291     NVEvoChannelPtr pChannel = pDevEvo->core;
6292     const NvU32 ctxdma = pSurfaceEvo ? pSurfaceEvo->planes[0].ctxDma : 0;
6293     const NvU64 offset = pSurfaceEvo ? pSurfaceEvo->planes[0].offset : 0;
6294     NvU32 headSetControlCursorValue = 0;
6295     NvBool ret;
6296 
6297     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
6298     nvAssert(pCursorCompParams->colorKeySelect ==
6299                 NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE);
6300     nvAssert(NVBIT(pCursorCompParams->blendingMode[1]) &
6301                 NV_EVO3_SUPPORTED_CURSOR_COMP_BLEND_MODES);
6302     /* These methods should only apply to a single pDpy */
6303     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
6304 
6305     nvAssert(!pSurfaceEvo || ctxdma);
6306 
6307     ret = EvoGetHeadSetControlCursorValueC3(pDevEvo, pSurfaceEvo,
6308                                             &headSetControlCursorValue);
6309     /*
6310      * The caller should have already validated the surface, so there
6311      * shouldn't be a failure.
6312      */
6313     if (!ret) {
6314         nvAssert(!"Could not construct HEAD_SET_CONTROL_CURSOR value");
6315     }
6316 
6317     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PRESENT_CONTROL_CURSOR(head), 1);
6318     nvDmaSetEvoMethodData(pChannel,
6319             DRF_DEF(C37D, _HEAD_SET_PRESENT_CONTROL_CURSOR, _MODE, _MONO));
6320 
6321     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTEXT_DMA_CURSOR(head, 0), 4);
6322     nvDmaSetEvoMethodData(pChannel,
6323             DRF_NUM(C37D, _HEAD_SET_CONTEXT_DMA_CURSOR, _HANDLE, ctxdma));
6324     // Always set the right cursor context DMA.
6325     // HW will just ignore this if it is not in stereo cursor mode.
6326     nvDmaSetEvoMethodData(pChannel,
6327             DRF_NUM(C37D, _HEAD_SET_CONTEXT_DMA_CURSOR, _HANDLE, ctxdma));
6328     nvDmaSetEvoMethodData(pChannel,
6329         DRF_NUM(C37D, _HEAD_SET_OFFSET_CURSOR, _ORIGIN,
6330                 nvCtxDmaOffsetFromBytes(offset)));
6331     nvDmaSetEvoMethodData(pChannel,
6332         DRF_NUM(C37D, _HEAD_SET_OFFSET_CURSOR, _ORIGIN,
6333                 nvCtxDmaOffsetFromBytes(offset)));
6334 
6335     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL_CURSOR(head), 1);
6336     nvDmaSetEvoMethodData(pChannel, headSetControlCursorValue);
6337 
6338     nvDmaSetStartEvoMethod(pChannel,
6339             NVC37D_HEAD_SET_CONTROL_CURSOR_COMPOSITION(head), 1);
6340     switch (pCursorCompParams->blendingMode[1]) {
6341     case NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE:
6342         nvDmaSetEvoMethodData(pChannel,
6343             DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1, 255) |
6344             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6345                     _CURSOR_COLOR_FACTOR_SELECT, _K1) |
6346             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6347                     _VIEWPORT_COLOR_FACTOR_SELECT, _ZERO) |
6348             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
6349         break;
6350     case NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA:
6351         nvDmaSetEvoMethodData(pChannel,
6352             DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1, 255) |
6353             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6354                     _CURSOR_COLOR_FACTOR_SELECT, _K1_TIMES_SRC) |
6355             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6356                     _VIEWPORT_COLOR_FACTOR_SELECT, _NEG_K1_TIMES_SRC) |
6357             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
6358         break;
6359     case NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA:
6360         nvDmaSetEvoMethodData(pChannel,
6361             DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1, 255) |
6362             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6363                     _CURSOR_COLOR_FACTOR_SELECT, _K1) |
6364             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6365                     _VIEWPORT_COLOR_FACTOR_SELECT, _NEG_K1_TIMES_SRC) |
6366             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
6367         break;
6368     case NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA:
6369         nvDmaSetEvoMethodData(pChannel,
6370             DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1,
6371                     pCursorCompParams->surfaceAlpha) |
6372             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6373                     _CURSOR_COLOR_FACTOR_SELECT, _K1_TIMES_SRC) |
6374             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6375                     _VIEWPORT_COLOR_FACTOR_SELECT, _NEG_K1_TIMES_SRC) |
6376             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
6377         break;
6378     case NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA:
6379         nvDmaSetEvoMethodData(pChannel,
6380             DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1,
6381                     pCursorCompParams->surfaceAlpha) |
6382             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6383                     _CURSOR_COLOR_FACTOR_SELECT, _K1) |
6384             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
6385                     _VIEWPORT_COLOR_FACTOR_SELECT, _NEG_K1_TIMES_SRC) |
6386             DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
6387         break;
6388     default:
6389         nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
6390             "%s: composition mode %d not supported for cursor",
6391             __func__, pCursorCompParams->blendingMode[1]);
6392         break;
6393     }
6394 }
6395 
6396 static NvBool EvoValidateCursorSurfaceC3(const NVDevEvoRec *pDevEvo,
6397                                          const NVSurfaceEvoRec *pSurfaceEvo)
6398 {
6399     return EvoGetHeadSetControlCursorValueC3(pDevEvo, pSurfaceEvo, NULL);
6400 }
6401 
6402 static NvBool ValidateWindowFormatSourceRectC3(
6403     const struct NvKmsRect *sourceFetchRect,
6404     const enum NvKmsSurfaceMemoryFormat format)
6405 {
6406     const NvKmsSurfaceMemoryFormatInfo *pFormatInfo =
6407         nvKmsGetSurfaceMemoryFormatInfo(format);
6408 
6409     /*
6410      * sourceFetchRect represents the dimensions of the source fetch rectangle.
6411      * If YUV crop and scaler overfetch are supported, it is up to the caller to
6412      * provide the correct dimensions (e.g., ValidSizeIn/ValidPointIn vs.
6413      * SizeIn/PointIn).
6414      *
6415      * For all YUV formats, the position and size of the fetch rectangle must be
6416      * even in the horizontal direction.
6417      *
6418      * For YUV420 formats, there is an additional restriction that the position
6419      * and size of the fetch rectangle must be even in the vertical direction as
6420      * well.
6421      */
6422     if (pFormatInfo->isYUV) {
6423         if (((sourceFetchRect->x & 1) != 0) ||
6424             (sourceFetchRect->width & 1) != 0) {
6425             return FALSE;
6426         }
6427 
6428         if (pFormatInfo->yuv.vertChromaDecimationFactor > 1) {
6429             if (((sourceFetchRect->y & 1) != 0) ||
6430                 (sourceFetchRect->height & 1) != 0) {
6431                 return FALSE;
6432             }
6433         }
6434     }
6435 
6436     return TRUE;
6437 }
6438 
6439 typedef typeof(ValidateWindowFormatSourceRectC3) val_src_rect_t;
6440 
6441 static NvBool EvoValidateWindowFormatWrapper(
6442     const enum NvKmsSurfaceMemoryFormat format,
6443     get_hw_fmt_t *pGetHwFmt,
6444     const struct NvKmsRect *sourceFetchRect,
6445     val_src_rect_t *pValSrcRect,
6446     NvU32 *hwFormatOut)
6447 {
6448     const NvU32 hwFormat = pGetHwFmt(format);
6449 
6450     if (hwFormat == 0) {
6451         return FALSE;
6452     }
6453 
6454     if (hwFormatOut != NULL) {
6455         *hwFormatOut = hwFormat;
6456     }
6457 
6458     /*
6459      * If sourceFetchRect is NULL, this function is only responsible for
6460      * verifying whether the given NvKmsSurfaceMemoryFormat has a corresponding
6461      * HW format.
6462      */
6463     if (sourceFetchRect == NULL) {
6464         return TRUE;
6465     }
6466 
6467     return pValSrcRect(sourceFetchRect, format);
6468 }
6469 
6470 static NvBool EvoValidateWindowFormatC3(
6471     const enum NvKmsSurfaceMemoryFormat format,
6472     const struct NvKmsRect *sourceFetchRect,
6473     NvU32 *hwFormatOut)
6474 {
6475     return EvoValidateWindowFormatWrapper(
6476             format,
6477             nvHwFormatFromKmsFormatC3,
6478             sourceFetchRect,
6479             ValidateWindowFormatSourceRectC3,
6480             hwFormatOut);
6481 }
6482 
6483 static NvBool EvoValidateWindowFormatC5(
6484     const enum NvKmsSurfaceMemoryFormat format,
6485     const struct NvKmsRect *sourceFetchRect,
6486     NvU32 *hwFormatOut)
6487 {
6488     return EvoValidateWindowFormatWrapper(
6489             format,
6490             nvHwFormatFromKmsFormatC5,
6491             sourceFetchRect,
6492             ValidateWindowFormatSourceRectC3,
6493             hwFormatOut);
6494 }
6495 
6496 static NvBool EvoValidateWindowFormatC6(
6497     const enum NvKmsSurfaceMemoryFormat format,
6498     const struct NvKmsRect *sourceFetchRect,
6499     NvU32 *hwFormatOut)
6500 {
6501     return EvoValidateWindowFormatWrapper(
6502             format,
6503             nvHwFormatFromKmsFormatC6,
6504             sourceFetchRect,
6505             ValidateWindowFormatSourceRectC3,
6506             hwFormatOut);
6507 }
6508 
6509 static NvU32 OffsetForNotifier(int idx)
6510 {
6511     /* NVDisplay notifiers are always the 16-byte variety.  We only care about
6512      * the NV_DISP_NOTIFIER__0 dword which contains the status. */
6513     NvU32 base = idx * (NV_DISP_NOTIFIER_SIZEOF / sizeof(NvU32));
6514     return base + NV_DISP_NOTIFIER__0;
6515 }
6516 
6517 static void EvoInitCompNotifierC3(const NVDispEvoRec *pDispEvo, int idx)
6518 {
6519     nvWriteEvoCoreNotifier(pDispEvo, OffsetForNotifier(idx),
6520                            DRF_DEF(_DISP, _NOTIFIER__0, _STATUS, _NOT_BEGUN));
6521 }
6522 
6523 static NvBool EvoIsCompNotifierCompleteC3(NVDispEvoPtr pDispEvo, int idx) {
6524     return nvEvoIsCoreNotifierComplete(pDispEvo, OffsetForNotifier(idx),
6525                                        DRF_BASE(NV_DISP_NOTIFIER__0_STATUS),
6526                                        DRF_EXTENT(NV_DISP_NOTIFIER__0_STATUS),
6527                                        NV_DISP_NOTIFIER__0_STATUS_FINISHED);
6528 }
6529 
6530 static void EvoWaitForCompNotifierC3(const NVDispEvoRec *pDispEvo, int idx)
6531 {
6532     nvEvoWaitForCoreNotifier(pDispEvo, OffsetForNotifier(idx),
6533                              DRF_BASE(NV_DISP_NOTIFIER__0_STATUS),
6534                              DRF_EXTENT(NV_DISP_NOTIFIER__0_STATUS),
6535                              NV_DISP_NOTIFIER__0_STATUS_FINISHED);
6536 }
6537 
6538 static void EvoSetDitherC3(NVDispEvoPtr pDispEvo, const int head,
6539                            const NvBool enabled, const NvU32 type,
6540                            const NvU32 algo,
6541                            NVEvoUpdateState *updateState)
6542 {
6543     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
6544     NVEvoChannelPtr pChannel = pDevEvo->core;
6545     NvU32 ditherControl;
6546 
6547     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
6548 
6549     if (enabled) {
6550         ditherControl = DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _ENABLE, _ENABLE);
6551 
6552         switch (type) {
6553         case NV0073_CTRL_SPECIFIC_OR_DITHER_TYPE_6_BITS:
6554             ditherControl |=
6555                 DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _BITS, _TO_6_BITS);
6556             break;
6557         case NV0073_CTRL_SPECIFIC_OR_DITHER_TYPE_8_BITS:
6558             ditherControl |=
6559                 DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _BITS, _TO_8_BITS);
6560             break;
6561         /* XXXnvdisplay: Support DITHER_TO_{10,12}_BITS (see also bug 1729668). */
6562         default:
6563             nvAssert(!"Unknown ditherType");
6564             // Fall through
6565         case NV0073_CTRL_SPECIFIC_OR_DITHER_TYPE_OFF:
6566             ditherControl = NVC37D_HEAD_SET_DITHER_CONTROL_ENABLE_DISABLE;
6567             break;
6568         }
6569 
6570     } else {
6571         ditherControl = DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _ENABLE, _DISABLE);
6572     }
6573 
6574     switch (algo) {
6575     case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_STATIC_ERR_ACC:
6576         ditherControl |=
6577             DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _STATIC_ERR_ACC);
6578         break;
6579     case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_DYNAMIC_2X2:
6580         ditherControl |=
6581             DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _DYNAMIC_2X2);
6582         break;
6583     case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_STATIC_2X2:
6584         ditherControl |=
6585             DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _STATIC_2X2);
6586         break;
6587     case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_TEMPORAL:
6588         ditherControl |=
6589             DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _TEMPORAL);
6590         break;
6591     default:
6592         nvAssert(!"Unknown DitherAlgo");
6593         // Fall through
6594     case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_UNKNOWN:
6595     case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_DYNAMIC_ERR_ACC:
6596         ditherControl |=
6597             DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _DYNAMIC_ERR_ACC);
6598         break;
6599     }
6600 
6601     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DITHER_CONTROL(head), 1);
6602     nvDmaSetEvoMethodData(pChannel, ditherControl);
6603 }
6604 
6605 static void EvoSetDisplayRateC3(NVDispEvoPtr pDispEvo, const int head,
6606                                 NvBool enable,
6607                                 NVEvoUpdateState *updateState,
6608                                 NvU32 timeoutMicroseconds)
6609 {
6610     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
6611     NVEvoChannelPtr pChannel = pDevEvo->core;
6612 
6613     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
6614 
6615     if (enable) {
6616         timeoutMicroseconds =
6617             NV_MIN(timeoutMicroseconds,
6618                    DRF_MASK(NVC37D_HEAD_SET_DISPLAY_RATE_MIN_REFRESH_INTERVAL));
6619 
6620         nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DISPLAY_RATE(head), 1);
6621         nvDmaSetEvoMethodData(pChannel,
6622             DRF_DEF(C37D, _HEAD_SET_DISPLAY_RATE, _RUN_MODE, _ONE_SHOT) |
6623             DRF_NUM(C37D, _HEAD_SET_DISPLAY_RATE, _MIN_REFRESH_INTERVAL,
6624                     timeoutMicroseconds) |
6625             (timeoutMicroseconds == 0 ?
6626                 DRF_DEF(C37D, _HEAD_SET_DISPLAY_RATE, _MIN_REFRESH, _DISABLE) :
6627                 DRF_DEF(C37D, _HEAD_SET_DISPLAY_RATE, _MIN_REFRESH, _ENABLE)));
6628     } else {
6629         nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DISPLAY_RATE(head), 1);
6630         nvDmaSetEvoMethodData(pChannel,
6631             DRF_DEF(C37D, _HEAD_SET_DISPLAY_RATE, _RUN_MODE, _CONTINUOUS));
6632     }
6633 }
6634 
6635 static void EvoSetStallLockC3(NVDispEvoPtr pDispEvo, const int head,
6636                               NvBool enable, NVEvoUpdateState *updateState)
6637 {
6638     NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
6639     NVEvoChannelPtr pChannel = pDevEvo->core;
6640     NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[pDispEvo->displayOwner];
6641     NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
6642 
6643     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
6644 
6645     if (enable) {
6646         NvU32 data = DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _ENABLE, _TRUE) |
6647                      DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _MODE, _ONE_SHOT);
6648 
6649         if (!pHC->useStallLockPin) {
6650             data |= DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _LOCK_PIN, _LOCK_PIN_NONE);
6651         } else  if (NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->stallLockPin)) {
6652             NvU32 pin = pHC->stallLockPin - NV_EVO_LOCK_PIN_INTERNAL_0;
6653             data |= DRF_NUM(C37D, _HEAD_SET_STALL_LOCK, _LOCK_PIN,
6654                             NVC37D_HEAD_SET_STALL_LOCK_LOCK_PIN_INTERNAL_SCAN_LOCK(pin));
6655         } else {
6656             NvU32 pin = pHC->stallLockPin - NV_EVO_LOCK_PIN_0;
6657             data |= DRF_NUM(C37D, _HEAD_SET_STALL_LOCK, _LOCK_PIN,
6658                             NVC37D_HEAD_SET_STALL_LOCK_LOCK_PIN_LOCK_PIN(pin));
6659         }
6660 
6661         if (pHC->crashLockUnstallMode) {
6662             data |= DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _UNSTALL_MODE, _CRASH_LOCK);
6663         } else {
6664             data |= DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _UNSTALL_MODE, _LINE_LOCK);
6665         }
6666 
6667         nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_STALL_LOCK(head), 1);
6668         nvDmaSetEvoMethodData(pChannel, data);
6669     } else {
6670         nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_STALL_LOCK(head), 1);
6671         nvDmaSetEvoMethodData(pChannel,
6672             DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _ENABLE, _FALSE));
6673     }
6674 }
6675 
6676 static NvBool GetChannelState(NVDevEvoPtr pDevEvo,
6677                               NVEvoChannelPtr pChan,
6678                               NvU32 sd,
6679                               NvU32 *result)
6680 {
6681     NVC370_CTRL_CMD_GET_CHANNEL_INFO_PARAMS info = { };
6682     NvU32 ret;
6683 
6684     info.base.subdeviceIndex = sd;
6685     info.channelClass = pChan->hwclass;
6686     info.channelInstance = pChan->instance;
6687 
6688     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
6689                          pDevEvo->displayHandle,
6690                          NVC370_CTRL_CMD_GET_CHANNEL_INFO,
6691                          &info, sizeof(info));
6692     if (ret != NVOS_STATUS_SUCCESS) {
6693         nvEvoLogDev(pDevEvo, EVO_LOG_ERROR,
6694                     "Failed to query display engine channel state: 0x%08x:%d:%d:0x%08x",
6695                     pChan->hwclass, pChan->instance, sd, ret);
6696         return FALSE;
6697     }
6698 
6699     *result = info.channelState;
6700 
6701     return TRUE;
6702 }
6703 
6704 static NvBool EvoIsChannelIdleC3(NVDevEvoPtr pDevEvo,
6705                                  NVEvoChannelPtr pChan,
6706                                  NvU32 sd,
6707                                  NvBool *result)
6708 {
6709     NvU32 channelState;
6710 
6711     if (!GetChannelState(pDevEvo, pChan, sd, &channelState)) {
6712         return FALSE;
6713     }
6714 
6715     *result = (channelState == NVC370_CTRL_GET_CHANNEL_INFO_STATE_IDLE);
6716 
6717     return TRUE;
6718 }
6719 
6720 static NvBool EvoIsChannelMethodPendingC3(NVDevEvoPtr pDevEvo,
6721                                           NVEvoChannelPtr pChan,
6722                                           NvU32 sd,
6723                                           NvBool *result)
6724 {
6725     NvBool tmpResult;
6726 
6727     /* With C370, Idle and NoMethodPending are equivalent. */
6728     ct_assert(NVC370_CTRL_GET_CHANNEL_INFO_STATE_IDLE ==
6729               NVC370_CTRL_GET_CHANNEL_INFO_STATE_NO_METHOD_PENDING);
6730 
6731     if (!EvoIsChannelIdleC3(pDevEvo, pChan, sd, &tmpResult)) {
6732         return FALSE;
6733     }
6734 
6735     *result = !tmpResult;
6736 
6737     return TRUE;
6738 }
6739 
6740 static NvBool EvoAllocRmCtrlObjectC3(NVDevEvoPtr pDevEvo)
6741 {
6742     const NvU32 handle = nvGenerateUnixRmHandle(&pDevEvo->handleAllocator);
6743 
6744     /* Note that this object is not at all related to the GF100_DISP_SW (9072)
6745      * or NV50_DISPLAY_SW (5072) objects, despite their similarity in name. */
6746     NvU32 status = nvRmApiAlloc(nvEvoGlobal.clientHandle,
6747                                 pDevEvo->deviceHandle,
6748                                 handle,
6749                                 NVC372_DISPLAY_SW, NULL);
6750     if (status != NVOS_STATUS_SUCCESS) {
6751         nvAssert(!"Failed to allocate nvdisplay rmctrl object");
6752         goto fail;
6753     }
6754 
6755     pDevEvo->rmCtrlHandle = handle;
6756 
6757     return TRUE;
6758 
6759 fail:
6760     nvFreeUnixRmHandle(&pDevEvo->handleAllocator, handle);
6761     return FALSE;
6762 }
6763 
6764 static NvU32 GetAccelerators(
6765     NVDevEvoPtr pDevEvo,
6766     NVEvoChannelPtr pChannel,
6767     NvU32 sd)
6768 {
6769     NVC370_CTRL_GET_ACCL_PARAMS params = { };
6770     NvU32 ret;
6771 
6772     params.base.subdeviceIndex = sd;
6773     params.channelClass = pChannel->hwclass;
6774     nvAssert(pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL);
6775     params.channelInstance =
6776         NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
6777 
6778     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
6779                          pDevEvo->displayHandle,
6780                          NVC370_CTRL_CMD_GET_ACCL,
6781                          &params, sizeof(params));
6782     if (ret != NVOS_STATUS_SUCCESS) {
6783         nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
6784                          "Failed to retrieve accelerators");
6785         return 0;
6786     }
6787 
6788     return params.accelerators;
6789 }
6790 
6791 static NvBool SetAccelerators(
6792     NVDevEvoPtr pDevEvo,
6793     NVEvoChannelPtr pChannel,
6794     NvU32 sd,
6795     NvU32 accelerators,
6796     NvU32 accelMask)
6797 {
6798     NVC370_CTRL_SET_ACCL_PARAMS params = { };
6799     NvU32 ret;
6800 
6801     params.base.subdeviceIndex = sd;
6802     params.channelClass = pChannel->hwclass;
6803     nvAssert(pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL);
6804     params.channelInstance =
6805         NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
6806     params.accelerators = accelerators;
6807     params.accelMask = accelMask;
6808 
6809     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
6810                          pDevEvo->displayHandle,
6811                          NVC370_CTRL_CMD_SET_ACCL,
6812                          &params, sizeof(params));
6813     if (ret != NVOS_STATUS_SUCCESS) {
6814         nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
6815                          "Failed to set accelerators");
6816         return FALSE;
6817     }
6818 
6819     return TRUE;
6820 }
6821 
6822 static void EvoAccelerateChannelC3(NVDevEvoPtr pDevEvo,
6823                                    NVEvoChannelPtr pChannel,
6824                                    const NvU32 sd,
6825                                    const NvBool trashPendingMethods,
6826                                    const NvBool unblockMethodsInExecutation,
6827                                    NvU32 *pOldAccelerators)
6828 {
6829     NvU32 accelMask = 0x0;
6830 
6831     if (trashPendingMethods) {
6832         accelMask |= NVC370_CTRL_ACCL_TRASH_ONLY;
6833     }
6834 
6835     /* Start with a conservative set of accelerators; may need to add more
6836      * later. */
6837     if (unblockMethodsInExecutation) {
6838         accelMask |= NVC370_CTRL_ACCL_IGNORE_PI |
6839                      NVC370_CTRL_ACCL_SKIP_SEMA |
6840                      NVC370_CTRL_ACCL_IGNORE_FLIPLOCK;
6841     }
6842 
6843     if (accelMask == 0x0) {
6844         return;
6845     }
6846 
6847     *pOldAccelerators = GetAccelerators(pDevEvo, pChannel, sd);
6848 
6849     /* Accelerate window channel. */
6850     if (!SetAccelerators(pDevEvo, pChannel, sd, accelMask, accelMask)) {
6851         nvAssert(!"Failed to set accelerators");
6852     }
6853 }
6854 
6855 static void EvoResetChannelAcceleratorsC3(NVDevEvoPtr pDevEvo,
6856                                           NVEvoChannelPtr pChannel,
6857                                           const NvU32 sd,
6858                                           const NvBool trashPendingMethods,
6859                                           const NvBool unblockMethodsInExecutation,
6860                                           NvU32 oldAccelerators)
6861 {
6862     NvU32 accelMask = 0x0;
6863 
6864     if (trashPendingMethods) {
6865         accelMask |= NVC370_CTRL_ACCL_TRASH_ONLY;
6866     }
6867 
6868     /* Start with a conservative set of accelerators; may need to add more
6869      * later. */
6870     if (unblockMethodsInExecutation) {
6871         accelMask |= NVC370_CTRL_ACCL_IGNORE_PI |
6872                      NVC370_CTRL_ACCL_SKIP_SEMA |
6873                      NVC370_CTRL_ACCL_IGNORE_FLIPLOCK;
6874     }
6875 
6876     if (accelMask == 0x0) {
6877         return;
6878     }
6879 
6880     /* Accelerate window channel. */
6881     if (!SetAccelerators(pDevEvo, pChannel, sd, oldAccelerators, accelMask)) {
6882         nvAssert(!"Failed to set accelerators");
6883     }
6884 }
6885 
6886 static void ForceFlipToNull(
6887     NVDevEvoPtr pDevEvo,
6888     NVEvoChannelPtr pChannel,
6889     NvU32 sd,
6890     NVEvoUpdateState *updateState)
6891 {
6892     NVFlipChannelEvoHwState hwState = { };
6893     const NvU32 subDeviceMask = (1 << sd);
6894 
6895     nvPushEvoSubDevMask(pDevEvo, subDeviceMask);
6896 
6897     pDevEvo->hal->Flip(pDevEvo, pChannel, &hwState, updateState,
6898                        FALSE /* bypassComposition */);
6899 
6900     nvPopEvoSubDevMask(pDevEvo);
6901 }
6902 
6903 static NvBool PollForChannelIdle(
6904     NVDevEvoPtr pDevEvo,
6905     NVEvoChannelPtr pChannel,
6906     NvU32 sd)
6907 {
6908     const NvU32 timeout = 2000000; // 2 seconds
6909     NvU64 startTime = 0;
6910     NvBool isMethodPending = TRUE;
6911 
6912     do {
6913         if (!EvoIsChannelMethodPendingC3(pDevEvo, pChannel, sd,
6914                                          &isMethodPending)) {
6915             break;
6916         }
6917 
6918         if (!isMethodPending) {
6919             break;
6920         }
6921 
6922         if (nvExceedsTimeoutUSec(&startTime, timeout)) {
6923             return FALSE;
6924         }
6925 
6926         nvkms_yield();
6927 
6928     } while (TRUE);
6929 
6930     return TRUE;
6931 }
6932 
6933 /*!
6934  * This function emulates the behavior of the STOP_BASE/STOP_OVERLAY RM control
6935  * calls for pre-EVO hardware.
6936  *
6937  * STOP_BASE/STOP_OVERLAY will apply hardware channel accelerators, push
6938  * methods via the debug interface to NULL context DMAs, and wait for the
6939  * channel to go idle (which means the surface programmed into the core channel
6940  * will become visible).
6941  *
6942  * If we asked RM to do the same thing for the window channel that is emulating
6943  * the base channel on nvdisplay, the display would just go black: there's no
6944  * surface in the core channel, so NULLing the context DMA in the window
6945  * channel will disable both "core" and "base".
6946  *
6947  * So instead, similar functionality is implemented here: we apply
6948  * accelerators, push methods to flip to core, and wait for the channel to
6949  * idle.
6950  */
6951 typedef struct {
6952     struct {
6953         NvU32 accelerators;
6954         NvBool overridden;
6955     } window[NVKMS_MAX_WINDOWS_PER_DISP];
6956 } EvoIdleChannelAcceleratorState;
6957 
6958 static NvBool EvoForceIdleSatelliteChannelsWithAccel(
6959     NVDevEvoPtr pDevEvo,
6960     const NVEvoIdleChannelState *idleChannelState,
6961     const NvU32 accelMask)
6962 {
6963     NvU32 sd, window;
6964     NVEvoUpdateState updateState = { };
6965     NvBool ret = FALSE;
6966 
6967     EvoIdleChannelAcceleratorState *pAcceleratorState = nvCalloc(
6968         pDevEvo->numSubDevices, sizeof(EvoIdleChannelAcceleratorState));
6969 
6970     if (!pAcceleratorState) {
6971         nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
6972                          "Failed to alloc accelerator state");
6973         return FALSE;
6974     }
6975 
6976     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
6977         /*
6978          * Forcing a channel to be idle is currently only implemented for window
6979          * channels.
6980          */
6981         if ((idleChannelState->subdev[sd].channelMask &
6982              ~NV_EVO_CHANNEL_MASK_WINDOW_ALL) != 0) {
6983 
6984             nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
6985                              "Forcing non-window channel idle not implemented");
6986             goto done;
6987         }
6988 
6989         for (window = 0; window < pDevEvo->numWindows; window++) {
6990             if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK,
6991                                    _WINDOW, window, _ENABLE,
6992                                    idleChannelState->subdev[sd].channelMask)) {
6993                 NVEvoChannelPtr pChannel = pDevEvo->window[window];
6994 
6995                 /* Save old window channel accelerators. */
6996                 NvU32 oldAccel = GetAccelerators(pDevEvo, pChannel, sd);
6997 
6998                 pAcceleratorState[sd].window[window].accelerators =
6999                     oldAccel;
7000 
7001                 /* Accelerate window channel. */
7002                 if (!SetAccelerators(pDevEvo, pChannel, sd, accelMask,
7003                                      accelMask)) {
7004                     nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
7005                                      "Failed to set accelerators");
7006                     goto done;
7007                 }
7008                 pAcceleratorState[sd].window[window].overridden = TRUE;
7009 
7010                 /* Push a flip to null in this channel. */
7011                 ForceFlipToNull(pDevEvo, pChannel, sd, &updateState);
7012             }
7013         }
7014     }
7015 
7016     /* Push one update for all of the flips programmed above. */
7017     EvoUpdateC3(pDevEvo, &updateState, TRUE /* releaseElv */);
7018 
7019     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
7020         for (window = 0; window < pDevEvo->numWindows; window++) {
7021             if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
7022                                    idleChannelState->subdev[sd].channelMask)) {
7023                 NVEvoChannelPtr pChannel = pDevEvo->window[window];
7024 
7025                 /* Wait for the flips to complete. */
7026                 if (!PollForChannelIdle(pDevEvo, pChannel, sd)) {
7027                     nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
7028                                      "Timed out while idling base channel");
7029                     goto done;
7030                 }
7031             }
7032         }
7033     }
7034 
7035     ret = TRUE;
7036 
7037 done:
7038     for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
7039         for (window = 0; window < pDevEvo->numWindows; window++) {
7040             if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
7041                                    idleChannelState->subdev[sd].channelMask)) {
7042                 NVEvoChannelPtr pChannel = pDevEvo->window[window];
7043 
7044                 const NvU32 oldAccel =
7045                     pAcceleratorState[sd].window[window].accelerators;
7046 
7047                 if (!pAcceleratorState[sd].window[window].overridden) {
7048                     continue;
7049                 }
7050 
7051                 /* Restore window channel accelerators. */
7052                 if (!SetAccelerators(pDevEvo, pChannel, sd, oldAccel,
7053                                      accelMask)) {
7054                     nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
7055                                      "Failed to restore accelerators");
7056                 }
7057             }
7058         }
7059     }
7060 
7061     nvFree(pAcceleratorState);
7062     return ret;
7063 }
7064 
7065 static NvBool EvoForceIdleSatelliteChannelC3(
7066     NVDevEvoPtr pDevEvo,
7067     const NVEvoIdleChannelState *idleChannelState)
7068 {
7069     /* Start with a conservative set of accelerators; may need to add more
7070      * later. */
7071     const NvU32 accelMask =
7072         NVC370_CTRL_ACCL_IGNORE_PI |
7073         NVC370_CTRL_ACCL_SKIP_SEMA;
7074 
7075     return EvoForceIdleSatelliteChannelsWithAccel(pDevEvo,
7076                                                   idleChannelState,
7077                                                   accelMask);
7078 }
7079 
7080 static NvBool EvoForceIdleSatelliteChannelIgnoreLockC3(
7081     NVDevEvoPtr pDevEvo,
7082     const NVEvoIdleChannelState *idleChannelState)
7083 {
7084     const NvU32 accelMask =
7085         NVC370_CTRL_ACCL_IGNORE_PI |
7086         NVC370_CTRL_ACCL_SKIP_SEMA |
7087         NVC370_CTRL_ACCL_IGNORE_FLIPLOCK |
7088         NVC370_CTRL_ACCL_IGNORE_INTERLOCK;
7089 
7090     return EvoForceIdleSatelliteChannelsWithAccel(pDevEvo,
7091                                                   idleChannelState,
7092                                                   accelMask);
7093 }
7094 
7095 static void EvoFreeRmCtrlObjectC3(NVDevEvoPtr pDevEvo)
7096 {
7097     if (pDevEvo->rmCtrlHandle) {
7098         NvU32 status;
7099 
7100         status = nvRmApiFree(nvEvoGlobal.clientHandle,
7101                              pDevEvo->deviceHandle,
7102                              pDevEvo->rmCtrlHandle);
7103 
7104         if (status != NVOS_STATUS_SUCCESS) {
7105             nvAssert(!"Failed to free nvdisplay rmctrl object");
7106         }
7107 
7108         nvFreeUnixRmHandle(&pDevEvo->handleAllocator, pDevEvo->rmCtrlHandle);
7109         pDevEvo->rmCtrlHandle = 0;
7110     }
7111 }
7112 
7113 static void EvoSetImmPointOutC3(NVDevEvoPtr pDevEvo,
7114                                 NVEvoChannelPtr pChannel,
7115                                 NvU32 sd,
7116                                 NVEvoUpdateState *updateState,
7117                                 NvU16 x, NvU16 y)
7118 {
7119     NVEvoChannelPtr pImmChannel = pChannel->imm.u.dma;
7120 
7121     nvAssert((pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL) != 0);
7122     nvAssert(pChannel->imm.type == NV_EVO_IMM_CHANNEL_DMA);
7123 
7124     nvDmaSetStartEvoMethod(pImmChannel,
7125         NVC37B_SET_POINT_OUT(0 /* Left eye */), 1);
7126 
7127     nvDmaSetEvoMethodData(pImmChannel,
7128         DRF_NUM(C37B, _SET_POINT_OUT, _X, x) |
7129         DRF_NUM(C37B, _SET_POINT_OUT, _Y, y));
7130 
7131     nvWinImmChannelUpdateState(pDevEvo, updateState, pChannel);
7132 }
7133 
7134 static void EvoStartHeadCRC32CaptureC3(NVDevEvoPtr pDevEvo,
7135                                        NVEvoDmaPtr pDma,
7136                                        NVConnectorEvoPtr pConnectorEvo,
7137                                        const enum nvKmsTimingsProtocol protocol,
7138                                        const NvU32 orIndex,
7139                                        NvU32 head,
7140                                        NvU32 sd,
7141                                        NVEvoUpdateState *updateState)
7142 {
7143     const NvU32 winChannel = head << 1;
7144     NVEvoChannelPtr pChannel = pDevEvo->core;
7145     NvU32 dmaCtx = pDma->ctxHandle;
7146     NvU32 orOutput = 0;
7147 
7148     /* These method should only apply to a single pDpy */
7149     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
7150 
7151     /* The window channel should fit in
7152      * NVC37D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL */
7153     nvAssert(winChannel < DRF_MASK(NVC37D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL));
7154 
7155     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
7156 
7157     switch (pConnectorEvo->or.type) {
7158     case NV0073_CTRL_SPECIFIC_OR_TYPE_SOR:
7159         if (protocol == NVKMS_PROTOCOL_SOR_DP_A ||
7160             protocol == NVKMS_PROTOCOL_SOR_DP_B) {
7161             orOutput = NVC37D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SF;
7162         } else {
7163             orOutput =
7164                 NVC37D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR(orIndex);
7165         }
7166         break;
7167     case NV0073_CTRL_SPECIFIC_OR_TYPE_PIOR:
7168         orOutput =
7169             NVC37D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_PIOR(orIndex);
7170         break;
7171     case NV0073_CTRL_SPECIFIC_OR_TYPE_DAC:
7172         /* No DAC support on nvdisplay.  Fall through. */
7173     default:
7174         nvAssert(!"Invalid pConnectorEvo->or.type");
7175         break;
7176     }
7177 
7178     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTEXT_DMA_CRC(head), 1);
7179     nvDmaSetEvoMethodData(pChannel,
7180             DRF_NUM(C37D, _HEAD_SET_CONTEXT_DMA_CRC, _HANDLE, dmaCtx));
7181 
7182     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CRC_CONTROL(head), 1);
7183     nvDmaSetEvoMethodData(pChannel,
7184         DRF_NUM(C37D, _HEAD_SET_CRC_CONTROL, _PRIMARY_CRC, orOutput) |
7185         DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _SECONDARY_CRC, _NONE) |
7186         DRF_NUM(C37D, _HEAD_SET_CRC_CONTROL, _CONTROLLING_CHANNEL, winChannel) |
7187         DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _EXPECT_BUFFER_COLLAPSE, _FALSE) |
7188         DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _CRC_DURING_SNOOZE, _DISABLE));
7189 
7190     /* Reset the CRC notifier */
7191     nvEvoResetCRC32Notifier(pDma->subDeviceAddress[sd],
7192                             NVC37D_NOTIFIER_CRC_STATUS_0,
7193                             DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_DONE),
7194                             NVC37D_NOTIFIER_CRC_STATUS_0_DONE_FALSE);
7195 }
7196 
7197 static void EvoStopHeadCRC32CaptureC3(NVDevEvoPtr pDevEvo,
7198                                       NvU32 head,
7199                                       NVEvoUpdateState *updateState)
7200 {
7201     NVEvoChannelPtr pChannel = pDevEvo->core;
7202 
7203     /* These method should only apply to a single pDpy */
7204     nvAssert(pDevEvo->subDevMaskStackDepth > 0);
7205 
7206     nvUpdateUpdateState(pDevEvo, updateState, pChannel);
7207 
7208     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTEXT_DMA_CRC(head), 1);
7209     nvDmaSetEvoMethodData(pChannel, 0);
7210 
7211     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CRC_CONTROL(head), 1);
7212     nvDmaSetEvoMethodData(pChannel,
7213         DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _PRIMARY_CRC, _NONE) |
7214         DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _SECONDARY_CRC, _NONE) |
7215         DRF_NUM(C37D, _HEAD_SET_CRC_CONTROL, _CONTROLLING_CHANNEL, 0) |
7216         DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _EXPECT_BUFFER_COLLAPSE, _FALSE) |
7217         DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _CRC_DURING_SNOOZE, _DISABLE));
7218 }
7219 
7220 /*!
7221  * Queries the current head's CRC Notifier and returns values if successful
7222  *
7223  * First waits for hardware to finish writing to the CRC32Notifier,
7224  * and performs a read of the Compositor, SF/OR CRCs,
7225  * and the RG CRC in numCRC32 frames.
7226  * Crc fields in input array crc32 should be calloc'd to 0s.
7227  *
7228  * \param[in]  pDevEvo          NVKMS device pointer
7229  * \param[in]  pDma             Pointer to DMA-mapped memory
7230  * \param[in]  sd               Subdevice index
7231  * \param[in]  entry_count      Number of independent frames to read CRCs from
7232  * \param[out] crc32            Contains pointers to CRC output arrays
7233  * \param[out] numCRC32         Number of CRC frames successfully read from DMA
7234  *
7235  * \return  Returns TRUE if was able to successfully read CRCs from DMA,
7236  *          otherwise FALSE
7237  */
7238 static NvBool EvoQueryHeadCRC32_C3(NVDevEvoPtr pDevEvo,
7239                                    NVEvoDmaPtr pDma,
7240                                    NvU32 sd,
7241                                    NvU32 entry_count,
7242                                    CRC32NotifierCrcOut *crc32,
7243                                    NvU32 *numCRC32)
7244 {
7245     volatile NvU32 *pCRC32Notifier = pDma->subDeviceAddress[sd];
7246     const NvU32 entry_stride =
7247           NVC37D_NOTIFIER_CRC_CRC_ENTRY1_21 - NVC37D_NOTIFIER_CRC_CRC_ENTRY0_13;
7248     // Define how many/which variables to read from each CRCNotifierEntry struct
7249     const CRC32NotifierEntryRec field_info[NV_EVO3_NUM_CRC_FIELDS] = {
7250         {
7251             .field_offset = NVC37D_NOTIFIER_CRC_CRC_ENTRY0_11,
7252             .field_base_bit =
7253              DRF_BASE(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_11_COMPOSITOR_CRC),
7254             .field_extent_bit =
7255              DRF_EXTENT(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_11_COMPOSITOR_CRC),
7256             .field_frame_values = crc32->compositorCrc32,
7257         },
7258         {
7259             .field_offset = NVC37D_NOTIFIER_CRC_CRC_ENTRY0_12,
7260             .field_base_bit =
7261              DRF_BASE(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_12_RG_CRC),
7262             .field_extent_bit =
7263              DRF_EXTENT(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_12_RG_CRC),
7264             .field_frame_values = crc32->rasterGeneratorCrc32,
7265         },
7266         {
7267             .field_offset = NVC37D_NOTIFIER_CRC_CRC_ENTRY0_13,
7268             .field_base_bit =
7269              DRF_BASE(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_13_PRIMARY_OUTPUT_CRC),
7270             .field_extent_bit =
7271              DRF_EXTENT(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_13_PRIMARY_OUTPUT_CRC),
7272             .field_frame_values = crc32->outputCrc32
7273         }
7274     };
7275 
7276     const CRC32NotifierEntryFlags flag_info[NV_EVO3_NUM_CRC_FLAGS] = {
7277         {
7278             .flag_base_bit =
7279              DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_COUNT),
7280             .flag_extent_bit =
7281              DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_COUNT),
7282             .flag_type = NVEvoCrc32NotifierFlagCount
7283         },
7284         {
7285             .flag_base_bit =
7286              DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_COMPOSITOR_OVERFLOW),
7287             .flag_extent_bit =
7288              DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_COMPOSITOR_OVERFLOW),
7289             .flag_type = NVEvoCrc32NotifierFlagCrcOverflow
7290         },
7291         {
7292             .flag_base_bit =
7293              DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_RG_OVERFLOW),
7294             .flag_extent_bit =
7295              DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_RG_OVERFLOW),
7296             .flag_type = NVEvoCrc32NotifierFlagCrcOverflow
7297         },
7298         {
7299             .flag_base_bit =
7300              DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_PRIMARY_OUTPUT_OVERFLOW),
7301             .flag_extent_bit =
7302              DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_PRIMARY_OUTPUT_OVERFLOW),
7303             .flag_type = NVEvoCrc32NotifierFlagCrcOverflow
7304         }
7305     };
7306 
7307     if (!nvEvoWaitForCRC32Notifier(pCRC32Notifier,
7308                                    NVC37D_NOTIFIER_CRC_STATUS_0,
7309                                    DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_DONE),
7310                                    DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_DONE),
7311                                    NVC37D_NOTIFIER_CRC_STATUS_0_DONE_TRUE)) {
7312         return FALSE;
7313     }
7314 
7315     *numCRC32 = nvEvoReadCRC32Notifier(pCRC32Notifier,
7316                                        entry_stride,
7317                                        entry_count,
7318                                        NVC37D_NOTIFIER_CRC_STATUS_0, /* Status offset */
7319                                        NV_EVO3_NUM_CRC_FIELDS,
7320                                        NV_EVO3_NUM_CRC_FLAGS,
7321                                        field_info,
7322                                        flag_info);
7323 
7324     nvEvoResetCRC32Notifier(pCRC32Notifier,
7325                             NVC37D_NOTIFIER_CRC_STATUS_0,
7326                             DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_DONE),
7327                             NVC37D_NOTIFIER_CRC_STATUS_0_DONE_FALSE);
7328 
7329     return TRUE;
7330 }
7331 
7332 static void EvoGetScanLineC3(const NVDispEvoRec *pDispEvo,
7333                              const NvU32 head,
7334                              NvU16 *pScanLine,
7335                              NvBool *pInBlankingPeriod)
7336 {
7337     const NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
7338     const NvU32 sd = pDispEvo->displayOwner;
7339     const NvU32 window = head << 1;
7340     void *pDma = pDevEvo->window[window]->pb.control[sd];
7341     const NvU32 scanLine = nvDmaLoadPioMethod(pDma, NVC37E_GET_LINE);
7342 
7343     if ((scanLine & NVBIT(15)) == 0) {
7344         *pInBlankingPeriod = FALSE;
7345         *pScanLine = scanLine & DRF_MASK(14:0);
7346     } else {
7347         *pInBlankingPeriod = TRUE;
7348     }
7349 }
7350 
7351 /*
7352  * This method configures and programs the RG Core Semaphores. Default behavior
7353  * is to continuously trigger on the specified rasterline when enabled.
7354  */
7355 static void
7356 EvoConfigureVblankSyncObjectC6(const NVDevEvoPtr pDevEvo,
7357                                const NvU16 rasterLine,
7358                                const NvU32 head,
7359                                const NvU32 semaphoreIndex,
7360                                const NvU32 hCtxDma,
7361                                NVEvoUpdateState* pUpdateState)
7362 {
7363     NVEvoChannelPtr pChannel = pDevEvo->core;
7364 
7365     /*
7366      * Populate the NVEvoUpdateState for the caller. The Update State contains
7367      * a mask of which display channels need to be updated.
7368      */
7369     nvUpdateUpdateState(pDevEvo, pUpdateState, pChannel);
7370 
7371     /*
7372      * Tell HW what ctxdma entry to use to look up actual RG semaphore surface.
7373      * If hCtxDma is 0, HW will disable the semaphore.
7374      */
7375     nvDmaSetStartEvoMethod(pChannel,
7376                            NVC67D_HEAD_SET_CONTEXT_DMA_RG_REL_SEMAPHORE(head, semaphoreIndex),
7377                            1);
7378     nvDmaSetEvoMethodData(pChannel,
7379         DRF_NUM(C67D, _HEAD_SET_CONTEXT_DMA_RG_REL_SEMAPHORE, _HANDLE, hCtxDma));
7380 
7381     if (hCtxDma == 0) {
7382         /* Disabling semaphore so no configuration necessary. */
7383         return;
7384     }
7385 
7386     /*
7387      * Configure the semaphore with the following:
7388      * Set OFFSET to 0 (default).
7389      * Set PAYLOAD_SIZE to 32bits (default).
7390      * Set REL_MODE to WRITE (default).
7391      * Set RUN_MODE to CONTINUOUS.
7392      * Set RASTER_LINE to start of Vblank: Vsync + Vbp + Vactive.
7393      *
7394      * Note that all these options together fit in 32bits, and that all 32 bits
7395      * must be written each time any given option changes.
7396      *
7397      * The actual payload value doesn't currently matter since this RG
7398      * semaphore will be mapped to a syncpt for now. Each HW-issued payload
7399      * write is converted to a single syncpt increment irrespective of what the
7400      * actual semaphore payload value is.
7401      */
7402     nvDmaSetStartEvoMethod(pChannel,
7403                            NVC67D_HEAD_SET_RG_REL_SEMAPHORE_CONTROL(head, semaphoreIndex),
7404                            1);
7405     nvDmaSetEvoMethodData(pChannel,
7406             DRF_NUM(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _OFFSET, 0) |
7407             DRF_DEF(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _PAYLOAD_SIZE,
7408                     _PAYLOAD_32BIT) |
7409             DRF_DEF(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _REL_MODE,
7410                     _WRITE) |
7411             DRF_DEF(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _RUN_MODE,
7412                     _CONTINUOUS) |
7413             DRF_NUM(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _RASTER_LINE,
7414                     rasterLine));
7415 }
7416 
7417 static void EvoSetHdmiDscParams(const NVDispEvoRec *pDispEvo,
7418                                    const NvU32 head,
7419                                    const NVDscInfoEvoRec *pDscInfo,
7420                                    const enum nvKmsPixelDepth pixelDepth)
7421 {
7422     NVEvoChannelPtr pChannel = pDispEvo->pDevEvo->core;
7423     NvU32 bpc, flatnessDetThresh;
7424     NvU32 i;
7425 
7426     nvAssert(pDispEvo->pDevEvo->hal->caps.supportsHDMIFRL &&
7427              pDscInfo->type == NV_DSC_INFO_EVO_TYPE_HDMI);
7428 
7429     bpc = nvPixelDepthToBitsPerComponent(pixelDepth);
7430     if (bpc < 8) {
7431         nvAssert(bpc >= 8);
7432         bpc = 8;
7433     }
7434     flatnessDetThresh = (2 << (bpc - 8));
7435 
7436     nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_DSC_CONTROL(head), 1);
7437     nvDmaSetEvoMethodData(pChannel,
7438         DRF_DEF(C67D, _HEAD_SET_DSC_CONTROL, _ENABLE, _TRUE) |
7439         ((pDscInfo->hdmi.dscMode == NV_DSC_EVO_MODE_DUAL) ?
7440              DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _MODE, _DUAL) :
7441              DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _MODE, _SINGLE)) |
7442         DRF_NUM(C67D, _HEAD_SET_DSC_CONTROL, _FLATNESS_DET_THRESH, flatnessDetThresh) |
7443         DRF_DEF(C67D, _HEAD_SET_DSC_CONTROL, _FULL_ICH_ERR_PRECISION, _ENABLE) |
7444         DRF_DEF(C67D, _HEAD_SET_DSC_CONTROL, _AUTO_RESET, _ENABLE) |
7445         DRF_DEF(C67D, _HEAD_SET_DSC_CONTROL, _FORCE_ICH_RESET, _FALSE));
7446 
7447     nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_DSC_PPS_CONTROL(head), 1);
7448     nvDmaSetEvoMethodData(pChannel,
7449         DRF_DEF(C67D, _HEAD_SET_DSC_PPS_CONTROL, _ENABLE, _TRUE) |
7450         DRF_DEF(C67D, _HEAD_SET_DSC_PPS_CONTROL, _LOCATION, _VBLANK) |
7451         DRF_DEF(C67D, _HEAD_SET_DSC_PPS_CONTROL, _FREQUENCY, _EVERY_FRAME) |
7452         /* MFS says "For FRL DSC CVTEM, it should be 0x21 (136bytes)." */
7453         DRF_NUM(C67D, _HEAD_SET_DSC_PPS_CONTROL, _SIZE, 0x21));
7454 
7455     /* The loop below assumes the methods are tightly packed. */
7456     ct_assert(ARRAY_LEN(pDscInfo->hdmi.pps) == 32);
7457     ct_assert((NVC67D_HEAD_SET_DSC_PPS_DATA1(0) - NVC67D_HEAD_SET_DSC_PPS_DATA0(0)) == 4);
7458     ct_assert((NVC67D_HEAD_SET_DSC_PPS_DATA31(0) - NVC67D_HEAD_SET_DSC_PPS_DATA0(0)) == (31 * 4));
7459     for (i = 0; i < ARRAY_LEN(pDscInfo->hdmi.pps); i++) {
7460         nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_DSC_PPS_DATA0(head) + (i * 4), 1);
7461         nvDmaSetEvoMethodData(pChannel, pDscInfo->hdmi.pps[i]);
7462     }
7463 
7464     /* Byte 0 must be 0x7f, the rest are don't care (will be filled in by HW) */
7465     nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_DSC_PPS_HEAD(head), 1);
7466     nvDmaSetEvoMethodData(pChannel,
7467                           DRF_NUM(C67D, _HEAD_SET_DSC_PPS_HEAD, _BYTE0, 0x7f));
7468 
7469     nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_HDMI_DSC_HCACTIVE(head), 1);
7470     nvDmaSetEvoMethodData(pChannel,
7471         DRF_NUM(C67D, _HEAD_SET_HDMI_DSC_HCACTIVE, _BYTES, pDscInfo->hdmi.dscHActiveBytes) |
7472         DRF_NUM(C67D, _HEAD_SET_HDMI_DSC_HCACTIVE, _TRI_BYTES, pDscInfo->hdmi.dscHActiveTriBytes));
7473     nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_HDMI_DSC_HCBLANK(head), 1);
7474     nvDmaSetEvoMethodData(pChannel,
7475         DRF_NUM(C67D, _HEAD_SET_HDMI_DSC_HCBLANK, _WIDTH, pDscInfo->hdmi.dscHBlankTriBytes));
7476 }
7477 
7478 static void EvoSetDpDscParams(const NVDispEvoRec *pDispEvo,
7479                               const NvU32 head,
7480                               const NVDscInfoEvoRec *pDscInfo)
7481 {
7482     NVEvoChannelPtr pChannel = pDispEvo->pDevEvo->core;
7483     NvU32 flatnessDetThresh;
7484     NvU32 i;
7485 
7486     nvAssert(pDscInfo->type == NV_DSC_INFO_EVO_TYPE_DP);
7487 
7488     // XXX: I'm pretty sure that this is wrong.
7489     // BitsPerPixelx16 is something like (24 * 16) = 384, and 2 << (384 - 8) is
7490     // an insanely large number.
7491     flatnessDetThresh = (2 << (pDscInfo->dp.bitsPerPixelX16 - 8)); /* ??? */
7492 
7493     nvAssert((pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) ||
7494                 (pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_SINGLE));
7495 
7496     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DSC_CONTROL(head), 1);
7497     nvDmaSetEvoMethodData(pChannel,
7498         DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _ENABLE, _TRUE) |
7499         ((pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) ?
7500              DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _MODE, _DUAL) :
7501              DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _MODE, _SINGLE)) |
7502         DRF_NUM(C57D, _HEAD_SET_DSC_CONTROL, _FLATNESS_DET_THRESH, flatnessDetThresh) |
7503         DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _FULL_ICH_ERR_PRECISION, _ENABLE) |
7504         DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _AUTO_RESET, _DISABLE) |
7505         DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _FORCE_ICH_RESET, _TRUE));
7506 
7507     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DSC_PPS_CONTROL(head), 1);
7508     nvDmaSetEvoMethodData(pChannel,
7509         DRF_DEF(C57D, _HEAD_SET_DSC_PPS_CONTROL, _ENABLE, _TRUE) |
7510         DRF_DEF(C57D, _HEAD_SET_DSC_PPS_CONTROL, _LOCATION, _VSYNC) |
7511         DRF_DEF(C57D, _HEAD_SET_DSC_PPS_CONTROL, _FREQUENCY, _EVERY_FRAME) |
7512         DRF_NUM(C57D, _HEAD_SET_DSC_PPS_CONTROL, _SIZE, 0x1F /* 32 PPS Dwords - 1 = 31 */));
7513 
7514 
7515 #define NV_EVO5_NUM_HEAD_SET_DSC_PPS_DATA_DWORDS \
7516     (((NVC57D_HEAD_SET_DSC_PPS_DATA31(0) - NVC57D_HEAD_SET_DSC_PPS_DATA0(0)) / 4) + 1)
7517 
7518     ct_assert(NV_EVO5_NUM_HEAD_SET_DSC_PPS_DATA_DWORDS <= ARRAY_LEN(pDscInfo->dp.pps));
7519 
7520     for (i = 0; i < NV_EVO5_NUM_HEAD_SET_DSC_PPS_DATA_DWORDS; i++) {
7521         nvDmaSetStartEvoMethod(pChannel,(NVC57D_HEAD_SET_DSC_PPS_DATA0(head) + (i * 4)), 1);
7522         nvDmaSetEvoMethodData(pChannel, pDscInfo->dp.pps[i]);
7523     }
7524 
7525     /*
7526      * In case of DP, PPS is sent using the SDP over the Main-Link
7527      * during the vertical blanking interval. The PPS SDP header is defined
7528      * in DP 1.4 specification under section 2.2.5.9.1.
7529      */
7530 
7531     nvDmaSetStartEvoMethod(pChannel,
7532                            NVC57D_HEAD_SET_DSC_PPS_HEAD(head), 1);
7533     nvDmaSetEvoMethodData(pChannel,
7534                           DRF_NUM(C57D, _HEAD_SET_DSC_PPS_HEAD, _BYTE0, 0x00) | /* SDP ID = 0x0 */
7535                           DRF_NUM(C57D, _HEAD_SET_DSC_PPS_HEAD, _BYTE1, 0x10) | /* SDP Type = 0x10 */
7536                           DRF_NUM(C57D, _HEAD_SET_DSC_PPS_HEAD, _BYTE2, 0x7f) | /* Number of payload data bytes - 1 = 0x7F */
7537                           DRF_NUM(C57D, _HEAD_SET_DSC_PPS_HEAD, _BYTE3, 0x00)); /* Reserved */
7538 }
7539 
7540 static void EvoSetDscParamsC5(const NVDispEvoRec *pDispEvo,
7541                               const NvU32 head,
7542                               const NVDscInfoEvoRec *pDscInfo,
7543                               const enum nvKmsPixelDepth pixelDepth)
7544 {
7545     if (pDscInfo->type == NV_DSC_INFO_EVO_TYPE_HDMI) {
7546         EvoSetHdmiDscParams(pDispEvo, head, pDscInfo, pixelDepth);
7547     } else if (pDscInfo->type == NV_DSC_INFO_EVO_TYPE_DP) {
7548         EvoSetDpDscParams(pDispEvo, head, pDscInfo);
7549     } else {
7550         NVEvoChannelPtr pChannel = pDispEvo->pDevEvo->core;
7551 
7552         nvAssert(pDscInfo->type == NV_DSC_INFO_EVO_TYPE_DISABLED);
7553 
7554         /* Disable DSC function */
7555         nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DSC_CONTROL(head), 1);
7556         nvDmaSetEvoMethodData(pChannel,
7557             DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _ENABLE, _FALSE));
7558 
7559         /* Disable PPS SDP (Secondary-Data Packet), DP won't send out PPS SDP */
7560         nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DSC_PPS_CONTROL(head), 1);
7561         nvDmaSetEvoMethodData(pChannel,
7562             DRF_DEF(C57D, _HEAD_SET_DSC_PPS_CONTROL, _ENABLE, _FALSE));
7563     }
7564 }
7565 
7566 static void
7567 EvoEnableMidFrameAndDWCFWatermarkC5(NVDevEvoPtr pDevEvo,
7568                                     NvU32 sd,
7569                                     NvU32 head,
7570                                     NvBool enable,
7571                                     NVEvoUpdateState *pUpdateState)
7572 {
7573     NVEvoChannelPtr pChannel = pDevEvo->core;
7574 
7575     if (enable) {
7576         pDevEvo->gpus[sd].setSwSpareA[head] =
7577             FLD_SET_DRF(C37D,
7578                         _HEAD_SET_SW_SPARE_A,
7579                         _DISABLE_MID_FRAME_AND_DWCF_WATERMARK,
7580                         _FALSE,
7581                         pDevEvo->gpus[sd].setSwSpareA[head]);
7582     } else {
7583         pDevEvo->gpus[sd].setSwSpareA[head] =
7584             FLD_SET_DRF(C37D,
7585                         _HEAD_SET_SW_SPARE_A,
7586                         _DISABLE_MID_FRAME_AND_DWCF_WATERMARK,
7587                         _TRUE,
7588                         pDevEvo->gpus[sd].setSwSpareA[head]);
7589     }
7590 
7591     nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
7592 
7593     nvUpdateUpdateState(pDevEvo, pUpdateState, pChannel);
7594 
7595     nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_SW_SPARE_A(head), 1);
7596     nvDmaSetEvoMethodData(pChannel, pDevEvo->gpus[sd].setSwSpareA[head]);
7597 
7598     nvPopEvoSubDevMask(pDevEvo);
7599 }
7600 
7601 static NvU32 EvoGetActiveViewportOffsetC3(NVDispEvoRec *pDispEvo, NvU32 head)
7602 {
7603     NVC372_CTRL_CMD_GET_ACTIVE_VIEWPORT_POINT_IN_PARAMS params = { };
7604     NvU32 ret;
7605     NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
7606 
7607     params.base.subdeviceIndex = pDispEvo->displayOwner;
7608     params.windowIndex = head << 1;
7609 
7610     ret = nvRmApiControl(nvEvoGlobal.clientHandle,
7611                          pDevEvo->rmCtrlHandle,
7612                          NVC372_CTRL_CMD_GET_ACTIVE_VIEWPORT_POINT_IN,
7613                          &params, sizeof(params));
7614 
7615     if (ret != NVOS_STATUS_SUCCESS) {
7616         nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
7617                          "Failed to query active viewport offset");
7618     }
7619 
7620     return params.activeViewportPointIn.y;
7621 }
7622 
7623 static NvBool EvoComputeWindowScalingTapsC3(const NVDevEvoRec *pDevEvo,
7624                                             const NVEvoChannel *pChannel,
7625                                             NVFlipChannelEvoHwState *pHwState)
7626 {
7627     NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
7628     const NVEvoScalerCaps *pScalerCaps =
7629         &pDevEvo->gpus[0].capabilities.window[win].scalerCaps;
7630 
7631     if (!nvAssignScalerTaps(pDevEvo,
7632                             pScalerCaps,
7633                             pHwState->sizeIn.width, pHwState->sizeIn.height,
7634                             pHwState->sizeOut.width, pHwState->sizeOut.height,
7635                             FALSE /* doubleScan */,
7636                             &pHwState->hTaps, &pHwState->vTaps)) {
7637         return FALSE;
7638     }
7639 
7640     return TRUE;
7641 }
7642 
7643 static NvBool EvoComputeWindowScalingTapsC5(const NVDevEvoRec *pDevEvo,
7644                                             const NVEvoChannel *pChannel,
7645                                             NVFlipChannelEvoHwState *pHwState)
7646 {
7647     if (!EvoComputeWindowScalingTapsC3(pDevEvo, pChannel, pHwState)) {
7648         return FALSE;
7649     }
7650 
7651     /*
7652      * If scaling is enabled, CSC11 will be used by NVKMS to convert from
7653      * linear FP16 LMS to linear FP16 RGB. As such, the user-supplied precomp
7654      * CSC can't be programmed into CSC11 in this case.
7655      */
7656     if ((pHwState->sizeIn.width != pHwState->sizeOut.width) ||
7657         (pHwState->sizeIn.height != pHwState->sizeOut.height)) {
7658         if (!nvIsCscMatrixIdentity(&pHwState->cscMatrix)) {
7659             return FALSE;
7660         }
7661     }
7662 
7663     return TRUE;
7664 }
7665 
7666 static inline const NVEvoScalerCaps*
7667 EvoGetWindowScalingCapsC3(const NVDevEvoRec *pDevEvo)
7668 {
7669     /*
7670      * Use window 0 by default. This should be fine for now since precomp
7671      * scaling will only be enabled on Orin, and all windows have the same
7672      * capabilities on Orin.
7673      *
7674      * The mapping in this function can be updated if/when precomp scaling
7675      * support is extended to other display architectures.
7676      */
7677     return &pDevEvo->gpus[0].capabilities.window[0].scalerCaps;
7678 }
7679 
7680 static void EvoSetMergeModeC5(const NVDispEvoRec *pDispEvo,
7681                               const NvU32 head,
7682                               const NVEvoMergeMode mode,
7683                               NVEvoUpdateState* pUpdateState)
7684 {
7685     NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
7686     NVEvoChannelPtr pChannel = pDevEvo->core;
7687     NvU32 data = 0x0;
7688 
7689     nvPushEvoSubDevMask(pDevEvo, NVBIT(pDispEvo->displayOwner));
7690 
7691     nvUpdateUpdateState(pDevEvo, pUpdateState, pChannel);
7692 
7693     switch (mode) {
7694         case NV_EVO_MERGE_MODE_DISABLED:
7695             data = DRF_DEF(C57D, _HEAD_SET_RG_MERGE, _MODE, _DISABLE);
7696             break;
7697         case NV_EVO_MERGE_MODE_SETUP:
7698             data = DRF_DEF(C57D, _HEAD_SET_RG_MERGE, _MODE, _SETUP);
7699             break;
7700         case NV_EVO_MERGE_MODE_PRIMARY:
7701             data = DRF_DEF(C57D, _HEAD_SET_RG_MERGE, _MODE, _MASTER);
7702             break;
7703         case NV_EVO_MERGE_MODE_SECONDARY:
7704             data = DRF_DEF(C57D, _HEAD_SET_RG_MERGE, _MODE, _SLAVE);
7705             break;
7706     }
7707 
7708     nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_RG_MERGE(head), 1);
7709     nvDmaSetEvoMethodData(pChannel, data);
7710 
7711     nvPopEvoSubDevMask(pDevEvo);
7712 }
7713 
7714 NVEvoHAL nvEvoC3 = {
7715     EvoSetRasterParamsC3,                         /* SetRasterParams */
7716     EvoSetProcAmpC3,                              /* SetProcAmp */
7717     EvoSetHeadControlC3,                          /* SetHeadControl */
7718     EvoSetHeadRefClkC3,                           /* SetHeadRefClk */
7719     EvoHeadSetControlORC3,                        /* HeadSetControlOR */
7720     EvoORSetControlC3,                            /* ORSetControl */
7721     EvoHeadSetDisplayIdC3,                        /* HeadSetDisplayId */
7722     EvoSetUsageBoundsC3,                          /* SetUsageBounds */
7723     EvoUpdateC3,                                  /* Update */
7724     EvoIsModePossibleC3,                          /* IsModePossible */
7725     EvoPrePostIMPC3,                              /* PrePostIMP */
7726     EvoSetNotifierC3,                             /* SetNotifier */
7727     EvoGetCapabilitiesC3,                         /* GetCapabilities */
7728     EvoFlipC3,                                    /* Flip */
7729     EvoFlipTransitionWARC3,                       /* FlipTransitionWAR */
7730     EvoFillLUTSurfaceC3,                          /* FillLUTSurface */
7731     EvoSetLUTContextDmaC3,                        /* SetLUTContextDma */
7732     EvoSetOutputScalerC3,                         /* SetOutputScaler */
7733     EvoSetViewportPointInC3,                      /* SetViewportPointIn */
7734     EvoSetViewportInOutC3,                        /* SetViewportInOut */
7735     EvoSetCursorImageC3,                          /* SetCursorImage */
7736     EvoValidateCursorSurfaceC3,                   /* ValidateCursorSurface */
7737     EvoValidateWindowFormatC3,                    /* ValidateWindowFormat */
7738     EvoInitCompNotifierC3,                        /* InitCompNotifier */
7739     EvoIsCompNotifierCompleteC3,                  /* IsCompNotifierComplete */
7740     EvoWaitForCompNotifierC3,                     /* WaitForCompNotifier */
7741     EvoSetDitherC3,                               /* SetDither */
7742     EvoSetStallLockC3,                            /* SetStallLock */
7743     EvoSetDisplayRateC3,                          /* SetDisplayRate */
7744     EvoInitChannelC3,                             /* InitChannel */
7745     NULL,                                         /* InitDefaultLut */
7746     EvoInitWindowMappingC3,                       /* InitWindowMapping */
7747     EvoIsChannelIdleC3,                           /* IsChannelIdle */
7748     EvoIsChannelMethodPendingC3,                  /* IsChannelMethodPending */
7749     EvoForceIdleSatelliteChannelC3,               /* ForceIdleSatelliteChannel */
7750     EvoForceIdleSatelliteChannelIgnoreLockC3,     /* ForceIdleSatelliteChannelIgnoreLock */
7751     EvoAccelerateChannelC3,                       /* AccelerateChannel */
7752     EvoResetChannelAcceleratorsC3,                /* ResetChannelAccelerators */
7753     EvoAllocRmCtrlObjectC3,                       /* AllocRmCtrlObject */
7754     EvoFreeRmCtrlObjectC3,                        /* FreeRmCtrlObject */
7755     EvoSetImmPointOutC3,                          /* SetImmPointOut */
7756     EvoStartHeadCRC32CaptureC3,                   /* StartCRC32Capture */
7757     EvoStopHeadCRC32CaptureC3,                    /* StopCRC32Capture */
7758     EvoQueryHeadCRC32_C3,                         /* QueryCRC32 */
7759     EvoGetScanLineC3,                             /* GetScanLine */
7760     NULL,                                         /* ConfigureVblankSyncObject */
7761     nvEvo1SetDscParams,                           /* SetDscParams */
7762     NULL,                                         /* EnableMidFrameAndDWCFWatermark */
7763     EvoGetActiveViewportOffsetC3,                 /* GetActiveViewportOffset */
7764     NULL,                                         /* ClearSurfaceUsage */
7765     EvoComputeWindowScalingTapsC3,                /* ComputeWindowScalingTaps */
7766     EvoGetWindowScalingCapsC3,                    /* GetWindowScalingCaps */
7767     NULL,                                         /* SetMergeMode */
7768     {                                             /* caps */
7769         TRUE,                                     /* supportsNonInterlockedUsageBoundsUpdate */
7770         TRUE,                                     /* supportsDisplayRate */
7771         FALSE,                                    /* supportsFlipLockRGStatus */
7772         FALSE,                                    /* needDefaultLutSurface */
7773         FALSE,                                    /* hasUnorm10OLUT */
7774         TRUE,                                     /* supportsDigitalVibrance */
7775         FALSE,                                    /* supportsImageSharpening */
7776         FALSE,                                    /* supportsHDMIVRR */
7777         FALSE,                                    /* supportsCoreChannelSurface */
7778         FALSE,                                    /* supportsHDMIFRL */
7779         TRUE,                                     /* supportsSetStorageMemoryLayout */
7780         FALSE,                                    /* supportsIndependentAcqRelSemaphore */
7781         FALSE,                                    /* supportsCoreLut */
7782         TRUE,                                     /* supportsSynchronizedOverlayPositionUpdate */
7783         FALSE,                                    /* supportsVblankSyncObjects */
7784         FALSE,                                    /* requiresScalingTapsInBothDimensions */
7785         FALSE,                                    /* supportsMergeMode */
7786         NV_EVO3_SUPPORTED_DITHERING_MODES,        /* supportedDitheringModes */
7787         sizeof(NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS), /* impStructSize */
7788         NV_EVO_SCALER_2TAPS,                      /* minScalerTaps */
7789         NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C3, /* xEmulatedSurfaceMemoryFormats */
7790     },
7791 };
7792 
7793 NVEvoHAL nvEvoC5 = {
7794     EvoSetRasterParamsC5,                         /* SetRasterParams */
7795     EvoSetProcAmpC5,                              /* SetProcAmp */
7796     EvoSetHeadControlC3,                          /* SetHeadControl */
7797     EvoSetHeadRefClkC3,                           /* SetHeadRefClk */
7798     EvoHeadSetControlORC5,                        /* HeadSetControlOR */
7799     EvoORSetControlC3,                            /* ORSetControl */
7800     EvoHeadSetDisplayIdC3,                        /* HeadSetDisplayId */
7801     EvoSetUsageBoundsC5,                          /* SetUsageBounds */
7802     EvoUpdateC3,                                  /* Update */
7803     EvoIsModePossibleC3,                          /* IsModePossible */
7804     EvoPrePostIMPC3,                              /* PrePostIMP */
7805     EvoSetNotifierC3,                             /* SetNotifier */
7806     EvoGetCapabilitiesC5,                         /* GetCapabilities */
7807     EvoFlipC5,                                    /* Flip */
7808     EvoFlipTransitionWARC5,                       /* FlipTransitionWAR */
7809     EvoFillLUTSurfaceC5,                          /* FillLUTSurface */
7810     EvoSetLUTContextDmaC5,                        /* SetLUTContextDma */
7811     EvoSetOutputScalerC3,                         /* SetOutputScaler */
7812     EvoSetViewportPointInC3,                      /* SetViewportPointIn */
7813     EvoSetViewportInOutC5,                        /* SetViewportInOut */
7814     EvoSetCursorImageC3,                          /* SetCursorImage */
7815     EvoValidateCursorSurfaceC3,                   /* ValidateCursorSurface */
7816     EvoValidateWindowFormatC5,                    /* ValidateWindowFormat */
7817     EvoInitCompNotifierC3,                        /* InitCompNotifier */
7818     EvoIsCompNotifierCompleteC3,                  /* IsCompNotifierComplete */
7819     EvoWaitForCompNotifierC3,                     /* WaitForCompNotifier */
7820     EvoSetDitherC3,                               /* SetDither */
7821     EvoSetStallLockC3,                            /* SetStallLock */
7822     EvoSetDisplayRateC3,                          /* SetDisplayRate */
7823     EvoInitChannelC5,                             /* InitChannel */
7824     EvoInitDefaultLutC5,                          /* InitDefaultLut */
7825     EvoInitWindowMappingC5,                       /* InitWindowMapping */
7826     EvoIsChannelIdleC3,                           /* IsChannelIdle */
7827     EvoIsChannelMethodPendingC3,                  /* IsChannelMethodPending */
7828     EvoForceIdleSatelliteChannelC3,               /* ForceIdleSatelliteChannel */
7829     EvoForceIdleSatelliteChannelIgnoreLockC3,     /* ForceIdleSatelliteChannelIgnoreLock */
7830     EvoAccelerateChannelC3,                       /* AccelerateChannel */
7831     EvoResetChannelAcceleratorsC3,                /* ResetChannelAccelerators */
7832     EvoAllocRmCtrlObjectC3,                       /* AllocRmCtrlObject */
7833     EvoFreeRmCtrlObjectC3,                        /* FreeRmCtrlObject */
7834     EvoSetImmPointOutC3,                          /* SetImmPointOut */
7835     EvoStartHeadCRC32CaptureC3,                   /* StartCRC32Capture */
7836     EvoStopHeadCRC32CaptureC3,                    /* StopCRC32Capture */
7837     EvoQueryHeadCRC32_C3,                         /* QueryCRC32 */
7838     EvoGetScanLineC3,                             /* GetScanLine */
7839     NULL,                                         /* ConfigureVblankSyncObject */
7840     EvoSetDscParamsC5,                            /* SetDscParams */
7841     EvoEnableMidFrameAndDWCFWatermarkC5,          /* EnableMidFrameAndDWCFWatermark */
7842     EvoGetActiveViewportOffsetC3,                 /* GetActiveViewportOffset */
7843     NULL,                                         /* ClearSurfaceUsage */
7844     EvoComputeWindowScalingTapsC5,                /* ComputeWindowScalingTaps */
7845     EvoGetWindowScalingCapsC3,                    /* GetWindowScalingCaps */
7846     EvoSetMergeModeC5,                            /* SetMergeMode */
7847     {                                             /* caps */
7848         TRUE,                                     /* supportsNonInterlockedUsageBoundsUpdate */
7849         TRUE,                                     /* supportsDisplayRate */
7850         FALSE,                                    /* supportsFlipLockRGStatus */
7851         TRUE,                                     /* needDefaultLutSurface */
7852         TRUE,                                     /* hasUnorm10OLUT */
7853         TRUE,                                     /* supportsDigitalVibrance */
7854         FALSE,                                    /* supportsImageSharpening */
7855         TRUE,                                     /* supportsHDMIVRR */
7856         FALSE,                                    /* supportsCoreChannelSurface */
7857         FALSE,                                    /* supportsHDMIFRL */
7858         TRUE,                                     /* supportsSetStorageMemoryLayout */
7859         FALSE,                                    /* supportsIndependentAcqRelSemaphore */
7860         FALSE,                                    /* supportsCoreLut */
7861         TRUE,                                     /* supportsSynchronizedOverlayPositionUpdate */
7862         FALSE,                                    /* supportsVblankSyncObjects */
7863         FALSE,                                    /* requiresScalingTapsInBothDimensions */
7864         TRUE,                                     /* supportsMergeMode */
7865         NV_EVO3_SUPPORTED_DITHERING_MODES,        /* supportedDitheringModes */
7866         sizeof(NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS), /* impStructSize */
7867         NV_EVO_SCALER_2TAPS,                      /* minScalerTaps */
7868         NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C5, /* xEmulatedSurfaceMemoryFormats */
7869     },
7870 };
7871 
7872 NVEvoHAL nvEvoC6 = {
7873     EvoSetRasterParamsC6,                         /* SetRasterParams */
7874     EvoSetProcAmpC5,                              /* SetProcAmp */
7875     EvoSetHeadControlC3,                          /* SetHeadControl */
7876     EvoSetHeadRefClkC3,                           /* SetHeadRefClk */
7877     EvoHeadSetControlORC5,                        /* HeadSetControlOR */
7878     EvoORSetControlC6,                            /* ORSetControl */
7879     EvoHeadSetDisplayIdC3,                        /* HeadSetDisplayId */
7880     EvoSetUsageBoundsC5,                          /* SetUsageBounds */
7881     EvoUpdateC3,                                  /* Update */
7882     EvoIsModePossibleC3,                          /* IsModePossible */
7883     EvoPrePostIMPC3,                              /* PrePostIMP */
7884     EvoSetNotifierC3,                             /* SetNotifier */
7885     EvoGetCapabilitiesC6,                         /* GetCapabilities */
7886     EvoFlipC6,                                    /* Flip */
7887     EvoFlipTransitionWARC6,                       /* FlipTransitionWAR */
7888     EvoFillLUTSurfaceC5,                          /* FillLUTSurface */
7889     EvoSetLUTContextDmaC5,                        /* SetLUTContextDma */
7890     EvoSetOutputScalerC3,                         /* SetOutputScaler */
7891     EvoSetViewportPointInC3,                      /* SetViewportPointIn */
7892     EvoSetViewportInOutC5,                        /* SetViewportInOut */
7893     EvoSetCursorImageC3,                          /* SetCursorImage */
7894     EvoValidateCursorSurfaceC3,                   /* ValidateCursorSurface */
7895     EvoValidateWindowFormatC6,                    /* ValidateWindowFormat */
7896     EvoInitCompNotifierC3,                        /* InitCompNotifier */
7897     EvoIsCompNotifierCompleteC3,                  /* IsCompNotifierComplete */
7898     EvoWaitForCompNotifierC3,                     /* WaitForCompNotifier */
7899     EvoSetDitherC3,                               /* SetDither */
7900     EvoSetStallLockC3,                            /* SetStallLock */
7901     EvoSetDisplayRateC3,                          /* SetDisplayRate */
7902     EvoInitChannelC5,                             /* InitChannel */
7903     EvoInitDefaultLutC5,                          /* InitDefaultLut */
7904     EvoInitWindowMappingC5,                       /* InitWindowMapping */
7905     EvoIsChannelIdleC3,                           /* IsChannelIdle */
7906     EvoIsChannelMethodPendingC3,                  /* IsChannelMethodPending */
7907     EvoForceIdleSatelliteChannelC3,               /* ForceIdleSatelliteChannel */
7908     EvoForceIdleSatelliteChannelIgnoreLockC3,     /* ForceIdleSatelliteChannelIgnoreLock */
7909     EvoAccelerateChannelC3,                       /* AccelerateChannel */
7910     EvoResetChannelAcceleratorsC3,                /* ResetChannelAccelerators */
7911     EvoAllocRmCtrlObjectC3,                       /* AllocRmCtrlObject */
7912     EvoFreeRmCtrlObjectC3,                        /* FreeRmCtrlObject */
7913     EvoSetImmPointOutC3,                          /* SetImmPointOut */
7914     EvoStartHeadCRC32CaptureC3,                   /* StartCRC32Capture */
7915     EvoStopHeadCRC32CaptureC3,                    /* StopCRC32Capture */
7916     EvoQueryHeadCRC32_C3,                         /* QueryCRC32 */
7917     EvoGetScanLineC3,                             /* GetScanLine */
7918     EvoConfigureVblankSyncObjectC6,               /* ConfigureVblankSyncObject */
7919     EvoSetDscParamsC5,                            /* SetDscParams */
7920     NULL,                                         /* EnableMidFrameAndDWCFWatermark */
7921     EvoGetActiveViewportOffsetC3,                 /* GetActiveViewportOffset */
7922     NULL,                                         /* ClearSurfaceUsage */
7923     EvoComputeWindowScalingTapsC5,                /* ComputeWindowScalingTaps */
7924     EvoGetWindowScalingCapsC3,                    /* GetWindowScalingCaps */
7925     EvoSetMergeModeC5,                            /* SetMergeMode */
7926     {                                             /* caps */
7927         TRUE,                                     /* supportsNonInterlockedUsageBoundsUpdate */
7928         TRUE,                                     /* supportsDisplayRate */
7929         FALSE,                                    /* supportsFlipLockRGStatus */
7930         TRUE,                                     /* needDefaultLutSurface */
7931         TRUE,                                     /* hasUnorm10OLUT */
7932         TRUE,                                     /* supportsDigitalVibrance */
7933         FALSE,                                    /* supportsImageSharpening */
7934         TRUE,                                     /* supportsHDMIVRR */
7935         FALSE,                                    /* supportsCoreChannelSurface */
7936         TRUE,                                     /* supportsHDMIFRL */
7937         FALSE,                                    /* supportsSetStorageMemoryLayout */
7938         TRUE,                                     /* supportsIndependentAcqRelSemaphore */
7939         FALSE,                                    /* supportsCoreLut */
7940         TRUE,                                     /* supportsSynchronizedOverlayPositionUpdate */
7941         TRUE,                                     /* supportsVblankSyncObjects */
7942         FALSE,                                    /* requiresScalingTapsInBothDimensions */
7943         TRUE,                                     /* supportsMergeMode */
7944         NV_EVO3_SUPPORTED_DITHERING_MODES,        /* supportedDitheringModes */
7945         sizeof(NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS), /* impStructSize */
7946         NV_EVO_SCALER_2TAPS,                      /* minScalerTaps */
7947         NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C6, /* xEmulatedSurfaceMemoryFormats */
7948     },
7949 };
7950