mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2026-02-26 09:53:59 +00:00
8164 lines
333 KiB
C
8164 lines
333 KiB
C
/*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2010-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* This file contains implementations of the EVO HAL methods for display class
|
|
* 3.x (also known as "nvdisplay").
|
|
*/
|
|
|
|
#include "nvkms-dma.h"
|
|
#include "nvkms-types.h"
|
|
#include "nvkms-rmapi.h"
|
|
#include "nvkms-surface.h"
|
|
#include "nvkms-softfloat.h"
|
|
#include "nvkms-evo.h"
|
|
#include "nvkms-evo1.h"
|
|
#include "nvkms-evo3.h"
|
|
#include "nvkms-prealloc.h"
|
|
#include "nv-float.h"
|
|
#include "nvkms-dpy.h"
|
|
#include "nvkms-vrr.h"
|
|
#include "nvkms-ctxdma.h"
|
|
|
|
#include <nvmisc.h>
|
|
|
|
#include <class/clc372sw.h> // NVC372_DISPLAY_SW
|
|
#include <class/clc373.h> // NVC373_DISP_CAPABILITIES
|
|
#include <class/clc37b.h> // NVC37B_WINDOW_IMM_CHANNEL_DMA
|
|
#include <class/clc37d.h> // NVC37D_CORE_CHANNEL_DMA
|
|
#include <class/clc37dcrcnotif.h> // NVC37D_NOTIFIER_CRC
|
|
#include <class/clc37dswspare.h> // NVC37D_HEAD_SET_SW_SPARE_*
|
|
#include <class/clc37e.h> // NVC37E_WINDOW_CHANNEL_DMA
|
|
#include <class/clc573.h> // NVC573_DISP_CAPABILITIES
|
|
#include <class/clc57d.h> // NVC57D_CORE_CHANNEL_DMA
|
|
#include <class/clc57e.h> // NVC57E_WINDOW_CHANNEL_DMA
|
|
#include <class/clc57esw.h>
|
|
#include <class/clc673.h> // NVC673_DISP_CAPABILITIES
|
|
#include <class/clc67d.h> // NVC67D_CORE_CHANNEL_DMA
|
|
#include <class/clc67e.h> // NVC67E_WINDOW_CHANNEL_DMA
|
|
|
|
#include <ctrl/ctrlc370/ctrlc370chnc.h>
|
|
#include <ctrl/ctrlc370/ctrlc370rg.h>
|
|
#include <ctrl/ctrlc372/ctrlc372chnc.h>
|
|
|
|
#define NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C3 \
|
|
(NVBIT64(NvKmsSurfaceMemoryFormatRF16GF16BF16XF16))
|
|
|
|
#define NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C5 \
|
|
(NVBIT64(NvKmsSurfaceMemoryFormatRF16GF16BF16XF16))
|
|
|
|
/** Number of CRCs supported by hardware on NVC37D hardware (SF/SOR, Comp, RG) */
|
|
#define NV_EVO3_NUM_CRC_FIELDS 3
|
|
|
|
/** Number of CRCs supported by hardware on NVC37D hardware SF/SOR, Comp, RG Ovf and Count */
|
|
#define NV_EVO3_NUM_CRC_FLAGS 4
|
|
|
|
enum FMTCoeffType
|
|
{
|
|
FMT_COEFF_TYPE_IDENTITY = 0,
|
|
|
|
FMT_COEFF_TYPE_REC709_YUV_8BPC_LTD_TO_RGB_16BPC_FULL,
|
|
FMT_COEFF_TYPE_REC709_YUV_8BPC_FULL_TO_RGB_16BPC_FULL,
|
|
FMT_COEFF_TYPE_REC709_YUV_10BPC_LTD_TO_RGB_16BPC_FULL,
|
|
FMT_COEFF_TYPE_REC709_YUV_10BPC_FULL_TO_RGB_16BPC_FULL,
|
|
FMT_COEFF_TYPE_REC709_YUV_12BPC_LTD_TO_RGB_16BPC_FULL,
|
|
FMT_COEFF_TYPE_REC709_YUV_12BPC_FULL_TO_RGB_16BPC_FULL,
|
|
|
|
// FMT is always identity for RGB to avoid possible calculation error.
|
|
|
|
// must be the last entry
|
|
FMT_COEFF_TYPE_MAX
|
|
};
|
|
|
|
static const NvU32 FMTMatrix[FMT_COEFF_TYPE_MAX][12] =
|
|
{
|
|
// FMT_COEFF_TYPE_IDENTITY
|
|
{ 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0 },
|
|
// FMT_COEFF_TYPE_REC709_YUV_8BPC_LTD_TO_RGB_16BPC_FULL
|
|
{ 0x1CCB7, 0x12B3C, 0, 0x1F06F1, 0x1F770C, 0x12B3C, 0x1FC933, 0x4D2D, 0, 0x12B3C, 0x21EDD, 0x1EDDDE },
|
|
// FMT_COEFF_TYPE_REC709_YUV_8BPC_FULL_TO_RGB_16BPC_FULL
|
|
{ 0x194B4, 0x100FD, 0, 0x1F373A, 0x1F87B3, 0x100FD, 0x1FCFDC, 0x5390, 0, 0x100FD, 0x1DCDE, 0x1F136E },
|
|
// FMT_COEFF_TYPE_REC709_YUV_10BPC_LTD_TO_RGB_16BPC_FULL
|
|
{ 0x1CCB7, 0x12B3C, 0, 0x1F06F1, 0x1F770C, 0x12B3C, 0x1FC933, 0x4D2D, 0, 0x12B3C, 0x21EDD, 0x1EDDDE },
|
|
// FMT_COEFF_TYPE_REC709_YUV_10BPC_FULL_TO_RGB_16BPC_FULL
|
|
{ 0x19385, 0x1003C, 0, 0x1F36A3, 0x1F880D, 0x1003C, 0x1FD000, 0x53CF, 0, 0x1003C, 0x1DB78, 0x1F12BB },
|
|
// FMT_COEFF_TYPE_REC709_YUV_12BPC_LTD_TO_RGB_16BPC_FULL
|
|
{ 0x1CCB7, 0x12B3C, 0, 0x1F06F1, 0x1F770C, 0x12B3C, 0x1FC933, 0x4D2D, 0, 0x12B3C, 0x21EDD, 0x1EDDDE },
|
|
// FMT_COEFF_TYPE_REC709_YUV_12BPC_FULL_TO_RGB_16BPC_FULL
|
|
{ 0x19339, 0x1000C, 0, 0x1F367D, 0x1F8823, 0x1000C, 0x1FD009, 0x53DF, 0, 0x1000C, 0x1DB1F, 0x1F128E },
|
|
};
|
|
|
|
static void SetCsc00MatrixC5(NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCscMatrix *matrix);
|
|
static void SetCsc11MatrixC5(NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCscMatrix *matrix);
|
|
static void
|
|
UpdateCompositionC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCompositionParams *pCompParams,
|
|
NVEvoUpdateState *updateState,
|
|
enum NvKmsSurfaceMemoryFormat format);
|
|
static void
|
|
UpdateCompositionC5(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCompositionParams *pCompParams,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition,
|
|
enum NvKmsSurfaceMemoryFormat format);
|
|
|
|
static void
|
|
EvoSetupIdentityOutputLutC5(NVEvoLutDataRec *pData,
|
|
enum NvKmsLUTState *lutState,
|
|
NvU32 *lutSize,
|
|
NvBool *isLutModeVss);
|
|
|
|
static void
|
|
EvoSetupIdentityBaseLutC5(NVEvoLutDataRec *pData,
|
|
enum NvKmsLUTState *lutState,
|
|
NvU32 *lutSize,
|
|
NvBool *isLutModeVss);
|
|
|
|
ct_assert(NV_EVO_LOCK_PIN_0 >
|
|
NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_PIN_INTERNAL_SCAN_LOCK__SIZE_1);
|
|
|
|
/* nvdisplay has a maximum of 2 eyes and 3 planes per surface */
|
|
ct_assert((NVKMS_MAX_EYES * NVKMS_MAX_PLANES_PER_SURFACE) == 6);
|
|
|
|
/* Windows support all composition modes. */
|
|
#define NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES \
|
|
((1 << NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE) | \
|
|
(1 << NVKMS_COMPOSITION_BLENDING_MODE_TRANSPARENT) | \
|
|
(1 << NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA) | \
|
|
(1 << NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA) | \
|
|
(1 << NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA) | \
|
|
(1 << NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA))
|
|
|
|
#define NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C3 \
|
|
(DRF_DEF(C37D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_LUT, _USAGE_1025) | \
|
|
DRF_DEF(C37D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_2) | \
|
|
DRF_DEF(C37D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE))
|
|
|
|
static inline NvU8 EyeAndPlaneToCtxDmaIdx(const NvU8 eye, const NvU8 plane)
|
|
{
|
|
/*
|
|
* See the definition of the SetContextDmaIso and SetOffset methods in the
|
|
* relevant nvdClass_01.mfs file to see how these method array indices are
|
|
* mapped.
|
|
*/
|
|
nvAssert((eye < NVKMS_MAX_EYES) && (plane < NVKMS_MAX_PLANES_PER_SURFACE));
|
|
|
|
return eye + (plane << 1);
|
|
}
|
|
|
|
static void InitChannelCapsC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel)
|
|
{
|
|
if ((pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL) != 0) {
|
|
static const NVEvoChannelCaps WindowCaps = {
|
|
/*
|
|
* Window classes always support timestamp flips, and allow full
|
|
* use of the 64-bit timestamp value.
|
|
*/
|
|
.validTimeStampBits = 64,
|
|
/* Window classes always support tearing flips. */
|
|
.tearingFlips = TRUE,
|
|
.vrrTearingFlips = TRUE,
|
|
/* Window classes support per-eye stereo flips. */
|
|
.perEyeStereoFlips = TRUE,
|
|
};
|
|
|
|
pChannel->caps = WindowCaps;
|
|
}
|
|
}
|
|
|
|
// The coefficient values are obtained from bug 1953108 comment 10
|
|
// Per MFS: However since all 5 coefficients have to add up to 1.0, only 4 need to be specified, and
|
|
// HW can derive the missing one. The center coefficient is the one that is left out, so
|
|
// if the 5 taps need weights (c0, c1, c2, c3, c4) then only (c0, c1, c3, c4) are stored,
|
|
// and c2 is calculated by HW.
|
|
// Phase 0 is the center phase and the corresponding filter kernel is symmetrical:
|
|
// c0=c4, c1=c3 --> only c0 and c1 need to be stored.
|
|
// Phase 16 (and -16) is the edge phase and the corresponding filter kernels are:
|
|
// (0, c0, c1, c1, c0) for phase +16
|
|
// (c0, c1, c1, c0, 0) for phase -16
|
|
// The difference between +16 and -16 is automatically handled by HW. The table only needs
|
|
// to store c0 and c1 for either case.
|
|
// Therefore, based on MFS above, the matrix below contains the values loaded to HW.
|
|
// Real Phase 0 is commented for easy reference.
|
|
// Also, phase 16 values (last row) are commented, but its C0,C1 values are loaded in row 0/phase 0.
|
|
const NvU32 scalerTaps5Coeff[NUM_SCALER_RATIOS][NUM_TAPS5_COEFF_PHASES][NUM_TAPS5_COEFF_VALUES] =
|
|
{
|
|
// ratio = 1
|
|
{{ 0 , 0 , -16 , 144}, // real phase 0:{ 0, 0, /*256,*/ 0, 0 },
|
|
{ 0 , -5 , /*255,*/ 5 , 0},
|
|
{ 0 , -9 , /*254,*/ 11 , 0},
|
|
{ -1 , -12 , /*251,*/ 18 , -1},
|
|
{ -1 , -15 , /*248,*/ 25 , -1},
|
|
{ -1 , -18 , /*243,*/ 33 , -2},
|
|
{ -2 , -20 , /*238,*/ 42 , -3},
|
|
{ -2 , -21 , /*232,*/ 51 , -3},
|
|
{ -3 , -22 , /*225,*/ 60 , -5},
|
|
{ -3 , -22 , /*217,*/ 70 , -6},
|
|
{ -4 , -22 , /*208,*/ 81 , -7},
|
|
{ -4 , -22 , /*199,*/ 91 , -9},
|
|
{ -5 , -21 , /*190,*/ 102 , -10},
|
|
{ -5 , -20 , /*180,*/ 113 , -12},
|
|
{ -5 , -19 , /*169,*/ 125 , -13},
|
|
{ -6 , -18 , /*158,*/ 136 , -15}
|
|
// real phase 16: { 0 , -16 , 144, 144 , -16 }
|
|
},
|
|
// ratio = 2
|
|
{{ 3, 60 , 20 , 108 }, // real phase 0: {3 , 60 , 130 , 60 , 3 },
|
|
{ 3 , 57 , /*130,*/ 63 , 4 },
|
|
{ 2 , 54 , /*130,*/ 66 , 4 },
|
|
{ 2 , 51 , /*129,*/ 69 , 5 },
|
|
{ 2 , 48 , /*128,*/ 72 , 6 },
|
|
{ 1 , 45 , /*128,*/ 75 , 7 },
|
|
{ 1 , 43 , /*127,*/ 78 , 7 },
|
|
{ 1 , 40 , /*125,*/ 81 , 8 },
|
|
{ 1 , 37 , /*124,*/ 84 , 9 },
|
|
{ 0 , 35 , /*122,*/ 88 , 10 },
|
|
{ 0 , 33 , /*121,*/ 91 , 12 },
|
|
{ 0 , 30 , /*119,*/ 94 , 13 },
|
|
{ 0 , 28 , /*117,*/ 97 , 14 },
|
|
{ 0 , 26 , /*115,*/ 99 , 16 },
|
|
{ 0 , 24 , /*112,*/ 102 , 17 },
|
|
{ 0 , 22 , /*110,*/ 105 , 19 },
|
|
// real phase 16:{0 , 20 , 108 , 108 , 20 },
|
|
},
|
|
// ratio = 4
|
|
{{ 4 , 62 , 23 , 105 }, // real phase 0: {4 , 62 , 124 , 62 , 4 ,
|
|
{ 4 , 59 , /*124,*/ 64 , 5 },
|
|
{ 3 , 56 , /*124,*/ 67 , 6 },
|
|
{ 3 , 53 , /*123,*/ 70 , 7 },
|
|
{ 2 , 51 , /*123,*/ 73 , 8 },
|
|
{ 2 , 48 , /*122,*/ 76 , 8 },
|
|
{ 2 , 45 , /*121,*/ 79 , 9 },
|
|
{ 1 , 43 , /*120,*/ 81 , 10 },
|
|
{ 1 , 40 , /*119,*/ 84 , 12 },
|
|
{ 1 , 38 , /*117,*/ 87 , 13 },
|
|
{ 1 , 36 , /*116,*/ 90 , 14 },
|
|
{ 0 , 34 , /*114,*/ 92 , 15 },
|
|
{ 0 , 31 , /*113,*/ 95 , 17 },
|
|
{ 0 , 29 , /*111,*/ 97 , 18 },
|
|
{ 0 , 27 , /*109,*/ 100 , 20 },
|
|
{ 0 , 25 , /*107,*/ 102 , 22 },
|
|
// real phase 16: {0 , 23 , 105 , 105 , 23 },
|
|
}
|
|
};
|
|
|
|
void nvInitScalerCoefficientsPrecomp5(NVEvoChannelPtr pChannel,
|
|
NvU32 coeff, NvU32 index)
|
|
{
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_INPUT_SCALER_COEFF_VALUE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_INPUT_SCALER_COEFF_VALUE, _DATA, coeff) |
|
|
DRF_NUM(C57E, _SET_INPUT_SCALER_COEFF_VALUE, _INDEX, index));
|
|
}
|
|
|
|
static void InitScalerCoefficientsPostcomp5(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NvU32 coeff, NvU32 index)
|
|
{
|
|
NvU32 h;
|
|
|
|
for (h = 0; h < pDevEvo->numHeads; h++) {
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC57D_HEAD_SET_OUTPUT_SCALER_COEFF_VALUE(h), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57D, _HEAD_SET_OUTPUT_SCALER_COEFF_VALUE, _DATA, coeff) |
|
|
DRF_NUM(C57D, _HEAD_SET_OUTPUT_SCALER_COEFF_VALUE, _INDEX, index));
|
|
}
|
|
}
|
|
|
|
static void InitTaps5ScalerCoefficientsC5(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NvBool isPrecomp)
|
|
{
|
|
NvU8 ratio;
|
|
|
|
if (isPrecomp) {
|
|
const NVEvoWindowCaps *pWinCaps =
|
|
&pDevEvo->gpus[0].capabilities.window[pChannel->instance];
|
|
const NVEvoScalerCaps *pScalerCaps = &pWinCaps->scalerCaps;
|
|
|
|
if (!pScalerCaps->present) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (ratio = 0; ratio < NUM_SCALER_RATIOS; ratio++) {
|
|
NvU8 phase;
|
|
for (phase = 0; phase < NUM_TAPS5_COEFF_PHASES; phase++) {
|
|
NvU8 coeffIdx;
|
|
for (coeffIdx = 0; coeffIdx < NUM_TAPS5_COEFF_VALUES; coeffIdx++) {
|
|
NvU32 coeff = scalerTaps5Coeff[ratio][phase][coeffIdx];
|
|
NvU32 index = ratio << 6 | phase << 2 | coeffIdx;
|
|
|
|
if (isPrecomp) {
|
|
nvInitScalerCoefficientsPrecomp5(pChannel, coeff, index);
|
|
} else {
|
|
InitScalerCoefficientsPostcomp5(pDevEvo,
|
|
pChannel, coeff, index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This is a 3x4 matrix with S5.14 coefficients (truncated from S5.16
|
|
* SW-specified values).
|
|
*/
|
|
static const struct NvKmsCscMatrix Rec2020RGBToLMS = {{
|
|
{ 0x697c, 0x8620, 0x1064, 0 },
|
|
{ 0x2aa8, 0xb86c, 0x1ce8, 0 },
|
|
{ 0x62c, 0x1354, 0xe684, 0 },
|
|
}};
|
|
|
|
/*
|
|
* This is a 3x4 matrix with S5.14 coefficients (truncated from S5.16
|
|
* SW-specified values).
|
|
*/
|
|
static const struct NvKmsCscMatrix Rec709RGBToLMS = {{
|
|
{ 0x4bb8, 0x9f84, 0x14c8, 0 },
|
|
{ 0x27fc, 0xba2c, 0x1dd4, 0 },
|
|
{ 0x8fc, 0x2818, 0xcef0, 0 },
|
|
}};
|
|
|
|
/*
|
|
* This is a 3x4 matrix with S5.14 coefficients (truncated from S5.16
|
|
* SW-specified values).
|
|
*/
|
|
static const struct NvKmsCscMatrix LMSToRec709RGB = {{
|
|
{ 0x62c48, 0x1aadf4, 0x25a8, 0 },
|
|
{ 0x1ead18, 0x28f64, 0x1fc390, 0 },
|
|
{ 0x1ffd00, 0x1fbc34, 0x146c4, 0 },
|
|
}};
|
|
|
|
/*
|
|
* This is a 3x4 matrix with S5.14 coefficients (truncated from S5.16
|
|
* SW-specified values).
|
|
*/
|
|
static const struct NvKmsCscMatrix LMSToRec2020RGB = {{
|
|
{ 0x36fc0, 0x1d7e54, 0x11e0, 0 },
|
|
{ 0x1f3584, 0x1fbc8, 0x1fcebc, 0 },
|
|
{ 0x1ff964, 0x1fe6a4, 0x11ff4, 0 },
|
|
}};
|
|
|
|
/*
|
|
* The two arrays below specify the PQ OETF transfer function that's used to
|
|
* convert from linear LMS FP16 to PQ encoded L'M'S' fixed-point.
|
|
*/
|
|
static const NvU32 OetfPQ512SegSizesLog2[] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3,
|
|
3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5,
|
|
5,
|
|
};
|
|
|
|
static const NvU16 OetfPQ512Entries[] = {
|
|
0x0000, 0x000C, 0x0014, 0x001C, 0x0028, 0x003C, 0x005C, 0x008C, 0x00D0, 0x0134, 0x0184, 0x01C8, 0x0238, 0x029C, 0x033C, 0x03C4,
|
|
0x043C, 0x04A4, 0x0504, 0x0560, 0x0600, 0x0690, 0x0714, 0x078C, 0x07FC, 0x0864, 0x08C8, 0x0924, 0x0980, 0x09D4, 0x0A24, 0x0A70,
|
|
0x0B04, 0x0B90, 0x0C10, 0x0C88, 0x0CFC, 0x0D68, 0x0DD4, 0x0E38, 0x0EF4, 0x0FA4, 0x1048, 0x10E4, 0x1174, 0x1200, 0x1284, 0x1304,
|
|
0x13F4, 0x14D0, 0x159C, 0x165C, 0x1714, 0x17C0, 0x1864, 0x1900, 0x1A28, 0x1B34, 0x1C30, 0x1D1C, 0x1DFC, 0x1ECC, 0x1F94, 0x2050,
|
|
0x2104, 0x21B0, 0x2258, 0x22F8, 0x2390, 0x2424, 0x24B4, 0x2540, 0x25C4, 0x2648, 0x26C4, 0x2740, 0x27B8, 0x282C, 0x289C, 0x290C,
|
|
0x29E0, 0x2AAC, 0x2B70, 0x2C2C, 0x2CE0, 0x2D90, 0x2E38, 0x2ED8, 0x2F74, 0x300C, 0x30A0, 0x3130, 0x31BC, 0x3244, 0x32C8, 0x3348,
|
|
0x3440, 0x352C, 0x360C, 0x36E4, 0x37B4, 0x387C, 0x393C, 0x39F8, 0x3AA8, 0x3B58, 0x3C00, 0x3CA4, 0x3D44, 0x3DDC, 0x3E74, 0x3F04,
|
|
0x401C, 0x4128, 0x4228, 0x431C, 0x4408, 0x44E8, 0x45C4, 0x4694, 0x475C, 0x4820, 0x48DC, 0x4994, 0x4A48, 0x4AF4, 0x4B9C, 0x4C3C,
|
|
0x4D78, 0x4EA0, 0x4FBC, 0x50CC, 0x51D0, 0x52CC, 0x53BC, 0x54A0, 0x5580, 0x5658, 0x5728, 0x57F0, 0x58B4, 0x5974, 0x5A2C, 0x5ADC,
|
|
0x5C34, 0x5D7C, 0x5EB4, 0x5FDC, 0x60F4, 0x6204, 0x630C, 0x6404, 0x64F8, 0x65E0, 0x66C4, 0x679C, 0x6870, 0x693C, 0x6A04, 0x6AC4,
|
|
0x6C38, 0x6D94, 0x6EE4, 0x7020, 0x7150, 0x7274, 0x738C, 0x7498, 0x7598, 0x7694, 0x7784, 0x786C, 0x794C, 0x7A24, 0x7AF8, 0x7BC4,
|
|
0x7D50, 0x7EC4, 0x8024, 0x8174, 0x82B4, 0x83E8, 0x850C, 0x8628, 0x8738, 0x883C, 0x8938, 0x8A2C, 0x8B18, 0x8BFC, 0x8CD8, 0x8DB0,
|
|
0x8F4C, 0x90D0, 0x9240, 0x939C, 0x94EC, 0x962C, 0x975C, 0x9880, 0x999C, 0x9AAC, 0x9BB0, 0x9CAC, 0x9DA0, 0x9E8C, 0x9F70, 0xA04C,
|
|
0xA1F4, 0xA384, 0xA500, 0xA664, 0xA7BC, 0xA904, 0xAA3C, 0xAB6C, 0xAC8C, 0xADA0, 0xAEAC, 0xAFAC, 0xB0A4, 0xB194, 0xB27C, 0xB360,
|
|
0xB510, 0xB6A4, 0xB824, 0xB994, 0xBAF0, 0xBC3C, 0xBD78, 0xBEA8, 0xBFCC, 0xC0E4, 0xC1F0, 0xC2F4, 0xC3F0, 0xC4E4, 0xC5CC, 0xC6B0,
|
|
0xC78C, 0xC860, 0xC930, 0xC9F8, 0xCABC, 0xCB7C, 0xCC38, 0xCCEC, 0xCD9C, 0xCE48, 0xCEF0, 0xCF94, 0xD034, 0xD0D4, 0xD16C, 0xD200,
|
|
0xD294, 0xD324, 0xD3B4, 0xD43C, 0xD4C4, 0xD54C, 0xD5CC, 0xD650, 0xD6CC, 0xD748, 0xD7C4, 0xD83C, 0xD8B0, 0xD924, 0xD994, 0xDA08,
|
|
0xDAE0, 0xDBB4, 0xDC84, 0xDD4C, 0xDE10, 0xDECC, 0xDF84, 0xE038, 0xE0E8, 0xE194, 0xE238, 0xE2DC, 0xE37C, 0xE418, 0xE4B0, 0xE544,
|
|
0xE5D4, 0xE664, 0xE6F0, 0xE778, 0xE800, 0xE884, 0xE904, 0xE984, 0xEA00, 0xEA7C, 0xEAF4, 0xEB68, 0xEBDC, 0xEC50, 0xECC0, 0xED30,
|
|
0xEE08, 0xEED8, 0xEFA4, 0xF068, 0xF128, 0xF1E4, 0xF298, 0xF348, 0xF3F4, 0xF49C, 0xF540, 0xF5E0, 0xF67C, 0xF714, 0xF7A8, 0xF83C,
|
|
0xF8CC, 0xF958, 0xF9E0, 0xFA68, 0xFAEC, 0xFB6C, 0xFBE8, 0xFC64, 0xFCE0, 0xFD58, 0xFDCC, 0xFE40, 0xFEB4, 0xFF24, 0xFF90, 0xFFFC,
|
|
};
|
|
|
|
/*
|
|
* The two arrays below specify the PQ EOTF transfer function that's used to
|
|
* convert from PQ encoded L'M'S' fixed-point to linear LMS FP16. This transfer
|
|
* function is the inverse of the OETF curve.
|
|
*/
|
|
static const NvU32 EotfPQ512SegSizesLog2[] = {
|
|
6, 6, 4, 4, 4, 3, 4, 3, 3, 3, 2, 2, 2, 3, 3, 2,
|
|
2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
6, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 1, 2,
|
|
2, 1, 1, 2, 2, 2, 2, 1, 2, 1, 1, 2, 1, 4, 2, 2,
|
|
};
|
|
|
|
static const NvU16 EotfPQ512Entries[] = {
|
|
0x0000, 0x0001, 0x0003, 0x0005, 0x0008, 0x000C, 0x0011, 0x0016, 0x001B, 0x0022, 0x0028, 0x002F, 0x0037, 0x003F, 0x0048, 0x0051,
|
|
0x005A, 0x0064, 0x006F, 0x007A, 0x0085, 0x0091, 0x009E, 0x00AB, 0x00B8, 0x00C6, 0x00D4, 0x00E3, 0x00F3, 0x0102, 0x0113, 0x0123,
|
|
0x0135, 0x0146, 0x0158, 0x016B, 0x017E, 0x0192, 0x01A6, 0x01BB, 0x01D0, 0x01E5, 0x01FC, 0x0212, 0x0229, 0x0241, 0x0259, 0x0272,
|
|
0x028B, 0x02A4, 0x02BE, 0x02D9, 0x02F4, 0x0310, 0x032C, 0x0349, 0x0366, 0x0384, 0x03A2, 0x03C1, 0x03E0, 0x0400, 0x0421, 0x0442,
|
|
0x0463, 0x0485, 0x04A8, 0x04CB, 0x04EF, 0x0513, 0x0538, 0x055D, 0x0583, 0x05AA, 0x05D1, 0x05F9, 0x0621, 0x064A, 0x0673, 0x069D,
|
|
0x06C7, 0x06F3, 0x071E, 0x074B, 0x0777, 0x07A5, 0x07D3, 0x0801, 0x0819, 0x0830, 0x0849, 0x0861, 0x087A, 0x0893, 0x08AD, 0x08C7,
|
|
0x08E1, 0x08FB, 0x0916, 0x0931, 0x094C, 0x0968, 0x0984, 0x09A0, 0x09BD, 0x09DA, 0x09F7, 0x0A15, 0x0A33, 0x0A51, 0x0A70, 0x0A8F,
|
|
0x0AAE, 0x0ACE, 0x0AEE, 0x0B0E, 0x0B2F, 0x0B50, 0x0B71, 0x0B93, 0x0BB5, 0x0BD7, 0x0BFA, 0x0C0F, 0x0C20, 0x0C32, 0x0C44, 0x0C56,
|
|
0x0C69, 0x0CB5, 0x0D03, 0x0D55, 0x0DA9, 0x0E01, 0x0E5B, 0x0EB9, 0x0F1B, 0x0F7F, 0x0FE7, 0x1029, 0x1061, 0x109A, 0x10D5, 0x1111,
|
|
0x1150, 0x1190, 0x11D3, 0x1217, 0x125E, 0x12A6, 0x12F0, 0x133D, 0x138B, 0x13DC, 0x1417, 0x1442, 0x146D, 0x149A, 0x14C8, 0x14F7,
|
|
0x1527, 0x1558, 0x158B, 0x15BF, 0x15F4, 0x162A, 0x1662, 0x169B, 0x16D5, 0x1711, 0x174E, 0x178C, 0x17CC, 0x1806, 0x1828, 0x184A,
|
|
0x186D, 0x18B4, 0x18FF, 0x194D, 0x199E, 0x19F3, 0x1A4B, 0x1AA7, 0x1B06, 0x1B37, 0x1B69, 0x1B9B, 0x1BCF, 0x1C02, 0x1C1D, 0x1C38,
|
|
0x1C54, 0x1C70, 0x1C8D, 0x1CAB, 0x1CC9, 0x1CE7, 0x1D06, 0x1D26, 0x1D46, 0x1D88, 0x1DCC, 0x1E13, 0x1E5C, 0x1EA8, 0x1EF6, 0x1F47,
|
|
0x1F9A, 0x1FF1, 0x2025, 0x2053, 0x2082, 0x20B3, 0x20E6, 0x211A, 0x214F, 0x2187, 0x21C0, 0x21FA, 0x2237, 0x2275, 0x22B5, 0x22F7,
|
|
0x233B, 0x23C9, 0x2430, 0x247F, 0x24D3, 0x252B, 0x2589, 0x25EB, 0x2653, 0x26C1, 0x2734, 0x27AD, 0x2817, 0x2838, 0x285A, 0x287C,
|
|
0x28A0, 0x28C5, 0x28EA, 0x2911, 0x2938, 0x2960, 0x298A, 0x29B4, 0x29DF, 0x2A0C, 0x2A39, 0x2A68, 0x2A98, 0x2AFA, 0x2B62, 0x2BCE,
|
|
0x2C20, 0x2C5B, 0x2C99, 0x2CDA, 0x2D1E, 0x2D65, 0x2DB0, 0x2DFD, 0x2E4E, 0x2EA3, 0x2EFC, 0x2F58, 0x2FB8, 0x300E, 0x3043, 0x307A,
|
|
0x30B3, 0x30D0, 0x30EE, 0x310D, 0x312C, 0x314C, 0x316D, 0x318E, 0x31B0, 0x31D3, 0x31F6, 0x321A, 0x323F, 0x3265, 0x328B, 0x32B2,
|
|
0x32DA, 0x332D, 0x3383, 0x33DC, 0x341D, 0x344D, 0x347F, 0x34B4, 0x34EA, 0x3523, 0x355E, 0x359B, 0x35DB, 0x361D, 0x3662, 0x36A9,
|
|
0x36F3, 0x3740, 0x3791, 0x37E4, 0x381D, 0x384A, 0x3879, 0x38A9, 0x38DB, 0x3910, 0x3946, 0x397E, 0x39B8, 0x39F5, 0x3A34, 0x3A75,
|
|
0x3AB9, 0x3AFF, 0x3B48, 0x3B94, 0x3BE2, 0x3C1A, 0x3C44, 0x3C70, 0x3C9D, 0x3CA0, 0x3CA3, 0x3CA6, 0x3CA9, 0x3CAC, 0x3CAF, 0x3CB1,
|
|
0x3CB4, 0x3CB7, 0x3CBA, 0x3CBD, 0x3CC0, 0x3CC3, 0x3CC6, 0x3CC9, 0x3CCC, 0x3CCF, 0x3CD2, 0x3CD5, 0x3CD8, 0x3CDB, 0x3CDE, 0x3CE1,
|
|
0x3CE4, 0x3CE7, 0x3CEA, 0x3CEE, 0x3CF1, 0x3CF4, 0x3CF7, 0x3CFA, 0x3CFD, 0x3D00, 0x3D03, 0x3D06, 0x3D09, 0x3D0D, 0x3D10, 0x3D13,
|
|
0x3D16, 0x3D19, 0x3D1C, 0x3D20, 0x3D23, 0x3D26, 0x3D29, 0x3D2C, 0x3D30, 0x3D33, 0x3D36, 0x3D39, 0x3D3D, 0x3D40, 0x3D43, 0x3D46,
|
|
0x3D4A, 0x3D4D, 0x3D50, 0x3D54, 0x3D57, 0x3D5A, 0x3D5D, 0x3D61, 0x3D64, 0x3D9B, 0x3DD3, 0x3E0D, 0x3E4A, 0x3E89, 0x3ECA, 0x3F0E,
|
|
0x3F54, 0x3F9C, 0x3FE8, 0x401B, 0x4043, 0x406D, 0x4099, 0x40C6, 0x40F4, 0x4124, 0x4156, 0x418A, 0x41C0, 0x41F8, 0x4232, 0x426D,
|
|
0x42AB, 0x42EB, 0x432E, 0x4373, 0x43BA, 0x4428, 0x4479, 0x44D0, 0x452D, 0x4591, 0x45FC, 0x466F, 0x46EB, 0x472C, 0x476F, 0x47B5,
|
|
0x47FE, 0x4824, 0x484B, 0x4874, 0x489D, 0x48F5, 0x4954, 0x4986, 0x49B9, 0x49EF, 0x4A26, 0x4A5F, 0x4A9B, 0x4AD9, 0x4B19, 0x4B9F,
|
|
0x4C18, 0x4C66, 0x4CBA, 0x4CE6, 0x4D13, 0x4D43, 0x4D74, 0x4DA7, 0x4DDC, 0x4E12, 0x4E4B, 0x4E86, 0x4EC3, 0x4F02, 0x4F44, 0x4F88,
|
|
0x4FCE, 0x500C, 0x5032, 0x5082, 0x50D8, 0x5106, 0x5135, 0x5166, 0x5199, 0x5205, 0x5278, 0x52F5, 0x537C, 0x53C3, 0x5406, 0x542D,
|
|
0x5454, 0x54A9, 0x5503, 0x550F, 0x551B, 0x5527, 0x5533, 0x5540, 0x554C, 0x5559, 0x5565, 0x5572, 0x557F, 0x558C, 0x5599, 0x55A7,
|
|
0x55B4, 0x55C1, 0x55CF, 0x5607, 0x5641, 0x567E, 0x56BC, 0x56FE, 0x5741, 0x5788, 0x57D1,
|
|
};
|
|
|
|
#define TMO_LUT_NUM_SEGMENTS 64
|
|
#define TMO_LUT_SEG_SIZE_LOG2 4
|
|
#define TMO_LUT_NUM_ENTRIES 1024
|
|
|
|
struct TmoLutSettings
|
|
{
|
|
NvU32 satMode;
|
|
NvU32 lowIntensityZoneEnd;
|
|
NvU32 lowIntensityValueLinWeight;
|
|
NvU32 lowIntensityValueNonLinWeight;
|
|
NvU32 lowIntensityValueThreshold;
|
|
|
|
NvU32 medIntensityZoneStart;
|
|
NvU32 medIntensityZoneEnd;
|
|
NvU32 medIntensityValueLinWeight;
|
|
NvU32 medIntensityValueNonLinWeight;
|
|
NvU32 medIntensityValueThreshold;
|
|
|
|
NvU32 highIntensityZoneStart;
|
|
NvU32 highIntensityValueLinWeight;
|
|
NvU32 highIntensityValueNonLinWeight;
|
|
NvU32 highIntensityValueThreshold;
|
|
};
|
|
|
|
// Default Rec.2020 weights, no color correction.
|
|
static const struct TmoLutSettings TMO_LUT_SETTINGS_REC2020 = { 2, 1280, 256, 256, 255, 4960, 4961, 256, 256, 255, 10640, 256, 256, 255 };
|
|
|
|
static void InitCsc0LUT(NVEvoChannelPtr pChannel,
|
|
const NvU32 *pSegmentSizes, NvU32 numSegmentSizes,
|
|
const NvU16 *pLUTEntries, NvU32 numEntries)
|
|
{
|
|
NvU32 i;
|
|
|
|
for (i = 0; i < numSegmentSizes; i++) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC0LUT_SEGMENT_SIZE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_CSC0LUT_SEGMENT_SIZE, _IDX, i) |
|
|
DRF_NUM(C57E, _SET_CSC0LUT_SEGMENT_SIZE, _VALUE, pSegmentSizes[i]));
|
|
}
|
|
|
|
for (i = 0; i < numEntries; i++) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC0LUT_ENTRY, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_CSC0LUT_ENTRY, _IDX, i) |
|
|
DRF_NUM(C57E, _SET_CSC0LUT_ENTRY, _VALUE, pLUTEntries[i]));
|
|
}
|
|
}
|
|
|
|
static void InitCsc1LUT(NVEvoChannelPtr pChannel,
|
|
const NvU32 *pSegmentSizes, NvU32 numSegmentSizes,
|
|
const NvU16 *pLUTEntries, NvU32 numEntries)
|
|
{
|
|
NvU32 i;
|
|
|
|
for (i = 0; i < numSegmentSizes; i++) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC1LUT_SEGMENT_SIZE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_CSC1LUT_SEGMENT_SIZE, _IDX, i) |
|
|
DRF_NUM(C57E, _SET_CSC1LUT_SEGMENT_SIZE, _VALUE, pSegmentSizes[i]));
|
|
}
|
|
|
|
for (i = 0; i < numEntries; i++) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC1LUT_ENTRY, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_CSC1LUT_ENTRY, _IDX, i) |
|
|
DRF_NUM(C57E, _SET_CSC1LUT_ENTRY, _VALUE, pLUTEntries[i]));
|
|
}
|
|
}
|
|
|
|
static void ConfigureCsc0C5(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
enum NvKmsInputColorSpace colorspace,
|
|
NvBool enable)
|
|
{
|
|
NVEvoWindowCaps *pWinCaps =
|
|
&pDevEvo->gpus[0].capabilities.window[pChannel->instance];
|
|
struct NvKmsCscMatrix matrix = { };
|
|
NvU32 lutData = 0;
|
|
NvU32 csc01Data = 0;
|
|
|
|
if (!pWinCaps->csc0MatricesPresent) {
|
|
return;
|
|
}
|
|
|
|
if (enable) {
|
|
if (colorspace == NVKMS_INPUT_COLORSPACE_BT2100_PQ) {
|
|
matrix = Rec2020RGBToLMS;
|
|
} else {
|
|
matrix = Rec709RGBToLMS;
|
|
}
|
|
|
|
lutData |= DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _INTERPOLATE, _ENABLE) |
|
|
DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _MIRROR, _DISABLE) |
|
|
DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _ENABLE, _ENABLE);
|
|
|
|
csc01Data |= DRF_DEF(C57E, _SET_CSC01CONTROL, _ENABLE, _ENABLE);
|
|
} else {
|
|
matrix = NVKMS_IDENTITY_CSC_MATRIX;
|
|
|
|
lutData |= DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _INTERPOLATE, _DISABLE) |
|
|
DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _MIRROR, _DISABLE) |
|
|
DRF_DEF(C57E, _SET_CSC0LUT_CONTROL, _ENABLE, _DISABLE);
|
|
|
|
csc01Data |= DRF_DEF(C57E, _SET_CSC01CONTROL, _ENABLE, _DISABLE);
|
|
}
|
|
|
|
/* Linear RGB FP16 -> Linear LMS FP16 */
|
|
SetCsc00MatrixC5(pChannel, &matrix);
|
|
|
|
/* Linear LMS FP16 -> PQ encoded L'M'S' fixed-point */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC0LUT_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, lutData);
|
|
|
|
/*
|
|
* PQ encoded L'M'S' fixed-point -> ICtCp
|
|
*
|
|
* Note that we're converting between fixed colorspaces, so the default HW
|
|
* coefficients are sufficient.
|
|
*/
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC01CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, csc01Data);
|
|
}
|
|
|
|
static inline float64_t maxF(float64_t a, float64_t b)
|
|
{
|
|
return f64_lt(a, b) ? b : a;
|
|
}
|
|
|
|
static inline float64_t clampF(float64_t value, float64_t min, float64_t max)
|
|
{
|
|
value = maxF(value, min);
|
|
value = f64_lt(max, value) ? max : value;
|
|
return value;
|
|
}
|
|
|
|
static float64_t PQEotf(float64_t colorValue, NvBool inverse)
|
|
{
|
|
const float64_t zero = {0x0000000000000000}; // 0.0
|
|
const float64_t one = {0x3FF0000000000000}; // 1.0
|
|
const float64_t m1 = {0x3FC463FFFFFFB9A2}; // 0.1593017578125
|
|
const float64_t m2 = {0x4053B60000000000}; // 78.84375
|
|
const float64_t c1 = {0x3FEAC00000000000}; // 0.8359375
|
|
const float64_t c2 = {0x4032DA0000000000}; // 18.8515625
|
|
const float64_t c3 = {0x4032B00000000000}; // 18.6875
|
|
|
|
const float64_t invm1 = {0x40191C0D56E72ABA}; // 1/m1 = 6.27739463602
|
|
const float64_t invm2 = {0x3F89F9B585D7C997}; // 1/m2 = 0.01268331351
|
|
|
|
if (inverse) {
|
|
// Convert from linear to PQ-encoded values.
|
|
float64_t L = clampF(colorValue, zero, one);
|
|
float64_t powLm1 = nvKmsPow(L, m1);
|
|
float64_t N = nvKmsPow(f64_div(f64_add(c1, f64_mul(c2, powLm1)),
|
|
f64_add(one, f64_mul(c3, powLm1))), m2);
|
|
|
|
return clampF(N, zero, one);
|
|
} else {
|
|
// Convert from PQ-encoded values to linear values.
|
|
float64_t N = clampF(colorValue, zero, one);
|
|
float64_t powNinvM2 = nvKmsPow(N, invm2);
|
|
float64_t L = nvKmsPow(f64_div(maxF(f64_sub(powNinvM2, c1), zero),
|
|
f64_sub(c2, f64_mul(c3, powNinvM2))),
|
|
invm1);
|
|
|
|
return clampF(L, zero, one);
|
|
}
|
|
}
|
|
|
|
// Hermite spline
|
|
static float64_t P(float64_t B, float64_t KS, float64_t maxLum)
|
|
{
|
|
const float64_t one = {0x3FF0000000000000}; // 1.0
|
|
const float64_t two = {0x4000000000000000}; // 2.0
|
|
const float64_t negtwo = {0xC000000000000000}; // -2.0
|
|
const float64_t three = {0x4008000000000000}; // 3.0
|
|
|
|
float64_t t = f64_div(f64_sub(B, KS), f64_sub(one, KS));
|
|
float64_t t2 = f64_mul(t, t);
|
|
float64_t t3 = f64_mul(t2, t);
|
|
|
|
return
|
|
f64_add(f64_add(
|
|
f64_mul(f64_add(f64_sub(f64_mul(two, t3), f64_mul(three, t2)), one), KS),
|
|
f64_mul(f64_add(f64_sub(t3, f64_mul(two, t2)), t), f64_sub(one, KS))),
|
|
f64_mul(f64_add(f64_mul(negtwo, t3), f64_mul(three, t2)), maxLum));
|
|
}
|
|
|
|
/*
|
|
* PQ tone mapping operator with no remapping of blacks or "toe" section of
|
|
* curve. Messing with nonlinearity and remapping in the SDR portion of the
|
|
* curve results in bad looking PC desktop and game content.
|
|
*
|
|
* Lmax = InvPQEotf(targetMaxLum/10000.0)
|
|
* Lw = InvPQEotf(srcMaxLum/10000.0)
|
|
* maxLumRatio = Lmax/Lw
|
|
* KS = 1.5*maxLumRatio - 0.5
|
|
* KSEqualsOne = (KS == 1.0)
|
|
*
|
|
* XXX HDR TODO: Remap blacks and implement toe section for video content?
|
|
*/
|
|
static NvU16 TmoLutEntry(NvU32 i,
|
|
const float64_t Lmax,
|
|
const float64_t Lw,
|
|
const float64_t maxLumRatio,
|
|
const float64_t KS,
|
|
const NvBool KSEqualsOne)
|
|
{
|
|
const float64_t zero = {0x0000000000000000}; // 0.0
|
|
const float64_t maxIntensity = {0x40CFFF8000000000}; // 16383.0
|
|
|
|
float64_t outputF;
|
|
float64_t inputF =
|
|
f64_div(ui32_to_f64(i), ui32_to_f64(TMO_LUT_NUM_ENTRIES - 1));
|
|
|
|
float64_t E1;
|
|
float64_t E2;
|
|
|
|
E1 = f64_div(inputF, Lw);
|
|
|
|
if (KSEqualsOne || f64_lt(E1, KS)) {
|
|
E2 = E1;
|
|
} else {
|
|
E2 = P(E1, KS, maxLumRatio);
|
|
}
|
|
|
|
outputF = clampF(f64_mul(E2, Lw), zero, Lmax);
|
|
|
|
return (NvU16) f64_to_ui32(clampF(f64_mul(outputF, maxIntensity),
|
|
zero, maxIntensity),
|
|
softfloat_round_near_even, FALSE) << 2;
|
|
}
|
|
|
|
static void InitializeTmoLut(const NVEvoChannelPtr pChannel,
|
|
NVLutSurfaceEvoPtr pLutSurfaceEvo,
|
|
NvU32 sd)
|
|
{
|
|
NVEvoLutDataRec *pData = pLutSurfaceEvo->subDeviceAddress[sd];
|
|
NvU64 vssHead = 0;
|
|
NvU32 lutEntryCounter = 0, i;
|
|
|
|
// Precalculate constants for TmoLutEntry().
|
|
const float64_t tenThousand = {0x40C3880000000000}; // 10000.0
|
|
const float64_t one = {0x3FF0000000000000}; // 1.0
|
|
const float64_t half = {0x3FE0000000000000}; // 0.5
|
|
const float64_t oneHalf = {0x3FF8000000000000}; // 1.5
|
|
// Lmax = InvPQEotf(targetMaxLum/10,000)
|
|
const float64_t Lmax =
|
|
PQEotf(f64_div(ui32_to_f64(pChannel->tmoParams.targetMaxLums[sd]),
|
|
tenThousand), TRUE);
|
|
// Lw = InvPQEotf(srcMaxLum/10,000)
|
|
const float64_t Lw =
|
|
PQEotf(f64_div(ui32_to_f64(pChannel->tmoParams.srcMaxLum),
|
|
tenThousand), TRUE);
|
|
// maxLumRatio = Lmax/Lw
|
|
const float64_t maxLumRatio = f64_div(Lmax, Lw);
|
|
// KS = 1.5*maxLumRatio - 0.5
|
|
const float64_t KS = f64_sub(f64_mul(oneHalf, maxLumRatio), half);
|
|
// KSEqualsOne = (KS == 1.0)
|
|
const NvBool KSEqualsOne = f64_eq(KS, one);
|
|
|
|
nvAssert(pChannel->tmoParams.srcMaxLum >=
|
|
pChannel->tmoParams.targetMaxLums[sd]);
|
|
|
|
// VSS Header
|
|
for (lutEntryCounter = 0; lutEntryCounter < NV_LUT_VSS_HEADER_SIZE; lutEntryCounter++) {
|
|
vssHead = 0;
|
|
for (i = 0; ((i < 16) && (((lutEntryCounter * 16) + i) < TMO_LUT_NUM_SEGMENTS)); i++) {
|
|
NvU64 temp = TMO_LUT_SEG_SIZE_LOG2;
|
|
vssHead |= temp << (i * 3);
|
|
}
|
|
nvkms_memcpy(&(pData->base[lutEntryCounter]), &vssHead, sizeof(NVEvoLutEntryRec));
|
|
}
|
|
|
|
for (i = 0; i < TMO_LUT_NUM_ENTRIES; i++) {
|
|
pData->base[i + NV_LUT_VSS_HEADER_SIZE].Red =
|
|
pData->base[i + NV_LUT_VSS_HEADER_SIZE].Green =
|
|
pData->base[i + NV_LUT_VSS_HEADER_SIZE].Blue =
|
|
TmoLutEntry(i, Lmax, Lw, maxLumRatio, KS, KSEqualsOne);
|
|
}
|
|
|
|
// Copy the last entry for interpolation
|
|
pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE].Red =
|
|
pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE - 1].Red;
|
|
pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE].Blue =
|
|
pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE - 1].Blue;
|
|
pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE].Green =
|
|
pData->base[TMO_LUT_NUM_ENTRIES + NV_LUT_VSS_HEADER_SIZE - 1].Green;
|
|
}
|
|
|
|
static NvBool UpdateTmoParams(NVEvoChannelPtr pChannel,
|
|
NvBool enabled,
|
|
NvU32 srcMaxLum,
|
|
const NvU32 targetMaxLums[NVKMS_MAX_SUBDEVICES])
|
|
{
|
|
NvU16 sd;
|
|
NvBool dirty = FALSE;
|
|
|
|
if (pChannel->tmoParams.enabled != enabled) {
|
|
pChannel->tmoParams.enabled = enabled;
|
|
dirty = TRUE;
|
|
}
|
|
|
|
if (pChannel->tmoParams.srcMaxLum != srcMaxLum) {
|
|
pChannel->tmoParams.srcMaxLum = srcMaxLum;
|
|
dirty = TRUE;
|
|
}
|
|
|
|
for (sd = 0; sd < NVKMS_MAX_SUBDEVICES; sd++) {
|
|
if (pChannel->tmoParams.targetMaxLums[sd] != targetMaxLums[sd]) {
|
|
pChannel->tmoParams.targetMaxLums[sd] = targetMaxLums[sd];
|
|
dirty = TRUE;
|
|
}
|
|
}
|
|
|
|
return dirty;
|
|
}
|
|
|
|
static void EvoSetTmoLutSurfaceAddressC5(
|
|
const NVDevEvoRec *pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 offset)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CONTEXT_DMA_TMO_LUT, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_CONTEXT_DMA_TMO_LUT, _HANDLE, ctxDmaHandle));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_OFFSET_TMO_LUT, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_OFFSET_TMO_LUT, _ORIGIN, offset >> 8));
|
|
}
|
|
|
|
static void ConfigureTmoLut(NVDevEvoPtr pDevEvo,
|
|
const NVFlipChannelEvoHwState *pHwState,
|
|
NVEvoChannelPtr pChannel)
|
|
{
|
|
NvU16 sd;
|
|
const NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
|
|
const NvU32 head = pDevEvo->headForWindow[win];
|
|
const NvU32 offset = offsetof(NVEvoLutDataRec, base);
|
|
const struct TmoLutSettings *tmoLutSettings;
|
|
const NvU32 srcMaxLum = nvGetHDRSrcMaxLum(pHwState);
|
|
|
|
NvBool needsTmoLut = FALSE;
|
|
NvU32 targetMaxLums[NVKMS_MAX_SUBDEVICES] = {0};
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
const NVDispHeadStateEvoRec *pHeadState =
|
|
&pDevEvo->pDispEvo[sd]->headState[head];
|
|
|
|
targetMaxLums[sd] = pHeadState->hdrInfoFrame.staticMetadata.maxCLL;
|
|
|
|
// If any head needs tone mapping, enable TMO for channel
|
|
if (nvNeedsTmoLut(pDevEvo, pChannel, pHwState,
|
|
srcMaxLum, targetMaxLums[sd])) {
|
|
needsTmoLut = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!UpdateTmoParams(pChannel, needsTmoLut, srcMaxLum, targetMaxLums)) {
|
|
// No change in parameters, no need to reconfigure.
|
|
return;
|
|
}
|
|
|
|
if (!pChannel->tmoParams.enabled) {
|
|
pDevEvo->hal->SetTmoLutSurfaceAddress(pDevEvo, pChannel,
|
|
NULL /* pSurfaceDesc */, 0 /* offset */);
|
|
return;
|
|
}
|
|
|
|
// Initialize TMO LUT on all subdevices
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
nvAssert(pHwState->tmoLut.pLutSurfaceEvo != NULL);
|
|
InitializeTmoLut(pChannel, pHwState->tmoLut.pLutSurfaceEvo, sd);
|
|
}
|
|
|
|
// Program TMO LUT
|
|
|
|
// XXX HDR TODO: Support other transfer functions
|
|
tmoLutSettings = &TMO_LUT_SETTINGS_REC2020;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_TMO_CONTROL, _SIZE,
|
|
NV_LUT_VSS_HEADER_SIZE + TMO_LUT_NUM_ENTRIES + 1) |
|
|
DRF_DEF(C57E, _SET_TMO_CONTROL, _INTERPOLATE, _ENABLE) |
|
|
DRF_NUM(C57E, _SET_TMO_CONTROL, _SAT_MODE, tmoLutSettings->satMode));
|
|
|
|
pDevEvo->hal->SetTmoLutSurfaceAddress(pDevEvo, pChannel,
|
|
&pHwState->tmoLut.pLutSurfaceEvo->surfaceDesc, offset);
|
|
|
|
// Low Intensity
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_LOW_INTENSITY_ZONE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_TMO_LOW_INTENSITY_ZONE, _END,
|
|
tmoLutSettings->lowIntensityZoneEnd));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_LOW_INTENSITY_VALUE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_TMO_LOW_INTENSITY_VALUE, _LIN_WEIGHT,
|
|
tmoLutSettings->lowIntensityValueLinWeight) |
|
|
DRF_NUM(C57E, _SET_TMO_LOW_INTENSITY_VALUE, _NON_LIN_WEIGHT,
|
|
tmoLutSettings->lowIntensityValueNonLinWeight) |
|
|
DRF_NUM(C57E, _SET_TMO_LOW_INTENSITY_VALUE, _THRESHOLD,
|
|
tmoLutSettings->lowIntensityValueThreshold));
|
|
|
|
// Medium Intensity
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_MEDIUM_INTENSITY_ZONE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_ZONE, _START,
|
|
tmoLutSettings->medIntensityZoneStart) |
|
|
DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_ZONE, _END,
|
|
tmoLutSettings->medIntensityZoneEnd));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_MEDIUM_INTENSITY_VALUE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_VALUE, _LIN_WEIGHT,
|
|
tmoLutSettings->medIntensityValueLinWeight) |
|
|
DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_VALUE, _NON_LIN_WEIGHT,
|
|
tmoLutSettings->medIntensityValueNonLinWeight) |
|
|
DRF_NUM(C57E, _SET_TMO_MEDIUM_INTENSITY_VALUE, _THRESHOLD,
|
|
tmoLutSettings->medIntensityValueThreshold));
|
|
|
|
// High Intensity
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_HIGH_INTENSITY_ZONE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_TMO_HIGH_INTENSITY_ZONE, _START,
|
|
tmoLutSettings->highIntensityZoneStart));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_TMO_HIGH_INTENSITY_VALUE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_TMO_HIGH_INTENSITY_VALUE, _LIN_WEIGHT,
|
|
tmoLutSettings->highIntensityValueLinWeight) |
|
|
DRF_NUM(C57E, _SET_TMO_HIGH_INTENSITY_VALUE, _NON_LIN_WEIGHT,
|
|
tmoLutSettings->highIntensityValueNonLinWeight) |
|
|
DRF_NUM(C57E, _SET_TMO_HIGH_INTENSITY_VALUE, _THRESHOLD,
|
|
tmoLutSettings->highIntensityValueThreshold));
|
|
}
|
|
|
|
static void ConfigureCsc1C5(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NvBool enable)
|
|
{
|
|
NVEvoWindowCaps *pWinCaps =
|
|
&pDevEvo->gpus[0].capabilities.window[pChannel->instance];
|
|
struct NvKmsCscMatrix matrix = { };
|
|
NvU32 lutData = 0;
|
|
NvU32 csc10Data = 0;
|
|
const NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
|
|
const NvU32 head = pDevEvo->headForWindow[win];
|
|
|
|
if (!pWinCaps->csc1MatricesPresent || (head == NV_INVALID_HEAD)) {
|
|
return;
|
|
}
|
|
|
|
if (enable) {
|
|
const NvU32 sdMask = nvPeekEvoSubDevMask(pDevEvo);
|
|
const NvU32 sd = (sdMask == 0) ? 0 : nv_ffs(sdMask) - 1;
|
|
const NVDispHeadStateEvoRec *pHeadState;
|
|
|
|
/*
|
|
* All callers of this path should push a single sd on the stack,
|
|
* so that ffs(sdMask) is safe.
|
|
*/
|
|
nvAssert(nvPopCount32(sdMask) == 1);
|
|
|
|
pHeadState = &pDevEvo->pDispEvo[sd]->headState[head];
|
|
|
|
// If postcomp is PQ, composite in Rec2020
|
|
if (pHeadState->tf == NVKMS_OUTPUT_TF_PQ) {
|
|
matrix = LMSToRec2020RGB;
|
|
} else {
|
|
matrix = LMSToRec709RGB;
|
|
}
|
|
|
|
lutData |= DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _INTERPOLATE, _ENABLE) |
|
|
DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _MIRROR, _DISABLE) |
|
|
DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _ENABLE, _ENABLE);
|
|
|
|
csc10Data |= DRF_DEF(C57E, _SET_CSC10CONTROL, _ENABLE, _ENABLE);
|
|
} else {
|
|
matrix = NVKMS_IDENTITY_CSC_MATRIX;
|
|
|
|
lutData |= DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _INTERPOLATE, _DISABLE) |
|
|
DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _MIRROR, _DISABLE) |
|
|
DRF_DEF(C57E, _SET_CSC1LUT_CONTROL, _ENABLE, _DISABLE);
|
|
|
|
csc10Data |= DRF_DEF(C57E, _SET_CSC10CONTROL, _ENABLE, _DISABLE);
|
|
}
|
|
|
|
/*
|
|
* ICtCp -> PQ encoded L'M'S' fixed-point
|
|
*
|
|
* Note that we're converting between fixed colorspaces, so the default HW
|
|
* coefficients are sufficient.
|
|
*/
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC10CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, csc10Data);
|
|
|
|
/* PQ encoded L'M'S' fixed-point -> Linear LMS FP16 */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CSC1LUT_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, lutData);
|
|
|
|
/* Linear LMS FP16 -> Linear RGB FP16 */
|
|
SetCsc11MatrixC5(pChannel, &matrix);
|
|
}
|
|
|
|
static void InitDesktopColorC3(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
|
|
{
|
|
NvU32 head;
|
|
|
|
for (head = 0; head < pDevEvo->numHeads; head++) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DESKTOP_COLOR(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_DESKTOP_COLOR, _RED, 0) |
|
|
DRF_NUM(C37D, _HEAD_SET_DESKTOP_COLOR, _GREEN, 0) |
|
|
DRF_NUM(C37D, _HEAD_SET_DESKTOP_COLOR, _BLUE, 0) |
|
|
DRF_NUM(C37D, _HEAD_SET_DESKTOP_COLOR, _ALPHA, 255));
|
|
}
|
|
}
|
|
|
|
static void InitDesktopColorC5(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
|
|
{
|
|
NvU32 head;
|
|
|
|
for (head = 0; head < pDevEvo->numHeads; head++) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DESKTOP_COLOR_ALPHA_RED(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57D, _HEAD_SET_DESKTOP_COLOR_ALPHA_RED, _ALPHA, 255) |
|
|
DRF_NUM(C57D, _HEAD_SET_DESKTOP_COLOR_ALPHA_RED, _RED, 0));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DESKTOP_COLOR_GREEN_BLUE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57D, _HEAD_SET_DESKTOP_COLOR_GREEN_BLUE, _GREEN, 0) |
|
|
DRF_NUM(C57D, _HEAD_SET_DESKTOP_COLOR_GREEN_BLUE, _BLUE, 0));
|
|
}
|
|
}
|
|
|
|
void nvEvoInitChannel3(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
|
|
{
|
|
InitChannelCapsC3(pDevEvo, pChannel);
|
|
}
|
|
|
|
static void EvoInitChannelC3(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
|
|
{
|
|
const NvBool isCore =
|
|
FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
|
|
pChannel->channelMask);
|
|
|
|
nvEvoInitChannel3(pDevEvo, pChannel);
|
|
|
|
if (isCore) {
|
|
InitDesktopColorC3(pDevEvo, pChannel);
|
|
}
|
|
}
|
|
|
|
static void EvoInitChannelC5(NVDevEvoPtr pDevEvo, NVEvoChannelPtr pChannel)
|
|
{
|
|
const NvBool isCore =
|
|
FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
|
|
pChannel->channelMask);
|
|
const NvBool isWindow =
|
|
((pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL) != 0);
|
|
|
|
nvEvoInitChannel3(pDevEvo, pChannel);
|
|
|
|
if (isCore) {
|
|
InitTaps5ScalerCoefficientsC5(pDevEvo, pChannel, FALSE);
|
|
InitDesktopColorC5(pDevEvo, pChannel);
|
|
} else if (isWindow) {
|
|
NVEvoWindowCaps *pWinCaps =
|
|
&pDevEvo->gpus[0].capabilities.window[pChannel->instance];
|
|
NvU32 csc0SizesLen = ARRAY_LEN(OetfPQ512SegSizesLog2);
|
|
NvU32 csc0EntriesLen = ARRAY_LEN(OetfPQ512Entries);
|
|
NvU32 csc1SizesLen = ARRAY_LEN(EotfPQ512SegSizesLog2);
|
|
NvU32 csc1EntriesLen = ARRAY_LEN(EotfPQ512Entries);
|
|
|
|
InitTaps5ScalerCoefficientsC5(pDevEvo, pChannel, TRUE);
|
|
|
|
if (pWinCaps->cscLUTsPresent) {
|
|
InitCsc0LUT(pChannel,
|
|
OetfPQ512SegSizesLog2, csc0SizesLen,
|
|
OetfPQ512Entries, csc0EntriesLen);
|
|
InitCsc1LUT(pChannel,
|
|
EotfPQ512SegSizesLog2, csc1SizesLen,
|
|
EotfPQ512Entries, csc1EntriesLen);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const NvU32* EvoGetFMTMatrixC5(
|
|
const enum NvKmsSurfaceMemoryFormat format,
|
|
const NVFlipChannelEvoHwState *pHwState)
|
|
{
|
|
const NvU32* retValue = NULL;
|
|
const NvKmsSurfaceMemoryFormatInfo *pFormatInfo =
|
|
nvKmsGetSurfaceMemoryFormatInfo(format);
|
|
|
|
// Choose FMT matrix based on input colorspace, bpc, and colorrange.
|
|
if (pFormatInfo->isYUV) {
|
|
NvBool specifiedFull = (pHwState->colorRange == NVKMS_INPUT_COLORRANGE_FULL);
|
|
if (pFormatInfo->yuv.depthPerComponent == 8) {
|
|
if (specifiedFull) {
|
|
retValue = FMTMatrix[FMT_COEFF_TYPE_REC709_YUV_8BPC_FULL_TO_RGB_16BPC_FULL];
|
|
} else {
|
|
retValue = FMTMatrix[FMT_COEFF_TYPE_REC709_YUV_8BPC_LTD_TO_RGB_16BPC_FULL];
|
|
}
|
|
} else if (pFormatInfo->yuv.depthPerComponent == 10) {
|
|
if (specifiedFull) {
|
|
retValue = FMTMatrix[FMT_COEFF_TYPE_REC709_YUV_10BPC_FULL_TO_RGB_16BPC_FULL];
|
|
} else {
|
|
retValue = FMTMatrix[FMT_COEFF_TYPE_REC709_YUV_10BPC_LTD_TO_RGB_16BPC_FULL];
|
|
}
|
|
} else if (pFormatInfo->yuv.depthPerComponent == 12) {
|
|
if (specifiedFull) {
|
|
retValue = FMTMatrix[FMT_COEFF_TYPE_REC709_YUV_12BPC_FULL_TO_RGB_16BPC_FULL];
|
|
} else {
|
|
retValue = FMTMatrix[FMT_COEFF_TYPE_REC709_YUV_12BPC_LTD_TO_RGB_16BPC_FULL];
|
|
}
|
|
} else {
|
|
// Unsupported bit depth, fail silently by defaulting to identity.
|
|
retValue = FMTMatrix[FMT_COEFF_TYPE_IDENTITY];
|
|
}
|
|
} else {
|
|
// All inputs with RGB colorspace receive an identity FMT.
|
|
retValue = FMTMatrix[FMT_COEFF_TYPE_IDENTITY];
|
|
}
|
|
|
|
return retValue;
|
|
}
|
|
|
|
static void EvoSetFMTMatrixC5(
|
|
NVEvoChannelPtr pChannel, const enum NvKmsSurfaceMemoryFormat format,
|
|
const NVFlipChannelEvoHwState *pHwState)
|
|
{
|
|
const NvU32 *matrix = EvoGetFMTMatrixC5(format, pHwState);
|
|
NvU32 method = NVC57E_SET_FMT_COEFFICIENT_C00;
|
|
int i;
|
|
|
|
for (i = 0; i < 12; i++) {
|
|
nvDmaSetStartEvoMethod(pChannel, method, 1);
|
|
nvDmaSetEvoMethodData(pChannel, matrix[i]);
|
|
|
|
method += 4;
|
|
}
|
|
}
|
|
|
|
void nvEvoInitDefaultLutC5(NVDevEvoPtr pDevEvo)
|
|
{
|
|
NVLutSurfaceEvoPtr pLut = pDevEvo->lut.defaultLut;
|
|
NvU16 sd;
|
|
|
|
nvAssert(pLut);
|
|
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
NvU32 lutSize;
|
|
NvBool isLutModeVss;
|
|
NVEvoLutDataRec *pData = pLut->subDeviceAddress[sd];
|
|
|
|
EvoSetupIdentityBaseLutC5(pData,
|
|
&pDevEvo->lut.defaultBaseLUTState[sd],
|
|
&lutSize, &isLutModeVss);
|
|
|
|
EvoSetupIdentityOutputLutC5(pData,
|
|
&pDevEvo->lut.defaultOutputLUTState[sd],
|
|
&lutSize, &isLutModeVss);
|
|
}
|
|
}
|
|
|
|
static void EvoInitWindowMapping3(NVDevEvoPtr pDevEvo,
|
|
NVEvoModesetUpdateState *pModesetUpdateState)
|
|
{
|
|
NVEvoUpdateState *updateState = &pModesetUpdateState->updateState;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 win, sd;
|
|
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
/* Bind each window to a head. On GV100, there is a fixed mapping. */
|
|
for (win = 0; win < pDevEvo->numWindows; win++) {
|
|
NvU32 head = pDevEvo->headForWindow[win];
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_WINDOW_SET_CONTROL(win), 1);
|
|
if ((head == NV_INVALID_HEAD) || (head >= pDevEvo->numHeads)) {
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _WINDOW_SET_CONTROL, _OWNER,
|
|
NVC37D_WINDOW_SET_CONTROL_OWNER_NONE));
|
|
} else {
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C37D, _WINDOW_SET_CONTROL, _OWNER, head));
|
|
}
|
|
}
|
|
|
|
pModesetUpdateState->windowMappingChanged = FALSE;
|
|
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
void *pCoreDma = pDevEvo->pSubDevices[sd]->pCoreDma;
|
|
/*
|
|
* Short timeout (100ms) because we don't expect display to be very
|
|
* busy at this point (it should at most be processing methods from
|
|
* InitChannel()).
|
|
*/
|
|
const NvU32 timeout = 100000;
|
|
NvU64 startTime = 0;
|
|
|
|
if (!((nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)))) {
|
|
continue;
|
|
}
|
|
|
|
/* This core channel must be idle before reading state cache */
|
|
do {
|
|
NvBool isIdle = NV_FALSE;
|
|
if (!nvEvoIsChannelIdleC3(pDevEvo, pChannel, sd, &isIdle)) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR, "nvEvoIsChannelIdleC3() failed!");
|
|
}
|
|
if (isIdle) {
|
|
break;
|
|
}
|
|
if (nvExceedsTimeoutUSec(pDevEvo, &startTime, timeout)) {
|
|
nvEvoLogDev(pDevEvo, EVO_LOG_ERROR,
|
|
"Timed out waiting for core channel idle.");
|
|
break;
|
|
}
|
|
} while (TRUE);
|
|
|
|
for (win = 0; win < pDevEvo->numWindows; win++) {
|
|
NvU32 data = nvDmaLoadPioMethod(pCoreDma, NVC37D_WINDOW_SET_CONTROL(win));
|
|
|
|
if (DRF_VAL(C37D,
|
|
_WINDOW_SET_CONTROL, _OWNER, data) !=
|
|
pDevEvo->headForWindow[win]) {
|
|
|
|
pModesetUpdateState->windowMappingChanged = TRUE;
|
|
|
|
nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
|
|
nvDisableCoreInterlockUpdateState(pDevEvo,
|
|
updateState,
|
|
pDevEvo->window[win]);
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void EvoInitWindowMappingC3(const NVDispEvoRec *pDispEvo,
|
|
NVEvoModesetUpdateState *pModesetUpdateState)
|
|
{
|
|
NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoUpdateState *updateState = &pModesetUpdateState->updateState;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 win;
|
|
|
|
nvPushEvoSubDevMaskDisp(pDispEvo);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
EvoInitWindowMapping3(pDevEvo,
|
|
pModesetUpdateState);
|
|
|
|
// Set window usage bounds
|
|
for (win = 0; win < pDevEvo->numWindows; win++) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_WINDOW_SET_WINDOW_USAGE_BOUNDS(win), 1);
|
|
/* XXXnvdisplay: window scaling */
|
|
nvDmaSetEvoMethodData(pChannel, NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C3);
|
|
}
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
|
|
void nvEvoInitWindowMappingC5(const NVDispEvoRec *pDispEvo,
|
|
NVEvoModesetUpdateState *pModesetUpdateState)
|
|
{
|
|
NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoUpdateState *updateState = &pModesetUpdateState->updateState;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 win;
|
|
|
|
nvPushEvoSubDevMaskDisp(pDispEvo);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
EvoInitWindowMapping3(pDevEvo,
|
|
pModesetUpdateState);
|
|
|
|
// Set window usage bounds
|
|
for (win = 0; win < pDevEvo->numWindows; win++) {
|
|
NvU32 bounds = NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C5;
|
|
|
|
bounds |=
|
|
DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_2) |
|
|
DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_WINDOW_SET_WINDOW_USAGE_BOUNDS(win), 1);
|
|
nvDmaSetEvoMethodData(pChannel, bounds);
|
|
}
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
|
|
NvBool nvComputeMinFrameIdle(
|
|
const NVHwModeTimingsEvo *pTimings,
|
|
NvU16 *pLeadingRasterLines,
|
|
NvU16 *pTrailingRasterLines)
|
|
{
|
|
const NVHwModeViewPortEvo *pViewPort = &pTimings->viewPort;
|
|
|
|
/*
|
|
* leadingRasterLines defines the number of lines between the start of the
|
|
* frame (vsync) and the start of the active region. This includes Vsync,
|
|
* Vertical Back Porch, and the top part of the overscan border. The
|
|
* minimum value is 2 because vsync and VBP must be at least 1 line each.
|
|
*
|
|
* trailingRasterLines defines the number of lines between the end of the
|
|
* active region and the end of the frame. This includes the bottom part
|
|
* of the overscan border and the Vertical Front Porch.
|
|
*/
|
|
const NvU32 activeHeight = (pTimings->rasterBlankStart.y -
|
|
pTimings->rasterBlankEnd.y);
|
|
/* This is how it's done in dispClassNVD20CoreUpdateErrorChecks_hls.c */
|
|
const NvU32 overscan = (activeHeight / 2) - (pViewPort->out.height / 2);
|
|
|
|
/*
|
|
* The +1 is justified by this comment in the error check:
|
|
*
|
|
* If the value is 1, that means there are 2 lines of vblank (lines 0 and
|
|
* 1) before active. That is why the uLeadingBorder equation needs +1;
|
|
*/
|
|
const NvU32 leadingRasterLines =
|
|
pTimings->rasterBlankEnd.y + overscan + pViewPort->out.yAdjust + 1;
|
|
const NvU32 trailingRasterLines =
|
|
pTimings->rasterSize.y - (leadingRasterLines + pViewPort->out.height);
|
|
|
|
/* nvdClass_01.mfs says: "The minimum value is 2 because vsync and VBP must
|
|
* be at least 1 line each." */
|
|
if (leadingRasterLines < 2) {
|
|
return FALSE;
|
|
}
|
|
|
|
*pLeadingRasterLines = leadingRasterLines;
|
|
*pTrailingRasterLines = trailingRasterLines;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void EvoSetRasterParams3(NVDevEvoPtr pDevEvo, int head,
|
|
const NVHwModeTimingsEvo *pTimings,
|
|
const NVEvoColorRec *pOverscanColor,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
/* XXXnvdisplay: Convert these for YCbCr, as necessary */
|
|
NvU32 overscanColor =
|
|
DRF_NUM(C37D, _HEAD_SET_OVERSCAN_COLOR, _RED_CR, pOverscanColor->red) |
|
|
DRF_NUM(C37D, _HEAD_SET_OVERSCAN_COLOR, _GREEN_Y, pOverscanColor->green) |
|
|
DRF_NUM(C37D, _HEAD_SET_OVERSCAN_COLOR, _BLUE_CB, pOverscanColor->blue);
|
|
NvU32 hdmiStereoCtrl;
|
|
NvU16 minFrameIdleLeadingRasterLines, minFrameIdleTrailingRasterLines;
|
|
NvBool ret;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
// XXX[AGP]: These methods are sequential and could use an incrementing
|
|
// method, but it's not clear if there's a bug in EVO that causes corruption
|
|
// sometimes. Play it safe and send methods with count=1.
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_OVERSCAN_COLOR(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, overscanColor);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_RASTER_SIZE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_RASTER_SIZE, _WIDTH, pTimings->rasterSize.x) |
|
|
DRF_NUM(C37D, _HEAD_SET_RASTER_SIZE, _HEIGHT, pTimings->rasterSize.y));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_RASTER_SYNC_END(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_RASTER_SYNC_END, _X, pTimings->rasterSyncEnd.x) |
|
|
DRF_NUM(C37D, _HEAD_SET_RASTER_SYNC_END, _Y, pTimings->rasterSyncEnd.y));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_RASTER_BLANK_END(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_RASTER_BLANK_END, _X, pTimings->rasterBlankEnd.x) |
|
|
DRF_NUM(C37D, _HEAD_SET_RASTER_BLANK_END, _Y, pTimings->rasterBlankEnd.y));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_RASTER_BLANK_START(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_RASTER_BLANK_START, _X, pTimings->rasterBlankStart.x) |
|
|
DRF_NUM(C37D, _HEAD_SET_RASTER_BLANK_START, _Y, pTimings->rasterBlankStart.y));
|
|
|
|
ret = nvComputeMinFrameIdle(pTimings,
|
|
&minFrameIdleLeadingRasterLines,
|
|
&minFrameIdleTrailingRasterLines);
|
|
if (!ret) {
|
|
/* This should have been ensured by IMP in AssignPerHeadImpParams. */
|
|
nvAssert(ret);
|
|
/* In case a mode validation override was used to skip IMP, program the
|
|
* default values. This may still cause a hardware exception. */
|
|
minFrameIdleLeadingRasterLines = 2;
|
|
minFrameIdleTrailingRasterLines = 1;
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_MIN_FRAME_IDLE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_MIN_FRAME_IDLE, _LEADING_RASTER_LINES,
|
|
minFrameIdleLeadingRasterLines) |
|
|
DRF_NUM(C37D, _HEAD_SET_MIN_FRAME_IDLE, _TRAILING_RASTER_LINES,
|
|
minFrameIdleTrailingRasterLines));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PIXEL_CLOCK_FREQUENCY(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_PIXEL_CLOCK_FREQUENCY, _HERTZ,
|
|
pTimings->pixelClock * 1000) |
|
|
DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_FREQUENCY, _ADJ1000DIV1001,_FALSE));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PIXEL_CLOCK_CONFIGURATION(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_CONFIGURATION, _NOT_DRIVER, _FALSE) |
|
|
DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_CONFIGURATION, _HOPPING, _DISABLE) |
|
|
DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_CONFIGURATION, _HOPPING_MODE, _VBLANK));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, _HERTZ,
|
|
pTimings->pixelClock * 1000) |
|
|
DRF_DEF(C37D, _HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, _ADJ1000DIV1001,_FALSE));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC37D_HEAD_SET_FRAME_PACKED_VACTIVE_COLOR(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_FRAME_PACKED_VACTIVE_COLOR, _RED_CR, 0) |
|
|
#if defined(DEBUG)
|
|
DRF_NUM(C37D, _HEAD_SET_FRAME_PACKED_VACTIVE_COLOR, _GREEN_Y, 512) |
|
|
#else
|
|
DRF_NUM(C37D, _HEAD_SET_FRAME_PACKED_VACTIVE_COLOR, _GREEN_Y, 0) |
|
|
#endif
|
|
DRF_NUM(C37D, _HEAD_SET_FRAME_PACKED_VACTIVE_COLOR, _BLUE_CB, 0));
|
|
|
|
hdmiStereoCtrl = DRF_NUM(C37D, _HEAD_SET_HDMI_CTRL, _HDMI_VIC, 0);
|
|
if (pTimings->hdmi3D) {
|
|
hdmiStereoCtrl =
|
|
FLD_SET_DRF(C37D, _HEAD_SET_HDMI_CTRL, _VIDEO_FORMAT, _STEREO3D, hdmiStereoCtrl);
|
|
} else {
|
|
hdmiStereoCtrl =
|
|
FLD_SET_DRF(C37D, _HEAD_SET_HDMI_CTRL, _VIDEO_FORMAT, _NORMAL, hdmiStereoCtrl);
|
|
}
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC37D_HEAD_SET_HDMI_CTRL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, hdmiStereoCtrl);
|
|
}
|
|
|
|
static void EvoSetRasterParamsC3(NVDevEvoPtr pDevEvo, int head,
|
|
const NVHwModeTimingsEvo *pTimings,
|
|
const NvU8 tilePosition,
|
|
const NVDscInfoEvoRec *pDscInfo,
|
|
const NVEvoColorRec *pOverscanColor,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
nvAssert(tilePosition == 0);
|
|
EvoSetRasterParams3(pDevEvo, head, pTimings, pOverscanColor, updateState);
|
|
}
|
|
|
|
static void EvoSetRasterParams5(NVDevEvoPtr pDevEvo, int head,
|
|
const NVHwModeTimingsEvo *pTimings,
|
|
const NvU8 tilePosition,
|
|
const NVEvoColorRec *pOverscanColor,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
EvoSetRasterParams3(pDevEvo, head, pTimings, pOverscanColor, updateState);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_TILE_POSITION(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57D, _HEAD_SET_TILE_POSITION, _X, tilePosition) |
|
|
DRF_NUM(C57D, _HEAD_SET_TILE_POSITION, _Y, 0));
|
|
}
|
|
|
|
static void EvoSetRasterParamsC5(NVDevEvoPtr pDevEvo, int head,
|
|
const NVHwModeTimingsEvo *pTimings,
|
|
const NvU8 tilePosition,
|
|
const NVDscInfoEvoRec *pDscInfo,
|
|
const NVEvoColorRec *pOverscanColor,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
nvAssert(pDscInfo->type != NV_DSC_INFO_EVO_TYPE_HDMI);
|
|
EvoSetRasterParams5(pDevEvo, head, pTimings, tilePosition, pOverscanColor,
|
|
updateState);
|
|
}
|
|
|
|
static NvU32 GetHdmiDscHBlankPixelTarget(const NVHwModeTimingsEvo *pTimings,
|
|
const NVDscInfoEvoRec *pDscInfo)
|
|
{
|
|
nvAssert((pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) ||
|
|
(pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_SINGLE));
|
|
|
|
const NvU32 hblankMin =
|
|
(pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) ?
|
|
((pDscInfo->hdmi.hblankMin + 1) / 2) :
|
|
pDscInfo->hdmi.hblankMin;
|
|
|
|
NvU32 hBlankPixelTarget =
|
|
NV_UNSIGNED_DIV_CEIL((pTimings->rasterSize.x *
|
|
pDscInfo->hdmi.dscTBlankToTTotalRatioX1k),
|
|
1000);
|
|
|
|
hBlankPixelTarget = NV_MAX(hblankMin, hBlankPixelTarget);
|
|
|
|
if (pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) {
|
|
hBlankPixelTarget += (hBlankPixelTarget % 2);
|
|
}
|
|
|
|
return hBlankPixelTarget;
|
|
}
|
|
|
|
static void EvoSetRasterParamsC6(NVDevEvoPtr pDevEvo, int head,
|
|
const NVHwModeTimingsEvo *pTimings,
|
|
const NvU8 tilePosition,
|
|
const NVDscInfoEvoRec *pDscInfo,
|
|
const NVEvoColorRec *pOverscanColor,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NvU32 rasterHBlankDelay;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
EvoSetRasterParams5(pDevEvo, head, pTimings, tilePosition, pOverscanColor,
|
|
updateState);
|
|
|
|
if (pDscInfo->type == NV_DSC_INFO_EVO_TYPE_HDMI) {
|
|
const NvU32 hBlank = pTimings->rasterSize.x -
|
|
pTimings->rasterBlankStart.x + pTimings->rasterBlankEnd.x;
|
|
const NvU32 hBlankPixelTarget =
|
|
GetHdmiDscHBlankPixelTarget(pTimings, pDscInfo);
|
|
|
|
const NvU32 headSetRasterHBlankDelayStart =
|
|
pTimings->rasterSize.x - pTimings->rasterBlankStart.x - 2;
|
|
const NvU32 headSetRasterHBlankDelayEnd =
|
|
hBlankPixelTarget - hBlank + headSetRasterHBlankDelayStart;
|
|
|
|
rasterHBlankDelay =
|
|
DRF_NUM(C67D, _HEAD_SET_RASTER_HBLANK_DELAY, _BLANK_START,
|
|
headSetRasterHBlankDelayStart);
|
|
rasterHBlankDelay |=
|
|
DRF_NUM(C67D, _HEAD_SET_RASTER_HBLANK_DELAY, _BLANK_END,
|
|
headSetRasterHBlankDelayEnd);
|
|
} else {
|
|
rasterHBlankDelay = 0;
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_RASTER_HBLANK_DELAY(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, rasterHBlankDelay);
|
|
}
|
|
|
|
static void EvoSetProcAmpC3(NVDispEvoPtr pDispEvo, const NvU32 head,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
|
|
NvU32 dynRange;
|
|
|
|
/* These methods should only apply to a single pDpyEvo */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
// These NVT defines match the HEAD_SET_PROCAMP ones.
|
|
ct_assert(NVT_COLORIMETRY_RGB == NVC37D_HEAD_SET_PROCAMP_COLOR_SPACE_RGB);
|
|
ct_assert(NVT_COLORIMETRY_YUV_601 == NVC37D_HEAD_SET_PROCAMP_COLOR_SPACE_YUV_601);
|
|
ct_assert(NVT_COLORIMETRY_YUV_709 == NVC37D_HEAD_SET_PROCAMP_COLOR_SPACE_YUV_709);
|
|
/* XXXnvdisplay add REC2020 */
|
|
ct_assert(NVT_COLOR_RANGE_FULL == NVC37D_HEAD_SET_PROCAMP_RANGE_COMPRESSION_DISABLE);
|
|
ct_assert(NVT_COLOR_RANGE_LIMITED == NVC37D_HEAD_SET_PROCAMP_RANGE_COMPRESSION_ENABLE);
|
|
|
|
if (pHeadState->procAmp.colorRange == NVT_COLOR_RANGE_FULL) {
|
|
dynRange = DRF_DEF(C37D, _HEAD_SET_PROCAMP, _DYNAMIC_RANGE, _VESA);
|
|
} else {
|
|
nvAssert(pHeadState->procAmp.colorRange == NVT_COLOR_RANGE_LIMITED);
|
|
dynRange = DRF_DEF(C37D, _HEAD_SET_PROCAMP, _DYNAMIC_RANGE, _CEA);
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PROCAMP(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_PROCAMP, _COLOR_SPACE,
|
|
pHeadState->procAmp.colorimetry) |
|
|
DRF_DEF(C37D, _HEAD_SET_PROCAMP, _CHROMA_LPF, _DISABLE) |
|
|
DRF_NUM(C37D, _HEAD_SET_PROCAMP, _SAT_COS,
|
|
pHeadState->procAmp.satCos) |
|
|
DRF_NUM(C37D, _HEAD_SET_PROCAMP, _SAT_SINE, 0) |
|
|
dynRange |
|
|
DRF_NUM(C37D, _HEAD_SET_PROCAMP, _RANGE_COMPRESSION,
|
|
pHeadState->procAmp.colorRange) |
|
|
DRF_DEF(C37D, _HEAD_SET_PROCAMP, _BLACK_LEVEL, _GRAPHICS));
|
|
}
|
|
|
|
static const struct NvKmsCscMatrix RGBToFullRangeYCbCrRec709Matrix = {{
|
|
{ 0x8000, 0x1f8bbc, 0x1ff444, 0x8000 },
|
|
{ 0x366c, 0xb718, 0x127c, 0 },
|
|
{ 0x1fe2ac, 0x1f9d54, 0x8000, 0x8000 },
|
|
}};
|
|
static const struct NvKmsCscMatrix RGBToFullRangeYCbCrRec601Matrix = {{
|
|
{ 0x8000, 0x1f94d0, 0x1feb30, 0x8000 },
|
|
{ 0x4c8c, 0x9644, 0x1d30, 0 },
|
|
{ 0x1fd4cc, 0x1fab34, 0x8000, 0x8000 },
|
|
}};
|
|
static const struct NvKmsCscMatrix RGBToLimitedRangeYCbCrRec2020Matrix = {{
|
|
{ 0x7000, 0x1f9900, 0x1ff700, 0x8000 },
|
|
{ 0x3988, 0x947c, 0xcfc, 0x1000 },
|
|
{ 0x1fe0b8, 0x1faf44, 0x7000, 0x8000 },
|
|
}};
|
|
static const struct NvKmsCscMatrix RGBToLimitedRangeYCbCrRec709Matrix = {{
|
|
{ 0x7000, 0x1f9a44, 0x1ff5bc, 0x8000 },
|
|
{ 0x2e90, 0x9ca4, 0xfd0, 0x1000 },
|
|
{ 0x1fe654, 0x1fa9a8, 0x7000, 0x8000 },
|
|
}};
|
|
static const struct NvKmsCscMatrix RGBToLimitedRangeYCbCrRec601Matrix = {{
|
|
{ 0x7000, 0x1fa234, 0x1fedc8, 0x8000 },
|
|
{ 0x417c, 0x8090, 0x18f8, 0x1000 },
|
|
{ 0x1fda34, 0x1fb5cc, 0x7000, 0x8000 },
|
|
}};
|
|
static const struct NvKmsCscMatrix RGBToLimitedRangeRGB = {{
|
|
{ 0xdb04, 0, 0, 0x1000 },
|
|
{ 0, 0xdb04, 0, 0x1000 },
|
|
{ 0, 0, 0xdb04, 0x1000 },
|
|
}};
|
|
|
|
|
|
/*!
|
|
* Return the appropriate OCSC1 matrix for the requested color range and
|
|
* colorimetry, or NULL if the OCSC1 should be disabled.
|
|
*/
|
|
const struct NvKmsCscMatrix* nvEvoGetOCsc1MatrixC5(const NVDispHeadStateEvoRec *pHeadState)
|
|
{
|
|
if (pHeadState->procAmp.colorRange == NVT_COLOR_RANGE_FULL) {
|
|
switch (pHeadState->procAmp.colorimetry) {
|
|
case NVT_COLORIMETRY_BT2020RGB:
|
|
// fall through
|
|
case NVT_COLORIMETRY_RGB:
|
|
// No OCSC1 needed.
|
|
return NULL;
|
|
case NVT_COLORIMETRY_YUV_601:
|
|
return &RGBToFullRangeYCbCrRec601Matrix;
|
|
case NVT_COLORIMETRY_YUV_709:
|
|
return &RGBToFullRangeYCbCrRec709Matrix;
|
|
default:
|
|
nvAssert(!"Unexpected colorimetry");
|
|
return NULL;
|
|
}
|
|
} else {
|
|
switch (pHeadState->procAmp.colorimetry) {
|
|
case NVT_COLORIMETRY_BT2020RGB:
|
|
// fall through
|
|
case NVT_COLORIMETRY_RGB:
|
|
return &RGBToLimitedRangeRGB;
|
|
case NVT_COLORIMETRY_YUV_601:
|
|
return &RGBToLimitedRangeYCbCrRec601Matrix;
|
|
case NVT_COLORIMETRY_YUV_709:
|
|
return &RGBToLimitedRangeYCbCrRec709Matrix;
|
|
case NVT_COLORIMETRY_BT2020YCC:
|
|
return &RGBToLimitedRangeYCbCrRec2020Matrix;
|
|
default:
|
|
nvAssert(!"Unexpected colorimetry");
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Return the output clamping ranges for the requested color range and
|
|
* colorimetry.
|
|
*/
|
|
struct EvoClampRangeC5
|
|
nvEvoGetOCsc1ClampRange(const NVDispHeadStateEvoRec *pHeadState)
|
|
{
|
|
if (pHeadState->procAmp.colorRange == NVT_COLOR_RANGE_FULL) {
|
|
return (struct EvoClampRangeC5) {
|
|
.green = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _LOW, 0x0) |
|
|
DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _HIGH, 0xFFF),
|
|
.red_blue = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _LOW, 0x0) |
|
|
DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _HIGH, 0xFFF),
|
|
};
|
|
} else {
|
|
switch (pHeadState->procAmp.colorimetry) {
|
|
default:
|
|
nvAssert(!"Unexpected colorimetry");
|
|
// fall through
|
|
case NVT_COLORIMETRY_BT2020RGB:
|
|
// fall through
|
|
case NVT_COLORIMETRY_RGB:
|
|
return (struct EvoClampRangeC5) {
|
|
.green = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _LOW, 0x100) |
|
|
DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _HIGH, 0xEB0),
|
|
.red_blue = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _LOW, 0x100) |
|
|
DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _HIGH, 0xEB0),
|
|
};
|
|
case NVT_COLORIMETRY_YUV_601:
|
|
case NVT_COLORIMETRY_YUV_709:
|
|
case NVT_COLORIMETRY_BT2020YCC:
|
|
return (struct EvoClampRangeC5) {
|
|
.green = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _LOW, 0x100) |
|
|
DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_GREEN, _HIGH, 0xEB0),
|
|
.red_blue = DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _LOW, 0x100) |
|
|
DRF_NUM(C57D, _HEAD_SET_CLAMP_RANGE_RED_BLUE, _HIGH, 0xF00),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 1.402 1.0 0.0
|
|
* -0.714136 1.0 -0.344136
|
|
* 0.0 1.0 1.772
|
|
*/
|
|
static const struct NvKmsMatrix CrYCb601toRGBMatrix = { {
|
|
{ 0x3fb374bc, 0x3f800000, 0x00000000 },
|
|
{ 0xbf36d19e, 0x3f800000, 0xbeb03298 },
|
|
{ 0x00000000, 0x3f800000, 0x3fe2d0e5 }
|
|
} };
|
|
|
|
/*
|
|
* 1.5748 1.0 0.0
|
|
* -0.468124 1.0 -0.187324
|
|
* 0.0 1.0 1.8556
|
|
*/
|
|
static const struct NvKmsMatrix CrYCb709toRGBMatrix = { {
|
|
{ 0x3fc9930c, 0x3f800000, 0x00000000 },
|
|
{ 0xbeefadf3, 0x3f800000, 0xbe3fd1dd },
|
|
{ 0x00000000, 0x3f800000, 0x3fed844d }
|
|
} };
|
|
|
|
/*
|
|
* 0.5 -0.418688 -0.081312
|
|
* 0.299 0.587 0.114
|
|
* -0.168736 -0.331264 0.5
|
|
*/
|
|
static const struct NvKmsMatrix RGBtoCrYCb601Matrix = { {
|
|
{ 0x3f000000, 0xbed65e46, 0xbda686e8 },
|
|
{ 0x3e991687, 0x3f1645a2, 0x3de978d5 },
|
|
{ 0xbe2cc921, 0xbea99b6f, 0x3f000000 }
|
|
} };
|
|
|
|
/*
|
|
* 0.5 -0.45415 -0.04585
|
|
* 0.21260 0.71520 0.07220
|
|
* -0.11457 -0.38543 0.5
|
|
*/
|
|
static const struct NvKmsMatrix RGBtoCrYCb709Matrix = { {
|
|
{ 0x3f000000, 0xbee88659, 0xbd3bcd36 },
|
|
{ 0x3e59b3d0, 0x3f371759, 0x3d93dd98 },
|
|
{ 0xbdeaa3ad, 0xbec55715, 0x3f000000 }
|
|
} };
|
|
|
|
static void EvoSetOCsc1C5(NVDispEvoPtr pDispEvo, const NvU32 head)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
|
|
const struct NvKmsCscMatrix *matrix = nvEvoGetOCsc1MatrixC5(pHeadState);
|
|
struct EvoClampRangeC5 clamp = nvEvoGetOCsc1ClampRange(pHeadState);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_CLAMP_RANGE_GREEN(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, clamp.green);
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_CLAMP_RANGE_RED_BLUE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, clamp.red_blue);
|
|
|
|
if (matrix) {
|
|
int x, y;
|
|
NvU32 method = NVC57D_HEAD_SET_OCSC1COEFFICIENT_C00(head);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC1CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57D, _HEAD_SET_OCSC1CONTROL, _ENABLE, _ENABLE));
|
|
|
|
for (y = 0; y < 3; y++) {
|
|
for (x = 0; x < 4; x++) {
|
|
nvDmaSetStartEvoMethod(pChannel, method, 1);
|
|
nvDmaSetEvoMethodData(pChannel, matrix->m[y][x]);
|
|
|
|
method += 4;
|
|
}
|
|
}
|
|
} else {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC1CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57D, _HEAD_SET_OCSC1CONTROL, _ENABLE, _DISABLE));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Sets up the OCSC0 matrix coefficients, used to perform saturation
|
|
* adjustment.
|
|
*
|
|
* The pipeline operates in FP16 RGB, however this adjustment must be
|
|
* performed in CrYCb. Therefore, we multiply the saturation
|
|
* adjustment matrix by the appropriate color space conversion
|
|
* matrix. The specific color space used depends on the colorimetry of
|
|
* the final output. Then we multiply by its inverse to convert back
|
|
* to RGB. Finally, we convert the coefficients to S5.14 fixed point
|
|
* format.
|
|
*/
|
|
void nvEvo3PickOCsc0(NVDispEvoPtr pDispEvo, const NvU32 head, struct NvKms3x4MatrixF32 *ocsc0MatrixOutput)
|
|
{
|
|
const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
|
|
|
|
const float32_t zeroF32 = NvU32viewAsF32(NV_FLOAT_ZERO);
|
|
const float32_t oneF32 = NvU32viewAsF32(NV_FLOAT_ONE);
|
|
const float32_t inv2048F32 = f32_div(NvU32viewAsF32(NV_FLOAT_HALF),
|
|
NvU32viewAsF32(NV_FLOAT_1024));
|
|
/* divide satCos by the default setting of 1024 */
|
|
const float32_t satCos = f32_div(i32_to_f32(pHeadState->procAmp.satCos),
|
|
NvU32viewAsF32(NV_FLOAT_1024));
|
|
const struct NvKmsMatrixF32 satHueMatrix = { {
|
|
{ satCos, zeroF32, zeroF32 },
|
|
{ zeroF32, oneF32, zeroF32 },
|
|
{ zeroF32, zeroF32, satCos }
|
|
} };
|
|
struct NvKms3x4MatrixF32 ocsc0Matrix = { {
|
|
{ oneF32, zeroF32, zeroF32, zeroF32 },
|
|
{ zeroF32, oneF32, zeroF32, zeroF32 },
|
|
{ zeroF32, zeroF32, oneF32, zeroF32 }
|
|
} };
|
|
|
|
struct NvKmsMatrixF32 CrYCbtoRGBMatrix;
|
|
struct NvKmsMatrixF32 RGBtoCrYCbMatrix;
|
|
|
|
|
|
switch (pHeadState->procAmp.colorimetry) {
|
|
default:
|
|
nvAssert(!"Unexpected colorimetry");
|
|
/* fallthrough */
|
|
case NVT_COLORIMETRY_RGB:
|
|
case NVT_COLORIMETRY_BT2020RGB:
|
|
/* fallthrough; for RGB output, perform saturation adjustment in YUV709 */
|
|
case NVT_COLORIMETRY_YUV_709:
|
|
CrYCbtoRGBMatrix = NvKmsMatrixToNvKmsMatrixF32(CrYCb709toRGBMatrix);
|
|
RGBtoCrYCbMatrix = NvKmsMatrixToNvKmsMatrixF32(RGBtoCrYCb709Matrix);
|
|
break;
|
|
case NVT_COLORIMETRY_YUV_601:
|
|
CrYCbtoRGBMatrix = NvKmsMatrixToNvKmsMatrixF32(CrYCb601toRGBMatrix);
|
|
RGBtoCrYCbMatrix = NvKmsMatrixToNvKmsMatrixF32(RGBtoCrYCb601Matrix);
|
|
break;
|
|
}
|
|
|
|
ocsc0Matrix = nvMultiply3x4Matrix(&RGBtoCrYCbMatrix, &ocsc0Matrix);
|
|
ocsc0Matrix = nvMultiply3x4Matrix(&satHueMatrix, &ocsc0Matrix);
|
|
ocsc0Matrix = nvMultiply3x4Matrix(&CrYCbtoRGBMatrix, &ocsc0Matrix);
|
|
|
|
if (nvkms_output_rounding_fix()) {
|
|
/*
|
|
* XXX Only apply WAR for bug 2267663 for linear output TFs. Non-linear
|
|
* TFs could amplify the 1/2048 factor to the point of being
|
|
* perceptible.
|
|
*/
|
|
if (pHeadState->tf == NVKMS_OUTPUT_TF_NONE) {
|
|
ocsc0Matrix.m[0][3] = f32_add(ocsc0Matrix.m[0][3], inv2048F32);
|
|
ocsc0Matrix.m[1][3] = f32_add(ocsc0Matrix.m[1][3], inv2048F32);
|
|
ocsc0Matrix.m[2][3] = f32_add(ocsc0Matrix.m[2][3], inv2048F32);
|
|
}
|
|
}
|
|
|
|
*ocsc0MatrixOutput = ocsc0Matrix;
|
|
}
|
|
|
|
/*
|
|
* Programs the OCSC0 matrix coefficients, used to perform saturation
|
|
* adjustment.
|
|
*
|
|
* The OCSC0 matrix will be enabled later in EvoSetLUTContextDmaC5 if
|
|
* and only if we also enable the OLUT as required by the
|
|
* specification.
|
|
*/
|
|
static void EvoSetOCsc0C5(NVDispEvoPtr pDispEvo, const NvU32 head)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
struct NvKms3x4MatrixF32 ocsc0Matrix;
|
|
|
|
nvEvo3PickOCsc0(pDispEvo, head, &ocsc0Matrix);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC0COEFFICIENT_C00(head), 12);
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C00, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[0][0])));
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C01, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[0][1])));
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C02, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[0][2])));
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C03, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[0][3])));
|
|
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C10, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[1][0])));
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C11, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[1][1])));
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C12, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[1][2])));
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C13, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[1][3])));
|
|
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C20, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[2][0])));
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C21, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[2][1])));
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C22, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[2][2])));
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57D, _HEAD_SET_OCSC0COEFFICIENT_C23, _VALUE, nvCscCoefConvertS514(ocsc0Matrix.m[2][3])));
|
|
}
|
|
|
|
static void EvoSetProcAmpC5(NVDispEvoPtr pDispEvo, const NvU32 head,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
|
|
NvU32 dynRange, chromaLpf, chromaDownV;
|
|
NvU32 colorimetry;
|
|
|
|
NVT_COLORIMETRY nvtColorimetry = pHeadState->procAmp.colorimetry;
|
|
NVT_COLOR_RANGE nvtColorRange = pHeadState->procAmp.colorRange;
|
|
|
|
/* These methods should only apply to a single pDpyEvo */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
switch (nvtColorimetry) {
|
|
default:
|
|
nvAssert(!"Unrecognized colorimetry");
|
|
// fall through
|
|
case NVT_COLORIMETRY_BT2020RGB:
|
|
// fall through
|
|
case NVT_COLORIMETRY_RGB:
|
|
colorimetry = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _COLOR_SPACE, _RGB);
|
|
break;
|
|
case NVT_COLORIMETRY_YUV_601:
|
|
colorimetry = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _COLOR_SPACE, _YUV_601);
|
|
break;
|
|
case NVT_COLORIMETRY_YUV_709:
|
|
colorimetry = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _COLOR_SPACE, _YUV_709);
|
|
break;
|
|
case NVT_COLORIMETRY_BT2020YCC:
|
|
colorimetry = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _COLOR_SPACE, _YUV_2020);
|
|
break;
|
|
}
|
|
|
|
if (nvtColorRange == NVT_COLOR_RANGE_FULL) {
|
|
dynRange = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _DYNAMIC_RANGE, _VESA);
|
|
} else {
|
|
nvAssert(nvtColorRange == NVT_COLOR_RANGE_LIMITED);
|
|
dynRange = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _DYNAMIC_RANGE, _CEA);
|
|
}
|
|
|
|
/*
|
|
* NVC67D_HEAD_SET_PROCAMP_CHROMA_DOWN_V is only defined in NVC67D, but
|
|
* it is an unused bit in NVC57D_HEAD_SET_PROCAMP, and YUV420 should only
|
|
* be set on >=nvdisplay 4.0, so it's okay to set it here.
|
|
*/
|
|
if (pHeadState->procAmp.colorFormat == NVT_COLOR_FORMAT_YCbCr420) {
|
|
chromaLpf = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _CHROMA_LPF, _ENABLE);
|
|
chromaDownV = DRF_DEF(C67D, _HEAD_SET_PROCAMP, _CHROMA_DOWN_V, _ENABLE);
|
|
} else {
|
|
chromaLpf = DRF_DEF(C57D, _HEAD_SET_PROCAMP, _CHROMA_LPF, _DISABLE);
|
|
chromaDownV = DRF_DEF(C67D, _HEAD_SET_PROCAMP, _CHROMA_DOWN_V, _DISABLE);
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_PROCAMP(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
colorimetry | dynRange | chromaLpf | chromaDownV);
|
|
|
|
EvoSetOCsc0C5(pDispEvo, head);
|
|
EvoSetOCsc1C5(pDispEvo, head);
|
|
}
|
|
|
|
/*
|
|
* With nvdisplay, external fliplock pins are controlled via a headless
|
|
* SetControl method, unlike previous EVO display implementations which
|
|
* specified this information in the per-head HeadSetControl method. This
|
|
* function loops over all of the core nvkms HeadControl data structures to
|
|
* determine which pins should be enabled in the SetControl method. It should
|
|
* be called any time the HeadControl data structures are updated.
|
|
*/
|
|
void nvEvoSetControlC3(NVDevEvoPtr pDevEvo, int sd)
|
|
{
|
|
NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[sd];
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 data = 0;
|
|
NvU32 head;
|
|
|
|
for (head = 0; head < pDevEvo->numHeads; head++) {
|
|
NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
|
|
if (pHC->flipLock && !NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->flipLockPin)) {
|
|
NvU32 pin = pHC->flipLockPin - NV_EVO_LOCK_PIN_0;
|
|
data = FLD_IDX_SET_DRF(C37D, _SET_CONTROL, _FLIP_LOCK_PIN,
|
|
pin, _ENABLE, data);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* GV100 HW bug 2062029 WAR
|
|
*
|
|
* GV100 always holds the external fliplock line low as if
|
|
* NVC37D_SET_CONTROL_FLIP_LOCK_PIN was enabled. To work around this,
|
|
* the GV100 VBIOS initializes the fliplock GPIOs to be software
|
|
* controlled (forced off). The following rmctrl needs to be called to
|
|
* switch HW control of the fliplock GPIOs back on whenever external
|
|
* fliplock is enabled.
|
|
*/
|
|
{
|
|
NVC370_CTRL_SET_SWAPRDY_GPIO_WAR_PARAMS params = { };
|
|
|
|
params.base.subdeviceIndex = pEvoSubDev->subDeviceInstance;
|
|
params.bEnable = (data != 0);
|
|
|
|
if (nvRmApiControl(
|
|
nvEvoGlobal.clientHandle,
|
|
pDevEvo->displayHandle,
|
|
NVC370_CTRL_CMD_SET_SWAPRDY_GPIO_WAR,
|
|
¶ms, sizeof(params)) != NVOS_STATUS_SUCCESS) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR, "Failed to override fliplock GPIO");
|
|
}
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_SET_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, data);
|
|
}
|
|
|
|
static void EvoSetHeadControlC3(NVDevEvoPtr pDevEvo, int sd, int head,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[sd];
|
|
/*
|
|
* NOTE: This function should only push state to the hardware based on data
|
|
* in the pHC. If not, then we may miss updates due to the memcmp of the
|
|
* HeadControl structure in UpdateEvoLockState().
|
|
*/
|
|
NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
|
|
NvU32 data = 0, pin;
|
|
NvU32 serverLockMode, clientLockMode;
|
|
|
|
/* These methods should only apply to a single subdevice */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
switch (pHC->serverLock) {
|
|
case NV_EVO_NO_LOCK:
|
|
serverLockMode = NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_MODE_NO_LOCK;
|
|
break;
|
|
case NV_EVO_FRAME_LOCK:
|
|
serverLockMode = NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_MODE_FRAME_LOCK;
|
|
break;
|
|
case NV_EVO_RASTER_LOCK:
|
|
serverLockMode = NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_MODE_RASTER_LOCK;
|
|
break;
|
|
default:
|
|
nvAssert(!"Invalid server lock mode");
|
|
return;
|
|
}
|
|
|
|
switch (pHC->clientLock) {
|
|
case NV_EVO_NO_LOCK:
|
|
clientLockMode = NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_MODE_NO_LOCK;
|
|
break;
|
|
case NV_EVO_FRAME_LOCK:
|
|
clientLockMode = NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_MODE_FRAME_LOCK;
|
|
break;
|
|
case NV_EVO_RASTER_LOCK:
|
|
clientLockMode = NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_MODE_RASTER_LOCK;
|
|
break;
|
|
default:
|
|
nvAssert(!"Invalid client lock mode");
|
|
return;
|
|
}
|
|
|
|
// Convert head control state to EVO method values.
|
|
nvAssert(!pHC->interlaced);
|
|
data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _STRUCTURE, _PROGRESSIVE);
|
|
|
|
nvAssert(pHC->serverLockPin != NV_EVO_LOCK_PIN_ERROR);
|
|
nvAssert(pHC->clientLockPin != NV_EVO_LOCK_PIN_ERROR);
|
|
|
|
if (serverLockMode == NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_MODE_NO_LOCK) {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _MASTER_LOCK_PIN, _LOCK_PIN_NONE);
|
|
} else if (NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->serverLockPin)) {
|
|
pin = pHC->serverLockPin - NV_EVO_LOCK_PIN_INTERNAL_0;
|
|
/*
|
|
* nvdClass_01.mfs says:
|
|
* "master lock pin, if internal, must be set to the corresponding
|
|
* internal pin for that head" (error check #12)
|
|
*/
|
|
nvAssert(pin == head);
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _MASTER_LOCK_PIN,
|
|
NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_PIN_INTERNAL_SCAN_LOCK(pin));
|
|
} else {
|
|
pin = pHC->serverLockPin - NV_EVO_LOCK_PIN_0;
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _MASTER_LOCK_PIN,
|
|
NVC37D_HEAD_SET_CONTROL_MASTER_LOCK_PIN_LOCK_PIN(pin));
|
|
}
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _MASTER_LOCK_MODE, serverLockMode);
|
|
|
|
if (clientLockMode == NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_MODE_NO_LOCK) {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCK_PIN, _LOCK_PIN_NONE);
|
|
} else if (NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->clientLockPin)) {
|
|
pin = pHC->clientLockPin - NV_EVO_LOCK_PIN_INTERNAL_0;
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCK_PIN,
|
|
NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_PIN_INTERNAL_SCAN_LOCK(pin));
|
|
} else {
|
|
pin = pHC->clientLockPin - NV_EVO_LOCK_PIN_0;
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCK_PIN,
|
|
NVC37D_HEAD_SET_CONTROL_SLAVE_LOCK_PIN_LOCK_PIN(pin));
|
|
}
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCK_MODE, clientLockMode);
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_LOCKOUT_WINDOW,
|
|
pHC->clientLockoutWindow);
|
|
|
|
/*
|
|
* We always enable stereo lock when it's available and either framelock
|
|
* or rasterlock is in use.
|
|
*/
|
|
if (pHC->stereoLocked) {
|
|
if (pHC->serverLock != NV_EVO_NO_LOCK) {
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _MASTER_STEREO_LOCK_MODE,
|
|
NVC37D_HEAD_SET_CONTROL_MASTER_STEREO_LOCK_MODE_ENABLE);
|
|
}
|
|
if (pHC->clientLock != NV_EVO_NO_LOCK) {
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _SLAVE_STEREO_LOCK_MODE,
|
|
NVC37D_HEAD_SET_CONTROL_SLAVE_STEREO_LOCK_MODE_ENABLE);
|
|
}
|
|
}
|
|
|
|
nvAssert(pHC->stereoPin != NV_EVO_LOCK_PIN_ERROR);
|
|
if (NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->stereoPin)) {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _STEREO_PIN, _LOCK_PIN_NONE);
|
|
} else {
|
|
pin = pHC->stereoPin - NV_EVO_LOCK_PIN_0;
|
|
data |= DRF_NUM(C37D, _HEAD_SET_CONTROL, _STEREO_PIN,
|
|
NVC37D_HEAD_SET_CONTROL_STEREO_PIN_LOCK_PIN(pin));
|
|
}
|
|
|
|
if (pHC->hdmi3D) {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _STEREO3D_STRUCTURE, _FRAME_PACKED);
|
|
} else {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_CONTROL, _STEREO3D_STRUCTURE, _NORMAL);
|
|
}
|
|
|
|
/*
|
|
* NVC67D_HEAD_SET_CONTROL_YUV420PACKER is only defined in NVC67D, but
|
|
* it is an unused bit in NVC37D_HEAD_SET_CONTROL, and YUV420 should only
|
|
* be set on >=nvdisplay 4.0, so it's okay to set it here.
|
|
*/
|
|
if (pHC->hwYuv420) {
|
|
data |= DRF_DEF(C67D, _HEAD_SET_CONTROL, _YUV420PACKER, _ENABLE);
|
|
} else {
|
|
data |= DRF_DEF(C67D, _HEAD_SET_CONTROL, _YUV420PACKER, _DISABLE);
|
|
}
|
|
|
|
// Send the HeadSetControl method.
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, data);
|
|
|
|
nvEvoSetControlC3(pDevEvo, sd);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_LOCK_CHAIN(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C37D, _HEAD_SET_LOCK_CHAIN, _POSITION,
|
|
pHC->lockChainPosition));
|
|
|
|
/* XXX temporary WAR; see bug 4028718 */
|
|
#if !defined(NVC37D_HEAD_SET_LOCK_OFFSET)
|
|
#define NVC37D_HEAD_SET_LOCK_OFFSET(a) (0x00002040 + (a)*0x00000400)
|
|
#define NVC37D_HEAD_SET_LOCK_OFFSET_X 14:0
|
|
#define NVC37D_HEAD_SET_LOCK_OFFSET_Y 30:16
|
|
#endif
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_LOCK_OFFSET(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, pHC->setLockOffsetX ?
|
|
DRF_NUM(C37D, _HEAD_SET_LOCK_OFFSET, _X,
|
|
27) : 0);
|
|
}
|
|
|
|
static void EvoSetHeadRefClkC3(NVDevEvoPtr pDevEvo, int head, NvBool external,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 sd;
|
|
|
|
/* These methods should only apply to a single subdevice */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
if (nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)) {
|
|
if (external) {
|
|
pDevEvo->gpus[sd].setSwSpareA[head] =
|
|
FLD_SET_DRF(C37D,
|
|
_HEAD_SET_SW_SPARE_A_CODE,
|
|
_VPLL_REF,
|
|
_QSYNC,
|
|
pDevEvo->gpus[sd].setSwSpareA[head]);
|
|
} else {
|
|
pDevEvo->gpus[sd].setSwSpareA[head] =
|
|
FLD_SET_DRF(C37D,
|
|
_HEAD_SET_SW_SPARE_A_CODE,
|
|
_VPLL_REF,
|
|
_NO_PREF,
|
|
pDevEvo->gpus[sd].setSwSpareA[head]);
|
|
}
|
|
|
|
nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_SW_SPARE_A(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, pDevEvo->gpus[sd].setSwSpareA[head]);
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void EvoSORSetControlC3(const NVConnectorEvoRec *pConnectorEvo,
|
|
const enum nvKmsTimingsProtocol protocol,
|
|
const NvU32 orIndex,
|
|
const NvU32 headMask)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pConnectorEvo->pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 hwProtocol = 0;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
nvAssert(orIndex != NV_INVALID_OR);
|
|
|
|
if (headMask != 0) {
|
|
switch (protocol) {
|
|
default:
|
|
nvAssert(!"Unknown SOR protocol");
|
|
/* Fall through */
|
|
case NVKMS_PROTOCOL_SOR_SINGLE_TMDS_A:
|
|
hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_A;
|
|
break;
|
|
case NVKMS_PROTOCOL_SOR_SINGLE_TMDS_B:
|
|
hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_B;
|
|
break;
|
|
case NVKMS_PROTOCOL_SOR_DUAL_TMDS:
|
|
hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_DUAL_TMDS;
|
|
break;
|
|
case NVKMS_PROTOCOL_SOR_DP_A:
|
|
hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_DP_A;
|
|
break;
|
|
case NVKMS_PROTOCOL_SOR_DP_B:
|
|
hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_DP_B;
|
|
break;
|
|
case NVKMS_PROTOCOL_SOR_LVDS_CUSTOM:
|
|
hwProtocol = NVC37D_SOR_SET_CONTROL_PROTOCOL_LVDS_CUSTOM;
|
|
break;
|
|
case NVKMS_PROTOCOL_SOR_HDMI_FRL:
|
|
hwProtocol = NVC67D_SOR_SET_CONTROL_PROTOCOL_HDMI_FRL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_SOR_SET_CONTROL(orIndex), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _SOR_SET_CONTROL, _OWNER_MASK, headMask) |
|
|
DRF_NUM(C37D, _SOR_SET_CONTROL, _PROTOCOL, hwProtocol) |
|
|
DRF_DEF(C37D, _SOR_SET_CONTROL, _DE_SYNC_POLARITY, _POSITIVE_TRUE) |
|
|
DRF_DEF(C37D, _SOR_SET_CONTROL, _PIXEL_REPLICATE_MODE, _OFF));
|
|
}
|
|
|
|
NvU32 nvEvoGetPixelDepthC3(const enum nvKmsPixelDepth pixelDepth)
|
|
{
|
|
switch (pixelDepth) {
|
|
case NVKMS_PIXEL_DEPTH_18_444:
|
|
return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_18_444;
|
|
case NVKMS_PIXEL_DEPTH_24_444:
|
|
return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_24_444;
|
|
case NVKMS_PIXEL_DEPTH_30_444:
|
|
return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_30_444;
|
|
case NVKMS_PIXEL_DEPTH_16_422:
|
|
return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_16_422;
|
|
case NVKMS_PIXEL_DEPTH_20_422:
|
|
return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_20_422;
|
|
|
|
}
|
|
nvAssert(!"Unexpected pixel depth");
|
|
return NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_24_444;
|
|
}
|
|
|
|
static void EvoPIORSetControlC3(const NVConnectorEvoRec *pConnectorEvo,
|
|
const enum nvKmsTimingsProtocol protocol,
|
|
const NvU32 orIndex,
|
|
const NvU32 headMask)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pConnectorEvo->pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
if (headMask != 0) {
|
|
nvAssert(protocol == NVKMS_PROTOCOL_PIOR_EXT_TMDS_ENC);
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_PIOR_SET_CONTROL(orIndex), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _PIOR_SET_CONTROL, _OWNER_MASK, headMask) |
|
|
DRF_DEF(C37D, _PIOR_SET_CONTROL, _PROTOCOL, _EXT_TMDS_ENC) |
|
|
DRF_DEF(C37D, _PIOR_SET_CONTROL, _DE_SYNC_POLARITY, _POSITIVE_TRUE));
|
|
}
|
|
|
|
static void EvoDSISetControlC6(const NVConnectorEvoRec *pConnectorEvo,
|
|
const enum nvKmsTimingsProtocol protocol,
|
|
const NvU32 orIndex,
|
|
const NvU32 headMask)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pConnectorEvo->pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
/* Only Head 0 can be used to drive DSI output on Orin */
|
|
nvAssert((headMask == 0x0) || (headMask == 0x1));
|
|
/* Only one DSI engine exists on Orin */
|
|
nvAssert(orIndex == 0);
|
|
|
|
if (headMask != 0) {
|
|
nvAssert(protocol == NVKMS_PROTOCOL_DSI);
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67D_DSI_SET_CONTROL(orIndex), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67D, _DSI_SET_CONTROL, _OWNER_MASK, headMask));
|
|
}
|
|
|
|
static void EvoORSetControlC3Helper(const NVConnectorEvoRec *pConnectorEvo,
|
|
const enum nvKmsTimingsProtocol protocol,
|
|
const NvU32 orIndex,
|
|
const NvU32 headMask)
|
|
{
|
|
switch (pConnectorEvo->or.type) {
|
|
case NV0073_CTRL_SPECIFIC_OR_TYPE_SOR:
|
|
EvoSORSetControlC3(pConnectorEvo, protocol, orIndex, headMask);
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_TYPE_PIOR:
|
|
EvoPIORSetControlC3(pConnectorEvo, protocol, orIndex, headMask);
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_TYPE_DAC:
|
|
/* No DAC support on nvdisplay. Fall through. */
|
|
default:
|
|
nvAssert(!"Invalid pConnectorEvo->or.type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void nvEvoORSetControlC3(NVDevEvoPtr pDevEvo,
|
|
const NVConnectorEvoRec *pConnectorEvo,
|
|
const enum nvKmsTimingsProtocol protocol,
|
|
const NvU32 orIndex,
|
|
const NvU32 headMask,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pDevEvo->core);
|
|
|
|
EvoORSetControlC3Helper(pConnectorEvo, protocol, orIndex, headMask);
|
|
}
|
|
|
|
static void EvoORSetControlC6(NVDevEvoPtr pDevEvo,
|
|
const NVConnectorEvoRec *pConnectorEvo,
|
|
const enum nvKmsTimingsProtocol protocol,
|
|
const NvU32 orIndex,
|
|
const NvU32 headMask,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pDevEvo->core);
|
|
|
|
switch (pConnectorEvo->or.type) {
|
|
case NV0073_CTRL_SPECIFIC_OR_TYPE_DSI:
|
|
EvoDSISetControlC6(pConnectorEvo, protocol, orIndex, headMask);
|
|
break;
|
|
default:
|
|
EvoORSetControlC3Helper(pConnectorEvo, protocol, orIndex, headMask);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void EvoHeadSetControlORC3(NVDevEvoPtr pDevEvo,
|
|
const int head,
|
|
const NVHwModeTimingsEvo *pTimings,
|
|
const enum nvKmsPixelDepth pixelDepth,
|
|
const NvBool colorSpaceOverride,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
const NvU32 hwPixelDepth = nvEvoGetPixelDepthC3(pixelDepth);
|
|
const NvU16 colorSpaceFlag = nvEvo1GetColorSpaceFlag(pDevEvo,
|
|
colorSpaceOverride);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL_OUTPUT_RESOURCE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _CRC_MODE, _COMPLETE_RASTER) |
|
|
(pTimings->hSyncPol ?
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _HSYNC_POLARITY, _NEGATIVE_TRUE) :
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _HSYNC_POLARITY, _POSITIVE_TRUE)) |
|
|
(pTimings->vSyncPol ?
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _VSYNC_POLARITY, _NEGATIVE_TRUE) :
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _VSYNC_POLARITY, _POSITIVE_TRUE)) |
|
|
DRF_NUM(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _PIXEL_DEPTH, hwPixelDepth) |
|
|
(colorSpaceOverride ?
|
|
(DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_OVERRIDE, _ENABLE) |
|
|
DRF_NUM(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_FLAG, colorSpaceFlag)) :
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_OVERRIDE, _DISABLE)));
|
|
}
|
|
|
|
static void EvoHeadSetControlORC5(NVDevEvoPtr pDevEvo,
|
|
const int head,
|
|
const NVHwModeTimingsEvo *pTimings,
|
|
const enum nvKmsPixelDepth pixelDepth,
|
|
const NvBool colorSpaceOverride,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
const NvU32 hwPixelDepth = nvEvoGetPixelDepthC3(pixelDepth);
|
|
const NvU16 colorSpaceFlag = nvEvo1GetColorSpaceFlag(pDevEvo,
|
|
colorSpaceOverride);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_CONTROL_OUTPUT_RESOURCE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _CRC_MODE, _COMPLETE_RASTER) |
|
|
(pTimings->hSyncPol ?
|
|
DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _HSYNC_POLARITY, _NEGATIVE_TRUE) :
|
|
DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _HSYNC_POLARITY, _POSITIVE_TRUE)) |
|
|
(pTimings->vSyncPol ?
|
|
DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _VSYNC_POLARITY, _NEGATIVE_TRUE) :
|
|
DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _VSYNC_POLARITY, _POSITIVE_TRUE)) |
|
|
DRF_NUM(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _PIXEL_DEPTH, hwPixelDepth) |
|
|
(colorSpaceOverride ?
|
|
(DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_OVERRIDE, _ENABLE) |
|
|
DRF_NUM(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_FLAG, colorSpaceFlag)) :
|
|
DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _COLOR_SPACE_OVERRIDE, _DISABLE)) |
|
|
DRF_DEF(C57D, _HEAD_SET_CONTROL_OUTPUT_RESOURCE, _EXT_PACKET_WIN, _NONE));
|
|
}
|
|
|
|
static void EvoHeadSetDisplayIdC3(NVDevEvoPtr pDevEvo,
|
|
const NvU32 head, const NvU32 displayId,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DISPLAY_ID(head, 0), 1);
|
|
nvDmaSetEvoMethodData(pChannel, displayId);
|
|
}
|
|
|
|
static void SetFormatUsageBoundsOneWindow3(NVDevEvoPtr pDevEvo, NvU32 window,
|
|
const NvU64 supportedFormats,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 value = 0;
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED1BPP) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_RGB_PACKED1BPP, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED2BPP) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_RGB_PACKED2BPP, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED4BPP) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_RGB_PACKED4BPP, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED8BPP) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_RGB_PACKED8BPP, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PACKED422) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_YUV_PACKED422, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP420) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_YUV_SEMI_PLANAR420, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP422) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_YUV_SEMI_PLANAR422, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP444) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_YUV_SEMI_PLANAR444, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP420) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_EXT_YUV_SEMI_PLANAR420, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP422) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_EXT_YUV_SEMI_PLANAR422, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP444) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_EXT_YUV_SEMI_PLANAR444, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PLANAR444) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_YUV_PLANAR444, _TRUE, value);
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PLANAR420) {
|
|
value = FLD_SET_DRF(C37D, _WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS,
|
|
_YUV_PLANAR420, _TRUE, value);
|
|
}
|
|
|
|
if (supportedFormats != 0 && value == 0) {
|
|
nvAssert(!"Unknown depth in SetFormatUsageBoundsOneWindow");
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_WINDOW_SET_WINDOW_FORMAT_USAGE_BOUNDS(window), 1);
|
|
nvDmaSetEvoMethodData(pChannel, value);
|
|
}
|
|
|
|
static void SetScalingUsageBoundsOneWindow5(
|
|
NVDevEvoPtr pDevEvo, NvU32 window,
|
|
const struct NvKmsScalingUsageBounds *pScaling,
|
|
NvBool layerUsable,
|
|
const NVHwModeViewPortEvo *pViewPort,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 setWindowUsageBounds = NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C5;
|
|
NvU32 maxPixelsFetchedPerLine;
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC57D_WINDOW_SET_MAX_INPUT_SCALE_FACTOR(window), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57D, _WINDOW_SET_MAX_INPUT_SCALE_FACTOR, _HORIZONTAL,
|
|
pScaling->maxHDownscaleFactor) |
|
|
DRF_NUM(C57D, _WINDOW_SET_MAX_INPUT_SCALE_FACTOR, _VERTICAL,
|
|
pScaling->maxVDownscaleFactor));
|
|
|
|
if (layerUsable) {
|
|
maxPixelsFetchedPerLine = nvGetMaxPixelsFetchedPerLine(pViewPort->in.width,
|
|
pScaling->maxHDownscaleFactor);
|
|
} else {
|
|
maxPixelsFetchedPerLine = 0;
|
|
}
|
|
|
|
setWindowUsageBounds |=
|
|
(DRF_NUM(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _MAX_PIXELS_FETCHED_PER_LINE,maxPixelsFetchedPerLine)) |
|
|
(pScaling->vTaps >= NV_EVO_SCALER_5TAPS ?
|
|
DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_5) :
|
|
DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_2)) |
|
|
(pScaling->vUpscalingAllowed ?
|
|
DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _TRUE) :
|
|
DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE));
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC57D_WINDOW_SET_WINDOW_USAGE_BOUNDS(window), 1);
|
|
nvDmaSetEvoMethodData(pChannel, setWindowUsageBounds);
|
|
}
|
|
|
|
static NvBool EvoSetUsageBounds3(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
|
|
const struct NvKmsUsageBounds *pUsage,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
const struct NvKmsUsageBounds *pCurrentUsage =
|
|
&pDevEvo->gpus[sd].headState[head].usage;
|
|
/* Return FALSE if a core channel UPDATE isn't actually needed. */
|
|
NvBool needCoreUpdate = FALSE;
|
|
NvU32 layer;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
for (layer = 0; layer < pDevEvo->head[head].numLayers; layer++) {
|
|
NvU64 currentFormats = 0;
|
|
NvU64 targetFormats = 0;
|
|
|
|
if (pCurrentUsage->layer[layer].usable) {
|
|
currentFormats =
|
|
pCurrentUsage->layer[layer].supportedSurfaceMemoryFormats;
|
|
}
|
|
|
|
if (pUsage->layer[layer].usable) {
|
|
targetFormats = pUsage->layer[layer].supportedSurfaceMemoryFormats;
|
|
}
|
|
|
|
if (targetFormats == currentFormats) {
|
|
continue;
|
|
}
|
|
|
|
SetFormatUsageBoundsOneWindow3(pDevEvo,
|
|
NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(
|
|
pDevEvo->head[head].layer[layer]->channelMask),
|
|
targetFormats,
|
|
updateState);
|
|
needCoreUpdate = TRUE;
|
|
}
|
|
|
|
return needCoreUpdate;
|
|
}
|
|
|
|
static NvBool EvoSetUsageBoundsC3(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
|
|
const struct NvKmsUsageBounds *pUsage,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
return EvoSetUsageBounds3(pDevEvo, sd, head, pUsage, updateState);
|
|
}
|
|
|
|
NvBool nvEvoSetUsageBoundsC5(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
|
|
const struct NvKmsUsageBounds *pUsage,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
const struct NvKmsUsageBounds *pCurrentUsage =
|
|
&pDevEvo->gpus[sd].headState[head].usage;
|
|
NvBool needCoreUpdate;
|
|
NvU32 layer;
|
|
|
|
needCoreUpdate = EvoSetUsageBounds3(pDevEvo, sd, head, pUsage, updateState);
|
|
|
|
for (layer = 0; layer < pDevEvo->head[head].numLayers; layer++) {
|
|
if ((pCurrentUsage->layer[layer].usable != pUsage->layer[layer].usable) ||
|
|
(!nvEvoScalingUsageBoundsEqual(&pCurrentUsage->layer[layer].scaling,
|
|
&pUsage->layer[layer].scaling))) {
|
|
const NVHwModeViewPortEvo *pViewPort =
|
|
&pDevEvo->gpus[sd].pDispEvo->headState[head].timings.viewPort;
|
|
|
|
SetScalingUsageBoundsOneWindow5(
|
|
pDevEvo,
|
|
NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(
|
|
pDevEvo->head[head].layer[layer]->channelMask),
|
|
&pUsage->layer[layer].scaling,
|
|
pUsage->layer[layer].usable,
|
|
pViewPort,
|
|
updateState);
|
|
needCoreUpdate = TRUE;
|
|
}
|
|
}
|
|
|
|
return needCoreUpdate;
|
|
}
|
|
|
|
static void EvoSetCoreNotifierSurfaceAddressAndControlC3(
|
|
const NVDevEvoRec *pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 notifierOffset,
|
|
NvU32 ctrlVal)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_SET_CONTEXT_DMA_NOTIFIER, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _SET_CONTEXT_DMA_NOTIFIER, _HANDLE, ctxDmaHandle));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_SET_NOTIFIER_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _SET_NOTIFIER_CONTROL, _OFFSET, notifierOffset) | ctrlVal);
|
|
}
|
|
|
|
void nvEvoSetNotifierC3(NVDevEvoRec *pDevEvo,
|
|
const NvBool notify,
|
|
const NvBool awaken,
|
|
const NvU32 notifier,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 ctrlVal = 0;
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
/*
|
|
* XXXnvdisplay: Note that nvdClass_01.mfs says:
|
|
* "The units of the offset are 16 bytes.", while dispClass_02.mfs says:
|
|
* "The units of the offset are 32 bit words."
|
|
* The "legacy" 32-bit notifier format is no longer supported. This will
|
|
* have to be exposed to upper layers.
|
|
*/
|
|
ASSERT_DRF_NUM(C37D, _SET_NOTIFIER_CONTROL, _OFFSET, notifier);
|
|
|
|
ctrlVal = (awaken ?
|
|
DRF_DEF(C37D, _SET_NOTIFIER_CONTROL, _MODE, _WRITE_AWAKEN) :
|
|
DRF_DEF(C37D, _SET_NOTIFIER_CONTROL, _MODE, _WRITE));
|
|
ctrlVal |= (notify ?
|
|
DRF_DEF(C37D, _SET_NOTIFIER_CONTROL, _NOTIFY, _ENABLE) :
|
|
DRF_DEF(C37D, _SET_NOTIFIER_CONTROL, _NOTIFY, _DISABLE));
|
|
|
|
// To work around HW BUG 1945716, set the core channel completion notifier
|
|
// context DMA to 0 when notification is not requested.
|
|
if (notify) {
|
|
NvU32 sd;
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
if (nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)) {
|
|
nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
|
|
pDevEvo->hal->SetCoreNotifierSurfaceAddressAndControl(pDevEvo,
|
|
pChannel, &pDevEvo->core->notifiersDma[sd].surfaceDesc,
|
|
notifier, ctrlVal);
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
}
|
|
} else {
|
|
pDevEvo->hal->SetCoreNotifierSurfaceAddressAndControl(pDevEvo, pChannel,
|
|
NULL /* pSurfaceDesc */, 0 /* offset */ , 0 /* ctrlVal */);
|
|
}
|
|
}
|
|
|
|
static void UpdateCoreC3(NVEvoChannelPtr pChannel,
|
|
NVEvoChannelMask interlockChannelMask,
|
|
NvU32 flipLockPin,
|
|
NvBool releaseElv)
|
|
{
|
|
NvU32 head, interlockFlags = 0;
|
|
NvU32 window, windowInterlockFlags = 0;
|
|
NvU32 update = DRF_NUM(C37D, _UPDATE, _FLIP_LOCK_PIN, flipLockPin);
|
|
|
|
update |= releaseElv ? DRF_DEF(C37D, _UPDATE, _RELEASE_ELV, _TRUE) : 0;
|
|
|
|
for (head = 0; head < NV_EVO_CHANNEL_MASK_CURSOR__SIZE; head++) {
|
|
if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _CURSOR, head, _ENABLE,
|
|
interlockChannelMask)) {
|
|
interlockFlags |=
|
|
DRF_IDX_DEF(C37D, _SET_INTERLOCK_FLAGS,
|
|
_INTERLOCK_WITH_CURSOR, head, _ENABLE);
|
|
}
|
|
}
|
|
|
|
for (window = 0; window < NV_EVO_CHANNEL_MASK_WINDOW__SIZE; window++) {
|
|
if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
|
|
interlockChannelMask)) {
|
|
windowInterlockFlags |=
|
|
DRF_IDX_DEF(C37D, _SET_WINDOW_INTERLOCK_FLAGS,
|
|
_INTERLOCK_WITH_WINDOW, window, _ENABLE);
|
|
}
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_SET_INTERLOCK_FLAGS, 1);
|
|
nvDmaSetEvoMethodData(pChannel, interlockFlags);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_SET_WINDOW_INTERLOCK_FLAGS, 1);
|
|
nvDmaSetEvoMethodData(pChannel, windowInterlockFlags);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_UPDATE, 1);
|
|
nvDmaSetEvoMethodData(pChannel, update);
|
|
|
|
nvDmaKickoffEvo(pChannel);
|
|
}
|
|
|
|
static void UpdateWindowIMM(NVEvoChannelPtr pChannel,
|
|
NVEvoChannelMask winImmChannelMask,
|
|
NVEvoChannelMask winImmInterlockMask,
|
|
NvBool releaseElv)
|
|
{
|
|
nvAssert((winImmChannelMask & ~NV_EVO_CHANNEL_MASK_WINDOW_ALL) == 0);
|
|
nvAssert((winImmInterlockMask & ~NV_EVO_CHANNEL_MASK_WINDOW_ALL) == 0);
|
|
|
|
if ((winImmChannelMask & pChannel->channelMask) != 0) {
|
|
NvU32 updateImm = 0;
|
|
|
|
if ((winImmInterlockMask & pChannel->channelMask) != 0) {
|
|
updateImm |= DRF_DEF(C37B, _UPDATE, _INTERLOCK_WITH_WINDOW, _ENABLE);
|
|
} else {
|
|
updateImm |= DRF_DEF(C37B, _UPDATE, _INTERLOCK_WITH_WINDOW, _DISABLE);
|
|
}
|
|
updateImm |= releaseElv ? DRF_DEF(C37B, _UPDATE, _RELEASE_ELV, _TRUE) : 0;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel->imm.u.dma, NVC37B_UPDATE, 1);
|
|
nvDmaSetEvoMethodData(pChannel->imm.u.dma, updateImm);
|
|
nvDmaKickoffEvo(pChannel->imm.u.dma);
|
|
}
|
|
}
|
|
|
|
static void UpdateWindowC3(NVEvoChannelPtr pChannel,
|
|
NVEvoChannelMask interlockChannelMask,
|
|
NVEvoChannelMask winImmChannelMask,
|
|
NVEvoChannelMask winImmInterlockMask,
|
|
NvBool transitionWAR,
|
|
NvU32 flipLockPin,
|
|
NvBool releaseElv)
|
|
{
|
|
NvU32 head, interlockFlags = 0;
|
|
NvU32 window, windowInterlockFlags = 0;
|
|
NvU32 update = DRF_NUM(C37E, _UPDATE, _FLIP_LOCK_PIN, flipLockPin);
|
|
|
|
update |= releaseElv ? DRF_DEF(C37E, _UPDATE, _RELEASE_ELV, _TRUE) : 0;
|
|
|
|
if ((winImmInterlockMask & pChannel->channelMask) != 0) {
|
|
/*
|
|
* We expect winImmChannelMask to always be a superset of
|
|
* winImmInterlockMask. We should never interlock with a window
|
|
* immediate channel if we're not also going to kick off that
|
|
* window immediate channel.
|
|
*/
|
|
nvAssert((winImmChannelMask & pChannel->channelMask) != 0);
|
|
|
|
update |= DRF_DEF(C37E, _UPDATE, _INTERLOCK_WITH_WIN_IMM, _ENABLE);
|
|
} else {
|
|
update |= DRF_DEF(C37E, _UPDATE, _INTERLOCK_WITH_WIN_IMM, _DISABLE);
|
|
}
|
|
|
|
// Nothing currently requires updating a window channel without releasing
|
|
// ELV.
|
|
nvAssert(releaseElv);
|
|
|
|
if (FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
|
|
interlockChannelMask)) {
|
|
interlockFlags |=
|
|
DRF_DEF(C37E, _SET_INTERLOCK_FLAGS, _INTERLOCK_WITH_CORE, _ENABLE);
|
|
}
|
|
|
|
for (head = 0; head < NV_EVO_CHANNEL_MASK_CURSOR__SIZE; head++) {
|
|
if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _CURSOR, head, _ENABLE,
|
|
interlockChannelMask)) {
|
|
interlockFlags |=
|
|
DRF_IDX_DEF(C37E, _SET_INTERLOCK_FLAGS,
|
|
_INTERLOCK_WITH_CURSOR, head, _ENABLE);
|
|
}
|
|
}
|
|
|
|
for (window = 0; window < NV_EVO_CHANNEL_MASK_WINDOW__SIZE; window++) {
|
|
if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
|
|
interlockChannelMask)) {
|
|
windowInterlockFlags |=
|
|
DRF_IDX_DEF(C37E, _SET_WINDOW_INTERLOCK_FLAGS,
|
|
_INTERLOCK_WITH_WINDOW, window, _ENABLE);
|
|
}
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_INTERLOCK_FLAGS, 1);
|
|
nvDmaSetEvoMethodData(pChannel, interlockFlags);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_WINDOW_INTERLOCK_FLAGS, 1);
|
|
nvDmaSetEvoMethodData(pChannel, windowInterlockFlags);
|
|
|
|
/*
|
|
* If we determined that this update will transition from NULL to non-NULL
|
|
* ctxdma or vice-versa, bookend this update method with software methods
|
|
* to notify RM to apply a workaround for hardware bug 2193096.
|
|
*/
|
|
if (transitionWAR) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SW_SET_MCLK_SWITCH, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57E, _SW_SET_MCLK_SWITCH, _ENABLE, _FALSE));
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_UPDATE, 1);
|
|
nvDmaSetEvoMethodData(pChannel, update);
|
|
|
|
if (transitionWAR) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SW_SET_MCLK_SWITCH, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57E, _SW_SET_MCLK_SWITCH, _ENABLE, _TRUE));
|
|
}
|
|
|
|
UpdateWindowIMM(pChannel, winImmChannelMask,
|
|
winImmInterlockMask, releaseElv);
|
|
|
|
nvDmaKickoffEvo(pChannel);
|
|
}
|
|
|
|
/*!
|
|
* This function finds any fliplocked channels in the current update and pushes
|
|
* flips for them setting the appropriate fliplock pin and interlock masks.
|
|
*
|
|
* All of this complexity is here to support the case where multiple heads on a
|
|
* single GPU are fliplocked together, but flip requests come in for only a
|
|
* subset of those heads at a time (e.g., separate X screens on a single GPU).
|
|
* Unlike previous hardware, we're required to interlock all channels which are
|
|
* part of a fliplock update, instead of just using fliplock across heads.
|
|
*/
|
|
/*
|
|
* There are two scenarios:
|
|
* a) All fliplocked channels on this GPU are already part of this update. In
|
|
* that case we just need to set the appropriate fliplock pin for each, and
|
|
* we're done -- they're already interlocked.
|
|
* b) Some fliplocked channels are not part of this update. We still need to
|
|
* set them in the interlock mask, but it's dangerous to interlock with any
|
|
* channels *not* in the fliplock group; as an example:
|
|
* With two separate X screens on a single GPU, each driving one monitor,
|
|
* fliplocked together, if we get a flip request for screen 0/head 0 that
|
|
* interlocks core and base, then a second flip request for screen 1/head1
|
|
* that interlocks core and base, we would end up programming one flip on
|
|
* the window on head 0, one flip on the window on head 1, and two flips in
|
|
* the core channel. The second core channel flip would never complete
|
|
* since it would be waiting for an interlock with the other window
|
|
* channels.
|
|
*
|
|
* To handle this case we pull the fliplocked channels out of this update
|
|
* and update them interlocked with all fliplocked channels (including those
|
|
* that aren't in this update), then proceed with a normal interlocked
|
|
* update excluding the fliplocked channels.
|
|
*
|
|
* \return Channel mask of channels which were handled by this function.
|
|
* Channels in this mask should be considered done and have no
|
|
* further updates pushed. No other channels should be
|
|
* interlocked with them.
|
|
*/
|
|
static NVEvoChannelMask ProcessFlipLockUpdates(
|
|
NVDevEvoPtr pDevEvo,
|
|
NvU32 sd,
|
|
NvU32 *pFlipLockPin,
|
|
const NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[sd];
|
|
NvU32 head, window;
|
|
/* Channels that are part of this update which need to be fliplocked. */
|
|
NVEvoChannelMask flipLockUpdateMask = 0;
|
|
/* All channels on this subdevice which are fliplocked. */
|
|
NVEvoChannelMask flipLockAllMask = 0;
|
|
/* Channels which this function has handled and do not need further
|
|
* processing. */
|
|
NVEvoChannelMask handledMask = 0;
|
|
NVEvoLockPin pin = NV_EVO_LOCK_PIN_ERROR;
|
|
NvU32 hwPin = NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN_NONE;
|
|
|
|
/* First check if any of the fliplock-qualifying channels are actually
|
|
* fliplocked, and determine which pin they're using. */
|
|
for (head = 0; head < pDevEvo->numHeads; head++) {
|
|
NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
|
|
|
|
if (pHC->flipLock) {
|
|
/* Convert the head index to a window index (two windows per head,
|
|
* one "base" and one "overlay"; we only fliplock "base") */
|
|
NVEvoChannelMask windowMask =
|
|
DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, head * 2, _ENABLE);
|
|
if (updateState->subdev[sd].flipLockQualifyingMask & windowMask) {
|
|
if (flipLockUpdateMask == 0) {
|
|
pin = pHC->flipLockPin;
|
|
} else {
|
|
/* For now, we only support kicking off a single fliplock
|
|
* group as part of a single update call. */
|
|
nvAssert(pin == pHC->flipLockPin);
|
|
}
|
|
flipLockUpdateMask |= windowMask;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If we don't have any fliplocked updates, then we're done. */
|
|
if (flipLockUpdateMask == 0) {
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Gather all of the channels on this GPU which are part of this fliplock
|
|
* group (some of which may not be part of this update).
|
|
*/
|
|
for (head = 0; head < pDevEvo->numHeads; head++) {
|
|
NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
|
|
|
|
if (pHC->flipLock && (pHC->flipLockPin == pin)) {
|
|
NVEvoChannelMask windowMask =
|
|
DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, head * 2, _ENABLE);
|
|
flipLockAllMask |= windowMask;
|
|
}
|
|
}
|
|
|
|
/* Convert the pin to a hardware enum. */
|
|
if (NV_EVO_LOCK_PIN_IS_INTERNAL(pin)) {
|
|
hwPin = NVC37E_UPDATE_FLIP_LOCK_PIN_INTERNAL_FLIP_LOCK_0 +
|
|
(pin - NV_EVO_LOCK_PIN_INTERNAL_0);
|
|
} else {
|
|
hwPin = NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN(pin - NV_EVO_LOCK_PIN_0);
|
|
}
|
|
|
|
/* If we're updating all of the fliplocked channels in this update, we can
|
|
* interlock with other channels as normal. */
|
|
if (flipLockUpdateMask == flipLockAllMask) {
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Kick off each of our update channels, using the full fliplock mask and
|
|
* hwPin calculated above.
|
|
*/
|
|
nvAssert((flipLockUpdateMask & ~NV_EVO_CHANNEL_MASK_WINDOW_ALL) == 0);
|
|
for (window = 0; window < pDevEvo->numWindows; window++) {
|
|
const NVEvoChannelMask windowMask =
|
|
DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE);
|
|
NVEvoChannelMask winImmChannelMask =
|
|
updateState->subdev[sd].winImmChannelMask;
|
|
NVEvoChannelMask winImmInterlockMask =
|
|
updateState->subdev[sd].winImmInterlockMask;
|
|
if (flipLockUpdateMask & windowMask) {
|
|
const NvBool transitionWAR =
|
|
(updateState->subdev[sd].flipTransitionWAR & windowMask) != 0;
|
|
UpdateWindowC3(pDevEvo->window[window],
|
|
flipLockAllMask,
|
|
winImmChannelMask,
|
|
winImmInterlockMask,
|
|
transitionWAR,
|
|
hwPin, TRUE /* releaseElv */);
|
|
} else {
|
|
UpdateWindowIMM(pDevEvo->window[window], winImmChannelMask,
|
|
winImmInterlockMask, TRUE /* releaseElv */);
|
|
}
|
|
}
|
|
handledMask = flipLockUpdateMask;
|
|
hwPin = NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN_NONE;
|
|
|
|
done:
|
|
*pFlipLockPin = hwPin;
|
|
return handledMask;
|
|
}
|
|
|
|
void nvEvoUpdateC3(NVDevEvoPtr pDevEvo,
|
|
const NVEvoUpdateState *updateState,
|
|
NvBool releaseElv)
|
|
{
|
|
NvU32 sd, window;
|
|
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
NVEvoChannelMask updateChannelMask =
|
|
updateState->subdev[sd].channelMask;
|
|
const NVEvoChannelMask noCoreInterlockMask =
|
|
updateState->subdev[sd].noCoreInterlockMask;
|
|
NVEvoChannelMask coreInterlockMask =
|
|
updateChannelMask & ~noCoreInterlockMask;
|
|
const NvU32 subDeviceMask = (1 << sd);
|
|
NvU32 flipLockPin = NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN_NONE;
|
|
|
|
nvPushEvoSubDevMask(pDevEvo, subDeviceMask);
|
|
|
|
if (updateState->subdev[sd].flipLockQualifyingMask) {
|
|
NVEvoChannelMask handledChannels = 0;
|
|
|
|
nvAssert((updateState->subdev[sd].flipLockQualifyingMask &
|
|
~updateChannelMask) == 0);
|
|
nvAssert((updateState->subdev[sd].flipLockQualifyingMask &
|
|
updateState->subdev[sd].noCoreInterlockMask) == 0);
|
|
|
|
handledChannels =
|
|
ProcessFlipLockUpdates(pDevEvo, sd, &flipLockPin, updateState);
|
|
|
|
updateChannelMask &= ~handledChannels;
|
|
coreInterlockMask &= ~handledChannels;
|
|
}
|
|
|
|
if (FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
|
|
updateChannelMask)) {
|
|
const NVEvoChannelMask thisInterlockMask =
|
|
FLD_TEST_DRF64(_EVO, _CHANNEL_MASK, _CORE, _ENABLE,
|
|
coreInterlockMask) ? coreInterlockMask : 0;
|
|
UpdateCoreC3(pDevEvo->core, thisInterlockMask, flipLockPin,
|
|
releaseElv);
|
|
}
|
|
|
|
for (window = 0; window < pDevEvo->numWindows; window++) {
|
|
const NVEvoChannelMask windowMask =
|
|
DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE);
|
|
NVEvoChannelMask winImmChannelMask =
|
|
updateState->subdev[sd].winImmChannelMask;
|
|
NVEvoChannelMask winImmInterlockMask =
|
|
updateState->subdev[sd].winImmInterlockMask;
|
|
if (updateChannelMask & windowMask) {
|
|
const NvBool transitionWAR =
|
|
(updateState->subdev[sd].flipTransitionWAR & windowMask) != 0;
|
|
NVEvoChannelMask thisInterlockMask =
|
|
FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
|
|
coreInterlockMask) ? coreInterlockMask : 0;
|
|
UpdateWindowC3(pDevEvo->window[window],
|
|
thisInterlockMask,
|
|
winImmChannelMask,
|
|
winImmInterlockMask,
|
|
transitionWAR,
|
|
flipLockPin,
|
|
releaseElv);
|
|
} else {
|
|
UpdateWindowIMM(pDevEvo->window[window], winImmChannelMask,
|
|
winImmInterlockMask, releaseElv);
|
|
}
|
|
}
|
|
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Initialize head-specific IMP param fields.
|
|
*
|
|
* Initialize the NVC372_CTRL_IMP_HEAD for the specific head.
|
|
*
|
|
* \param[out] pImpHead The param structure to initialize.
|
|
* \param[in] pTimings The rastering timings and viewport configuration.
|
|
* \param[in] head The number of the head that will be driven.
|
|
*
|
|
* \return FALSE iff the parameters aren't even legal for HW.
|
|
*/
|
|
static NvBool AssignPerHeadImpParams(NVC372_CTRL_IMP_HEAD *pImpHead,
|
|
const NVHwModeTimingsEvo *pTimings,
|
|
const NvBool enableDsc,
|
|
const NvBool b2Heads1Or,
|
|
const int head,
|
|
const NVEvoScalerCaps *pScalerCaps)
|
|
{
|
|
const NVHwModeViewPortEvo *pViewPort = &pTimings->viewPort;
|
|
struct NvKmsScalingUsageBounds scalingUsageBounds = { };
|
|
|
|
pImpHead->headIndex = head;
|
|
|
|
/* raster timings */
|
|
|
|
pImpHead->maxPixelClkKHz = pTimings->pixelClock;
|
|
|
|
pImpHead->rasterSize.width = pTimings->rasterSize.x;
|
|
pImpHead->rasterSize.height = pTimings->rasterSize.y;
|
|
pImpHead->rasterBlankStart.X = pTimings->rasterBlankStart.x;
|
|
pImpHead->rasterBlankStart.Y = pTimings->rasterBlankStart.y;
|
|
pImpHead->rasterBlankEnd.X = pTimings->rasterBlankEnd.x;
|
|
pImpHead->rasterBlankEnd.Y = pTimings->rasterBlankEnd.y;
|
|
pImpHead->rasterVertBlank2.yStart = pTimings->rasterVertBlank2Start;
|
|
pImpHead->rasterVertBlank2.yEnd = pTimings->rasterVertBlank2End;
|
|
|
|
/* XXX TODO: Fill in correct scanlock information (only needed for
|
|
* MIN_VPSTATE). */
|
|
pImpHead->control.masterLockMode = NV_DISP_LOCK_MODE_NO_LOCK;
|
|
pImpHead->control.masterLockPin = NV_DISP_LOCK_PIN_UNSPECIFIED;
|
|
pImpHead->control.slaveLockMode = NV_DISP_LOCK_MODE_NO_LOCK;
|
|
pImpHead->control.slaveLockPin = NV_DISP_LOCK_PIN_UNSPECIFIED;
|
|
|
|
if (!nvComputeScalingUsageBounds(pScalerCaps,
|
|
pViewPort->in.width, pViewPort->in.height,
|
|
pViewPort->out.width, pViewPort->out.height,
|
|
pViewPort->hTaps, pViewPort->vTaps,
|
|
&scalingUsageBounds)) {
|
|
return FALSE;
|
|
}
|
|
pImpHead->bUpscalingAllowedV = scalingUsageBounds.vUpscalingAllowed;
|
|
pImpHead->maxDownscaleFactorV = scalingUsageBounds.maxVDownscaleFactor;
|
|
pImpHead->maxDownscaleFactorH = scalingUsageBounds.maxHDownscaleFactor;
|
|
pImpHead->outputScalerVerticalTaps =
|
|
NVEvoScalerTapsToNum(scalingUsageBounds.vTaps);
|
|
|
|
if (!nvComputeMinFrameIdle(pTimings,
|
|
&pImpHead->minFrameIdle.leadingRasterLines,
|
|
&pImpHead->minFrameIdle.trailingRasterLines)) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* Assume we'll need the full 1025-entry output LUT. */
|
|
pImpHead->lut = NVC372_CTRL_IMP_LUT_USAGE_1025;
|
|
|
|
/* Cursor width, in units of 32 pixels. Assume we use the maximum size. */
|
|
pImpHead->cursorSize32p = 256 / 32;
|
|
|
|
pImpHead->bEnableDsc = enableDsc;
|
|
|
|
pImpHead->bIs2Head1Or = b2Heads1Or;
|
|
|
|
pImpHead->bYUV420Format =
|
|
(pTimings->yuv420Mode == NV_YUV420_MODE_HW);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*!
|
|
* Initialize window-specific IMP param fields.
|
|
*
|
|
* Initialize the NVC372_CTRL_IMP_WINDOW for the specific window.
|
|
*
|
|
* \param[out] pImpWindow The param structure to initialize.
|
|
* \param[in] pViewPort The viewport configuration for the head that
|
|
* the window is bound to.
|
|
* \param[in] supportedFormats The surface memory formats that can be
|
|
* supported on this window.
|
|
* \param[in] window The number of the window.
|
|
* \param[in] head The number of the head that the window is
|
|
* bound to.
|
|
*/
|
|
static void AssignPerWindowImpParams(NVC372_CTRL_IMP_WINDOW *pImpWindow,
|
|
const NVHwModeViewPortEvo *pViewPort,
|
|
const NvU64 supportedFormats,
|
|
const struct NvKmsScalingUsageBounds *pScaling,
|
|
const int window,
|
|
const int head)
|
|
{
|
|
pImpWindow->windowIndex = window;
|
|
pImpWindow->owningHead = head;
|
|
|
|
pImpWindow->formatUsageBound = 0;
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED1BPP) {
|
|
pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_RGB_PACKED_1_BPP;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED2BPP) {
|
|
pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_RGB_PACKED_2_BPP;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED4BPP) {
|
|
pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_RGB_PACKED_4_BPP;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_RGB_PACKED8BPP) {
|
|
pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_RGB_PACKED_8_BPP;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PACKED422) {
|
|
pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_YUV_PACKED_422;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP420) {
|
|
pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_YUV_SEMI_PLANAR_420;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP422) {
|
|
pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_YUV_SEMI_PLANAR_422;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_SP444) {
|
|
pImpWindow->formatUsageBound |= NVC372_CTRL_FORMAT_YUV_SEMI_PLANAR_444;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP420) {
|
|
pImpWindow->formatUsageBound |=
|
|
NVC372_CTRL_FORMAT_EXT_YUV_SEMI_PLANAR_420;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP422) {
|
|
pImpWindow->formatUsageBound |=
|
|
NVC372_CTRL_FORMAT_EXT_YUV_SEMI_PLANAR_422;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_EXT_YUV_SP444) {
|
|
pImpWindow->formatUsageBound |=
|
|
NVC372_CTRL_FORMAT_EXT_YUV_SEMI_PLANAR_444;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PLANAR444) {
|
|
pImpWindow->formatUsageBound |=
|
|
NVC372_CTRL_FORMAT_YUV_PLANAR_444;
|
|
}
|
|
if (supportedFormats & NVKMS_SURFACE_MEMORY_FORMATS_YUV_PLANAR420) {
|
|
pImpWindow->formatUsageBound |=
|
|
NVC372_CTRL_FORMAT_YUV_PLANAR_420;
|
|
}
|
|
|
|
if (pImpWindow->formatUsageBound == 0) {
|
|
nvAssert(!"Unknown format in AssignPerWindowImpParams");
|
|
}
|
|
|
|
pImpWindow->maxPixelsFetchedPerLine =
|
|
nvGetMaxPixelsFetchedPerLine(pViewPort->in.width,
|
|
pScaling->maxHDownscaleFactor);
|
|
|
|
pImpWindow->maxDownscaleFactorH = pScaling->maxHDownscaleFactor;
|
|
pImpWindow->maxDownscaleFactorV = pScaling->maxVDownscaleFactor;
|
|
pImpWindow->bUpscalingAllowedV = pScaling->vUpscalingAllowed;
|
|
pImpWindow->inputScalerVerticalTaps =
|
|
NVEvoScalerTapsToNum(pScaling->vTaps);
|
|
|
|
/* Assume we need a full 1025-entry window (input) and tone-mapping
|
|
* output (TMO) LUT. */
|
|
pImpWindow->lut = NVC372_CTRL_IMP_LUT_USAGE_1025;
|
|
pImpWindow->tmoLut = NVC372_CTRL_IMP_LUT_USAGE_1025;
|
|
}
|
|
|
|
NvBool
|
|
nvEvoSetCtrlIsModePossibleParams3(NVDispEvoPtr pDispEvo,
|
|
const NVEvoIsModePossibleDispInput *pInput,
|
|
NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS *pImp)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
const NVEvoCapabilitiesPtr pEvoCaps = &pDevEvo->gpus[0].capabilities;
|
|
NvU32 head;
|
|
|
|
nvkms_memset(pImp, 0, sizeof(*pImp));
|
|
|
|
for (head = 0; head < NVKMS_MAX_HEADS_PER_DISP; head++) {
|
|
const NVHwModeTimingsEvo *pTimings = pInput->head[head].pTimings;
|
|
const NvU32 enableDsc = pInput->head[head].enableDsc;
|
|
const NvBool b2Heads1Or = pInput->head[head].b2Heads1Or;
|
|
const struct NvKmsUsageBounds *pUsage = pInput->head[head].pUsage;
|
|
const NVHwModeViewPortEvo *pViewPort;
|
|
NvU8 impHeadIndex;
|
|
NvU32 layer;
|
|
|
|
if (pTimings == NULL) {
|
|
continue;
|
|
}
|
|
|
|
pViewPort = &pTimings->viewPort;
|
|
|
|
impHeadIndex = pImp->numHeads;
|
|
pImp->numHeads++;
|
|
nvAssert(impHeadIndex < NVC372_CTRL_MAX_POSSIBLE_HEADS);
|
|
|
|
if (!AssignPerHeadImpParams(&pImp->head[impHeadIndex],
|
|
pTimings,
|
|
enableDsc,
|
|
b2Heads1Or,
|
|
head,
|
|
&pEvoCaps->head[head].scalerCaps)) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* XXXnvdisplay: This assumes a fixed window<->head mapping */
|
|
for (layer = 0; layer < pDevEvo->head[head].numLayers; layer++) {
|
|
if (!pUsage->layer[layer].usable) {
|
|
continue;
|
|
}
|
|
|
|
nvAssert(pImp->numWindows < NVC372_CTRL_MAX_POSSIBLE_WINDOWS);
|
|
|
|
AssignPerWindowImpParams(
|
|
&pImp->window[pImp->numWindows],
|
|
pViewPort,
|
|
pUsage->layer[layer].supportedSurfaceMemoryFormats,
|
|
&pUsage->layer[layer].scaling,
|
|
NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(
|
|
pDevEvo->head[head].layer[layer]->channelMask),
|
|
head);
|
|
|
|
pImp->numWindows++;
|
|
}
|
|
}
|
|
|
|
pImp->base.subdeviceIndex = pDispEvo->displayOwner;
|
|
|
|
/* XXXnvdisplay: Set bUseCachedPerfState? */
|
|
|
|
/*
|
|
* Set NEED_MIN_VPSTATE if reallocBandwidth != NONE. RM-IMP will only
|
|
* output the min required display bandwidth values if NEED_MIN_VPSTATE
|
|
* is set.
|
|
*/
|
|
if (pInput->requireBootClocks ||
|
|
(pInput->reallocBandwidth != NV_EVO_REALLOCATE_BANDWIDTH_MODE_NONE)) {
|
|
// XXX TODO: IMP requires lock pin information if pstate information is
|
|
// requested. For now, just assume no locking.
|
|
pImp->options = NVC372_CTRL_IS_MODE_POSSIBLE_OPTIONS_NEED_MIN_VPSTATE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
nvEvoSetIsModePossibleDispOutput3(const NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS *pImp,
|
|
const NvBool result,
|
|
NVEvoIsModePossibleDispOutput *pOutput)
|
|
{
|
|
pOutput->possible = result;
|
|
if (pOutput->possible) {
|
|
pOutput->minRequiredBandwidthKBPS = pImp->minRequiredBandwidthKBPS;
|
|
pOutput->floorBandwidthKBPS = pImp->floorBandwidthKBPS;
|
|
}
|
|
}
|
|
|
|
void
|
|
nvEvoIsModePossibleC3(NVDispEvoPtr pDispEvo,
|
|
const NVEvoIsModePossibleDispInput *pInput,
|
|
NVEvoIsModePossibleDispOutput *pOutput)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS *pImp =
|
|
nvPreallocGet(pDevEvo, PREALLOC_TYPE_IMP_PARAMS, sizeof(*pImp));
|
|
NvBool result = FALSE;
|
|
NvU32 ret;
|
|
|
|
if (!nvEvoSetCtrlIsModePossibleParams3(pDispEvo, pInput, pImp)) {
|
|
goto done;
|
|
}
|
|
|
|
ret = nvRmApiControl(nvEvoGlobal.clientHandle,
|
|
pDevEvo->rmCtrlHandle,
|
|
NVC372_CTRL_CMD_IS_MODE_POSSIBLE,
|
|
pImp, sizeof(*pImp));
|
|
|
|
// XXXnvdisplay TODO: check pImp->minImpVPState if
|
|
// pInput->requireBootClocks is true?
|
|
if (ret != NV_OK || !pImp->bIsPossible) {
|
|
goto done;
|
|
}
|
|
|
|
result = TRUE;
|
|
|
|
done:
|
|
nvEvoSetIsModePossibleDispOutput3(pImp, result, pOutput);
|
|
|
|
nvPreallocRelease(pDevEvo, PREALLOC_TYPE_IMP_PARAMS);
|
|
}
|
|
|
|
void nvEvoPrePostIMPC3(NVDispEvoPtr pDispEvo, NvBool isPre)
|
|
{
|
|
/* Nothing to do on nvdisplay -- pre/post IMP calls are not required. */
|
|
}
|
|
|
|
static void
|
|
EvoFlipC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition);
|
|
|
|
/*
|
|
* Returns TRUE iff the CSC should be enabled (i.e., the matrix is not the
|
|
* identity matrix).
|
|
*/
|
|
static NvBool SetCscMatrixC3(NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCscMatrix *matrix)
|
|
{
|
|
NvU32 method = NVC37E_SET_CSC_RED2RED;
|
|
int y;
|
|
|
|
if (nvIsCscMatrixIdentity(matrix)) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (y = 0; y < 3; y++) {
|
|
int x;
|
|
|
|
for (x = 0; x < 4; x++) {
|
|
// Use DRF_NUM to truncate client-supplied values that are out of
|
|
// range.
|
|
NvU32 val = DRF_NUM(C37E, _SET_CSC_RED2RED, _COEFF,
|
|
matrix->m[y][x]);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, method, 1);
|
|
nvDmaSetEvoMethodData(pChannel, val);
|
|
|
|
method += 4;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void SetCscMatrixC5Wrapper(NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCscMatrix *matrix,
|
|
NvU32 coeffMethod, NvU32 controlMethod,
|
|
NvU32 enableMethodData,
|
|
NvU32 disableMethodData)
|
|
{
|
|
int y;
|
|
|
|
if (nvIsCscMatrixIdentity(matrix)) {
|
|
nvDmaSetStartEvoMethod(pChannel, controlMethod, 1);
|
|
nvDmaSetEvoMethodData(pChannel, disableMethodData);
|
|
return;
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, controlMethod, 1);
|
|
nvDmaSetEvoMethodData(pChannel, enableMethodData);
|
|
|
|
for (y = 0; y < 3; y++) {
|
|
int x;
|
|
|
|
for (x = 0; x < 4; x++) {
|
|
// Use DRF_NUM to truncate client-supplied values that are out of
|
|
// range.
|
|
//
|
|
// Note that it doesn't matter whether we use the CSC00 or CSC11
|
|
// methods to truncate since they're identical.
|
|
NvU32 val = DRF_NUM(C57E, _SET_CSC00COEFFICIENT_C00, _VALUE,
|
|
matrix->m[y][x]);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, coeffMethod, 1);
|
|
nvDmaSetEvoMethodData(pChannel, val);
|
|
|
|
coeffMethod += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SetCsc00MatrixC5(NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCscMatrix *matrix)
|
|
{
|
|
SetCscMatrixC5Wrapper(pChannel,
|
|
matrix,
|
|
NVC57E_SET_CSC00COEFFICIENT_C00, NVC57E_SET_CSC00CONTROL,
|
|
DRF_DEF(C57E, _SET_CSC00CONTROL, _ENABLE, _ENABLE),
|
|
DRF_DEF(C57E, _SET_CSC00CONTROL, _ENABLE, _DISABLE));
|
|
}
|
|
|
|
static void SetCsc11MatrixC5(NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCscMatrix *matrix)
|
|
{
|
|
SetCscMatrixC5Wrapper(pChannel,
|
|
matrix,
|
|
NVC57E_SET_CSC11COEFFICIENT_C00, NVC57E_SET_CSC11CONTROL,
|
|
DRF_DEF(C57E, _SET_CSC11CONTROL, _ENABLE, _ENABLE),
|
|
DRF_DEF(C57E, _SET_CSC11CONTROL, _ENABLE, _DISABLE));
|
|
}
|
|
|
|
/*
|
|
* WAR for GV100 HW bug 1978592:
|
|
*
|
|
* Timestamped flips allow SW to specify the earliest time that the next UPDATE
|
|
* will complete. Due to a HW bug, GV100 waits for the timestamp in the ARMED
|
|
* state (i.e. the timestamps that were pushed in the previous UPDATE) instead
|
|
* of the timestamp in the ASSEMBLY state (the time we want to postpone this
|
|
* flip until).
|
|
*
|
|
* This WAR inserts an additional UPDATE to push the timestamp from ASSEMBLY to
|
|
* ARMED while changing no other state, so the following normal UPDATE can
|
|
* wait for the correct timestamp.
|
|
*
|
|
* This update needs to have the following characteristics:
|
|
*
|
|
* - MIN_PRESENT_INTERVAL 0
|
|
* - TIMESTAMP_MODE _ENABLE
|
|
* - All other SET_PRESENT_CONTROL fields unmodified from previous UPDATE
|
|
* - SET_UPDATE_TIMESTAMP (target timestamp)
|
|
* - RELEASE_ELV _FALSE
|
|
* - Non-interlocked
|
|
* - Non-fliplocked
|
|
*/
|
|
static void
|
|
InsertAdditionalTimestampFlip(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NvU32 presentControl = pChannel->oldPresentControl;
|
|
|
|
/* This hardware bug is only present on GV100 which uses window
|
|
* class C37E. */
|
|
nvAssert(pChannel->hwclass == NVC37E_WINDOW_CHANNEL_DMA);
|
|
|
|
nvAssert(pHwState->timeStamp != 0);
|
|
|
|
/*
|
|
* Update the necessary fields in SET_PRESENT_CONTROL without modifying
|
|
* the existing values by using the cached SET_PRESENT_CONTROL values
|
|
* from the previous update.
|
|
*
|
|
* Note that BEGIN_MODE must not be changed here; even though BEGIN_MODE
|
|
* may currently be NON_TEARING, a NON_TEARING + MIN_PRESENT_INTERVAL 0
|
|
* flip will be correctly collapsed with the surrounding
|
|
* MIN_PRESENT_INTERVAL 1 flips. If we were to change BEGIN_MODE to
|
|
* IMMEDIATE, this would cause an additional delay due to the transition
|
|
* from NON_TEARING to IMMEDIATE.
|
|
*/
|
|
presentControl = FLD_SET_DRF_NUM(C37E, _SET_PRESENT_CONTROL,
|
|
_MIN_PRESENT_INTERVAL,
|
|
0, presentControl);
|
|
presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL,
|
|
_TIMESTAMP_MODE,
|
|
_ENABLE, presentControl);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_PRESENT_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, presentControl);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_UPDATE_TIMESTAMP_LO, 2);
|
|
nvDmaSetEvoMethodData(pChannel, NvU64_LO32(pHwState->timeStamp));
|
|
nvDmaSetEvoMethodData(pChannel, NvU64_HI32(pHwState->timeStamp));
|
|
|
|
// Issue non-interlocked, non-fliplocked, non-ReleaseElv UPDATE
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_INTERLOCK_FLAGS, 1);
|
|
nvDmaSetEvoMethodData(pChannel, 0);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC37E_SET_WINDOW_INTERLOCK_FLAGS,
|
|
1);
|
|
nvDmaSetEvoMethodData(pChannel, 0);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_UPDATE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37E, _UPDATE, _RELEASE_ELV, _FALSE) |
|
|
DRF_NUM(C37E, _UPDATE, _FLIP_LOCK_PIN,
|
|
NVC37E_UPDATE_FLIP_LOCK_PIN_LOCK_PIN_NONE) |
|
|
DRF_DEF(C37E, _UPDATE, _INTERLOCK_WITH_WIN_IMM,
|
|
_DISABLE));
|
|
}
|
|
|
|
static void
|
|
EvoProgramSemaphore3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState)
|
|
{
|
|
nvAssertSameSemaphoreSurface(pHwState);
|
|
|
|
if (pHwState->syncObject.u.semaphores.acquireSurface.pSurfaceEvo == NULL) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_SEMAPHORE, 1);
|
|
nvDmaSetEvoMethodData(pChannel, 0);
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, 0);
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_ACQUIRE, 1);
|
|
nvDmaSetEvoMethodData(pChannel, 0);
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_RELEASE, 1);
|
|
nvDmaSetEvoMethodData(pChannel, 0);
|
|
} else {
|
|
const NVFlipNIsoSurfaceEvoHwState *pNIso =
|
|
&pHwState->syncObject.u.semaphores.acquireSurface;
|
|
|
|
nvAssert(pNIso->format == NVKMS_NISO_FORMAT_FOUR_WORD_NVDISPLAY);
|
|
/* XXX nvdisplay: enforce this at a higher level */
|
|
nvAssert((pNIso->offsetInWords % 4) == 0);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_SEMAPHORE, 1);
|
|
nvDmaSetEvoMethodData(pChannel, pNIso->pSurfaceEvo->planes[0].surfaceDesc.ctxDmaHandle);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_ACQUIRE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
pHwState->syncObject.u.semaphores.acquireValue);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_RELEASE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
pHwState->syncObject.u.semaphores.releaseValue);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SEMAPHORE_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C37E, _SET_SEMAPHORE_CONTROL, _OFFSET,
|
|
pNIso->offsetInWords / 4));
|
|
}
|
|
}
|
|
|
|
static void EvoSetSemaphoreSurfaceAddressAndControlC6(
|
|
const NVDevEvoRec *pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 semaphoreOffset,
|
|
NvU32 ctrlVal)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
/*! set ctx dma handle */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_CONTEXT_DMA_SEMAPHORE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67E, _SET_CONTEXT_DMA_SEMAPHORE, _HANDLE, ctxDmaHandle));
|
|
|
|
/*! set semaphore control and acq-rel mode */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_SEMAPHORE_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, semaphoreOffset | ctrlVal);
|
|
}
|
|
|
|
static void EvoSetAcqSemaphoreSurfaceAddressAndControlC6(
|
|
const NVDevEvoRec *pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 semaphoreOffset,
|
|
NvU32 ctrlVal)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
/*! set ctx dma handle */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_CONTEXT_DMA_ACQ_SEMAPHORE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67E, _SET_CONTEXT_DMA_ACQ, _SEMAPHORE_HANDLE, ctxDmaHandle));
|
|
|
|
/*! set semaphore control and acq mode */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_ACQ_SEMAPHORE_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, semaphoreOffset | ctrlVal);
|
|
}
|
|
|
|
/*!
|
|
* On Tegra, syncpts are used for synchronization between SW and HW,
|
|
* and also across HW engines. Since NvDisplay 4.0 only natively
|
|
* understands semaphores, there's a SHIM layer in the memory subsystem
|
|
* that will convert semaphore acquires/releases into corresponding
|
|
* syncpoint reads/writes. As such, each syncpoint is mapped to an
|
|
* underlying 'dummy' semaphore surface, and the methods for these surfaces
|
|
* need to be programmed as if they were real memory-backed semaphores.
|
|
*/
|
|
|
|
static void
|
|
EvoProgramSemaphore6(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState)
|
|
{
|
|
NvU32 offset, acqMode, relMode, value;
|
|
const NVSurfaceDescriptor *pSurfaceDesc = NULL;
|
|
const NVFlipNIsoSurfaceEvoHwState *pNIso;
|
|
|
|
/*! Program Acq-only semaphore */
|
|
pSurfaceDesc = NULL;
|
|
offset = acqMode = relMode = value = 0;
|
|
if (pHwState->syncObject.usingSyncpt &&
|
|
pHwState->syncObject.u.syncpts.isPreSyncptSpecified) {
|
|
NvU32 id = pHwState->syncObject.u.syncpts.preSyncpt;
|
|
pSurfaceDesc = &pDevEvo->preSyncptTable[id].surfaceDesc;
|
|
acqMode = DRF_DEF(C67E, _SET_ACQ_SEMAPHORE_CONTROL, _ACQ_MODE, _CGEQ);
|
|
value = pHwState->syncObject.u.syncpts.preValue;
|
|
} else {
|
|
if (pHwState->syncObject.u.semaphores.acquireSurface.pSurfaceEvo != NULL) {
|
|
pNIso = &pHwState->syncObject.u.semaphores.acquireSurface;
|
|
pSurfaceDesc = &pNIso->pSurfaceEvo->planes[0].surfaceDesc;
|
|
offset = pNIso->offsetInWords / 4;
|
|
acqMode = DRF_DEF(C67E, _SET_ACQ_SEMAPHORE_CONTROL, _ACQ_MODE, _EQ);
|
|
value = pHwState->syncObject.u.semaphores.acquireValue;
|
|
}
|
|
}
|
|
|
|
pDevEvo->hal->SetAcqSemaphoreSurfaceAddressAndControl(pDevEvo, pChannel,
|
|
pSurfaceDesc, offset, acqMode);
|
|
|
|
/*! set semaphore value */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_ACQ_SEMAPHORE_VALUE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67E, _SET_ACQ_SEMAPHORE_VALUE, _VALUE, value));
|
|
|
|
/*! Program Rel-only semaphore */
|
|
pSurfaceDesc = NULL;
|
|
offset = acqMode = relMode = value = 0;
|
|
if (pHwState->syncObject.usingSyncpt &&
|
|
pHwState->syncObject.u.syncpts.isPostSyncptSpecified) {
|
|
pSurfaceDesc = &pHwState->syncObject.u.syncpts.surfaceDesc;
|
|
acqMode = DRF_DEF(C67E, _SET_SEMAPHORE_CONTROL, _SKIP_ACQ, _TRUE);
|
|
relMode = DRF_DEF(C67E, _SET_SEMAPHORE_CONTROL, _REL_MODE, _WRITE);
|
|
value = pHwState->syncObject.u.syncpts.postValue;
|
|
/*! increase local max val as well */
|
|
pChannel->postSyncpt.syncptMaxVal++;
|
|
} else {
|
|
if (pHwState->syncObject.u.semaphores.releaseSurface.pSurfaceEvo != NULL) {
|
|
pNIso = &pHwState->syncObject.u.semaphores.releaseSurface;
|
|
pSurfaceDesc = &pNIso->pSurfaceEvo->planes[0].surfaceDesc;
|
|
offset = pNIso->offsetInWords / 4;
|
|
acqMode = DRF_DEF(C67E, _SET_SEMAPHORE_CONTROL, _SKIP_ACQ, _TRUE);
|
|
relMode = DRF_DEF(C67E, _SET_SEMAPHORE_CONTROL, _REL_MODE, _WRITE);
|
|
value = pHwState->syncObject.u.semaphores.releaseValue;
|
|
}
|
|
}
|
|
|
|
pDevEvo->hal->SetSemaphoreSurfaceAddressAndControl(pDevEvo, pChannel,
|
|
pSurfaceDesc, offset, (acqMode | relMode));
|
|
|
|
/*! set semaphore value */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_SEMAPHORE_RELEASE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67E, _SET_SEMAPHORE_RELEASE, _VALUE, value));
|
|
}
|
|
|
|
static void EvoSetWinNotifierSurfaceAddressAndControlC3(
|
|
const NVDevEvoRec *pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 notifierOffset,
|
|
NvU32 ctrlVal)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_NOTIFIER, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_CONTEXT_DMA_NOTIFIER, _HANDLE, ctxDmaHandle));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_NOTIFIER_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_NOTIFIER_CONTROL, _OFFSET, notifierOffset) | ctrlVal);
|
|
}
|
|
|
|
static void EvoSetISOSurfaceAddressC3(
|
|
const NVDevEvoRec *pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 offset,
|
|
NvU32 ctxDmaIdx,
|
|
NvBool isBlocklinear)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_ISO(ctxDmaIdx), 1);
|
|
nvDmaSetEvoMethodData(pChannel, ctxDmaHandle);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_OFFSET(ctxDmaIdx), 1);
|
|
nvDmaSetEvoMethodData(pChannel, nvCtxDmaOffsetFromBytes(offset));
|
|
}
|
|
|
|
static NvBool
|
|
EvoFlipC3Common(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState,
|
|
NVEvoUpdateState *updateState,
|
|
NvU32 head)
|
|
{
|
|
const NvKmsSurfaceMemoryFormatInfo *pFormatInfo;
|
|
NvU32 presentControl, eye;
|
|
NvU32 storage;
|
|
NvU8 planeIndex;
|
|
NVSurfaceDescriptor *pSurfaceDesc = NULL;
|
|
NvU32 offset, ctrlVal;
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
/* program notifier */
|
|
|
|
if (pHwState->completionNotifier.surface.pSurfaceEvo == NULL) {
|
|
/*
|
|
* if no notifier surface is attached but vrr RGLine calculations
|
|
* for frame pacing are enabled then we need to provide our own
|
|
* surface and keep getting flip completion updates.
|
|
*/
|
|
const NvU32 sdMask = nvPeekEvoSubDevMask(pDevEvo);
|
|
const NvU32 sd = (sdMask == 0) ? 0 : nv_ffs(sdMask) - 1;
|
|
NVDispHeadStateEvoRec *pHeadState = &pDevEvo->pDispEvo[sd]->headState[head];
|
|
struct NvKmsVrrFramePacingInfo *pVrrFramePacingInfo = &pHeadState->vrrFramePacingInfo;
|
|
|
|
if (pVrrFramePacingInfo->framePacingActive) {
|
|
pSurfaceDesc = &pChannel->notifiersDma[sd].surfaceDesc;
|
|
offset = nvPrepareNextVrrNotifier(pChannel, sd, head);
|
|
ctrlVal = DRF_DEF(C37E, _SET_NOTIFIER_CONTROL, _MODE, _WRITE_AWAKEN);
|
|
pDevEvo->hal->SetWinNotifierSurfaceAddressAndControl(pDevEvo,
|
|
pChannel, pSurfaceDesc, offset, ctrlVal);
|
|
} else {
|
|
offset = ctrlVal = 0;
|
|
pDevEvo->hal->SetWinNotifierSurfaceAddressAndControl(pDevEvo,
|
|
pChannel, NULL, offset, ctrlVal);
|
|
}
|
|
} else {
|
|
const NVFlipNIsoSurfaceEvoHwState *pNIso =
|
|
&pHwState->completionNotifier.surface;
|
|
|
|
nvAssert(pNIso->format == NVKMS_NISO_FORMAT_FOUR_WORD_NVDISPLAY);
|
|
/* XXX nvdisplay: enforce this at a higher level */
|
|
nvAssert((pNIso->offsetInWords % 4) == 0);
|
|
|
|
pSurfaceDesc = &pNIso->pSurfaceEvo->planes[0].surfaceDesc;
|
|
offset = pNIso->offsetInWords / 4;
|
|
ctrlVal = 0;
|
|
if (pHwState->completionNotifier.awaken) {
|
|
ctrlVal = FLD_SET_DRF(C37E, _SET_NOTIFIER_CONTROL, _MODE,
|
|
_WRITE_AWAKEN, ctrlVal);
|
|
} else {
|
|
ctrlVal = FLD_SET_DRF(C37E, _SET_NOTIFIER_CONTROL, _MODE,
|
|
_WRITE, ctrlVal);
|
|
}
|
|
|
|
pDevEvo->hal->SetWinNotifierSurfaceAddressAndControl(pDevEvo,
|
|
pChannel, pSurfaceDesc, offset, ctrlVal);
|
|
}
|
|
|
|
if (!pHwState->pSurfaceEvo[NVKMS_LEFT]) {
|
|
// Disable this window, and set all its ctxdma entries to NULL.
|
|
for (eye = 0; eye < NVKMS_MAX_EYES; eye++) {
|
|
for (planeIndex = 0;
|
|
planeIndex < NVKMS_MAX_PLANES_PER_SURFACE;
|
|
planeIndex++) {
|
|
const NvU8 ctxDmaIdx = EyeAndPlaneToCtxDmaIdx(eye, planeIndex);
|
|
pDevEvo->hal->SetISOSurfaceAddress(pDevEvo, pChannel,
|
|
NULL /* pSurfaceDec */, 0 /* offset */, ctxDmaIdx,
|
|
NV_FALSE /* isBlocklinear */);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
presentControl = DRF_NUM(C37E, _SET_PRESENT_CONTROL, _MIN_PRESENT_INTERVAL,
|
|
pHwState->minPresentInterval);
|
|
|
|
if (pHwState->timeStamp != 0) {
|
|
presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _TIMESTAMP_MODE,
|
|
_ENABLE, presentControl);
|
|
} else {
|
|
presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _TIMESTAMP_MODE,
|
|
_DISABLE, presentControl);
|
|
}
|
|
|
|
if (pHwState->tearing) {
|
|
presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _BEGIN_MODE,
|
|
_IMMEDIATE, presentControl);
|
|
} else {
|
|
presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _BEGIN_MODE,
|
|
_NON_TEARING, presentControl);
|
|
}
|
|
|
|
if (pHwState->pSurfaceEvo[NVKMS_RIGHT]) {
|
|
if (pHwState->perEyeStereoFlip) {
|
|
presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _STEREO_MODE,
|
|
_AT_ANY_FRAME, presentControl);
|
|
} else {
|
|
presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _STEREO_MODE,
|
|
_PAIR_FLIP, presentControl);
|
|
}
|
|
} else {
|
|
presentControl = FLD_SET_DRF(C37E, _SET_PRESENT_CONTROL, _STEREO_MODE,
|
|
_MONO, presentControl);
|
|
}
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_PRESENT_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel, presentControl);
|
|
|
|
/*
|
|
* GV100 timestamped flips need a duplicate update which only changes
|
|
* TIMESTAMP_MODE and MIN_PRESENT_INTERVAL fields in SET_PRESENT_CONTROL;
|
|
* to allow updating these fields without changing anything else in
|
|
* SET_PRESENT_CONTROL, cache the values we sent in previous flips here.
|
|
* (bug 1990958)
|
|
*/
|
|
pChannel->oldPresentControl = presentControl;
|
|
|
|
/* Set the surface parameters. */
|
|
FOR_ALL_EYES(eye) {
|
|
const NVSurfaceEvoRec *pSurfaceEvoPerEye = pHwState->pSurfaceEvo[eye];
|
|
NvU8 numSurfacePlanes = 0;
|
|
NvBool isBlockLinear = NV_FALSE;
|
|
|
|
if (pSurfaceEvoPerEye != NULL) {
|
|
pFormatInfo =
|
|
nvKmsGetSurfaceMemoryFormatInfo(pSurfaceEvoPerEye->format);
|
|
numSurfacePlanes = pFormatInfo->numPlanes;
|
|
isBlockLinear =
|
|
(pSurfaceEvoPerEye->layout == NvKmsSurfaceMemoryLayoutBlockLinear);
|
|
}
|
|
|
|
for (planeIndex = 0;
|
|
planeIndex < NVKMS_MAX_PLANES_PER_SURFACE;
|
|
planeIndex++) {
|
|
const NVSurfaceDescriptor *pSurfaceDesc = NULL;
|
|
NvU64 offset = 0;
|
|
const NvU8 ctxDmaIdx = EyeAndPlaneToCtxDmaIdx(eye, planeIndex);
|
|
|
|
if (planeIndex < numSurfacePlanes) {
|
|
pSurfaceDesc = &pSurfaceEvoPerEye->planes[planeIndex].surfaceDesc;
|
|
offset = pSurfaceEvoPerEye->planes[planeIndex].offset;
|
|
}
|
|
|
|
pDevEvo->hal->SetISOSurfaceAddress(pDevEvo, pChannel,
|
|
pSurfaceDesc, offset, ctxDmaIdx, isBlockLinear);
|
|
}
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SIZE, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_SIZE, _WIDTH, pHwState->pSurfaceEvo[NVKMS_LEFT]->widthInPixels) |
|
|
DRF_NUM(C37E, _SET_SIZE, _HEIGHT, pHwState->pSurfaceEvo[NVKMS_LEFT]->heightInPixels));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SIZE_IN, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_SIZE_IN, _WIDTH, pHwState->sizeIn.width) |
|
|
DRF_NUM(C37E, _SET_SIZE_IN, _HEIGHT, pHwState->sizeIn.height));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_SIZE_OUT, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_SIZE_OUT, _WIDTH, pHwState->sizeOut.width) |
|
|
DRF_NUM(C37E, _SET_SIZE_OUT, _HEIGHT, pHwState->sizeOut.height));
|
|
|
|
/* XXX nvdisplay: enforce pitch/BL layout are consistent between eyes at a
|
|
* higher level */
|
|
|
|
storage = 0;
|
|
if (pHwState->pSurfaceEvo[NVKMS_LEFT]->layout ==
|
|
NvKmsSurfaceMemoryLayoutBlockLinear) {
|
|
const NvU32 blockHeight = pHwState->pSurfaceEvo[NVKMS_LEFT]->log2GobsPerBlockY;
|
|
storage |= DRF_NUM(C37E, _SET_STORAGE, _BLOCK_HEIGHT, blockHeight);
|
|
if (pDevEvo->hal->caps.supportsSetStorageMemoryLayout) {
|
|
storage |= DRF_DEF(C37E, _SET_STORAGE, _MEMORY_LAYOUT, _BLOCKLINEAR);
|
|
}
|
|
} else if (pDevEvo->hal->caps.supportsSetStorageMemoryLayout) {
|
|
storage |= DRF_DEF(C37E, _SET_STORAGE, _MEMORY_LAYOUT, _PITCH);
|
|
}
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_STORAGE, 1);
|
|
nvDmaSetEvoMethodData(pChannel, storage);
|
|
|
|
pFormatInfo = nvKmsGetSurfaceMemoryFormatInfo(
|
|
pHwState->pSurfaceEvo[NVKMS_LEFT]->format);
|
|
|
|
for (planeIndex = 0;
|
|
planeIndex < NVKMS_MAX_PLANES_PER_SURFACE;
|
|
planeIndex++) {
|
|
NvU32 pitch;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_PLANAR_STORAGE(planeIndex),
|
|
1);
|
|
|
|
if (planeIndex >= pFormatInfo->numPlanes) {
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_PLANAR_STORAGE, _PITCH, 0));
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Per nvdClass_01.mfs, the HEAD_SET_STORAGE_PITCH "units are blocks
|
|
* if the layout is BLOCKLINEAR, the units are multiples of 64 bytes
|
|
* if the layout is PITCH."
|
|
*/
|
|
pitch = pHwState->pSurfaceEvo[NVKMS_LEFT]->planes[planeIndex].pitch;
|
|
if (pHwState->pSurfaceEvo[NVKMS_LEFT]->layout ==
|
|
NvKmsSurfaceMemoryLayoutBlockLinear) {
|
|
/* pitch is already in units of blocks; no conversion needed. */
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_PLANAR_STORAGE, _PITCH, pitch));
|
|
} else {
|
|
/* XXX nvdisplay: enforce this at a higher level */
|
|
nvAssert((pitch & 63) == 0);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_PLANAR_STORAGE, _PITCH, pitch >> 6));
|
|
}
|
|
}
|
|
|
|
ASSERT_EYES_MATCH(pHwState->pSurfaceEvo, format);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_UPDATE_TIMESTAMP_LO, 2);
|
|
nvDmaSetEvoMethodData(pChannel, NvU64_LO32(pHwState->timeStamp));
|
|
nvDmaSetEvoMethodData(pChannel, NvU64_HI32(pHwState->timeStamp));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* This function returns TRUE if precomp needs to swap the U and V components to
|
|
* support the given input surface format. For all such formats,
|
|
* SetParams.SwapUV needs to be enabled.
|
|
*
|
|
* Due to the "feature" described in bug 1640117, there's a mismatch in the
|
|
* ihub<->precomp interface:
|
|
* - For all Yx___UxVx_N444 and Yx___UxVx_N422 formats, ihub will fetch and send
|
|
* the V sample as the first chroma byte, and the U sample as the second byte.
|
|
* However, precomp expects the U sample as the first byte, and the V sample
|
|
* as the second byte.
|
|
* - For all Yx___VxUx_N420 formats, ihub will fetch and send the U sample as
|
|
* the first chroma byte, and the V sample as the second byte.
|
|
* However, precomp expects the V sample as the first byte, and the U sample
|
|
* as the second byte.
|
|
*
|
|
* In the above explanation, note that ihub simply fetches and sends the chroma
|
|
* bytes in the same order that they're packed in memory.
|
|
*/
|
|
static NvBool IsSurfaceFormatUVSwapped(
|
|
const enum NvKmsSurfaceMemoryFormat format)
|
|
{
|
|
switch (format) {
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N420:
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N444:
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N422:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N420:
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N444:
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N422:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N420:
|
|
return TRUE;
|
|
case NvKmsSurfaceMemoryFormatY8_U8__Y8_V8_N422:
|
|
case NvKmsSurfaceMemoryFormatU8_Y8__V8_Y8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N420:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N444:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N422:
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N420:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N444:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N422:
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N420:
|
|
case NvKmsSurfaceMemoryFormatY8___U8___V8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___U8___V8_N420:
|
|
return FALSE;
|
|
case NvKmsSurfaceMemoryFormatI8:
|
|
case NvKmsSurfaceMemoryFormatA1R5G5B5:
|
|
case NvKmsSurfaceMemoryFormatX1R5G5B5:
|
|
case NvKmsSurfaceMemoryFormatR5G6B5:
|
|
case NvKmsSurfaceMemoryFormatA8R8G8B8:
|
|
case NvKmsSurfaceMemoryFormatX8R8G8B8:
|
|
case NvKmsSurfaceMemoryFormatA8B8G8R8:
|
|
case NvKmsSurfaceMemoryFormatX8B8G8R8:
|
|
case NvKmsSurfaceMemoryFormatA2B10G10R10:
|
|
case NvKmsSurfaceMemoryFormatX2B10G10R10:
|
|
case NvKmsSurfaceMemoryFormatRF16GF16BF16AF16:
|
|
case NvKmsSurfaceMemoryFormatRF16GF16BF16XF16:
|
|
case NvKmsSurfaceMemoryFormatR16G16B16A16:
|
|
case NvKmsSurfaceMemoryFormatRF32GF32BF32AF32:
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Map the given NvKmsSurfaceMemoryFormat to its corresponding HW format for the
|
|
* C370 (Volta) NVDISPLAY class.
|
|
*
|
|
* Volta supports YUV422 packed, but this function excludes the corresponding
|
|
* mappings because the required programming support hasn't been added to NVKMS
|
|
* yet.
|
|
*
|
|
* Return 0 in the case of an unrecognized NvKmsSurfaceMemoryFormat.
|
|
*/
|
|
static NvU32 nvHwFormatFromKmsFormatC3(
|
|
const enum NvKmsSurfaceMemoryFormat format)
|
|
{
|
|
switch (format) {
|
|
case NvKmsSurfaceMemoryFormatI8:
|
|
return NVC37E_SET_PARAMS_FORMAT_I8;
|
|
case NvKmsSurfaceMemoryFormatA1R5G5B5:
|
|
case NvKmsSurfaceMemoryFormatX1R5G5B5:
|
|
return NVC37E_SET_PARAMS_FORMAT_A1R5G5B5;
|
|
case NvKmsSurfaceMemoryFormatR5G6B5:
|
|
return NVC37E_SET_PARAMS_FORMAT_R5G6B5;
|
|
case NvKmsSurfaceMemoryFormatA8R8G8B8:
|
|
return NVC37E_SET_PARAMS_FORMAT_A8R8G8B8;
|
|
case NvKmsSurfaceMemoryFormatX8R8G8B8:
|
|
return NVC37E_SET_PARAMS_FORMAT_X8R8G8B8;
|
|
case NvKmsSurfaceMemoryFormatA8B8G8R8:
|
|
return NVC37E_SET_PARAMS_FORMAT_A8B8G8R8;
|
|
case NvKmsSurfaceMemoryFormatX8B8G8R8:
|
|
return NVC37E_SET_PARAMS_FORMAT_X8B8G8R8;
|
|
case NvKmsSurfaceMemoryFormatA2B10G10R10:
|
|
return NVC37E_SET_PARAMS_FORMAT_A2B10G10R10;
|
|
case NvKmsSurfaceMemoryFormatX2B10G10R10:
|
|
return NVC37E_SET_PARAMS_FORMAT_X2BL10GL10RL10_XRBIAS;
|
|
case NvKmsSurfaceMemoryFormatRF16GF16BF16AF16:
|
|
case NvKmsSurfaceMemoryFormatRF16GF16BF16XF16:
|
|
return NVC37E_SET_PARAMS_FORMAT_RF16_GF16_BF16_AF16;
|
|
case NvKmsSurfaceMemoryFormatR16G16B16A16:
|
|
return NVC37E_SET_PARAMS_FORMAT_R16_G16_B16_A16;
|
|
case NvKmsSurfaceMemoryFormatRF32GF32BF32AF32:
|
|
case NvKmsSurfaceMemoryFormatY8_U8__Y8_V8_N422:
|
|
case NvKmsSurfaceMemoryFormatU8_Y8__V8_Y8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N420:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N420:
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N444:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N444:
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N422:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N422:
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N420:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N420:
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N444:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N444:
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N422:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N422:
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N420:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N420:
|
|
case NvKmsSurfaceMemoryFormatY8___U8___V8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___U8___V8_N420:
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Map the given NvKmsSurfaceMemoryFormat to its corresponding HW format for the
|
|
* C570 (Turing) NVDISPLAY class.
|
|
*
|
|
* Return 0 in the case of an unrecognized NvKmsSurfaceMemoryFormat.
|
|
*/
|
|
static NvU32 nvHwFormatFromKmsFormatC5(
|
|
const enum NvKmsSurfaceMemoryFormat format)
|
|
{
|
|
switch (format) {
|
|
case NvKmsSurfaceMemoryFormatY8_U8__Y8_V8_N422:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y8_U8__Y8_V8_N422;
|
|
case NvKmsSurfaceMemoryFormatU8_Y8__V8_Y8_N422:
|
|
return NVC57E_SET_PARAMS_FORMAT_U8_Y8__V8_Y8_N422;
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N444:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y8___U8V8_N444;
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N422:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y8___U8V8_N422;
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N420:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N420:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y8___V8U8_N420;
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N444:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N444:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y10___U10V10_N444;
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N422:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N422:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y10___U10V10_N422;
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N420:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N420:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y10___V10U10_N420;
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N444:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N444:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y12___U12V12_N444;
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N422:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N422:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y12___U12V12_N422;
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N420:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N420:
|
|
return NVC57E_SET_PARAMS_FORMAT_Y12___V12U12_N420;
|
|
case NvKmsSurfaceMemoryFormatY8___U8___V8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___U8___V8_N420:
|
|
case NvKmsSurfaceMemoryFormatI8:
|
|
case NvKmsSurfaceMemoryFormatA1R5G5B5:
|
|
case NvKmsSurfaceMemoryFormatX1R5G5B5:
|
|
case NvKmsSurfaceMemoryFormatR5G6B5:
|
|
case NvKmsSurfaceMemoryFormatA8R8G8B8:
|
|
case NvKmsSurfaceMemoryFormatX8R8G8B8:
|
|
case NvKmsSurfaceMemoryFormatA8B8G8R8:
|
|
case NvKmsSurfaceMemoryFormatX8B8G8R8:
|
|
case NvKmsSurfaceMemoryFormatA2B10G10R10:
|
|
case NvKmsSurfaceMemoryFormatX2B10G10R10:
|
|
case NvKmsSurfaceMemoryFormatRF16GF16BF16AF16:
|
|
case NvKmsSurfaceMemoryFormatRF16GF16BF16XF16:
|
|
case NvKmsSurfaceMemoryFormatR16G16B16A16:
|
|
case NvKmsSurfaceMemoryFormatRF32GF32BF32AF32:
|
|
return nvHwFormatFromKmsFormatC3(format);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Map the given NvKmsSurfaceMemoryFormat to its corresponding HW format for the
|
|
* C670 (Orin and Ampere) NVDISPLAY class.
|
|
*
|
|
* Return 0 in the case of an unrecognized NvKmsSurfaceMemoryFormat.
|
|
*/
|
|
static NvU32 nvHwFormatFromKmsFormatC6(
|
|
const enum NvKmsSurfaceMemoryFormat format)
|
|
{
|
|
switch (format) {
|
|
case NvKmsSurfaceMemoryFormatY8___U8___V8_N444:
|
|
return NVC67E_SET_PARAMS_FORMAT_Y8___U8___V8_N444;
|
|
case NvKmsSurfaceMemoryFormatY8___U8___V8_N420:
|
|
return NVC67E_SET_PARAMS_FORMAT_Y8___U8___V8_N420;
|
|
case NvKmsSurfaceMemoryFormatX2B10G10R10:
|
|
return NVC67E_SET_PARAMS_FORMAT_A2B10G10R10;
|
|
case NvKmsSurfaceMemoryFormatY8_U8__Y8_V8_N422:
|
|
case NvKmsSurfaceMemoryFormatU8_Y8__V8_Y8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N444:
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N422:
|
|
case NvKmsSurfaceMemoryFormatY8___U8V8_N420:
|
|
case NvKmsSurfaceMemoryFormatY8___V8U8_N420:
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N444:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N444:
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N422:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N422:
|
|
case NvKmsSurfaceMemoryFormatY10___U10V10_N420:
|
|
case NvKmsSurfaceMemoryFormatY10___V10U10_N420:
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N444:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N444:
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N422:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N422:
|
|
case NvKmsSurfaceMemoryFormatY12___U12V12_N420:
|
|
case NvKmsSurfaceMemoryFormatY12___V12U12_N420:
|
|
case NvKmsSurfaceMemoryFormatI8:
|
|
case NvKmsSurfaceMemoryFormatA1R5G5B5:
|
|
case NvKmsSurfaceMemoryFormatX1R5G5B5:
|
|
case NvKmsSurfaceMemoryFormatR5G6B5:
|
|
case NvKmsSurfaceMemoryFormatA8R8G8B8:
|
|
case NvKmsSurfaceMemoryFormatX8R8G8B8:
|
|
case NvKmsSurfaceMemoryFormatA8B8G8R8:
|
|
case NvKmsSurfaceMemoryFormatX8B8G8R8:
|
|
case NvKmsSurfaceMemoryFormatA2B10G10R10:
|
|
case NvKmsSurfaceMemoryFormatRF16GF16BF16AF16:
|
|
case NvKmsSurfaceMemoryFormatRF16GF16BF16XF16:
|
|
case NvKmsSurfaceMemoryFormatR16G16B16A16:
|
|
case NvKmsSurfaceMemoryFormatRF32GF32BF32AF32:
|
|
return nvHwFormatFromKmsFormatC5(format);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
NVLutSurfaceEvoPtr EvoGetLutSurface3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState)
|
|
{
|
|
NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
|
|
NvU32 head = pDevEvo->headForWindow[win];
|
|
NvBool found = FALSE;
|
|
const NVDispEvoRec *pDispEvo = NULL;
|
|
NvU32 sd;
|
|
|
|
if ((pHwState->pSurfaceEvo[NVKMS_LEFT] == NULL) ||
|
|
(head == NV_INVALID_HEAD)) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Input Lut is explicitly enabled by client */
|
|
if (pHwState->inputLut.pLutSurfaceEvo != NULL) {
|
|
return pHwState->inputLut.pLutSurfaceEvo;
|
|
}
|
|
|
|
/*
|
|
* For everything but I8 surfaces, we can just use the specified
|
|
* LUT, even if it's NULL.
|
|
* For I8 surfaces, we can only use the specified surface if it's
|
|
* non-NULL (an input LUT is required).
|
|
*/
|
|
if (pHwState->pSurfaceEvo[NVKMS_LEFT]->format !=
|
|
NvKmsSurfaceMemoryFormatI8) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* The rest of the function is to handle the I8 case where no input
|
|
* LUT was specified: look up the LUT to use from the device.
|
|
*/
|
|
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
if (nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)) {
|
|
if (found) {
|
|
nvAssert(pDispEvo == pDevEvo->gpus[sd].pDispEvo);
|
|
} else {
|
|
pDispEvo = pDevEvo->gpus[sd].pDispEvo;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
nvAssert(found);
|
|
|
|
/*
|
|
* It is not allowed to change the input LUT on immediate flips. The
|
|
* higher-level code should makes sure to disable tearing if there is change
|
|
* in the surface format and curLUTIndex does not change until next
|
|
* EvoSetLUTContextDma3() call which also makes sure to disable tearing.
|
|
*/
|
|
return pDispEvo->headState[head].lut.pCurrSurface;
|
|
}
|
|
|
|
static void
|
|
EvoFlipC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition)
|
|
{
|
|
NvBool enableCSC, swapUV, flip3Return;
|
|
enum NvKmsSurfaceMemoryFormat format;
|
|
NVLutSurfaceEvoPtr pLutSurfaceEvo =
|
|
EvoGetLutSurface3(pDevEvo, pChannel, pHwState);
|
|
NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
|
|
NvU32 head = pDevEvo->headForWindow[win];
|
|
|
|
if (pHwState->timeStamp != 0) {
|
|
InsertAdditionalTimestampFlip(pDevEvo, pChannel, pHwState,
|
|
updateState);
|
|
}
|
|
|
|
flip3Return = EvoFlipC3Common(pDevEvo, pChannel, pHwState, updateState, head);
|
|
|
|
/* program semaphore */
|
|
EvoProgramSemaphore3(pDevEvo, pChannel, pHwState);
|
|
|
|
if (!flip3Return) {
|
|
return;
|
|
}
|
|
|
|
format = pHwState->pSurfaceEvo[NVKMS_LEFT]->format;
|
|
|
|
enableCSC = SetCscMatrixC3(pChannel, &pHwState->cscMatrix);
|
|
swapUV = IsSurfaceFormatUVSwapped(format);
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_PARAMS, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
(enableCSC ? DRF_DEF(C37E, _SET_PARAMS, _CSC, _ENABLE) :
|
|
DRF_DEF(C37E, _SET_PARAMS, _CSC, _DISABLE)) |
|
|
DRF_NUM(C37E, _SET_PARAMS, _FORMAT, nvHwFormatFromKmsFormatC3(format)) |
|
|
(swapUV ? DRF_DEF(C37E, _SET_PARAMS, _SWAP_UV, _ENABLE) :
|
|
DRF_DEF(C37E, _SET_PARAMS, _SWAP_UV, _DISABLE)) |
|
|
DRF_DEF(C37E, _SET_PARAMS, _UNDERREPLICATE, _DISABLE));
|
|
|
|
if (pLutSurfaceEvo) {
|
|
const NvU32 ctxDma = pLutSurfaceEvo->surfaceDesc.ctxDmaHandle;
|
|
const NvU32 origin = offsetof(NVEvoLutDataRec, base);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTROL_INPUT_LUT, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37E, _SET_CONTROL_INPUT_LUT, _SIZE, _SIZE_1025) |
|
|
DRF_DEF(C37E, _SET_CONTROL_INPUT_LUT, _RANGE, _UNITY) |
|
|
DRF_DEF(C37E, _SET_CONTROL_INPUT_LUT, _OUTPUT_MODE, _INDEX));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_OFFSET_INPUT_LUT, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_OFFSET_INPUT_LUT, _ORIGIN, origin));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_INPUT_LUT, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_CONTEXT_DMA_INPUT_LUT, _HANDLE, ctxDma));
|
|
} else {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_CONTEXT_DMA_INPUT_LUT, 1);
|
|
nvDmaSetEvoMethodData(pChannel, 0);
|
|
}
|
|
|
|
UpdateCompositionC3(pDevEvo, pChannel,
|
|
&pHwState->composition, updateState,
|
|
format);
|
|
}
|
|
|
|
static void EvoSetupPQEotfBaseLutC5(NVEvoLutDataRec *pData,
|
|
enum NvKmsLUTState *lutState,
|
|
NvU32 *lutSize,
|
|
NvBool *isLutModeVss)
|
|
{
|
|
NvU32 lutDataStartingIndex = NV_LUT_VSS_HEADER_SIZE;
|
|
NvU32 numEotfPQ512Entries = ARRAY_LEN(EotfPQ512Entries);
|
|
NvU32 eotfTableIdx;
|
|
NvU64 vssHead = 0;
|
|
NvU32 lutEntryCounter = 0, i;
|
|
|
|
// Skip LUT data init if already done
|
|
if (*lutState == NvKmsLUTStatePQ) {
|
|
goto skipInit;
|
|
}
|
|
|
|
// VSS Header
|
|
for (lutEntryCounter = 0; lutEntryCounter < NV_LUT_VSS_HEADER_SIZE; lutEntryCounter++) {
|
|
vssHead = 0;
|
|
for (i = 0; ((i < 16) && (((lutEntryCounter * 16) + i) < ARRAY_LEN(EotfPQ512SegSizesLog2))); i++) {
|
|
NvU64 temp = EotfPQ512SegSizesLog2[(lutEntryCounter * 16) + i];
|
|
temp = temp << (i * 3);
|
|
vssHead |= temp;
|
|
}
|
|
nvkms_memcpy(&(pData->base[lutEntryCounter]), &vssHead, sizeof(NVEvoLutEntryRec));
|
|
}
|
|
|
|
for (eotfTableIdx = 0; eotfTableIdx < numEotfPQ512Entries; eotfTableIdx++) {
|
|
/*
|
|
* Values are in range [0.0, 125.0], will be scaled back by OLUT.
|
|
* XXX HDR TODO: Divide by 125.0 if output mode is not HDR?
|
|
*/
|
|
pData->base[eotfTableIdx + lutDataStartingIndex].Red =
|
|
pData->base[eotfTableIdx + lutDataStartingIndex].Green =
|
|
pData->base[eotfTableIdx + lutDataStartingIndex].Blue =
|
|
EotfPQ512Entries[eotfTableIdx];
|
|
}
|
|
|
|
// Copy the last entry for interpolation
|
|
pData->base[numEotfPQ512Entries + lutDataStartingIndex].Red =
|
|
pData->base[numEotfPQ512Entries + lutDataStartingIndex - 1].Red;
|
|
pData->base[numEotfPQ512Entries + lutDataStartingIndex].Green =
|
|
pData->base[numEotfPQ512Entries + lutDataStartingIndex - 1].Green;
|
|
pData->base[numEotfPQ512Entries + lutDataStartingIndex].Blue =
|
|
pData->base[numEotfPQ512Entries + lutDataStartingIndex - 1].Blue;
|
|
|
|
skipInit:
|
|
*lutState = NvKmsLUTStatePQ;
|
|
*lutSize = NV_LUT_VSS_HEADER_SIZE + numEotfPQ512Entries + 1;
|
|
*isLutModeVss = TRUE;
|
|
}
|
|
|
|
static void
|
|
EvoSetupIdentityBaseLutC5(NVEvoLutDataRec *pData,
|
|
enum NvKmsLUTState *lutState,
|
|
NvU32 *lutSize,
|
|
NvBool *isLutModeVss)
|
|
{
|
|
int i;
|
|
|
|
// Skip LUT data init if already done
|
|
if (*lutState == NvKmsLUTStateIdentity) {
|
|
goto skipInit;
|
|
}
|
|
|
|
ct_assert(NV_NUM_EVO_LUT_ENTRIES == 1025);
|
|
|
|
// nvdisplay 3 uses FP16 entries in the ILUT.
|
|
for (i = 0; i < 1024; i++) {
|
|
pData->base[NV_LUT_VSS_HEADER_SIZE + i].Red =
|
|
pData->base[NV_LUT_VSS_HEADER_SIZE + i].Green =
|
|
pData->base[NV_LUT_VSS_HEADER_SIZE + i].Blue = nvUnorm10ToFp16(i).v;
|
|
}
|
|
pData->base[NV_LUT_VSS_HEADER_SIZE + 1024] =
|
|
pData->base[NV_LUT_VSS_HEADER_SIZE + 1023];
|
|
|
|
skipInit:
|
|
*lutState = NvKmsLUTStateIdentity;
|
|
*lutSize = NV_LUT_VSS_HEADER_SIZE + NV_NUM_EVO_LUT_ENTRIES;
|
|
*isLutModeVss = FALSE;
|
|
}
|
|
|
|
static void EvoSetILUTSurfaceAddressC5(
|
|
const NVDevEvoRec *pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 offset)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CONTEXT_DMA_ILUT, 1);
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57E, _SET_CONTEXT_DMA_ILUT, _HANDLE, ctxDmaHandle));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_OFFSET_ILUT, 1);
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C57E, _SET_OFFSET_ILUT, _ORIGIN, offset));
|
|
}
|
|
|
|
static void
|
|
EvoFlipC5Common(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition)
|
|
{
|
|
enum NvKmsSurfaceMemoryFormat format;
|
|
NvBool swapUV;
|
|
NvU32 hTaps, vTaps;
|
|
NvBool scaling = FALSE;
|
|
NVLutSurfaceEvoPtr pLutSurfaceEvo = NULL;
|
|
NvU32 lutSize = NV_NUM_EVO_LUT_ENTRIES;
|
|
NvBool isLutModeVss = FALSE;
|
|
|
|
NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
|
|
NvU32 head = pDevEvo->headForWindow[win];
|
|
|
|
const NvU32 sdMask = nvPeekEvoSubDevMask(pDevEvo);
|
|
const NvU32 sd = (sdMask == 0) ? 0 : nv_ffs(sdMask) - 1;
|
|
const NVDispHeadStateEvoRec *pHeadState = &pDevEvo->pDispEvo[sd]->headState[head];
|
|
|
|
// XXX HDR TODO: Handle other colorspaces
|
|
// XXX HDR TODO: Enable custom input LUTs with HDR
|
|
if (pHwState->colorSpace != NVKMS_INPUT_COLORSPACE_BT2100_PQ) {
|
|
pLutSurfaceEvo = EvoGetLutSurface3(pDevEvo, pChannel, pHwState);
|
|
}
|
|
|
|
if (!EvoFlipC3Common(pDevEvo, pChannel, pHwState, updateState, head)) {
|
|
ConfigureTmoLut(pDevEvo, pHwState, pChannel);
|
|
return;
|
|
}
|
|
|
|
format = pHwState->pSurfaceEvo[NVKMS_LEFT]->format;
|
|
|
|
swapUV = IsSurfaceFormatUVSwapped(format);
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_PARAMS, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_PARAMS, _FORMAT, nvHwFormatFromKmsFormatC6(format)) |
|
|
(swapUV ? DRF_DEF(C57E, _SET_PARAMS, _SWAP_UV, _ENABLE) :
|
|
DRF_DEF(C57E, _SET_PARAMS, _SWAP_UV, _DISABLE)));
|
|
|
|
/*
|
|
* In nvdisplay 2, there was a fixed-function block in the precomp FMT
|
|
* module that was responsible for YUV->RGB conversion.
|
|
*
|
|
* In nvdisplay 3, that fixed-function block no longer exists.
|
|
* In its place, there's a generic 3x4 S5.16 coefficient matrix that SW must
|
|
* explicitly configure to convert the input surface format to the internal
|
|
* RGB pipe native format.
|
|
*/
|
|
EvoSetFMTMatrixC5(pChannel, format, pHwState);
|
|
|
|
vTaps = (pHwState->vTaps >= NV_EVO_SCALER_5TAPS) ?
|
|
NVC57E_SET_CONTROL_INPUT_SCALER_VERTICAL_TAPS_TAPS_5 :
|
|
NVC57E_SET_CONTROL_INPUT_SCALER_VERTICAL_TAPS_TAPS_2;
|
|
hTaps = (pHwState->hTaps >= NV_EVO_SCALER_5TAPS) ?
|
|
NVC57E_SET_CONTROL_INPUT_SCALER_HORIZONTAL_TAPS_TAPS_5 :
|
|
NVC57E_SET_CONTROL_INPUT_SCALER_HORIZONTAL_TAPS_TAPS_2;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_CONTROL_INPUT_SCALER, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57E, _SET_CONTROL_INPUT_SCALER, _VERTICAL_TAPS, vTaps) |
|
|
DRF_NUM(C57E, _SET_CONTROL_INPUT_SCALER, _HORIZONTAL_TAPS, hTaps));
|
|
|
|
scaling = (pHwState->sizeIn.width != pHwState->sizeOut.width) ||
|
|
(pHwState->sizeIn.height != pHwState->sizeOut.height);
|
|
nvAssert(!(scaling && bypassComposition));
|
|
|
|
/*
|
|
* If scaling or tonemapping, we must enable the CSC0 and CSC1 pipelines.
|
|
*
|
|
* If no scaling or tonemapping, just use CSC11 to convert from the input
|
|
* gamut to the output (panel) gamut, and disable everything else.
|
|
*/
|
|
if (scaling ||
|
|
nvNeedsTmoLut(pDevEvo, pChannel, pHwState,
|
|
nvGetHDRSrcMaxLum(pHwState),
|
|
pHeadState->hdrInfoFrame.staticMetadata.maxCLL)) {
|
|
ConfigureCsc0C5(pDevEvo, pChannel, pHwState->colorSpace, TRUE);
|
|
ConfigureCsc1C5(pDevEvo, pChannel, TRUE);
|
|
} else {
|
|
ConfigureCsc0C5(pDevEvo, pChannel, pHwState->colorSpace, FALSE);
|
|
ConfigureCsc1C5(pDevEvo, pChannel, FALSE);
|
|
|
|
SetCsc11MatrixC5(pChannel, &pHwState->cscMatrix);
|
|
}
|
|
|
|
// In nvdisplay 3, an ILUT is required to convert the input surface to FP16,
|
|
// unless the surface being displayed is already FP16 to begin with.
|
|
if ((format == NvKmsSurfaceMemoryFormatRF16GF16BF16AF16) ||
|
|
(format == NvKmsSurfaceMemoryFormatRF16GF16BF16XF16) || bypassComposition) {
|
|
nvAssert((pHwState->colorSpace == NVKMS_INPUT_COLORSPACE_SCRGB_LINEAR) ||
|
|
(pHwState->colorSpace == NVKMS_INPUT_COLORSPACE_NONE));
|
|
pLutSurfaceEvo = NULL;
|
|
} else if (!pLutSurfaceEvo) {
|
|
NVEvoLutDataRec *pData = NULL;
|
|
|
|
pLutSurfaceEvo = pDevEvo->lut.defaultLut;
|
|
pData = pLutSurfaceEvo->subDeviceAddress[sd];
|
|
|
|
nvAssert(pData);
|
|
|
|
switch (pHwState->colorSpace) {
|
|
case NVKMS_INPUT_COLORSPACE_BT2100_PQ:
|
|
EvoSetupPQEotfBaseLutC5(pData,
|
|
&pDevEvo->lut.defaultBaseLUTState[sd],
|
|
&lutSize, &isLutModeVss);
|
|
break;
|
|
case NVKMS_INPUT_COLORSPACE_NONE:
|
|
EvoSetupIdentityBaseLutC5(pData,
|
|
&pDevEvo->lut.defaultBaseLUTState[sd],
|
|
&lutSize, &isLutModeVss);
|
|
break;
|
|
default: // XXX HDR TODO: Handle other colorspaces
|
|
nvAssert(FALSE);
|
|
EvoSetupIdentityBaseLutC5(pData,
|
|
&pDevEvo->lut.defaultBaseLUTState[sd],
|
|
&lutSize, &isLutModeVss);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pLutSurfaceEvo) {
|
|
const NvU32 origin = offsetof(NVEvoLutDataRec, base);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_ILUT_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
(isLutModeVss ? DRF_DEF(C57E, _SET_ILUT_CONTROL, _INTERPOLATE, _ENABLE) :
|
|
DRF_DEF(C57E, _SET_ILUT_CONTROL, _INTERPOLATE, _DISABLE)) |
|
|
DRF_DEF(C57E, _SET_ILUT_CONTROL, _MIRROR, _DISABLE) |
|
|
(isLutModeVss ? DRF_DEF(C57E, _SET_ILUT_CONTROL, _MODE, _SEGMENTED) :
|
|
DRF_DEF(C57E, _SET_ILUT_CONTROL, _MODE, _DIRECT10)) |
|
|
DRF_NUM(C57E, _SET_ILUT_CONTROL, _SIZE, lutSize));
|
|
|
|
pDevEvo->hal->SetILUTSurfaceAddress(pDevEvo, pChannel,
|
|
&pLutSurfaceEvo->surfaceDesc, origin);
|
|
} else {
|
|
pDevEvo->hal->SetILUTSurfaceAddress(pDevEvo, pChannel,
|
|
NULL /* pSurfaceDesc */, 0 /* offset */);
|
|
}
|
|
|
|
ConfigureTmoLut(pDevEvo, pHwState, pChannel);
|
|
|
|
UpdateCompositionC5(pDevEvo, pChannel,
|
|
&pHwState->composition, updateState,
|
|
bypassComposition,
|
|
format);
|
|
}
|
|
|
|
static void
|
|
EvoFlipC5(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition)
|
|
{
|
|
EvoFlipC5Common(pDevEvo, pChannel, pHwState, updateState, bypassComposition);
|
|
|
|
/* Work around bug 2117571: whenever the tearing mode is changing, send a
|
|
* software method to notify RM. */
|
|
if (pHwState->tearing != pChannel->oldTearingMode) {
|
|
NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
|
|
NvU32 head = pDevEvo->headForWindow[win];
|
|
|
|
if (head != NV_INVALID_HEAD) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_WINDOWS_NOTIFY_RM, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57E, _WINDOWS_NOTIFY_RM, _VSYNC_STATE_CHANGE, _TRUE) |
|
|
DRF_NUM(C57E, _WINDOWS_NOTIFY_RM, _ASSOCIATED_HEAD, head) |
|
|
(pHwState->tearing ?
|
|
DRF_DEF(C57E, _WINDOWS_NOTIFY_RM, _VSYNC_STATE, _OFF) :
|
|
DRF_DEF(C57E, _WINDOWS_NOTIFY_RM, _VSYNC_STATE, _ON)));
|
|
}
|
|
|
|
pChannel->oldTearingMode = pHwState->tearing;
|
|
}
|
|
|
|
/* program semaphore */
|
|
EvoProgramSemaphore3(pDevEvo, pChannel, pHwState);
|
|
}
|
|
|
|
void
|
|
nvEvoFlipC6(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NVFlipChannelEvoHwState *pHwState,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition)
|
|
{
|
|
NvBool fromTop = TRUE;
|
|
NvBool fromLeft = TRUE;
|
|
|
|
NvU32 vDirVal = 0;
|
|
NvU32 hDirVal = 0;
|
|
|
|
switch (pHwState->rrParams.rotation) {
|
|
case NVKMS_ROTATION_90:
|
|
case NVKMS_ROTATION_270:
|
|
nvAssert(!"Invalid rotation requested.");
|
|
/* Fall-through */
|
|
case NVKMS_ROTATION_0:
|
|
break;
|
|
case NVKMS_ROTATION_180:
|
|
fromTop = FALSE;
|
|
fromLeft = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (pHwState->rrParams.reflectionX) {
|
|
fromLeft = !fromLeft;
|
|
}
|
|
if (pHwState->rrParams.reflectionY) {
|
|
fromTop = !fromTop;
|
|
}
|
|
|
|
vDirVal = (fromTop ?
|
|
DRF_DEF(C67E, _SET_SCAN_DIRECTION, _VERTICAL_DIRECTION, _FROM_TOP) :
|
|
DRF_DEF(C67E, _SET_SCAN_DIRECTION, _VERTICAL_DIRECTION, _FROM_BOTTOM));
|
|
hDirVal = (fromLeft ?
|
|
DRF_DEF(C67E, _SET_SCAN_DIRECTION, _HORIZONTAL_DIRECTION, _FROM_LEFT) :
|
|
DRF_DEF(C67E, _SET_SCAN_DIRECTION, _HORIZONTAL_DIRECTION, _FROM_RIGHT));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67E_SET_SCAN_DIRECTION, 1);
|
|
nvDmaSetEvoMethodData(pChannel, vDirVal | hDirVal);
|
|
|
|
EvoFlipC5Common(pDevEvo, pChannel, pHwState, updateState, bypassComposition);
|
|
|
|
/* program semaphore */
|
|
EvoProgramSemaphore6(pDevEvo, pChannel, pHwState);
|
|
}
|
|
|
|
static void UpdateComposition(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
/* smaller => closer to front */
|
|
NvU32 depth,
|
|
NvU32 colorKeySelect,
|
|
NvU32 constantAlpha,
|
|
NvU32 compositionFactorSelect,
|
|
const NVColorKey key,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_COMPOSITION_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_COMPOSITION_CONTROL, _COLOR_KEY_SELECT, colorKeySelect) |
|
|
DRF_NUM(C37E, _SET_COMPOSITION_CONTROL, _DEPTH, depth));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_COMPOSITION_CONSTANT_ALPHA, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37E, _SET_COMPOSITION_CONSTANT_ALPHA, _K1, constantAlpha));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_COMPOSITION_FACTOR_SELECT, 1);
|
|
nvDmaSetEvoMethodData(pChannel, compositionFactorSelect);
|
|
|
|
#define UPDATE_COMPONENT(_COMP, _C, _c) \
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37E_SET_KEY_##_COMP, 1); \
|
|
if (key.match##_C) { \
|
|
nvDmaSetEvoMethodData(pChannel, \
|
|
DRF_NUM(C37E, _SET_KEY_##_COMP, _MIN, key._c) | \
|
|
DRF_NUM(C37E, _SET_KEY_##_COMP, _MAX, key._c)); \
|
|
} else { \
|
|
nvDmaSetEvoMethodData(pChannel, \
|
|
DRF_NUM(C37E, _SET_KEY_##_COMP, _MIN, 0) | \
|
|
DRF_SHIFTMASK(NVC37E_SET_KEY_##_COMP##_MAX)); \
|
|
}
|
|
|
|
if (colorKeySelect !=
|
|
NVC37E_SET_COMPOSITION_CONTROL_COLOR_KEY_SELECT_DISABLE) {
|
|
UPDATE_COMPONENT(ALPHA, A, a);
|
|
UPDATE_COMPONENT(RED_CR, R, r);
|
|
UPDATE_COMPONENT(GREEN_Y, G, g);
|
|
UPDATE_COMPONENT(BLUE_CB, B, b);
|
|
}
|
|
|
|
#undef UPDATE_COMPONENT
|
|
}
|
|
|
|
static void EvoFlipTransitionWARC3(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
|
|
const NVEvoSubDevHeadStateRec *pSdHeadState,
|
|
const NVFlipEvoHwState *pFlipState,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
/* Nothing to do for Volta */
|
|
}
|
|
|
|
/*
|
|
* Hardware bug 2193096 requires that we send special software methods around
|
|
* a window channel update that transitions from NULL ctxdma to non-NULL or
|
|
* vice versa. Below we compare the current hardware state in pSdHeadState
|
|
* against the state to be pushed in this update in pFlipState, and add any
|
|
* window(s) that qualify to the 'flipTransitionWAR' mask in the updateState.
|
|
*/
|
|
static void EvoFlipTransitionWARC5(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
|
|
const NVEvoSubDevHeadStateRec *pSdHeadState,
|
|
const NVFlipEvoHwState *pFlipState,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NvU32 layer;
|
|
|
|
for (layer = 0; layer < pDevEvo->head[head].numLayers; layer++) {
|
|
const NvBool enabledPrev =
|
|
pSdHeadState->layer[layer].pSurfaceEvo[NVKMS_LEFT] != NULL;
|
|
const NvBool enabledNext =
|
|
pFlipState->layer[layer].pSurfaceEvo[NVKMS_LEFT] != NULL;
|
|
|
|
if (enabledPrev != enabledNext) {
|
|
/* XXX TODO: dynamic window assignment */
|
|
const NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(
|
|
pDevEvo->head[head].layer[layer]->channelMask);
|
|
updateState->subdev[sd].flipTransitionWAR |=
|
|
DRF_IDX_DEF64(_EVO, _CHANNEL_MASK, _WINDOW, win, _ENABLE);
|
|
|
|
nvAssert(pFlipState->dirty.layer[layer]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void nvEvoFlipTransitionWARC6(NVDevEvoPtr pDevEvo, NvU32 sd, NvU32 head,
|
|
const NVEvoSubDevHeadStateRec *pSdHeadState,
|
|
const NVFlipEvoHwState *pFlipState,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
/* Nothing to do for Orin/Ampere for now */
|
|
}
|
|
|
|
static void
|
|
UpdateCompositionC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCompositionParams *pCompParams,
|
|
NVEvoUpdateState *updateState,
|
|
enum NvKmsSurfaceMemoryFormat format)
|
|
{
|
|
NvU32 colorKeySelect;
|
|
NvU32 compositionFactorSelect = 0;
|
|
NvU32 constantAlpha = 0;
|
|
NvU32 match;
|
|
|
|
switch (pCompParams->colorKeySelect) {
|
|
case NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE:
|
|
colorKeySelect =
|
|
NVC37E_SET_COMPOSITION_CONTROL_COLOR_KEY_SELECT_DISABLE;
|
|
break;
|
|
case NVKMS_COMPOSITION_COLOR_KEY_SELECT_SRC:
|
|
colorKeySelect =
|
|
NVC37E_SET_COMPOSITION_CONTROL_COLOR_KEY_SELECT_SRC;
|
|
|
|
break;
|
|
case NVKMS_COMPOSITION_COLOR_KEY_SELECT_DST:
|
|
colorKeySelect =
|
|
NVC37E_SET_COMPOSITION_CONTROL_COLOR_KEY_SELECT_DST;
|
|
|
|
break;
|
|
default:
|
|
nvAssert(!"Invalid color key select");
|
|
return;
|
|
}
|
|
|
|
/* Match and nomatch pixels should not use alpha blending mode at once. */
|
|
nvAssert((colorKeySelect == NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE) ||
|
|
(!NvKmsIsCompositionModeUseAlpha(pCompParams->blendingMode[0])) ||
|
|
(!NvKmsIsCompositionModeUseAlpha(pCompParams->blendingMode[1])));
|
|
|
|
/*
|
|
* Match and nomatch pixels should not use blending mode PREMULT_ALPHA,
|
|
* NON_PREMULT_ALPHA, PREMULT_SURFACE_ALPHA, and NON_PREMULT_SURFACE_ALPHA
|
|
* at once.
|
|
*/
|
|
nvAssert(pCompParams->blendingMode[0] == NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE ||
|
|
pCompParams->blendingMode[0] == NVKMS_COMPOSITION_BLENDING_MODE_TRANSPARENT ||
|
|
pCompParams->blendingMode[1] == NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE ||
|
|
pCompParams->blendingMode[1] == NVKMS_COMPOSITION_BLENDING_MODE_TRANSPARENT);
|
|
|
|
for (match = 0; match <= 1; match++) {
|
|
switch (pCompParams->blendingMode[match]) {
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE:
|
|
if (match == 1) {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _ONE) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _ZERO);
|
|
} else {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _ONE) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _ZERO);
|
|
}
|
|
break;
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_TRANSPARENT:
|
|
if (match == 1) {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _ZERO) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _ONE);
|
|
} else {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _ZERO) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _ONE);
|
|
}
|
|
break;
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA:
|
|
constantAlpha = 255;
|
|
if (match == 1) {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1_TIMES_SRC) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1_TIMES_SRC);
|
|
} else {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1_TIMES_SRC) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1_TIMES_SRC);
|
|
}
|
|
break;
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA:
|
|
constantAlpha = 255;
|
|
if (match == 1) {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1_TIMES_SRC);
|
|
} else {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1_TIMES_SRC);
|
|
}
|
|
break;
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA:
|
|
constantAlpha = pCompParams->surfaceAlpha;
|
|
if (match == 1) {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1_TIMES_SRC) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1_TIMES_SRC);
|
|
} else {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1_TIMES_SRC) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1_TIMES_SRC);
|
|
}
|
|
break;
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA:
|
|
constantAlpha = pCompParams->surfaceAlpha;
|
|
if (match == 1) {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1_TIMES_SRC);
|
|
} else {
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1_TIMES_SRC);
|
|
}
|
|
break;
|
|
default:
|
|
nvAssert(!"Invalid blend mode");
|
|
return;
|
|
}
|
|
|
|
/* Override the composition factors for X channel emulated surface format. */
|
|
if (NvKmsIsCompositionModeUseAlpha(pCompParams->blendingMode[match]) &&
|
|
((pDevEvo->hal->caps.xEmulatedSurfaceMemoryFormats & NVBIT64(format)) != 0U)) {
|
|
if (match == 1) {
|
|
/* Clear the previously selected composition factors for match pixels. */
|
|
compositionFactorSelect &= ~(DRF_MASK(NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_MATCH_SELECT) <<
|
|
DRF_SHIFT(NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_MATCH_SELECT));
|
|
compositionFactorSelect &= ~(DRF_MASK(NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_MATCH_SELECT) <<
|
|
DRF_SHIFT(NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_MATCH_SELECT));
|
|
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_MATCH_SELECT, _K1) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_MATCH_SELECT, _NEG_K1);
|
|
} else {
|
|
/* Clear the previously selected composition factors for no-match pixels. */
|
|
compositionFactorSelect &= ~(DRF_MASK(NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_NO_MATCH_SELECT) <<
|
|
DRF_SHIFT(NVC37E_SET_COMPOSITION_FACTOR_SELECT_SRC_COLOR_FACTOR_NO_MATCH_SELECT));
|
|
compositionFactorSelect &= ~(DRF_MASK(NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_NO_MATCH_SELECT) <<
|
|
DRF_SHIFT(NVC37E_SET_COMPOSITION_FACTOR_SELECT_DST_COLOR_FACTOR_NO_MATCH_SELECT));
|
|
|
|
compositionFactorSelect |=
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _SRC_COLOR_FACTOR_NO_MATCH_SELECT, _K1) |
|
|
DRF_DEF(C37E, _SET_COMPOSITION_FACTOR_SELECT, _DST_COLOR_FACTOR_NO_MATCH_SELECT, _NEG_K1);
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateComposition(pDevEvo,
|
|
pChannel,
|
|
pCompParams->depth,
|
|
colorKeySelect,
|
|
constantAlpha,
|
|
compositionFactorSelect,
|
|
pCompParams->colorKey,
|
|
updateState);
|
|
}
|
|
|
|
static void EvoBypassCompositionC5(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57E_SET_COMPOSITION_CONTROL, 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57E, _SET_COMPOSITION_CONTROL, _BYPASS, _ENABLE));
|
|
}
|
|
|
|
static void
|
|
UpdateCompositionC5(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const struct NvKmsCompositionParams *pCompParams,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition,
|
|
enum NvKmsSurfaceMemoryFormat format)
|
|
{
|
|
if (bypassComposition) {
|
|
EvoBypassCompositionC5(pDevEvo, pChannel, updateState);
|
|
} else {
|
|
UpdateCompositionC3(pDevEvo, pChannel, pCompParams,
|
|
updateState, format);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The LUT entries in INDEX_1025_UNITY_RANGE have 16 bits, with the
|
|
* black value at 24576, and the white at 49151. Since the effective
|
|
* range is 16384, we treat this as a 14-bit LUT. However, we need to
|
|
* clear the low 3 bits to WAR hardware bug 813188. This gives us
|
|
* 14-bit LUT values, but only 11 bits of precision.
|
|
* XXXnvdisplay: Bug 813188 is supposed to be fixed on NVDisplay; can we expose
|
|
* more precision?
|
|
*/
|
|
static inline NvU16 ColorToLUTEntry(NvU16 val)
|
|
{
|
|
const NvU16 val14bit = val >> 2;
|
|
return (val14bit & ~7) + 24576;
|
|
}
|
|
|
|
/*
|
|
* Unlike earlier EVO implementations, the INDEX mode of the input LUT on
|
|
* NVDisplay is straightforward: the value of the input component is expanded
|
|
* to the LUT size by simply shifting left by the difference between the LUT
|
|
* index width and the component width. We do the same, here, to select the
|
|
* right LUT entry to fill.
|
|
*/
|
|
static inline NvU32 GetLUTIndex(int i, int componentSize)
|
|
{
|
|
return i << (10 - componentSize);
|
|
}
|
|
|
|
static void
|
|
EvoFillLUTSurfaceC3(NVEvoLutEntryRec *pLUTBuffer,
|
|
const NvU16 *red,
|
|
const NvU16 *green,
|
|
const NvU16 *blue,
|
|
int nColorMapEntries, int depth)
|
|
{
|
|
int i;
|
|
NvU32 rSize, gSize, bSize;
|
|
|
|
switch (depth) {
|
|
case 15:
|
|
rSize = gSize = bSize = 5;
|
|
break;
|
|
case 16:
|
|
rSize = bSize = 5;
|
|
gSize = 6;
|
|
break;
|
|
case 8:
|
|
case 24:
|
|
rSize = gSize = bSize = 8;
|
|
break;
|
|
case 30:
|
|
rSize = gSize = bSize = 10;
|
|
break;
|
|
default:
|
|
nvAssert(!"invalid depth");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < nColorMapEntries; i++) {
|
|
if (i < (1 << rSize)) {
|
|
pLUTBuffer[GetLUTIndex(i, rSize)].Red = ColorToLUTEntry(red[i]);
|
|
}
|
|
if (i < (1 << gSize)) {
|
|
pLUTBuffer[GetLUTIndex(i, gSize)].Green = ColorToLUTEntry(green[i]);
|
|
}
|
|
if (i < (1 << bSize)) {
|
|
pLUTBuffer[GetLUTIndex(i, bSize)].Blue = ColorToLUTEntry(blue[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline float16_t ColorToFp16(NvU16 val, float32_t maxf)
|
|
{
|
|
return nvUnormToFp16(val, maxf);
|
|
}
|
|
|
|
void
|
|
nvEvoFillLUTSurfaceC5(NVEvoLutEntryRec *pLUTBuffer,
|
|
const NvU16 *red,
|
|
const NvU16 *green,
|
|
const NvU16 *blue,
|
|
int nColorMapEntries, int depth)
|
|
{
|
|
int i;
|
|
NvU32 rSize, gSize, bSize;
|
|
const float32_t maxf = ui32_to_f32(0xffff);
|
|
|
|
switch (depth) {
|
|
case 15:
|
|
rSize = gSize = bSize = 5;
|
|
break;
|
|
case 16:
|
|
rSize = bSize = 5;
|
|
gSize = 6;
|
|
break;
|
|
case 8:
|
|
case 24:
|
|
rSize = gSize = bSize = 8;
|
|
break;
|
|
case 30:
|
|
rSize = gSize = bSize = 10;
|
|
break;
|
|
default:
|
|
nvAssert(!"invalid depth");
|
|
return;
|
|
}
|
|
|
|
// Skip the VSS header
|
|
pLUTBuffer += NV_LUT_VSS_HEADER_SIZE;
|
|
|
|
for (i = 0; i < nColorMapEntries; i++) {
|
|
if (i < (1 << rSize)) {
|
|
pLUTBuffer[GetLUTIndex(i, rSize)].Red =
|
|
ColorToFp16(red[i], maxf).v;
|
|
}
|
|
if (i < (1 << gSize)) {
|
|
pLUTBuffer[GetLUTIndex(i, gSize)].Green =
|
|
ColorToFp16(green[i], maxf).v;
|
|
}
|
|
if (i < (1 << bSize)) {
|
|
pLUTBuffer[GetLUTIndex(i, bSize)].Blue =
|
|
ColorToFp16(blue[i], maxf).v;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void EvoSetLUTContextDma3(NVDevEvoPtr pDevEvo,
|
|
const int head,
|
|
NVLutSurfaceEvoPtr pLutSurfEvo,
|
|
NvBool enableBaseLut,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 sd;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
/*
|
|
* For Window semaphores and Notifiers, the general rule of thumb is that
|
|
* the current semaphore/notifier will be released if the address for the
|
|
* semaphore/notifier changes (via context DMA change or offset change).
|
|
* This allows SW to push updates in the window channel that change other
|
|
* methods, but do not cause the semaphore or notifier to be released. This
|
|
* make it possible to reprogram the window channel with new input Lut
|
|
* without releasing semaphore.
|
|
*
|
|
* Note that higher-level code will use core channel notifiers to
|
|
* synchronize these LUT updates, but that's okay because nvEvoUpdateC3()
|
|
* will interlock the core and window channel(s) updates together.
|
|
*/
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
if (nvPeekEvoSubDevMask(pDevEvo) & (1 << sd)) {
|
|
NVEvoChannelPtr pChannel = pDevEvo->window[head << 1];
|
|
NVEvoSubDevHeadStateRec *pSdHeadState =
|
|
&pDevEvo->gpus[sd].headState[head];
|
|
NVFlipChannelEvoHwState *pMainFlipState =
|
|
&pSdHeadState->layer[NVKMS_MAIN_LAYER];
|
|
NVLutSurfaceEvoPtr pInputLutSurfEvo = enableBaseLut ?
|
|
pLutSurfEvo : NULL;
|
|
|
|
if (pMainFlipState->inputLut.pLutSurfaceEvo == pInputLutSurfEvo) {
|
|
continue;
|
|
}
|
|
|
|
pMainFlipState->inputLut.pLutSurfaceEvo = pInputLutSurfEvo;
|
|
/* It is not allowed to change the input LUT on immediate flips. */
|
|
pMainFlipState->tearing = FALSE;
|
|
|
|
nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
|
|
|
|
pDevEvo->hal->Flip(pDevEvo, pChannel, pMainFlipState, updateState,
|
|
bypassComposition);
|
|
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void EvoSetLUTContextDmaC3(const NVDispEvoRec *pDispEvo,
|
|
const int head,
|
|
NVLutSurfaceEvoPtr pLutSurfEvo,
|
|
NvBool enableBaseLut,
|
|
NvBool enableOutputLut,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition)
|
|
{
|
|
NvU32 ctxdma = (pLutSurfEvo != NULL) ? pLutSurfEvo->surfaceDesc.ctxDmaHandle : 0;
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU64 offset;
|
|
|
|
nvAssert(ctxdma || (!enableBaseLut && !enableOutputLut));
|
|
|
|
nvPushEvoSubDevMaskDisp(pDispEvo);
|
|
|
|
EvoSetLUTContextDma3(pDevEvo,
|
|
head,
|
|
pLutSurfEvo,
|
|
enableBaseLut,
|
|
updateState,
|
|
bypassComposition);
|
|
|
|
/* Program the output LUT */
|
|
|
|
offset = offsetof(NVEvoLutDataRec, output);
|
|
nvAssert((offset & 0xff) == 0);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL_OUTPUT_LUT(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_LUT, _SIZE, _SIZE_1025) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_LUT, _RANGE, _UNITY) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_OUTPUT_LUT, _OUTPUT_MODE, _INTERPOLATE));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_OFFSET_OUTPUT_LUT(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_OFFSET_OUTPUT_LUT, _ORIGIN, offset >> 8));
|
|
|
|
/* Set the ctxdma for the output LUT */
|
|
|
|
if (!enableOutputLut) {
|
|
/* Class C37D has no separate enable flag. */
|
|
ctxdma = 0;
|
|
}
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTEXT_DMA_OUTPUT_LUT(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTEXT_DMA_OUTPUT_LUT, _HANDLE, ctxdma));
|
|
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
|
|
static void EvoSetupPQOetfOutputLutC5(NVEvoLutDataRec *pData,
|
|
enum NvKmsLUTState *lutState,
|
|
NvU32 *lutSize,
|
|
NvBool *isLutModeVss)
|
|
{
|
|
NvU32 lutDataStartingIndex = NV_LUT_VSS_HEADER_SIZE;
|
|
NvU32 numOetfPQ512Entries = ARRAY_LEN(OetfPQ512Entries);
|
|
NvU32 oetfTableIdx;
|
|
NvU64 vssHead = 0;
|
|
NvU32 lutEntryCounter = 0, i;
|
|
|
|
// Skip LUT data init if already done
|
|
if (*lutState == NvKmsLUTStatePQ) {
|
|
goto skipInit;
|
|
}
|
|
|
|
// VSS Header
|
|
for (lutEntryCounter = 0; lutEntryCounter < NV_LUT_VSS_HEADER_SIZE; lutEntryCounter++) {
|
|
vssHead = 0;
|
|
for (i = 0; ((i < 16) && (((lutEntryCounter * 16) + i) < ARRAY_LEN(OetfPQ512SegSizesLog2))); i++) {
|
|
NvU64 temp = OetfPQ512SegSizesLog2[(lutEntryCounter * 16) + i];
|
|
temp = temp << (i * 3);
|
|
vssHead |= temp;
|
|
}
|
|
nvkms_memcpy(&(pData->output[lutEntryCounter]), &vssHead, sizeof(NVEvoLutEntryRec));
|
|
}
|
|
|
|
for (oetfTableIdx = 0; oetfTableIdx < numOetfPQ512Entries; oetfTableIdx++) {
|
|
pData->output[oetfTableIdx + lutDataStartingIndex].Red =
|
|
pData->output[oetfTableIdx + lutDataStartingIndex].Green =
|
|
pData->output[oetfTableIdx + lutDataStartingIndex].Blue =
|
|
OetfPQ512Entries[oetfTableIdx];
|
|
}
|
|
|
|
// Copy the last entry for interpolation
|
|
pData->output[numOetfPQ512Entries + lutDataStartingIndex].Red =
|
|
pData->output[numOetfPQ512Entries + lutDataStartingIndex - 1].Red;
|
|
pData->output[numOetfPQ512Entries + lutDataStartingIndex].Green =
|
|
pData->output[numOetfPQ512Entries + lutDataStartingIndex - 1].Green;
|
|
pData->output[numOetfPQ512Entries + lutDataStartingIndex].Blue =
|
|
pData->output[numOetfPQ512Entries + lutDataStartingIndex - 1].Blue;
|
|
|
|
skipInit:
|
|
*lutState = NvKmsLUTStatePQ;
|
|
*lutSize = numOetfPQ512Entries + 1;
|
|
*isLutModeVss = TRUE;
|
|
}
|
|
|
|
static void EvoSetupIdentityOutputLutC5(NVEvoLutDataRec *pData,
|
|
enum NvKmsLUTState *lutState,
|
|
NvU32 *lutSize,
|
|
NvBool *isLutModeVss)
|
|
{
|
|
NvU32 i;
|
|
|
|
// Skip LUT data init if already done
|
|
if (*lutState == NvKmsLUTStateIdentity) {
|
|
goto skipInit;
|
|
}
|
|
|
|
ct_assert(NV_NUM_EVO_LUT_ENTRIES == 1025);
|
|
|
|
// nvdisplay 3 uses 16-bit fixed-point entries in the OLUT.
|
|
for (i = 0; i < 1024; i++) {
|
|
pData->output[NV_LUT_VSS_HEADER_SIZE + i].Red =
|
|
pData->output[NV_LUT_VSS_HEADER_SIZE + i].Green =
|
|
pData->output[NV_LUT_VSS_HEADER_SIZE + i].Blue = (i << (16 - 10));
|
|
}
|
|
pData->output[NV_LUT_VSS_HEADER_SIZE + 1024] =
|
|
pData->output[NV_LUT_VSS_HEADER_SIZE + 1023];
|
|
|
|
skipInit:
|
|
*lutState = NvKmsLUTStateIdentity;
|
|
*lutSize = 1025;
|
|
*isLutModeVss = FALSE;
|
|
}
|
|
|
|
static void SetupHDROutputLUT(NVDevEvoPtr pDevEvo,
|
|
const NVDispHeadStateEvoRec *pHeadState,
|
|
NvU32 sd,
|
|
enum NvKmsLUTState *lutState,
|
|
NvU32 *lutSize,
|
|
NvBool *isLutModeVss)
|
|
{
|
|
NVLutSurfaceEvoPtr pLut = pDevEvo->lut.defaultLut;
|
|
NVEvoLutDataRec *pData = pLut->subDeviceAddress[sd];
|
|
|
|
// XXX HDR TODO: Support other transfer functions
|
|
nvAssert(pHeadState->tf == NVKMS_OUTPUT_TF_PQ);
|
|
|
|
EvoSetupPQOetfOutputLutC5(pData, lutState, lutSize, isLutModeVss);
|
|
}
|
|
|
|
void nvSetupOutputLUT5(NVDevEvoPtr pDevEvo,
|
|
const NVDispHeadStateEvoRec *pHeadState,
|
|
const int head,
|
|
NVLutSurfaceEvoPtr pLutSurfEvo,
|
|
NvBool enableBaseLut,
|
|
NvBool enableOutputLut,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition,
|
|
NVSurfaceDescriptor **pSurfaceDesc,
|
|
NvU32 *lutSize,
|
|
NvBool *disableOcsc0,
|
|
NvU32 *fpNormScale,
|
|
NvBool *isLutModeVss)
|
|
{
|
|
NvU32 sd;
|
|
|
|
EvoSetLUTContextDma3(pDevEvo,
|
|
head,
|
|
pLutSurfEvo,
|
|
enableBaseLut,
|
|
updateState,
|
|
bypassComposition);
|
|
|
|
/* Set the ctxdma for the output LUT */
|
|
|
|
if (bypassComposition) {
|
|
*pSurfaceDesc = NULL;
|
|
|
|
/* if we're not enabling the OLUT, OCSC0 also needs to be disabled */
|
|
*disableOcsc0 = TRUE;
|
|
} else if (!enableOutputLut) {
|
|
/* Use the default OLUT if the client didn't provide one */
|
|
*pSurfaceDesc = &pDevEvo->lut.defaultLut->surfaceDesc;
|
|
|
|
// Setup default OLUT
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
// XXX HDR TODO: Support other transfer functions
|
|
if (pHeadState->tf == NVKMS_OUTPUT_TF_PQ) {
|
|
SetupHDROutputLUT(pDevEvo, pHeadState, sd,
|
|
&pDevEvo->lut.defaultOutputLUTState[sd],
|
|
lutSize, isLutModeVss);
|
|
|
|
*disableOcsc0 = TRUE;
|
|
|
|
/*
|
|
* Scale from [0.0, 125.0] to [0.0, 1.0]
|
|
* XXX HDR TODO: Assumes input is in this range, SDR is not.
|
|
*/
|
|
*fpNormScale = 0xFFFFFFFF / 125;
|
|
} else {
|
|
NVLutSurfaceEvoPtr pLut = pDevEvo->lut.defaultLut;
|
|
NVEvoLutDataRec *pData = pLut->subDeviceAddress[sd];
|
|
|
|
EvoSetupIdentityOutputLutC5(
|
|
pData,
|
|
&pDevEvo->lut.defaultOutputLUTState[sd],
|
|
lutSize, isLutModeVss);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SetOLUTSurfaceAddress(
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 offset,
|
|
NvU32 head)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_CONTEXT_DMA_OLUT(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57D, _HEAD_SET_CONTEXT_DMA_OLUT, _HANDLE, ctxDmaHandle));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OFFSET_OLUT(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57D, _HEAD_SET_OFFSET_OLUT, _ORIGIN, offset >> 8));
|
|
}
|
|
|
|
static void EvoSetLUTContextDmaC5(const NVDispEvoRec *pDispEvo,
|
|
const int head,
|
|
NVLutSurfaceEvoPtr pLutSurfEvo,
|
|
NvBool enableBaseLut,
|
|
NvBool enableOutputLut,
|
|
NVEvoUpdateState *updateState,
|
|
NvBool bypassComposition)
|
|
{
|
|
NVSurfaceDescriptor *pSurfaceDesc =
|
|
(pLutSurfEvo != NULL) ? &pLutSurfEvo->surfaceDesc : NULL;
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU64 offset;
|
|
NvU32 lutSize = NV_NUM_EVO_LUT_ENTRIES;
|
|
NvBool isLutModeVss = FALSE;
|
|
const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
|
|
NvBool disableOcsc0 = FALSE;
|
|
NvU32 fpNormScale = 0xFFFFFFFF;
|
|
|
|
// XXX HDR TODO: Enable custom output LUTs with HDR
|
|
// XXX HDR TODO: Support other transfer functions
|
|
if (pHeadState->tf == NVKMS_OUTPUT_TF_PQ) {
|
|
enableOutputLut = FALSE;
|
|
}
|
|
|
|
nvAssert(pSurfaceDesc || (!enableBaseLut && !enableOutputLut));
|
|
|
|
nvPushEvoSubDevMaskDisp(pDispEvo);
|
|
|
|
nvSetupOutputLUT5(pDevEvo,
|
|
pHeadState,
|
|
head,
|
|
pLutSurfEvo,
|
|
enableBaseLut,
|
|
enableOutputLut,
|
|
updateState,
|
|
bypassComposition,
|
|
&pSurfaceDesc,
|
|
&lutSize,
|
|
&disableOcsc0,
|
|
&fpNormScale,
|
|
&isLutModeVss);
|
|
|
|
if (disableOcsc0) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC0CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, DRF_DEF(C57D, _HEAD_SET_OCSC0CONTROL, _ENABLE, _DISABLE));
|
|
}
|
|
|
|
/* Program the output LUT */
|
|
|
|
offset = offsetof(NVEvoLutDataRec, output);
|
|
nvAssert((offset & 0xff) == 0);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OLUT_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
((isLutModeVss || !nvkms_output_rounding_fix()) ?
|
|
DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _INTERPOLATE, _ENABLE) :
|
|
DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _INTERPOLATE, _DISABLE)) |
|
|
DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _MIRROR, _DISABLE) |
|
|
(isLutModeVss ? DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _MODE, _SEGMENTED) :
|
|
DRF_DEF(C57D, _HEAD_SET_OLUT_CONTROL, _MODE, _DIRECT10)) |
|
|
DRF_NUM(C57D, _HEAD_SET_OLUT_CONTROL, _SIZE, NV_LUT_VSS_HEADER_SIZE +
|
|
lutSize));
|
|
|
|
SetOLUTSurfaceAddress(pChannel, pSurfaceDesc, offset, head);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OLUT_FP_NORM_SCALE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, fpNormScale);
|
|
|
|
if (!disableOcsc0) {
|
|
/* only enable OCSC0 after enabling the OLUT */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_OCSC0CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, DRF_DEF(C57D, _HEAD_SET_OCSC0CONTROL, _ENABLE, _ENABLE));
|
|
}
|
|
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
|
|
static inline NvU32 ReadCapReg(volatile const NvU32 *pCaps, NvU32 offset)
|
|
{
|
|
/* Offsets are in bytes, but the array has dword-sized elements. */
|
|
return pCaps[offset / sizeof(NvU32)];
|
|
}
|
|
|
|
static NvBool QueryStereoPinC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoSubDevPtr pEvoSubDev,
|
|
NvU32 *pStereoPin)
|
|
{
|
|
NVC370_CTRL_GET_LOCKPINS_CAPS_PARAMS params = { };
|
|
|
|
params.base.subdeviceIndex = pEvoSubDev->subDeviceInstance;
|
|
|
|
if (nvRmApiControl(nvEvoGlobal.clientHandle,
|
|
pDevEvo->displayHandle,
|
|
NVC370_CTRL_CMD_GET_LOCKPINS_CAPS,
|
|
¶ms, sizeof(params)) != NVOS_STATUS_SUCCESS) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"Failed to query stereo pin");
|
|
return FALSE;
|
|
}
|
|
|
|
if ((params.stereoPin >= NV_EVO_NUM_LOCK_PIN_CAPS) ||
|
|
(params.stereoPin == NVC370_CTRL_GET_LOCKPINS_CAPS_STEREO_PIN_NONE)) {
|
|
return FALSE;
|
|
} else {
|
|
*pStereoPin = params.stereoPin;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static void EvoParseCapabilityNotifier3(NVDevEvoPtr pDevEvo,
|
|
NVEvoSubDevPtr pEvoSubDev,
|
|
volatile const NvU32 *pCaps)
|
|
{
|
|
NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
|
|
const NvU32 sysCap = ReadCapReg(pCaps, NVC373_SYS_CAP);
|
|
const NvU32 sysCapB = ReadCapReg(pCaps, NVC373_SYS_CAPB);
|
|
NvU32 i, stereoPin;
|
|
NvU32 layer;
|
|
|
|
pDevEvo->caps.cursorCompositionCaps =
|
|
(struct NvKmsCompositionCapabilities) {
|
|
.supportedColorKeySelects =
|
|
NVBIT(NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE),
|
|
|
|
.colorKeySelect = {
|
|
[NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE] = {
|
|
.supportedBlendModes = {
|
|
[1] = NV_EVO3_SUPPORTED_CURSOR_COMP_BLEND_MODES,
|
|
},
|
|
},
|
|
}
|
|
};
|
|
|
|
for (layer = 0;
|
|
layer < ARRAY_LEN(pDevEvo->caps.layerCaps); layer++) {
|
|
pDevEvo->caps.layerCaps[layer].composition =
|
|
(struct NvKmsCompositionCapabilities) {
|
|
.supportedColorKeySelects =
|
|
NVBIT(NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE) |
|
|
NVBIT(NVKMS_COMPOSITION_COLOR_KEY_SELECT_SRC) |
|
|
NVBIT(NVKMS_COMPOSITION_COLOR_KEY_SELECT_DST),
|
|
|
|
.colorKeySelect = {
|
|
[NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE] = {
|
|
.supportedBlendModes = {
|
|
[1] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
|
|
},
|
|
},
|
|
|
|
[NVKMS_COMPOSITION_COLOR_KEY_SELECT_SRC] = {
|
|
.supportedBlendModes = {
|
|
[0] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
|
|
[1] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
|
|
},
|
|
},
|
|
|
|
[NVKMS_COMPOSITION_COLOR_KEY_SELECT_DST] = {
|
|
.supportedBlendModes = {
|
|
[0] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
|
|
[1] = NV_EVO3_SUPPORTED_WINDOW_COMP_BLEND_MODES,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
/*
|
|
* Previous EVO display implementations exposed capabilities for lock pins,
|
|
* detailing which pin(s) could be used for which functions. The idea was
|
|
* that it didn't make sense to try to drive a stereo pin with a fliplock
|
|
* signal (for example), so the pin associated with the stereo function was
|
|
* marked as stereo-capable but not any other function; attempting to use a
|
|
* non-stereo-capable pin for stereo or vice-versa would result in an error.
|
|
*
|
|
* With nvdisplay, the meaning of lock pins was changed such that they no
|
|
* longer have a shared namespace. So stereo lockpin 0 is not the same as
|
|
* fliplock lockpin 0 and neither is the same as scanlock lockpin 0. With
|
|
* this scheme, there is no way to specify a pin that is incapable of a
|
|
* given function, so the entire capabilities mechanism was removed.
|
|
*
|
|
* However, the pins chosen for HEAD_SET_CONTROL still need to match the
|
|
* pins selected for each function in the VBIOS DCB. Fliplock and scanlock
|
|
* query this information through
|
|
* NV5070_CTRL_CMD_GET_FRAMELOCK_HEADER_LOCKPINS. Stereo is handled
|
|
* here, using NVC370_CTRL_CMD_GET_LOCKPINS_CAPS.
|
|
*/
|
|
|
|
for (i = 0; i < NV_EVO_NUM_LOCK_PIN_CAPS; i++) {
|
|
pEvoCaps->pin[i].flipLock = TRUE;
|
|
pEvoCaps->pin[i].scanLock = TRUE;
|
|
}
|
|
|
|
if (QueryStereoPinC3(pDevEvo, pEvoSubDev, &stereoPin)) {
|
|
pEvoCaps->pin[stereoPin].stereo = TRUE;
|
|
}
|
|
|
|
// Miscellaneous capabilities
|
|
// NVDisplay does not support interlaced modes.
|
|
pEvoCaps->misc.supportsInterlaced = FALSE;
|
|
|
|
/* XXX temporary WAR; see bug 4028718 */
|
|
#if !defined(NVC373_HEAD_CLK_CAP)
|
|
#define NVC373_HEAD_CLK_CAP(i) (0x5e8+(i)*4) /* RW-4A */
|
|
#define NVC373_HEAD_CLK_CAP__SIZE_1 8 /* */
|
|
#define NVC373_HEAD_CLK_CAP_PCLK_MAX 7:0 /* RWIUF */
|
|
#define NVC373_HEAD_CLK_CAP_PCLK_MAX_INIT 0x00000085 /* RWI-V */
|
|
#endif
|
|
|
|
// Heads
|
|
ct_assert(ARRAY_LEN(pEvoCaps->head) >= NVC373_HEAD_CAPA__SIZE_1);
|
|
for (i = 0; i < NVC373_HEAD_CAPA__SIZE_1; i++) {
|
|
NVEvoHeadCaps *pHeadCaps = &pEvoCaps->head[i];
|
|
|
|
pHeadCaps->usable =
|
|
FLD_IDX_TEST_DRF(C373, _SYS_CAP, _HEAD_EXISTS, i, _YES, sysCap);
|
|
if (pHeadCaps->usable) {
|
|
pHeadCaps->maxPClkKHz =
|
|
DRF_VAL(C373, _HEAD_CLK_CAP, _PCLK_MAX,
|
|
ReadCapReg(pCaps, NVC373_HEAD_CLK_CAP(i))) * 10000;
|
|
}
|
|
|
|
}
|
|
|
|
// SORs
|
|
ct_assert(ARRAY_LEN(pEvoCaps->sor) >= NVC373_SOR_CAP__SIZE_1);
|
|
for (i = 0; i < NVC373_SOR_CAP__SIZE_1; i++) {
|
|
NVEvoSorCaps *pSorCaps = &pEvoCaps->sor[i];
|
|
|
|
NvBool sorUsable =
|
|
FLD_IDX_TEST_DRF(C373, _SYS_CAP, _SOR_EXISTS, i, _YES, sysCap);
|
|
|
|
/* XXXnvdisplay: add SOR caps: max DP clk, ... */
|
|
if (sorUsable) {
|
|
const NvU32 sorCap = ReadCapReg(pCaps, NVC373_SOR_CAP(i));
|
|
pSorCaps->dualTMDS =
|
|
FLD_TEST_DRF(C373, _SOR_CAP, _DUAL_TMDS, _TRUE, sorCap);
|
|
|
|
/*
|
|
* Assume that all SORs are equally capable, and that all SORs
|
|
* support HDMI FRL if the display class supports it. (If this
|
|
* assert fires, we may need to rework SOR assignment for such HDMI
|
|
* sinks.)
|
|
*
|
|
* Although HDMI_FRL is only defined for class C6, classes C3 and
|
|
* C5 don't use that bit in the SOR_CAP register so it should
|
|
* always be 0 on those chips.
|
|
*/
|
|
nvAssert(!!FLD_TEST_DRF(C673, _SOR_CAP, _HDMI_FRL, _TRUE, sorCap) ==
|
|
!!pDevEvo->hal->caps.supportsHDMIFRL);
|
|
|
|
pSorCaps->maxTMDSClkKHz =
|
|
DRF_VAL(C373, _SOR_CLK_CAP, _TMDS_MAX,
|
|
ReadCapReg(pCaps, NVC373_SOR_CLK_CAP(i))) * 10000;
|
|
}
|
|
}
|
|
|
|
// Don't need any PIOR caps currently.
|
|
|
|
// Windows
|
|
ct_assert(ARRAY_LEN(pEvoCaps->window) >= NVC373_SYS_CAPB_WINDOW_EXISTS__SIZE_1);
|
|
for (i = 0; i < NVC373_SYS_CAPB_WINDOW_EXISTS__SIZE_1; i++) {
|
|
NVEvoWindowCaps *pWinCaps = &pEvoCaps->window[i];
|
|
|
|
pWinCaps->usable =
|
|
FLD_IDX_TEST_DRF(C373, _SYS_CAPB, _WINDOW_EXISTS, i, _YES, sysCapB);
|
|
}
|
|
}
|
|
|
|
static void EvoParseCapabilityNotifierC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoSubDevPtr pEvoSubDev,
|
|
volatile const NvU32 *pCaps)
|
|
{
|
|
NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
|
|
NvU32 i;
|
|
|
|
// Miscellaneous capabilities
|
|
pEvoCaps->misc.supportsSemiPlanar = FALSE;
|
|
pEvoCaps->misc.supportsPlanar = FALSE;
|
|
pEvoCaps->misc.supportsDSI = FALSE;
|
|
|
|
// Heads
|
|
ct_assert(ARRAY_LEN(pEvoCaps->head) >= NVC373_HEAD_CAPA__SIZE_1);
|
|
for (i = 0; i < NVC373_HEAD_CAPA__SIZE_1; i++) {
|
|
NVEvoHeadCaps *pHeadCaps = &pEvoCaps->head[i];
|
|
|
|
/* XXXnvdisplay: add caps for hsat, ocsc, lut */
|
|
if (pHeadCaps->usable) {
|
|
NVEvoScalerCaps *pScalerCaps = &pHeadCaps->scalerCaps;
|
|
|
|
pScalerCaps->present =
|
|
FLD_TEST_DRF(C373, _HEAD_CAPA, _SCALER, _TRUE,
|
|
ReadCapReg(pCaps, NVC373_HEAD_CAPA(i)));
|
|
if (pScalerCaps->present) {
|
|
NVEvoScalerTapsCaps *pTapsCaps;
|
|
NvU32 tmp;
|
|
|
|
/*
|
|
* Note that some of these may be zero (e.g., only 2-tap 444
|
|
* mode is supported on GV100, so the rest are all zero.
|
|
*
|
|
* Downscaling by more than 2x in either direction is not
|
|
* allowed by state error check for both horizontal and
|
|
* vertical 2-tap scaling.
|
|
*
|
|
* Downscaling by more than 4x in either direction is not
|
|
* allowed by argument error check (and state error check) for
|
|
* 5-tap scaling.
|
|
*
|
|
* 5-tap scaling is not implemented on GV100, though, so we
|
|
* should never see numTaps == 5 on GV100, and we can just use a
|
|
* max of 2 here all the time.
|
|
*/
|
|
|
|
/* 2-tap capabilities */
|
|
tmp = ReadCapReg(pCaps, NVC373_HEAD_CAPD(i));
|
|
pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_2TAPS];
|
|
pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
pTapsCaps->maxPixelsVTaps =
|
|
NV_MAX(DRF_VAL(C373, _HEAD_CAPD, _MAX_PIXELS_2TAP422, tmp),
|
|
DRF_VAL(C373, _HEAD_CAPD, _MAX_PIXELS_2TAP444, tmp));
|
|
|
|
/*
|
|
* Note that there is a capability register for 1TAP, but there
|
|
* doesn't appear to be a way to select 1-tap scaling in the
|
|
* channel methods, so don't bother reading it for now.
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void EvoParsePrecompScalerCaps5(NVEvoCapabilitiesPtr pEvoCaps,
|
|
volatile const NvU32 *pCaps)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NVC573_SYS_CAPB_WINDOW_EXISTS__SIZE_1; i++) {
|
|
NVEvoWindowCaps *pWinCaps = &pEvoCaps->window[i];
|
|
NVEvoScalerCaps *pScalerCaps = &pWinCaps->scalerCaps;
|
|
NVEvoScalerTapsCaps *pTapsCaps;
|
|
NvU32 capA = ReadCapReg(pCaps, NVC573_PRECOMP_WIN_PIPE_HDR_CAPA(i));
|
|
NvU32 capD, capF;
|
|
|
|
pScalerCaps->present =
|
|
FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA, _SCLR_PRESENT,
|
|
_TRUE, capA);
|
|
if (pScalerCaps->present) {
|
|
capD = ReadCapReg(pCaps, NVC573_PRECOMP_WIN_PIPE_HDR_CAPD(i));
|
|
capF = ReadCapReg(pCaps, NVC573_PRECOMP_WIN_PIPE_HDR_CAPF(i));
|
|
|
|
/* 5-tap capabilities */
|
|
pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_5TAPS];
|
|
if (FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPD,
|
|
_SCLR_VS_MAX_SCALE_FACTOR, _4X, capD)) {
|
|
pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_4X;
|
|
} else {
|
|
pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
}
|
|
|
|
if (FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPD,
|
|
_SCLR_HS_MAX_SCALE_FACTOR, _4X, capD)) {
|
|
pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_4X;
|
|
} else {
|
|
pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
}
|
|
|
|
pTapsCaps->maxPixelsVTaps =
|
|
DRF_VAL(C573, _PRECOMP_WIN_PIPE_HDR_CAPF,
|
|
_VSCLR_MAX_PIXELS_5TAP, capF);
|
|
|
|
/* 2-tap capabilities */
|
|
pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_2TAPS];
|
|
pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
pTapsCaps->maxPixelsVTaps =
|
|
DRF_VAL(C573, _PRECOMP_WIN_PIPE_HDR_CAPF, _VSCLR_MAX_PIXELS_2TAP,
|
|
capF);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void EvoParseCapabilityNotifierC5C6Common(NVEvoCapabilitiesPtr pEvoCaps,
|
|
volatile const NvU32 *pCaps)
|
|
{
|
|
NvU32 i;
|
|
NvBool postcompScalingSupported = FALSE;
|
|
|
|
// Heads
|
|
ct_assert(ARRAY_LEN(pEvoCaps->head) >= NVC573_SYS_CAP_HEAD_EXISTS__SIZE_1);
|
|
for (i = 0; i < NVC573_SYS_CAP_HEAD_EXISTS__SIZE_1; i++) {
|
|
NVEvoHeadCaps *pHeadCaps = &pEvoCaps->head[i];
|
|
|
|
if (pHeadCaps->usable) {
|
|
NVEvoScalerCaps *pScalerCaps = &pHeadCaps->scalerCaps;
|
|
NVEvoScalerTapsCaps *pTapsCaps;
|
|
NvU32 capA = ReadCapReg(pCaps, NVC573_POSTCOMP_HEAD_HDR_CAPA(i));
|
|
NvU32 capC, capD;
|
|
|
|
pScalerCaps->present =
|
|
FLD_TEST_DRF(C573, _POSTCOMP_HEAD_HDR_CAPA, _SCLR_PRESENT,
|
|
_TRUE, capA);
|
|
if (pScalerCaps->present) {
|
|
postcompScalingSupported = TRUE;
|
|
|
|
capC = ReadCapReg(pCaps, NVC573_POSTCOMP_HEAD_HDR_CAPC(i));
|
|
capD = ReadCapReg(pCaps, NVC573_POSTCOMP_HEAD_HDR_CAPD(i));
|
|
|
|
/*
|
|
* Note that some of these may be zero.
|
|
*
|
|
* XXXnvdisplay: what about POSTCOMP_HEAD_HDR_CAPC_SCLR_*?
|
|
*/
|
|
|
|
/* 5-tap capabilities */
|
|
pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_5TAPS];
|
|
if (FLD_TEST_DRF(C573, _POSTCOMP_HEAD_HDR_CAPC,
|
|
_SCLR_VS_MAX_SCALE_FACTOR, _4X, capC)) {
|
|
pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_4X;
|
|
} else {
|
|
pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
}
|
|
|
|
if (FLD_TEST_DRF(C573, _POSTCOMP_HEAD_HDR_CAPC,
|
|
_SCLR_HS_MAX_SCALE_FACTOR, _4X, capC)) {
|
|
pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_4X;
|
|
} else {
|
|
pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
}
|
|
|
|
pTapsCaps->maxPixelsVTaps =
|
|
DRF_VAL(C573, _POSTCOMP_HEAD_HDR_CAPD,
|
|
_VSCLR_MAX_PIXELS_5TAP, capD);
|
|
|
|
/* 2-tap capabilities */
|
|
pTapsCaps = &pScalerCaps->taps[NV_EVO_SCALER_2TAPS];
|
|
pTapsCaps->maxVDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
pTapsCaps->maxHDownscaleFactor = NV_EVO_SCALE_FACTOR_2X;
|
|
pTapsCaps->maxPixelsVTaps =
|
|
DRF_VAL(C573, _POSTCOMP_HEAD_HDR_CAPD,
|
|
_VSCLR_MAX_PIXELS_2TAP, capD);
|
|
}
|
|
|
|
#if defined(NV_DEBUG)
|
|
NvU32 capA = ReadCapReg(pCaps, NVC573_POSTCOMP_HEAD_HDR_CAPA(i));
|
|
NvU32 unitWidth = DRF_VAL(C573, _POSTCOMP_HEAD_HDR_CAPA, _UNIT_WIDTH, capA);
|
|
|
|
// EvoInitChannelC5 assumes 16-bit fixed-point.
|
|
nvAssert(unitWidth == 16);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NVC573_SYS_CAPB_WINDOW_EXISTS__SIZE_1; i++) {
|
|
NVEvoWindowCaps *pWinCaps = &pEvoCaps->window[i];
|
|
NvU32 capA = ReadCapReg(pCaps, NVC573_PRECOMP_WIN_PIPE_HDR_CAPA(i));
|
|
|
|
pWinCaps->tmoPresent = FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
|
|
_TMO_PRESENT, _TRUE, capA);
|
|
|
|
pWinCaps->csc0MatricesPresent =
|
|
FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
|
|
_CSC00_PRESENT, _TRUE, capA) &&
|
|
FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
|
|
_CSC01_PRESENT, _TRUE, capA);
|
|
|
|
pWinCaps->csc1MatricesPresent =
|
|
(FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
|
|
_CSC10_PRESENT, _TRUE, capA) &&
|
|
FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
|
|
_CSC11_PRESENT, _TRUE, capA));
|
|
|
|
pWinCaps->cscLUTsPresent =
|
|
FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
|
|
_CSC0LUT_PRESENT, _TRUE, capA) &&
|
|
FLD_TEST_DRF(C573, _PRECOMP_WIN_PIPE_HDR_CAPA,
|
|
_CSC1LUT_PRESENT, _TRUE, capA);
|
|
|
|
nvAssert(!pWinCaps->tmoPresent ||
|
|
(pWinCaps->csc0MatricesPresent &&
|
|
pWinCaps->csc1MatricesPresent &&
|
|
pWinCaps->cscLUTsPresent));
|
|
}
|
|
|
|
/*
|
|
* To keep the design simple, NVKMS will expose support for precomp scaling
|
|
* iff postcomp scaling isn't supported. This means that on chips which have
|
|
* both precomp and postcomp scalers (e.g., Turing), NVKMS will only report
|
|
* that postcomp scaling is supported.
|
|
*/
|
|
if (!postcompScalingSupported) {
|
|
EvoParsePrecompScalerCaps5(pEvoCaps, pCaps);
|
|
}
|
|
|
|
// XXXnvdisplay3: add SOR caps for DP over USB
|
|
}
|
|
|
|
static void EvoParseCapabilityNotifierC5(NVDevEvoPtr pDevEvo,
|
|
NVEvoSubDevPtr pEvoSubDev,
|
|
volatile const NvU32 *pCaps)
|
|
{
|
|
NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
|
|
|
|
// Miscellaneous capabilities
|
|
|
|
/*
|
|
* On Turing, the NVC573_IHUB_COMMON_CAPA_SUPPORT_PLANAR bit actually
|
|
* reports whether IHUB supports YUV _semi-planar_ formats.
|
|
*/
|
|
pEvoCaps->misc.supportsSemiPlanar =
|
|
FLD_TEST_DRF(C573, _IHUB_COMMON_CAPA, _SUPPORT_PLANAR, _TRUE,
|
|
ReadCapReg(pCaps, NVC573_IHUB_COMMON_CAPA));
|
|
pEvoCaps->misc.supportsDSI = FALSE;
|
|
|
|
EvoParseCapabilityNotifierC5C6Common(pEvoCaps, pCaps);
|
|
}
|
|
|
|
static void EvoParseCapabilityNotifierC6(NVDevEvoPtr pDevEvo,
|
|
NVEvoSubDevPtr pEvoSubDev,
|
|
volatile const NvU32 *pCaps)
|
|
{
|
|
NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
|
|
NvU32 capC = ReadCapReg(pCaps, NVC673_IHUB_COMMON_CAPC);
|
|
NvU32 i;
|
|
|
|
// Miscellaneous capabilities
|
|
|
|
pEvoCaps->misc.supportsPlanar =
|
|
FLD_TEST_DRF(C673, _IHUB_COMMON_CAPA, _SUPPORT_PLANAR, _TRUE,
|
|
ReadCapReg(pCaps, NVC673_IHUB_COMMON_CAPA));
|
|
|
|
pEvoCaps->misc.supportsSemiPlanar =
|
|
FLD_TEST_DRF(C673, _IHUB_COMMON_CAPC, _SUPPORT_SEMI_PLANAR, _TRUE, capC);
|
|
|
|
pEvoCaps->misc.supportsHVFlip =
|
|
FLD_TEST_DRF(C673, _IHUB_COMMON_CAPC, _SUPPORT_HOR_VER_FLIP, _TRUE, capC);
|
|
|
|
ct_assert(ARRAY_LEN(pEvoCaps->head) >= NVC673_SYS_CAP_HEAD_EXISTS__SIZE_1);
|
|
|
|
// DSI is currently supported on just Orin, which has only 1 DSI engine (DSI0).
|
|
pEvoCaps->misc.supportsDSI =
|
|
FLD_TEST_DRF(C673, _SYS_CAP, _DSI0_EXISTS, _YES,
|
|
ReadCapReg(pCaps, NVC673_SYS_CAP));
|
|
|
|
for (i = 0; i < NVC673_SYS_CAP_HEAD_EXISTS__SIZE_1; i++) {
|
|
NVEvoHeadCaps *pHeadCaps = &pEvoCaps->head[i];
|
|
|
|
if (pHeadCaps->usable) {
|
|
NvU32 capA = ReadCapReg(pCaps, NVC673_POSTCOMP_HEAD_HDR_CAPA(i));
|
|
NvBool hclpfPresent =
|
|
FLD_TEST_DRF(C673, _POSTCOMP_HEAD_HDR_CAPA, _HCLPF_PRESENT,
|
|
_TRUE, capA);
|
|
NvBool vfilterPresent =
|
|
FLD_TEST_DRF(C673, _POSTCOMP_HEAD_HDR_CAPA, _VFILTER_PRESENT,
|
|
_TRUE, capA);
|
|
|
|
pHeadCaps->supportsHDMIYUV420HW = hclpfPresent && vfilterPresent;
|
|
}
|
|
}
|
|
|
|
EvoParseCapabilityNotifierC5C6Common(pEvoCaps, pCaps);
|
|
}
|
|
|
|
static NvU32 UsableWindowCount(const NVEvoCapabilities *pEvoCaps)
|
|
{
|
|
NvU32 i, count = 0;
|
|
NvBool foundUnusable = FALSE;
|
|
|
|
for (i = 0; i < ARRAY_LEN(pEvoCaps->window); i++) {
|
|
if (pEvoCaps->window[i].usable) {
|
|
count++;
|
|
/* Assert that usable windows are contiguous starting from 0. */
|
|
if (foundUnusable) {
|
|
nvAssert(!foundUnusable);
|
|
}
|
|
} else {
|
|
foundUnusable = TRUE;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
typedef typeof(EvoParseCapabilityNotifierC3) parse_caps_t;
|
|
typedef typeof(nvHwFormatFromKmsFormatC3) get_hw_fmt_t;
|
|
|
|
static void SetHDRLayerCaps(NVDevEvoPtr pDevEvo)
|
|
{
|
|
NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[0];
|
|
NVEvoCapabilitiesPtr pEvoCaps = &pEvoSubDev->capabilities;
|
|
NvU32 win;
|
|
#if defined(DEBUG)
|
|
NvBool hdrLayerCapSet[NVKMS_MAX_LAYERS_PER_HEAD] = {FALSE};
|
|
#endif
|
|
NvU32 numLayers[NVKMS_MAX_HEADS_PER_DISP] = {0};
|
|
|
|
/*
|
|
* XXX HDR: This assumes the window => layer mapping from
|
|
* nvAllocCoreChannelEvo().
|
|
*
|
|
* TODO: Rework API to explicitly factor in window => layer mapping.
|
|
*/
|
|
for (win = 0; win < pDevEvo->numWindows; win++) {
|
|
NVEvoWindowCaps *pWinCaps = &pEvoCaps->window[win];
|
|
const NvU32 head = pDevEvo->headForWindow[win];
|
|
|
|
if (head == NV_INVALID_HEAD) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* XXX HDR: layerCaps assumes that every head has layers with the
|
|
* same capabilities.
|
|
*
|
|
* TODO: Rework API for per-head layerCaps if this assert trips.
|
|
*/
|
|
nvAssert(!hdrLayerCapSet[numLayers[head]] ||
|
|
(pDevEvo->caps.layerCaps[numLayers[head]].supportsHDR ==
|
|
pWinCaps->tmoPresent));
|
|
|
|
pDevEvo->caps.layerCaps[numLayers[head]].supportsHDR = pWinCaps->tmoPresent;
|
|
|
|
#if defined(DEBUG)
|
|
hdrLayerCapSet[numLayers[head]] = TRUE;
|
|
#endif
|
|
|
|
numLayers[head]++;
|
|
}
|
|
}
|
|
|
|
static NvBool EvoGetCapabilities3(NVDevEvoPtr pDevEvo,
|
|
parse_caps_t *pParse,
|
|
get_hw_fmt_t *pGetHwFmt,
|
|
NvU32 hwclass,
|
|
size_t length)
|
|
{
|
|
NvU32 capsHandle = nvGenerateUnixRmHandle(&pDevEvo->handleAllocator);
|
|
NVDispEvoPtr pDispEvo;
|
|
NvU32 sd;
|
|
NvU32 status;
|
|
NvBool ret = FALSE;
|
|
NvBool first = TRUE;
|
|
NvBool supportsSemiPlanar = TRUE;
|
|
NvBool supportsPlanar = TRUE;
|
|
NvBool supportsHVFlip = TRUE;
|
|
unsigned int i;
|
|
enum NvKmsRotation curRotation;
|
|
NvBool reflectionX;
|
|
NvBool reflectionY;
|
|
NvU8 layer;
|
|
|
|
/* With nvdisplay, capabilities are exposed in a separate object. */
|
|
status = nvRmApiAlloc(nvEvoGlobal.clientHandle,
|
|
pDevEvo->displayHandle,
|
|
capsHandle,
|
|
hwclass, NULL);
|
|
if (status != NVOS_STATUS_SUCCESS) {
|
|
nvAssert(!"Failed to allocate caps object");
|
|
goto free_handle;
|
|
}
|
|
|
|
for (layer = 0;
|
|
layer < ARRAY_LEN(pDevEvo->caps.layerCaps);
|
|
layer++) {
|
|
pDevEvo->caps.layerCaps[layer].supportsWindowMode = TRUE;
|
|
}
|
|
|
|
FOR_ALL_EVO_DISPLAYS(pDispEvo, sd, pDevEvo) {
|
|
|
|
NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[sd];
|
|
void *ptr;
|
|
|
|
status = nvRmApiMapMemory(nvEvoGlobal.clientHandle,
|
|
pDevEvo->pSubDevices[sd]->handle,
|
|
capsHandle,
|
|
0,
|
|
length,
|
|
&ptr,
|
|
0);
|
|
if (status != NVOS_STATUS_SUCCESS) {
|
|
nvAssert(!"Failed to map caps memory");
|
|
goto free_object;
|
|
}
|
|
|
|
nvkms_memset(&pEvoSubDev->capabilities, 0,
|
|
sizeof(pEvoSubDev->capabilities));
|
|
|
|
EvoParseCapabilityNotifier3(pDevEvo, pEvoSubDev, ptr);
|
|
pParse(pDevEvo, pEvoSubDev, ptr);
|
|
|
|
status = nvRmApiUnmapMemory(nvEvoGlobal.clientHandle,
|
|
pDevEvo->pSubDevices[sd]->handle,
|
|
capsHandle, ptr, 0);
|
|
if (status != NVOS_STATUS_SUCCESS) {
|
|
nvAssert(!"Failed to unmap caps memory");
|
|
}
|
|
|
|
if (first) {
|
|
pDevEvo->numWindows =
|
|
UsableWindowCount(&pEvoSubDev->capabilities);
|
|
first = FALSE;
|
|
} else {
|
|
/* Assert that each subdevice has the same number of windows. */
|
|
nvAssert(pDevEvo->numWindows ==
|
|
UsableWindowCount(&pEvoSubDev->capabilities));
|
|
}
|
|
|
|
/*
|
|
* Expose YUV semi-planar iff all of the disps belonging to pDevEvo
|
|
* support it.
|
|
*/
|
|
supportsSemiPlanar &=
|
|
pEvoSubDev->capabilities.misc.supportsSemiPlanar;
|
|
|
|
/*
|
|
* Expose YUV planar iff all of the disps belonging to pDevEvo
|
|
* support it.
|
|
*/
|
|
supportsPlanar &=
|
|
pEvoSubDev->capabilities.misc.supportsPlanar;
|
|
|
|
supportsHVFlip &=
|
|
pEvoSubDev->capabilities.misc.supportsHVFlip;
|
|
}
|
|
|
|
SetHDRLayerCaps(pDevEvo);
|
|
|
|
for (i = NvKmsSurfaceMemoryFormatMin;
|
|
i <= NvKmsSurfaceMemoryFormatMax;
|
|
i++) {
|
|
const NvKmsSurfaceMemoryFormatInfo *pFormatInfo =
|
|
nvKmsGetSurfaceMemoryFormatInfo(i);
|
|
|
|
if ((pFormatInfo->numPlanes == 2 && !supportsSemiPlanar) ||
|
|
(pFormatInfo->numPlanes == 3 && !supportsPlanar)) {
|
|
continue;
|
|
}
|
|
|
|
if (pGetHwFmt(i) != 0) {
|
|
NvU8 layer;
|
|
|
|
for (layer = 0;
|
|
layer < ARRAY_LEN(pDevEvo->caps.layerCaps);
|
|
layer++) {
|
|
pDevEvo->caps.layerCaps[layer].supportedSurfaceMemoryFormats |=
|
|
NVBIT64(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (reflectionX = FALSE;
|
|
reflectionX <= TRUE;
|
|
reflectionX++) {
|
|
|
|
for (reflectionY = FALSE;
|
|
reflectionY <= TRUE;
|
|
reflectionY++) {
|
|
|
|
for (curRotation = NVKMS_ROTATION_MIN;
|
|
curRotation <= NVKMS_ROTATION_MAX;
|
|
curRotation++) {
|
|
struct NvKmsRRParams rrParams = { curRotation,
|
|
reflectionX,
|
|
reflectionY };
|
|
NvU8 bitPosition;
|
|
|
|
if ((reflectionX || reflectionY) && !supportsHVFlip) {
|
|
continue;
|
|
}
|
|
|
|
if (curRotation == NVKMS_ROTATION_180 && !supportsHVFlip) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Skipping over rotations by 90 and 270 degrees
|
|
* because these rotations require support for
|
|
* SCAN_COLUMN rotation, which hasn't been added
|
|
* to NVKMS yet.
|
|
*/
|
|
if (curRotation == NVKMS_ROTATION_90 ||
|
|
curRotation == NVKMS_ROTATION_270) {
|
|
continue;
|
|
}
|
|
|
|
bitPosition = NvKmsRRParamsToCapBit(&rrParams);
|
|
pDevEvo->caps.validLayerRRTransforms |= NVBIT(bitPosition);
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = TRUE;
|
|
|
|
free_object:
|
|
status = nvRmApiFree(nvEvoGlobal.clientHandle,
|
|
pDevEvo->displayHandle,
|
|
capsHandle);
|
|
if (status != NVOS_STATUS_SUCCESS) {
|
|
nvAssert(!"Failed to free caps object");
|
|
}
|
|
|
|
free_handle:
|
|
nvFreeUnixRmHandle(&pDevEvo->handleAllocator, capsHandle);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static NvBool EvoGetCapabilitiesC3(NVDevEvoPtr pDevEvo)
|
|
{
|
|
return EvoGetCapabilities3(pDevEvo, EvoParseCapabilityNotifierC3,
|
|
nvHwFormatFromKmsFormatC3,
|
|
NVC373_DISP_CAPABILITIES,
|
|
sizeof(_NvC373DispCapabilities));
|
|
}
|
|
|
|
static NvBool EvoGetCapabilitiesC5(NVDevEvoPtr pDevEvo)
|
|
{
|
|
return EvoGetCapabilities3(pDevEvo, EvoParseCapabilityNotifierC5,
|
|
nvHwFormatFromKmsFormatC5,
|
|
NVC573_DISP_CAPABILITIES,
|
|
sizeof(_NvC573DispCapabilities));
|
|
}
|
|
|
|
NvBool nvEvoGetCapabilitiesC6(NVDevEvoPtr pDevEvo)
|
|
{
|
|
return EvoGetCapabilities3(pDevEvo, EvoParseCapabilityNotifierC6,
|
|
nvHwFormatFromKmsFormatC6,
|
|
NVC673_DISP_CAPABILITIES,
|
|
sizeof(_NvC673DispCapabilities));
|
|
}
|
|
|
|
static void EvoSetViewportPointInC3(NVDevEvoPtr pDevEvo, const int head,
|
|
NvU16 x, NvU16 y,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
/* Set the input viewport point */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_VIEWPORT_POINT_IN(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, DRF_NUM(C37D, _HEAD_SET_VIEWPORT_POINT_IN, _X, x) |
|
|
DRF_NUM(C37D, _HEAD_SET_VIEWPORT_POINT_IN, _Y, y));
|
|
/* XXXnvdisplay set ViewportValidPointIn to configure overfetch */
|
|
}
|
|
|
|
static void EvoSetOutputScalerC3(const NVDispEvoRec *pDispEvo, const NvU32 head,
|
|
const NvU32 imageSharpeningValue,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
const NVDispHeadStateEvoRec *pHeadState = &pDispEvo->headState[head];
|
|
const NVHwModeViewPortEvo *pViewPort = &pHeadState->timings.viewPort;
|
|
|
|
/* These methods should only apply to a single pDpyEvo */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
NvU32 vTaps = pViewPort->vTaps > NV_EVO_SCALER_2TAPS ?
|
|
NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER_VERTICAL_TAPS_TAPS_5 :
|
|
NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER_VERTICAL_TAPS_TAPS_2;
|
|
NvU32 hTaps = pViewPort->hTaps > NV_EVO_SCALER_2TAPS ?
|
|
NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER_HORIZONTAL_TAPS_TAPS_5 :
|
|
NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER_HORIZONTAL_TAPS_TAPS_2;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL_OUTPUT_SCALER(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTROL_OUTPUT_SCALER, _VERTICAL_TAPS, vTaps) |
|
|
DRF_NUM(C37D, _HEAD_SET_CONTROL_OUTPUT_SCALER, _HORIZONTAL_TAPS, hTaps));
|
|
}
|
|
|
|
static NvBool EvoSetViewportInOut3(NVDevEvoPtr pDevEvo, const int head,
|
|
const NVHwModeViewPortEvo *pViewPortMin,
|
|
const NVHwModeViewPortEvo *pViewPort,
|
|
const NVHwModeViewPortEvo *pViewPortMax,
|
|
NVEvoUpdateState *updateState,
|
|
NvU32 setWindowUsageBounds)
|
|
{
|
|
const NVEvoCapabilitiesPtr pEvoCaps = &pDevEvo->gpus[0].capabilities;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
struct NvKmsScalingUsageBounds scalingUsageBounds = { };
|
|
NvU32 win;
|
|
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
/* The input viewport shouldn't vary. */
|
|
nvAssert(pViewPortMin->in.width == pViewPort->in.width);
|
|
nvAssert(pViewPortMax->in.width == pViewPort->in.width);
|
|
nvAssert(pViewPortMin->in.height == pViewPort->in.height);
|
|
nvAssert(pViewPortMax->in.height == pViewPort->in.height);
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_VIEWPORT_SIZE_IN(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_VIEWPORT_SIZE_IN, _WIDTH, pViewPort->in.width) |
|
|
DRF_NUM(C37D, _HEAD_SET_VIEWPORT_SIZE_IN, _HEIGHT, pViewPort->in.height));
|
|
/* XXXnvdisplay set ViewportValidSizeIn to configure overfetch */
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_VIEWPORT_POINT_OUT_ADJUST(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_VIEWPORT_POINT_OUT, _ADJUST_X, pViewPort->out.xAdjust) |
|
|
DRF_NUM(C37D, _HEAD_SET_VIEWPORT_POINT_OUT, _ADJUST_Y, pViewPort->out.yAdjust));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_VIEWPORT_SIZE_OUT(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_VIEWPORT_SIZE_OUT, _WIDTH, pViewPort->out.width) |
|
|
DRF_NUM(C37D, _HEAD_SET_VIEWPORT_SIZE_OUT, _HEIGHT, pViewPort->out.height));
|
|
|
|
/* XXXnvdisplay deal with pViewPortMin, pViewPortMax */
|
|
|
|
if (!nvComputeScalingUsageBounds(&pEvoCaps->head[head].scalerCaps,
|
|
pViewPort->in.width, pViewPort->in.height,
|
|
pViewPort->out.width, pViewPort->out.height,
|
|
pViewPort->hTaps, pViewPort->vTaps,
|
|
&scalingUsageBounds)) {
|
|
/* Should have been rejected by validation */
|
|
nvAssert(!"Attempt to program invalid viewport");
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_MAX_OUTPUT_SCALE_FACTOR(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_MAX_OUTPUT_SCALE_FACTOR, _HORIZONTAL,
|
|
scalingUsageBounds.maxHDownscaleFactor) |
|
|
DRF_NUM(C37D, _HEAD_SET_MAX_OUTPUT_SCALE_FACTOR, _VERTICAL,
|
|
scalingUsageBounds.maxVDownscaleFactor));
|
|
|
|
/*
|
|
* Program MAX_PIXELS_FETCHED_PER_LINE window usage bounds
|
|
* for each window that is attached to the head.
|
|
*
|
|
* Precomp will clip the post-scaled window to the input viewport, reverse-scale
|
|
* this cropped size back to the input surface domain, and isohub will fetch
|
|
* this cropped size. This function assumes that there's no window scaling yet,
|
|
* so the MAX_PIXELS_FETCHED_PER_LINE will be bounded by the input viewport
|
|
* width. SetScalingUsageBoundsOneWindow5() will take care of updating
|
|
* MAX_PIXELS_FETCHED_PER_LINE, if window scaling is enabled later.
|
|
*
|
|
* Program MAX_PIXELS_FETCHED_PER_LINE for each window that is attached to
|
|
* head. For Turing+, SetScalingUsageBoundsOneWindow5() will take care of
|
|
* programming window usage bounds only for the layers/windows in use.
|
|
*/
|
|
setWindowUsageBounds |=
|
|
DRF_NUM(C37D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _MAX_PIXELS_FETCHED_PER_LINE,
|
|
nvGetMaxPixelsFetchedPerLine(pViewPort->in.width,
|
|
NV_EVO_SCALE_FACTOR_1X));
|
|
|
|
for (win = 0; win < pDevEvo->numWindows; win++) {
|
|
if (head != pDevEvo->headForWindow[win]) {
|
|
continue;
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_WINDOW_SET_WINDOW_USAGE_BOUNDS(win), 1);
|
|
nvDmaSetEvoMethodData(pChannel, setWindowUsageBounds);
|
|
}
|
|
|
|
return scalingUsageBounds.vUpscalingAllowed;
|
|
}
|
|
|
|
static void EvoSetViewportInOutC3(NVDevEvoPtr pDevEvo, const int head,
|
|
const NVHwModeViewPortEvo *pViewPortMin,
|
|
const NVHwModeViewPortEvo *pViewPort,
|
|
const NVHwModeViewPortEvo *pViewPortMax,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvBool verticalUpscalingAllowed =
|
|
EvoSetViewportInOut3(pDevEvo, head, pViewPortMin, pViewPort,
|
|
pViewPortMax, updateState,
|
|
NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C3);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC37D_HEAD_SET_HEAD_USAGE_BOUNDS(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37D, _HEAD_SET_HEAD_USAGE_BOUNDS, _CURSOR, _USAGE_W256_H256) |
|
|
DRF_DEF(C37D, _HEAD_SET_HEAD_USAGE_BOUNDS, _OUTPUT_LUT, _USAGE_1025) |
|
|
(verticalUpscalingAllowed ?
|
|
DRF_DEF(C37D, _HEAD_SET_HEAD_USAGE_BOUNDS, _UPSCALING_ALLOWED, _TRUE) :
|
|
DRF_DEF(C37D, _HEAD_SET_HEAD_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE)));
|
|
}
|
|
|
|
static void EvoSetViewportInOutC5(NVDevEvoPtr pDevEvo, const int head,
|
|
const NVHwModeViewPortEvo *pViewPortMin,
|
|
const NVHwModeViewPortEvo *pViewPort,
|
|
const NVHwModeViewPortEvo *pViewPortMax,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 setWindowUsageBounds =
|
|
(NV_EVO3_DEFAULT_WINDOW_USAGE_BOUNDS_C5 |
|
|
DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _INPUT_SCALER_TAPS, _TAPS_2) |
|
|
DRF_DEF(C57D, _WINDOW_SET_WINDOW_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE));
|
|
NvU32 verticalUpscalingAllowed =
|
|
EvoSetViewportInOut3(pDevEvo, head, pViewPortMin, pViewPort,
|
|
pViewPortMax, updateState, setWindowUsageBounds);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC57D_HEAD_SET_HEAD_USAGE_BOUNDS(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _CURSOR, _USAGE_W256_H256) |
|
|
DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _OLUT_ALLOWED, _TRUE) |
|
|
/* Despite the generic name of this field, it's specific to vertical taps. */
|
|
(pViewPort->vTaps > NV_EVO_SCALER_2TAPS ?
|
|
DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _OUTPUT_SCALER_TAPS, _TAPS_5) :
|
|
DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _OUTPUT_SCALER_TAPS, _TAPS_2)) |
|
|
(verticalUpscalingAllowed ?
|
|
DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _UPSCALING_ALLOWED, _TRUE) :
|
|
DRF_DEF(C57D, _HEAD_SET_HEAD_USAGE_BOUNDS, _UPSCALING_ALLOWED, _FALSE)));
|
|
}
|
|
|
|
/*!
|
|
* Compute the C37D_HEAD_SET_CONTROL_CURSOR method value.
|
|
*
|
|
* This function also validates that the given NVSurfaceEvoRec can be
|
|
* used as a cursor image.
|
|
|
|
*
|
|
* \param[in] pDevEvo The device on which the cursor will be programmed.
|
|
* \param[in] pSurfaceEvo The surface to be used as the cursor image.
|
|
* \param[out] pValue The C37D_HEAD_SET_CONTROL_CURSOR method value.
|
|
|
|
* \return If TRUE, the surface can be used as a cursor image, and
|
|
* pValue contains the method value. If FALSE, the surface
|
|
* cannot be used as a cursor image.
|
|
*/
|
|
NvBool nvEvoGetHeadSetControlCursorValueC3(const NVDevEvoRec *pDevEvo,
|
|
const NVSurfaceEvoRec *pSurfaceEvo,
|
|
NvU32 *pValue)
|
|
{
|
|
NvU32 value = 0;
|
|
|
|
if (pSurfaceEvo == NULL) {
|
|
value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _ENABLE, _DISABLE);
|
|
value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _FORMAT, _A8R8G8B8);
|
|
goto done;
|
|
} else {
|
|
value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _ENABLE, _ENABLE);
|
|
}
|
|
|
|
/* The cursor must always be pitch. */
|
|
|
|
if (pSurfaceEvo->layout != NvKmsSurfaceMemoryLayoutPitch) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* The only supported cursor image memory format is A8R8G8B8.
|
|
*/
|
|
if (pSurfaceEvo->format == NvKmsSurfaceMemoryFormatA8R8G8B8) {
|
|
value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _FORMAT, _A8R8G8B8);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* The cursor only supports a few image sizes.
|
|
*/
|
|
if ((pSurfaceEvo->widthInPixels == 32) &&
|
|
(pSurfaceEvo->heightInPixels == 32)) {
|
|
value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _SIZE, _W32_H32);
|
|
} else if ((pSurfaceEvo->widthInPixels == 64) &&
|
|
(pSurfaceEvo->heightInPixels == 64)) {
|
|
value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _SIZE, _W64_H64);
|
|
} else if ((pDevEvo->cursorHal->caps.maxSize >= 128) &&
|
|
(pSurfaceEvo->widthInPixels == 128) &&
|
|
(pSurfaceEvo->heightInPixels == 128)) {
|
|
value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _SIZE, _W128_H128);
|
|
} else if ((pDevEvo->cursorHal->caps.maxSize >= 256) &&
|
|
(pSurfaceEvo->widthInPixels == 256) &&
|
|
(pSurfaceEvo->heightInPixels == 256)) {
|
|
value |= DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR, _SIZE, _W256_H256);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Hard code the cursor hotspot.
|
|
*/
|
|
value |= DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR, _HOT_SPOT_Y, 0);
|
|
value |= DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR, _HOT_SPOT_X, 0);
|
|
|
|
// XXXnvdisplay: Add support for cursor de-gamma.
|
|
|
|
done:
|
|
|
|
if (pValue != NULL) {
|
|
*pValue = value;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void SetCursorSurfaceAddress(
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 offset,
|
|
NvU32 head)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
nvAssert(!pSurfaceDesc || ctxDmaHandle);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTEXT_DMA_CURSOR(head, 0), 4);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTEXT_DMA_CURSOR, _HANDLE, ctxDmaHandle));
|
|
|
|
// Always set the right cursor context DMA.
|
|
// HW will just ignore this if it is not in stereo cursor mode.
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTEXT_DMA_CURSOR, _HANDLE, ctxDmaHandle));
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_OFFSET_CURSOR, _ORIGIN,
|
|
nvCtxDmaOffsetFromBytes(offset)));
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_OFFSET_CURSOR, _ORIGIN,
|
|
nvCtxDmaOffsetFromBytes(offset)));
|
|
}
|
|
|
|
static void EvoSetCursorImageC3(NVDevEvoPtr pDevEvo, const int head,
|
|
const NVSurfaceEvoRec *pSurfaceEvo,
|
|
NVEvoUpdateState *updateState,
|
|
const struct NvKmsCompositionParams *pCursorCompParams)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
const NVSurfaceDescriptor *pSurfaceDesc =
|
|
pSurfaceEvo ? &pSurfaceEvo->planes[0].surfaceDesc : NULL;
|
|
const NvU64 offset = pSurfaceEvo ? pSurfaceEvo->planes[0].offset : 0;
|
|
NvU32 headSetControlCursorValue = 0;
|
|
NvBool ret;
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
nvAssert(pCursorCompParams->colorKeySelect ==
|
|
NVKMS_COMPOSITION_COLOR_KEY_SELECT_DISABLE);
|
|
nvAssert(NVBIT(pCursorCompParams->blendingMode[1]) &
|
|
NV_EVO3_SUPPORTED_CURSOR_COMP_BLEND_MODES);
|
|
/* These methods should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
ret = nvEvoGetHeadSetControlCursorValueC3(pDevEvo, pSurfaceEvo,
|
|
&headSetControlCursorValue);
|
|
/*
|
|
* The caller should have already validated the surface, so there
|
|
* shouldn't be a failure.
|
|
*/
|
|
if (!ret) {
|
|
nvAssert(!"Could not construct HEAD_SET_CONTROL_CURSOR value");
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_PRESENT_CONTROL_CURSOR(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37D, _HEAD_SET_PRESENT_CONTROL_CURSOR, _MODE, _MONO));
|
|
|
|
SetCursorSurfaceAddress(pChannel, pSurfaceDesc, offset, head);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTROL_CURSOR(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, headSetControlCursorValue);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC37D_HEAD_SET_CONTROL_CURSOR_COMPOSITION(head), 1);
|
|
switch (pCursorCompParams->blendingMode[1]) {
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE:
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1, 255) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_CURSOR_COLOR_FACTOR_SELECT, _K1) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_VIEWPORT_COLOR_FACTOR_SELECT, _ZERO) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
|
|
break;
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA:
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1, 255) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_CURSOR_COLOR_FACTOR_SELECT, _K1_TIMES_SRC) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_VIEWPORT_COLOR_FACTOR_SELECT, _NEG_K1_TIMES_SRC) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
|
|
break;
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA:
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1, 255) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_CURSOR_COLOR_FACTOR_SELECT, _K1) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_VIEWPORT_COLOR_FACTOR_SELECT, _NEG_K1_TIMES_SRC) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
|
|
break;
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA:
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1,
|
|
pCursorCompParams->surfaceAlpha) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_CURSOR_COLOR_FACTOR_SELECT, _K1_TIMES_SRC) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_VIEWPORT_COLOR_FACTOR_SELECT, _NEG_K1_TIMES_SRC) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
|
|
break;
|
|
case NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA:
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _K1,
|
|
pCursorCompParams->surfaceAlpha) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_CURSOR_COLOR_FACTOR_SELECT, _K1) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION,
|
|
_VIEWPORT_COLOR_FACTOR_SELECT, _NEG_K1_TIMES_SRC) |
|
|
DRF_DEF(C37D, _HEAD_SET_CONTROL_CURSOR_COMPOSITION, _MODE, _BLEND));
|
|
break;
|
|
default:
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"%s: composition mode %d not supported for cursor",
|
|
__func__, pCursorCompParams->blendingMode[1]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
NvBool nvEvoValidateCursorSurfaceC3(const NVDevEvoRec *pDevEvo,
|
|
const NVSurfaceEvoRec *pSurfaceEvo)
|
|
{
|
|
return nvEvoGetHeadSetControlCursorValueC3(pDevEvo, pSurfaceEvo, NULL);
|
|
}
|
|
|
|
static NvBool ValidateWindowFormatSourceRectC3(
|
|
const struct NvKmsRect *sourceFetchRect,
|
|
const enum NvKmsSurfaceMemoryFormat format)
|
|
{
|
|
const NvKmsSurfaceMemoryFormatInfo *pFormatInfo =
|
|
nvKmsGetSurfaceMemoryFormatInfo(format);
|
|
|
|
/*
|
|
* sourceFetchRect represents the dimensions of the source fetch rectangle.
|
|
* If YUV crop and scaler overfetch are supported, it is up to the caller to
|
|
* provide the correct dimensions (e.g., ValidSizeIn/ValidPointIn vs.
|
|
* SizeIn/PointIn).
|
|
*
|
|
* For all YUV formats, the position and size of the fetch rectangle must be
|
|
* even in the horizontal direction.
|
|
*
|
|
* For YUV420 formats, there is an additional restriction that the position
|
|
* and size of the fetch rectangle must be even in the vertical direction as
|
|
* well.
|
|
*/
|
|
if (pFormatInfo->isYUV) {
|
|
if (((sourceFetchRect->x & 1) != 0) ||
|
|
(sourceFetchRect->width & 1) != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (pFormatInfo->yuv.vertChromaDecimationFactor > 1) {
|
|
if (((sourceFetchRect->y & 1) != 0) ||
|
|
(sourceFetchRect->height & 1) != 0) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
typedef typeof(ValidateWindowFormatSourceRectC3) val_src_rect_t;
|
|
|
|
static NvBool EvoValidateWindowFormatWrapper(
|
|
const enum NvKmsSurfaceMemoryFormat format,
|
|
get_hw_fmt_t *pGetHwFmt,
|
|
const struct NvKmsRect *sourceFetchRect,
|
|
val_src_rect_t *pValSrcRect,
|
|
NvU32 *hwFormatOut)
|
|
{
|
|
const NvU32 hwFormat = pGetHwFmt(format);
|
|
|
|
if (hwFormat == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (hwFormatOut != NULL) {
|
|
*hwFormatOut = hwFormat;
|
|
}
|
|
|
|
/*
|
|
* If sourceFetchRect is NULL, this function is only responsible for
|
|
* verifying whether the given NvKmsSurfaceMemoryFormat has a corresponding
|
|
* HW format.
|
|
*/
|
|
if (sourceFetchRect == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
return pValSrcRect(sourceFetchRect, format);
|
|
}
|
|
|
|
static NvBool EvoValidateWindowFormatC3(
|
|
const enum NvKmsSurfaceMemoryFormat format,
|
|
const struct NvKmsRect *sourceFetchRect,
|
|
NvU32 *hwFormatOut)
|
|
{
|
|
return EvoValidateWindowFormatWrapper(
|
|
format,
|
|
nvHwFormatFromKmsFormatC3,
|
|
sourceFetchRect,
|
|
ValidateWindowFormatSourceRectC3,
|
|
hwFormatOut);
|
|
}
|
|
|
|
static NvBool EvoValidateWindowFormatC5(
|
|
const enum NvKmsSurfaceMemoryFormat format,
|
|
const struct NvKmsRect *sourceFetchRect,
|
|
NvU32 *hwFormatOut)
|
|
{
|
|
return EvoValidateWindowFormatWrapper(
|
|
format,
|
|
nvHwFormatFromKmsFormatC5,
|
|
sourceFetchRect,
|
|
ValidateWindowFormatSourceRectC3,
|
|
hwFormatOut);
|
|
}
|
|
|
|
NvBool nvEvoValidateWindowFormatC6(
|
|
const enum NvKmsSurfaceMemoryFormat format,
|
|
const struct NvKmsRect *sourceFetchRect,
|
|
NvU32 *hwFormatOut)
|
|
{
|
|
return EvoValidateWindowFormatWrapper(
|
|
format,
|
|
nvHwFormatFromKmsFormatC6,
|
|
sourceFetchRect,
|
|
ValidateWindowFormatSourceRectC3,
|
|
hwFormatOut);
|
|
}
|
|
|
|
static NvU32 OffsetForNotifier(int idx)
|
|
{
|
|
/* NVDisplay notifiers are always the 16-byte variety. We only care about
|
|
* the NV_DISP_NOTIFIER__0 dword which contains the status. */
|
|
NvU32 base = idx * (NV_DISP_NOTIFIER_SIZEOF / sizeof(NvU32));
|
|
return base + NV_DISP_NOTIFIER__0;
|
|
}
|
|
|
|
void nvEvoInitCompNotifierC3(const NVDispEvoRec *pDispEvo, int idx)
|
|
{
|
|
nvWriteEvoCoreNotifier(pDispEvo, OffsetForNotifier(idx),
|
|
DRF_DEF(_DISP, _NOTIFIER__0, _STATUS, _NOT_BEGUN));
|
|
}
|
|
|
|
NvBool nvEvoIsCompNotifierCompleteC3(NVDispEvoPtr pDispEvo, int idx) {
|
|
return nvEvoIsCoreNotifierComplete(pDispEvo, OffsetForNotifier(idx),
|
|
DRF_BASE(NV_DISP_NOTIFIER__0_STATUS),
|
|
DRF_EXTENT(NV_DISP_NOTIFIER__0_STATUS),
|
|
NV_DISP_NOTIFIER__0_STATUS_FINISHED);
|
|
}
|
|
|
|
void nvEvoWaitForCompNotifierC3(const NVDispEvoRec *pDispEvo, int idx)
|
|
{
|
|
nvEvoWaitForCoreNotifier(pDispEvo, OffsetForNotifier(idx),
|
|
DRF_BASE(NV_DISP_NOTIFIER__0_STATUS),
|
|
DRF_EXTENT(NV_DISP_NOTIFIER__0_STATUS),
|
|
NV_DISP_NOTIFIER__0_STATUS_FINISHED);
|
|
}
|
|
|
|
static void EvoSetDitherC3(NVDispEvoPtr pDispEvo, const int head,
|
|
const NvBool enabled, const NvU32 type,
|
|
const NvU32 algo,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 ditherControl;
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
if (enabled) {
|
|
ditherControl = DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _ENABLE, _ENABLE);
|
|
|
|
switch (type) {
|
|
case NV0073_CTRL_SPECIFIC_OR_DITHER_TYPE_6_BITS:
|
|
ditherControl |=
|
|
DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _BITS, _TO_6_BITS);
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_DITHER_TYPE_8_BITS:
|
|
ditherControl |=
|
|
DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _BITS, _TO_8_BITS);
|
|
break;
|
|
/* XXXnvdisplay: Support DITHER_TO_{10,12}_BITS (see also bug 1729668). */
|
|
default:
|
|
nvAssert(!"Unknown ditherType");
|
|
// Fall through
|
|
case NV0073_CTRL_SPECIFIC_OR_DITHER_TYPE_OFF:
|
|
ditherControl = NVC37D_HEAD_SET_DITHER_CONTROL_ENABLE_DISABLE;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
ditherControl = DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _ENABLE, _DISABLE);
|
|
}
|
|
|
|
switch (algo) {
|
|
case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_STATIC_ERR_ACC:
|
|
ditherControl |=
|
|
DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _STATIC_ERR_ACC);
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_DYNAMIC_2X2:
|
|
ditherControl |=
|
|
DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _DYNAMIC_2X2);
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_STATIC_2X2:
|
|
ditherControl |=
|
|
DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _STATIC_2X2);
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_TEMPORAL:
|
|
ditherControl |=
|
|
DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _TEMPORAL);
|
|
break;
|
|
default:
|
|
nvAssert(!"Unknown DitherAlgo");
|
|
// Fall through
|
|
case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_UNKNOWN:
|
|
case NV0073_CTRL_SPECIFIC_OR_DITHER_ALGO_DYNAMIC_ERR_ACC:
|
|
ditherControl |=
|
|
DRF_DEF(C37D, _HEAD_SET_DITHER_CONTROL, _MODE, _DYNAMIC_ERR_ACC);
|
|
break;
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DITHER_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, ditherControl);
|
|
}
|
|
|
|
static void EvoSetDisplayRateC3(NVDispEvoPtr pDispEvo, const int head,
|
|
NvBool enable,
|
|
NVEvoUpdateState *updateState,
|
|
NvU32 timeoutMicroseconds)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
if (enable) {
|
|
timeoutMicroseconds =
|
|
NV_MIN(timeoutMicroseconds,
|
|
DRF_MASK(NVC37D_HEAD_SET_DISPLAY_RATE_MIN_REFRESH_INTERVAL));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DISPLAY_RATE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37D, _HEAD_SET_DISPLAY_RATE, _RUN_MODE, _ONE_SHOT) |
|
|
DRF_NUM(C37D, _HEAD_SET_DISPLAY_RATE, _MIN_REFRESH_INTERVAL,
|
|
timeoutMicroseconds) |
|
|
(timeoutMicroseconds == 0 ?
|
|
DRF_DEF(C37D, _HEAD_SET_DISPLAY_RATE, _MIN_REFRESH, _DISABLE) :
|
|
DRF_DEF(C37D, _HEAD_SET_DISPLAY_RATE, _MIN_REFRESH, _ENABLE)));
|
|
} else {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_DISPLAY_RATE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37D, _HEAD_SET_DISPLAY_RATE, _RUN_MODE, _CONTINUOUS));
|
|
}
|
|
}
|
|
|
|
static void EvoSetStallLockC3(NVDispEvoPtr pDispEvo, const int head,
|
|
NvBool enable, NVEvoUpdateState *updateState)
|
|
{
|
|
NVDevEvoPtr pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NVEvoSubDevPtr pEvoSubDev = &pDevEvo->gpus[pDispEvo->displayOwner];
|
|
NVEvoHeadControlPtr pHC = &pEvoSubDev->headControl[head];
|
|
NvU32 data = 0x0;
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
if (pHC->crashLockUnstallMode) {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _UNSTALL_MODE, _CRASH_LOCK);
|
|
} else {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _UNSTALL_MODE, _LINE_LOCK);
|
|
}
|
|
|
|
if (enable) {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _ENABLE, _TRUE) |
|
|
DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _MODE, _ONE_SHOT);
|
|
|
|
if (!pHC->useStallLockPin) {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _LOCK_PIN, _LOCK_PIN_NONE);
|
|
} else if (NV_EVO_LOCK_PIN_IS_INTERNAL(pHC->stallLockPin)) {
|
|
NvU32 pin = pHC->stallLockPin - NV_EVO_LOCK_PIN_INTERNAL_0;
|
|
data |= DRF_NUM(C37D, _HEAD_SET_STALL_LOCK, _LOCK_PIN,
|
|
NVC37D_HEAD_SET_STALL_LOCK_LOCK_PIN_INTERNAL_SCAN_LOCK(pin));
|
|
} else {
|
|
NvU32 pin = pHC->stallLockPin - NV_EVO_LOCK_PIN_0;
|
|
data |= DRF_NUM(C37D, _HEAD_SET_STALL_LOCK, _LOCK_PIN,
|
|
NVC37D_HEAD_SET_STALL_LOCK_LOCK_PIN_LOCK_PIN(pin));
|
|
}
|
|
} else {
|
|
data |= DRF_DEF(C37D, _HEAD_SET_STALL_LOCK, _ENABLE, _FALSE);
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_STALL_LOCK(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, data);
|
|
}
|
|
|
|
static NvBool GetChannelState(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChan,
|
|
NvU32 sd,
|
|
NvU32 *result)
|
|
{
|
|
NVC370_CTRL_CMD_GET_CHANNEL_INFO_PARAMS info = { };
|
|
NvU32 ret;
|
|
|
|
info.base.subdeviceIndex = sd;
|
|
info.channelClass = pChan->hwclass;
|
|
info.channelInstance = pChan->instance;
|
|
|
|
ret = nvRmApiControl(nvEvoGlobal.clientHandle,
|
|
pDevEvo->displayHandle,
|
|
NVC370_CTRL_CMD_GET_CHANNEL_INFO,
|
|
&info, sizeof(info));
|
|
if (ret != NVOS_STATUS_SUCCESS) {
|
|
nvEvoLogDev(pDevEvo, EVO_LOG_ERROR,
|
|
"Failed to query display engine channel state: 0x%08x:%d:%d:0x%08x",
|
|
pChan->hwclass, pChan->instance, sd, ret);
|
|
return FALSE;
|
|
}
|
|
|
|
*result = info.channelState;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NvBool nvEvoIsChannelIdleC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChan,
|
|
NvU32 sd,
|
|
NvBool *result)
|
|
{
|
|
NvU32 channelState;
|
|
|
|
if (!GetChannelState(pDevEvo, pChan, sd, &channelState)) {
|
|
return FALSE;
|
|
}
|
|
|
|
*result = (channelState == NVC370_CTRL_GET_CHANNEL_INFO_STATE_IDLE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NvBool nvEvoIsChannelMethodPendingC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChan,
|
|
NvU32 sd,
|
|
NvBool *result)
|
|
{
|
|
NvBool tmpResult;
|
|
|
|
/* With C370, Idle and NoMethodPending are equivalent. */
|
|
ct_assert(NVC370_CTRL_GET_CHANNEL_INFO_STATE_IDLE ==
|
|
NVC370_CTRL_GET_CHANNEL_INFO_STATE_NO_METHOD_PENDING);
|
|
|
|
if (!nvEvoIsChannelIdleC3(pDevEvo, pChan, sd, &tmpResult)) {
|
|
return FALSE;
|
|
}
|
|
|
|
*result = !tmpResult;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NvBool nvEvoAllocRmCtrlObjectC3(NVDevEvoPtr pDevEvo)
|
|
{
|
|
const NvU32 handle = nvGenerateUnixRmHandle(&pDevEvo->handleAllocator);
|
|
|
|
/* Note that this object is not at all related to the GF100_DISP_SW (9072)
|
|
* or NV50_DISPLAY_SW (5072) objects, despite their similarity in name. */
|
|
NvU32 status = nvRmApiAlloc(nvEvoGlobal.clientHandle,
|
|
pDevEvo->deviceHandle,
|
|
handle,
|
|
NVC372_DISPLAY_SW, NULL);
|
|
if (status != NVOS_STATUS_SUCCESS) {
|
|
nvAssert(!"Failed to allocate nvdisplay rmctrl object");
|
|
goto fail;
|
|
}
|
|
|
|
pDevEvo->rmCtrlHandle = handle;
|
|
|
|
return TRUE;
|
|
|
|
fail:
|
|
nvFreeUnixRmHandle(&pDevEvo->handleAllocator, handle);
|
|
return FALSE;
|
|
}
|
|
|
|
static NvU32 GetAccelerators(
|
|
NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NvU32 sd)
|
|
{
|
|
NVC370_CTRL_GET_ACCL_PARAMS params = { };
|
|
NvU32 ret;
|
|
|
|
params.base.subdeviceIndex = sd;
|
|
params.channelClass = pChannel->hwclass;
|
|
nvAssert(pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL);
|
|
params.channelInstance =
|
|
NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
|
|
|
|
ret = nvRmApiControl(nvEvoGlobal.clientHandle,
|
|
pDevEvo->displayHandle,
|
|
NVC370_CTRL_CMD_GET_ACCL,
|
|
¶ms, sizeof(params));
|
|
if (ret != NVOS_STATUS_SUCCESS) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"Failed to retrieve accelerators");
|
|
return 0;
|
|
}
|
|
|
|
return params.accelerators;
|
|
}
|
|
|
|
static NvBool SetAccelerators(
|
|
NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NvU32 sd,
|
|
NvU32 accelerators,
|
|
NvU32 accelMask)
|
|
{
|
|
NVC370_CTRL_SET_ACCL_PARAMS params = { };
|
|
NvU32 ret;
|
|
|
|
params.base.subdeviceIndex = sd;
|
|
params.channelClass = pChannel->hwclass;
|
|
nvAssert(pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL);
|
|
params.channelInstance =
|
|
NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
|
|
params.accelerators = accelerators;
|
|
params.accelMask = accelMask;
|
|
|
|
ret = nvRmApiControl(nvEvoGlobal.clientHandle,
|
|
pDevEvo->displayHandle,
|
|
NVC370_CTRL_CMD_SET_ACCL,
|
|
¶ms, sizeof(params));
|
|
if (ret != NVOS_STATUS_SUCCESS) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"Failed to set accelerators");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void nvEvoAccelerateChannelC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NvU32 sd,
|
|
const NvBool trashPendingMethods,
|
|
const NvBool unblockMethodsInExecutation,
|
|
NvU32 *pOldAccelerators)
|
|
{
|
|
NvU32 accelMask = 0x0;
|
|
|
|
if (trashPendingMethods) {
|
|
accelMask |= NVC370_CTRL_ACCL_TRASH_ONLY;
|
|
}
|
|
|
|
/* Start with a conservative set of accelerators; may need to add more
|
|
* later. */
|
|
if (unblockMethodsInExecutation) {
|
|
accelMask |= NVC370_CTRL_ACCL_IGNORE_PI |
|
|
NVC370_CTRL_ACCL_SKIP_SEMA |
|
|
NVC370_CTRL_ACCL_IGNORE_FLIPLOCK;
|
|
}
|
|
|
|
if (accelMask == 0x0) {
|
|
return;
|
|
}
|
|
|
|
*pOldAccelerators = GetAccelerators(pDevEvo, pChannel, sd);
|
|
|
|
/* Accelerate window channel. */
|
|
if (!SetAccelerators(pDevEvo, pChannel, sd, accelMask, accelMask)) {
|
|
nvAssert(!"Failed to set accelerators");
|
|
}
|
|
}
|
|
|
|
void nvEvoResetChannelAcceleratorsC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
const NvU32 sd,
|
|
const NvBool trashPendingMethods,
|
|
const NvBool unblockMethodsInExecutation,
|
|
NvU32 oldAccelerators)
|
|
{
|
|
NvU32 accelMask = 0x0;
|
|
|
|
if (trashPendingMethods) {
|
|
accelMask |= NVC370_CTRL_ACCL_TRASH_ONLY;
|
|
}
|
|
|
|
/* Start with a conservative set of accelerators; may need to add more
|
|
* later. */
|
|
if (unblockMethodsInExecutation) {
|
|
accelMask |= NVC370_CTRL_ACCL_IGNORE_PI |
|
|
NVC370_CTRL_ACCL_SKIP_SEMA |
|
|
NVC370_CTRL_ACCL_IGNORE_FLIPLOCK;
|
|
}
|
|
|
|
if (accelMask == 0x0) {
|
|
return;
|
|
}
|
|
|
|
/* Accelerate window channel. */
|
|
if (!SetAccelerators(pDevEvo, pChannel, sd, oldAccelerators, accelMask)) {
|
|
nvAssert(!"Failed to set accelerators");
|
|
}
|
|
}
|
|
|
|
static void ForceFlipToNull(
|
|
NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NvU32 sd,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVFlipChannelEvoHwState hwState = { };
|
|
const NvU32 subDeviceMask = (1 << sd);
|
|
|
|
nvPushEvoSubDevMask(pDevEvo, subDeviceMask);
|
|
|
|
pDevEvo->hal->Flip(pDevEvo, pChannel, &hwState, updateState,
|
|
FALSE /* bypassComposition */);
|
|
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
|
|
static NvBool PollForChannelIdle(
|
|
NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NvU32 sd)
|
|
{
|
|
const NvU32 timeout = 2000000; // 2 seconds
|
|
NvU64 startTime = 0;
|
|
NvBool isMethodPending = TRUE;
|
|
|
|
do {
|
|
if (!nvEvoIsChannelMethodPendingC3(pDevEvo, pChannel, sd,
|
|
&isMethodPending)) {
|
|
break;
|
|
}
|
|
|
|
if (!isMethodPending) {
|
|
break;
|
|
}
|
|
|
|
if (nvExceedsTimeoutUSec(pDevEvo, &startTime, timeout)) {
|
|
return FALSE;
|
|
}
|
|
|
|
nvkms_yield();
|
|
|
|
} while (TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*!
|
|
* This function emulates the behavior of the STOP_BASE/STOP_OVERLAY RM control
|
|
* calls for pre-EVO hardware.
|
|
*
|
|
* STOP_BASE/STOP_OVERLAY will apply hardware channel accelerators, push
|
|
* methods via the debug interface to NULL context DMAs, and wait for the
|
|
* channel to go idle (which means the surface programmed into the core channel
|
|
* will become visible).
|
|
*
|
|
* If we asked RM to do the same thing for the window channel that is emulating
|
|
* the base channel on nvdisplay, the display would just go black: there's no
|
|
* surface in the core channel, so NULLing the context DMA in the window
|
|
* channel will disable both "core" and "base".
|
|
*
|
|
* So instead, similar functionality is implemented here: we apply
|
|
* accelerators, push methods to flip to core, and wait for the channel to
|
|
* idle.
|
|
*/
|
|
typedef struct {
|
|
struct {
|
|
NvU32 accelerators;
|
|
NvBool overridden;
|
|
} window[NVKMS_MAX_WINDOWS_PER_DISP];
|
|
} EvoIdleChannelAcceleratorState;
|
|
|
|
static NvBool EvoForceIdleSatelliteChannelsWithAccel(
|
|
NVDevEvoPtr pDevEvo,
|
|
const NVEvoIdleChannelState *idleChannelState,
|
|
const NvU32 accelMask)
|
|
{
|
|
NvU32 sd, window;
|
|
NVEvoUpdateState updateState = { };
|
|
NvBool ret = FALSE;
|
|
|
|
EvoIdleChannelAcceleratorState *pAcceleratorState = nvCalloc(
|
|
pDevEvo->numSubDevices, sizeof(EvoIdleChannelAcceleratorState));
|
|
|
|
if (!pAcceleratorState) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"Failed to alloc accelerator state");
|
|
return FALSE;
|
|
}
|
|
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
/*
|
|
* Forcing a channel to be idle is currently only implemented for window
|
|
* channels.
|
|
*/
|
|
if ((idleChannelState->subdev[sd].channelMask &
|
|
~NV_EVO_CHANNEL_MASK_WINDOW_ALL) != 0) {
|
|
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"Forcing non-window channel idle not implemented");
|
|
goto done;
|
|
}
|
|
|
|
for (window = 0; window < pDevEvo->numWindows; window++) {
|
|
if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK,
|
|
_WINDOW, window, _ENABLE,
|
|
idleChannelState->subdev[sd].channelMask)) {
|
|
NVEvoChannelPtr pChannel = pDevEvo->window[window];
|
|
|
|
/* Save old window channel accelerators. */
|
|
NvU32 oldAccel = GetAccelerators(pDevEvo, pChannel, sd);
|
|
|
|
pAcceleratorState[sd].window[window].accelerators =
|
|
oldAccel;
|
|
|
|
/* Accelerate window channel. */
|
|
if (!SetAccelerators(pDevEvo, pChannel, sd, accelMask,
|
|
accelMask)) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"Failed to set accelerators");
|
|
goto done;
|
|
}
|
|
pAcceleratorState[sd].window[window].overridden = TRUE;
|
|
|
|
/* Push a flip to null in this channel. */
|
|
ForceFlipToNull(pDevEvo, pChannel, sd, &updateState);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Push one update for all of the flips programmed above. */
|
|
nvEvoUpdateC3(pDevEvo, &updateState, TRUE /* releaseElv */);
|
|
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
for (window = 0; window < pDevEvo->numWindows; window++) {
|
|
if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
|
|
idleChannelState->subdev[sd].channelMask)) {
|
|
NVEvoChannelPtr pChannel = pDevEvo->window[window];
|
|
|
|
/* Wait for the flips to complete. */
|
|
if (!PollForChannelIdle(pDevEvo, pChannel, sd)) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"Timed out while idling base channel");
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = TRUE;
|
|
|
|
done:
|
|
for (sd = 0; sd < pDevEvo->numSubDevices; sd++) {
|
|
for (window = 0; window < pDevEvo->numWindows; window++) {
|
|
if (FLD_IDX_TEST_DRF64(_EVO, _CHANNEL_MASK, _WINDOW, window, _ENABLE,
|
|
idleChannelState->subdev[sd].channelMask)) {
|
|
NVEvoChannelPtr pChannel = pDevEvo->window[window];
|
|
|
|
const NvU32 oldAccel =
|
|
pAcceleratorState[sd].window[window].accelerators;
|
|
|
|
if (!pAcceleratorState[sd].window[window].overridden) {
|
|
continue;
|
|
}
|
|
|
|
/* Restore window channel accelerators. */
|
|
if (!SetAccelerators(pDevEvo, pChannel, sd, oldAccel,
|
|
accelMask)) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"Failed to restore accelerators");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nvFree(pAcceleratorState);
|
|
return ret;
|
|
}
|
|
|
|
NvBool nvEvoForceIdleSatelliteChannelC3(
|
|
NVDevEvoPtr pDevEvo,
|
|
const NVEvoIdleChannelState *idleChannelState)
|
|
{
|
|
/* Start with a conservative set of accelerators; may need to add more
|
|
* later. */
|
|
const NvU32 accelMask =
|
|
NVC370_CTRL_ACCL_IGNORE_PI |
|
|
NVC370_CTRL_ACCL_SKIP_SEMA;
|
|
|
|
return EvoForceIdleSatelliteChannelsWithAccel(pDevEvo,
|
|
idleChannelState,
|
|
accelMask);
|
|
}
|
|
|
|
NvBool nvEvoForceIdleSatelliteChannelIgnoreLockC3(
|
|
NVDevEvoPtr pDevEvo,
|
|
const NVEvoIdleChannelState *idleChannelState)
|
|
{
|
|
const NvU32 accelMask =
|
|
NVC370_CTRL_ACCL_IGNORE_PI |
|
|
NVC370_CTRL_ACCL_SKIP_SEMA |
|
|
NVC370_CTRL_ACCL_IGNORE_FLIPLOCK |
|
|
NVC370_CTRL_ACCL_IGNORE_INTERLOCK;
|
|
|
|
return EvoForceIdleSatelliteChannelsWithAccel(pDevEvo,
|
|
idleChannelState,
|
|
accelMask);
|
|
}
|
|
|
|
void nvEvoFreeRmCtrlObjectC3(NVDevEvoPtr pDevEvo)
|
|
{
|
|
if (pDevEvo->rmCtrlHandle) {
|
|
NvU32 status;
|
|
|
|
status = nvRmApiFree(nvEvoGlobal.clientHandle,
|
|
pDevEvo->deviceHandle,
|
|
pDevEvo->rmCtrlHandle);
|
|
|
|
if (status != NVOS_STATUS_SUCCESS) {
|
|
nvAssert(!"Failed to free nvdisplay rmctrl object");
|
|
}
|
|
|
|
nvFreeUnixRmHandle(&pDevEvo->handleAllocator, pDevEvo->rmCtrlHandle);
|
|
pDevEvo->rmCtrlHandle = 0;
|
|
}
|
|
}
|
|
|
|
void nvEvoSetImmPointOutC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NvU32 sd,
|
|
NVEvoUpdateState *updateState,
|
|
NvU16 x, NvU16 y)
|
|
{
|
|
NVEvoChannelPtr pImmChannel = pChannel->imm.u.dma;
|
|
|
|
nvAssert((pChannel->channelMask & NV_EVO_CHANNEL_MASK_WINDOW_ALL) != 0);
|
|
nvAssert(pChannel->imm.type == NV_EVO_IMM_CHANNEL_DMA);
|
|
|
|
/* This should only be called for one GPU at a time, since the
|
|
* pre-nvdisplay version uses PIO and cannot broadcast. */
|
|
nvAssert(ONEBITSET(nvPeekEvoSubDevMask(pDevEvo)));
|
|
|
|
nvDmaSetStartEvoMethod(pImmChannel,
|
|
NVC37B_SET_POINT_OUT(0 /* Left eye */), 1);
|
|
|
|
nvDmaSetEvoMethodData(pImmChannel,
|
|
DRF_NUM(C37B, _SET_POINT_OUT, _X, x) |
|
|
DRF_NUM(C37B, _SET_POINT_OUT, _Y, y));
|
|
|
|
nvWinImmChannelUpdateState(pDevEvo, updateState, pChannel);
|
|
}
|
|
|
|
static void SetCrcSurfaceAddress(
|
|
NVEvoChannelPtr pChannel,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 head)
|
|
{
|
|
NvU32 ctxDmaHandle = pSurfaceDesc ? pSurfaceDesc->ctxDmaHandle : 0;
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CONTEXT_DMA_CRC(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CONTEXT_DMA_CRC, _HANDLE, ctxDmaHandle));
|
|
}
|
|
|
|
static void EvoStartHeadCRC32CaptureC3(NVDevEvoPtr pDevEvo,
|
|
NVEvoDmaPtr pDma,
|
|
NVConnectorEvoPtr pConnectorEvo,
|
|
const enum nvKmsTimingsProtocol protocol,
|
|
const NvU32 orIndex,
|
|
NvU32 head,
|
|
NvU32 sd,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
const NvU32 winChannel = head << 1;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 orOutput = 0;
|
|
|
|
/* These method should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
/* The window channel should fit in
|
|
* NVC37D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL */
|
|
nvAssert(winChannel < DRF_MASK(NVC37D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL));
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
switch (pConnectorEvo->or.type) {
|
|
case NV0073_CTRL_SPECIFIC_OR_TYPE_SOR:
|
|
if (protocol == NVKMS_PROTOCOL_SOR_DP_A ||
|
|
protocol == NVKMS_PROTOCOL_SOR_DP_B) {
|
|
orOutput = NVC37D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SF;
|
|
} else {
|
|
orOutput =
|
|
NVC37D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR(orIndex);
|
|
}
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_TYPE_PIOR:
|
|
orOutput =
|
|
NVC37D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_PIOR(orIndex);
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_TYPE_DAC:
|
|
/* No DAC support on nvdisplay. Fall through. */
|
|
default:
|
|
nvAssert(!"Invalid pConnectorEvo->or.type");
|
|
break;
|
|
}
|
|
|
|
SetCrcSurfaceAddress(pChannel, &pDma->surfaceDesc, head);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CRC_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C37D, _HEAD_SET_CRC_CONTROL, _PRIMARY_CRC, orOutput) |
|
|
DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _SECONDARY_CRC, _NONE) |
|
|
DRF_NUM(C37D, _HEAD_SET_CRC_CONTROL, _CONTROLLING_CHANNEL, winChannel) |
|
|
DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _EXPECT_BUFFER_COLLAPSE, _FALSE) |
|
|
DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _CRC_DURING_SNOOZE, _DISABLE));
|
|
|
|
/* Reset the CRC notifier */
|
|
nvEvoResetCRC32Notifier(pDma->subDeviceAddress[sd],
|
|
NVC37D_NOTIFIER_CRC_STATUS_0,
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_DONE),
|
|
NVC37D_NOTIFIER_CRC_STATUS_0_DONE_FALSE);
|
|
}
|
|
|
|
static void EvoStopHeadCRC32CaptureC3(NVDevEvoPtr pDevEvo,
|
|
NvU32 head,
|
|
NVEvoUpdateState *updateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
/* These method should only apply to a single pDpy */
|
|
nvAssert(pDevEvo->subDevMaskStackDepth > 0);
|
|
|
|
nvUpdateUpdateState(pDevEvo, updateState, pChannel);
|
|
|
|
SetCrcSurfaceAddress(pChannel, NULL /* pSurfaceDesc */, head);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_CRC_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _PRIMARY_CRC, _NONE) |
|
|
DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _SECONDARY_CRC, _NONE) |
|
|
DRF_NUM(C37D, _HEAD_SET_CRC_CONTROL, _CONTROLLING_CHANNEL, 0) |
|
|
DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _EXPECT_BUFFER_COLLAPSE, _FALSE) |
|
|
DRF_DEF(C37D, _HEAD_SET_CRC_CONTROL, _CRC_DURING_SNOOZE, _DISABLE));
|
|
}
|
|
|
|
/*!
|
|
* Queries the current head's CRC Notifier and returns values if successful
|
|
*
|
|
* First waits for hardware to finish writing to the CRC32Notifier,
|
|
* and performs a read of the Compositor, SF/OR CRCs,
|
|
* and the RG CRC in numCRC32 frames.
|
|
* Crc fields in input array crc32 should be calloc'd to 0s.
|
|
*
|
|
* \param[in] pDevEvo NVKMS device pointer
|
|
* \param[in] pDma Pointer to DMA-mapped memory
|
|
* \param[in] sd Subdevice index
|
|
* \param[in] entry_count Number of independent frames to read CRCs from
|
|
* \param[out] crc32 Contains pointers to CRC output arrays
|
|
* \param[out] numCRC32 Number of CRC frames successfully read from DMA
|
|
*
|
|
* \return Returns TRUE if was able to successfully read CRCs from DMA,
|
|
* otherwise FALSE
|
|
*/
|
|
NvBool nvEvoQueryHeadCRC32_C3(NVDevEvoPtr pDevEvo,
|
|
NVEvoDmaPtr pDma,
|
|
NvU32 sd,
|
|
NvU32 entry_count,
|
|
CRC32NotifierCrcOut *crc32,
|
|
NvU32 *numCRC32)
|
|
{
|
|
volatile NvU32 *pCRC32Notifier = pDma->subDeviceAddress[sd];
|
|
const NvU32 entry_stride =
|
|
NVC37D_NOTIFIER_CRC_CRC_ENTRY1_21 - NVC37D_NOTIFIER_CRC_CRC_ENTRY0_13;
|
|
// Define how many/which variables to read from each CRCNotifierEntry struct
|
|
const CRC32NotifierEntryRec field_info[NV_EVO3_NUM_CRC_FIELDS] = {
|
|
{
|
|
.field_offset = NVC37D_NOTIFIER_CRC_CRC_ENTRY0_11,
|
|
.field_base_bit =
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_11_COMPOSITOR_CRC),
|
|
.field_extent_bit =
|
|
DRF_EXTENT(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_11_COMPOSITOR_CRC),
|
|
.field_frame_values = crc32->compositorCrc32,
|
|
},
|
|
{
|
|
.field_offset = NVC37D_NOTIFIER_CRC_CRC_ENTRY0_12,
|
|
.field_base_bit =
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_12_RG_CRC),
|
|
.field_extent_bit =
|
|
DRF_EXTENT(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_12_RG_CRC),
|
|
.field_frame_values = crc32->rasterGeneratorCrc32,
|
|
},
|
|
{
|
|
.field_offset = NVC37D_NOTIFIER_CRC_CRC_ENTRY0_13,
|
|
.field_base_bit =
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_13_PRIMARY_OUTPUT_CRC),
|
|
.field_extent_bit =
|
|
DRF_EXTENT(NVC37D_NOTIFIER_CRC_CRC_ENTRY0_13_PRIMARY_OUTPUT_CRC),
|
|
.field_frame_values = crc32->outputCrc32
|
|
}
|
|
};
|
|
|
|
const CRC32NotifierEntryFlags flag_info[NV_EVO3_NUM_CRC_FLAGS] = {
|
|
{
|
|
.flag_base_bit =
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_COUNT),
|
|
.flag_extent_bit =
|
|
DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_COUNT),
|
|
.flag_type = NVEvoCrc32NotifierFlagCount
|
|
},
|
|
{
|
|
.flag_base_bit =
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_COMPOSITOR_OVERFLOW),
|
|
.flag_extent_bit =
|
|
DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_COMPOSITOR_OVERFLOW),
|
|
.flag_type = NVEvoCrc32NotifierFlagCrcOverflow
|
|
},
|
|
{
|
|
.flag_base_bit =
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_RG_OVERFLOW),
|
|
.flag_extent_bit =
|
|
DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_RG_OVERFLOW),
|
|
.flag_type = NVEvoCrc32NotifierFlagCrcOverflow
|
|
},
|
|
{
|
|
.flag_base_bit =
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_PRIMARY_OUTPUT_OVERFLOW),
|
|
.flag_extent_bit =
|
|
DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_PRIMARY_OUTPUT_OVERFLOW),
|
|
.flag_type = NVEvoCrc32NotifierFlagCrcOverflow
|
|
}
|
|
};
|
|
|
|
if (!nvEvoWaitForCRC32Notifier(pDevEvo,
|
|
pCRC32Notifier,
|
|
NVC37D_NOTIFIER_CRC_STATUS_0,
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_DONE),
|
|
DRF_EXTENT(NVC37D_NOTIFIER_CRC_STATUS_0_DONE),
|
|
NVC37D_NOTIFIER_CRC_STATUS_0_DONE_TRUE)) {
|
|
return FALSE;
|
|
}
|
|
|
|
*numCRC32 = nvEvoReadCRC32Notifier(pCRC32Notifier,
|
|
entry_stride,
|
|
entry_count,
|
|
NVC37D_NOTIFIER_CRC_STATUS_0, /* Status offset */
|
|
NV_EVO3_NUM_CRC_FIELDS,
|
|
NV_EVO3_NUM_CRC_FLAGS,
|
|
field_info,
|
|
flag_info);
|
|
|
|
nvEvoResetCRC32Notifier(pCRC32Notifier,
|
|
NVC37D_NOTIFIER_CRC_STATUS_0,
|
|
DRF_BASE(NVC37D_NOTIFIER_CRC_STATUS_0_DONE),
|
|
NVC37D_NOTIFIER_CRC_STATUS_0_DONE_FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void nvEvoGetScanLineC3(const NVDispEvoRec *pDispEvo,
|
|
const NvU32 head,
|
|
NvU16 *pScanLine,
|
|
NvBool *pInBlankingPeriod)
|
|
{
|
|
const NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
|
|
const NvU32 sd = pDispEvo->displayOwner;
|
|
const NvU32 window = head << 1;
|
|
void *pDma = pDevEvo->window[window]->pb.control[sd];
|
|
const NvU32 scanLine = nvDmaLoadPioMethod(pDma, NVC37E_GET_LINE);
|
|
|
|
if ((scanLine & NVBIT(15)) == 0) {
|
|
*pInBlankingPeriod = FALSE;
|
|
*pScanLine = scanLine & DRF_MASK(14:0);
|
|
} else {
|
|
*pInBlankingPeriod = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This method configures and programs the RG Core Semaphores. Default behavior
|
|
* is to continuously trigger on the specified rasterline when enabled.
|
|
*/
|
|
static void
|
|
EvoConfigureVblankSyncObjectC6(const NVDevEvoPtr pDevEvo,
|
|
const NvU16 rasterLine,
|
|
const NvU32 head,
|
|
const NvU32 semaphoreIndex,
|
|
const NVSurfaceDescriptor *pSurfaceDesc,
|
|
NVEvoUpdateState* pUpdateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
/*
|
|
* Populate the NVEvoUpdateState for the caller. The Update State contains
|
|
* a mask of which display channels need to be updated.
|
|
*/
|
|
nvUpdateUpdateState(pDevEvo, pUpdateState, pChannel);
|
|
|
|
/*
|
|
* Tell HW what ctxdma entry to use to look up actual RG semaphore surface.
|
|
* If ctxdma handle is 0, HW will disable the semaphore.
|
|
*/
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC67D_HEAD_SET_CONTEXT_DMA_RG_REL_SEMAPHORE(head, semaphoreIndex),
|
|
1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67D, _HEAD_SET_CONTEXT_DMA_RG_REL_SEMAPHORE, _HANDLE,
|
|
(pSurfaceDesc == NULL) ? 0 : pSurfaceDesc->ctxDmaHandle));
|
|
|
|
if ((pSurfaceDesc == NULL) || (pSurfaceDesc->ctxDmaHandle == 0)) {
|
|
/* Disabling semaphore so no configuration necessary. */
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Configure the semaphore with the following:
|
|
* Set OFFSET to 0 (default).
|
|
* Set PAYLOAD_SIZE to 32bits (default).
|
|
* Set REL_MODE to WRITE (default).
|
|
* Set RUN_MODE to CONTINUOUS.
|
|
* Set RASTER_LINE to start of Vblank: Vsync + Vbp + Vactive.
|
|
*
|
|
* Note that all these options together fit in 32bits, and that all 32 bits
|
|
* must be written each time any given option changes.
|
|
*
|
|
* The actual payload value doesn't currently matter since this RG
|
|
* semaphore will be mapped to a syncpt for now. Each HW-issued payload
|
|
* write is converted to a single syncpt increment irrespective of what the
|
|
* actual semaphore payload value is.
|
|
*/
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC67D_HEAD_SET_RG_REL_SEMAPHORE_CONTROL(head, semaphoreIndex),
|
|
1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _OFFSET, 0) |
|
|
DRF_DEF(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _PAYLOAD_SIZE,
|
|
_PAYLOAD_32BIT) |
|
|
DRF_DEF(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _REL_MODE,
|
|
_WRITE) |
|
|
DRF_DEF(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _RUN_MODE,
|
|
_CONTINUOUS) |
|
|
DRF_NUM(C67D, _HEAD_SET_RG_REL_SEMAPHORE_CONTROL, _RASTER_LINE,
|
|
rasterLine));
|
|
}
|
|
|
|
static void EvoSetHdmiDscParams(const NVDispEvoRec *pDispEvo,
|
|
const NvU32 head,
|
|
const NVDscInfoEvoRec *pDscInfo,
|
|
const enum nvKmsPixelDepth pixelDepth)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDispEvo->pDevEvo->core;
|
|
NvU32 bpc, flatnessDetThresh;
|
|
NvU32 i;
|
|
|
|
nvAssert(pDispEvo->pDevEvo->hal->caps.supportsHDMIFRL &&
|
|
pDscInfo->type == NV_DSC_INFO_EVO_TYPE_HDMI);
|
|
|
|
bpc = nvPixelDepthToBitsPerComponent(pixelDepth);
|
|
if (bpc < 8) {
|
|
nvAssert(bpc >= 8);
|
|
bpc = 8;
|
|
}
|
|
flatnessDetThresh = (2 << (bpc - 8));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_DSC_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C67D, _HEAD_SET_DSC_CONTROL, _ENABLE, _TRUE) |
|
|
((pDscInfo->hdmi.dscMode == NV_DSC_EVO_MODE_DUAL) ?
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _MODE, _DUAL) :
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _MODE, _SINGLE)) |
|
|
DRF_NUM(C67D, _HEAD_SET_DSC_CONTROL, _FLATNESS_DET_THRESH, flatnessDetThresh) |
|
|
DRF_DEF(C67D, _HEAD_SET_DSC_CONTROL, _FULL_ICH_ERR_PRECISION, _ENABLE) |
|
|
DRF_DEF(C67D, _HEAD_SET_DSC_CONTROL, _AUTO_RESET, _ENABLE) |
|
|
DRF_DEF(C67D, _HEAD_SET_DSC_CONTROL, _FORCE_ICH_RESET, _FALSE));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_DSC_PPS_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C67D, _HEAD_SET_DSC_PPS_CONTROL, _ENABLE, _TRUE) |
|
|
DRF_DEF(C67D, _HEAD_SET_DSC_PPS_CONTROL, _LOCATION, _VBLANK) |
|
|
DRF_DEF(C67D, _HEAD_SET_DSC_PPS_CONTROL, _FREQUENCY, _EVERY_FRAME) |
|
|
/* MFS says "For FRL DSC CVTEM, it should be 0x21 (136bytes)." */
|
|
DRF_NUM(C67D, _HEAD_SET_DSC_PPS_CONTROL, _SIZE, 0x21));
|
|
|
|
/* The loop below assumes the methods are tightly packed. */
|
|
ct_assert(ARRAY_LEN(pDscInfo->hdmi.pps) == 32);
|
|
ct_assert((NVC67D_HEAD_SET_DSC_PPS_DATA1(0) - NVC67D_HEAD_SET_DSC_PPS_DATA0(0)) == 4);
|
|
ct_assert((NVC67D_HEAD_SET_DSC_PPS_DATA31(0) - NVC67D_HEAD_SET_DSC_PPS_DATA0(0)) == (31 * 4));
|
|
for (i = 0; i < ARRAY_LEN(pDscInfo->hdmi.pps); i++) {
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_DSC_PPS_DATA0(head) + (i * 4), 1);
|
|
nvDmaSetEvoMethodData(pChannel, pDscInfo->hdmi.pps[i]);
|
|
}
|
|
|
|
/* Byte 0 must be 0x7f, the rest are don't care (will be filled in by HW) */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_DSC_PPS_HEAD(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67D, _HEAD_SET_DSC_PPS_HEAD, _BYTE0, 0x7f));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_HDMI_DSC_HCACTIVE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67D, _HEAD_SET_HDMI_DSC_HCACTIVE, _BYTES, pDscInfo->hdmi.dscHActiveBytes) |
|
|
DRF_NUM(C67D, _HEAD_SET_HDMI_DSC_HCACTIVE, _TRI_BYTES, pDscInfo->hdmi.dscHActiveTriBytes));
|
|
nvDmaSetStartEvoMethod(pChannel, NVC67D_HEAD_SET_HDMI_DSC_HCBLANK(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C67D, _HEAD_SET_HDMI_DSC_HCBLANK, _WIDTH, pDscInfo->hdmi.dscHBlankTriBytes));
|
|
}
|
|
|
|
static void EvoSetDpDscParams(const NVDispEvoRec *pDispEvo,
|
|
const NvU32 head,
|
|
const NVDscInfoEvoRec *pDscInfo)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDispEvo->pDevEvo->core;
|
|
NvU32 flatnessDetThresh;
|
|
NvU32 i;
|
|
|
|
nvAssert(pDscInfo->type == NV_DSC_INFO_EVO_TYPE_DP);
|
|
|
|
// XXX: I'm pretty sure that this is wrong.
|
|
// BitsPerPixelx16 is something like (24 * 16) = 384, and 2 << (384 - 8) is
|
|
// an insanely large number.
|
|
flatnessDetThresh = (2 << (pDscInfo->dp.bitsPerPixelX16 - 8)); /* ??? */
|
|
|
|
nvAssert((pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) ||
|
|
(pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_SINGLE));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DSC_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _ENABLE, _TRUE) |
|
|
((pDscInfo->dp.dscMode == NV_DSC_EVO_MODE_DUAL) ?
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _MODE, _DUAL) :
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _MODE, _SINGLE)) |
|
|
DRF_NUM(C57D, _HEAD_SET_DSC_CONTROL, _FLATNESS_DET_THRESH, flatnessDetThresh) |
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _FULL_ICH_ERR_PRECISION, _ENABLE) |
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _AUTO_RESET, _DISABLE) |
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _FORCE_ICH_RESET, _TRUE));
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DSC_PPS_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_PPS_CONTROL, _ENABLE, _TRUE) |
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_PPS_CONTROL, _LOCATION, _VSYNC) |
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_PPS_CONTROL, _FREQUENCY, _EVERY_FRAME) |
|
|
DRF_NUM(C57D, _HEAD_SET_DSC_PPS_CONTROL, _SIZE, 0x1F /* 32 PPS Dwords - 1 = 31 */));
|
|
|
|
|
|
#define NV_EVO5_NUM_HEAD_SET_DSC_PPS_DATA_DWORDS \
|
|
(((NVC57D_HEAD_SET_DSC_PPS_DATA31(0) - NVC57D_HEAD_SET_DSC_PPS_DATA0(0)) / 4) + 1)
|
|
|
|
ct_assert(NV_EVO5_NUM_HEAD_SET_DSC_PPS_DATA_DWORDS <= ARRAY_LEN(pDscInfo->dp.pps));
|
|
|
|
for (i = 0; i < NV_EVO5_NUM_HEAD_SET_DSC_PPS_DATA_DWORDS; i++) {
|
|
nvDmaSetStartEvoMethod(pChannel,(NVC57D_HEAD_SET_DSC_PPS_DATA0(head) + (i * 4)), 1);
|
|
nvDmaSetEvoMethodData(pChannel, pDscInfo->dp.pps[i]);
|
|
}
|
|
|
|
/*
|
|
* In case of DP, PPS is sent using the SDP over the Main-Link
|
|
* during the vertical blanking interval. The PPS SDP header is defined
|
|
* in DP 1.4 specification under section 2.2.5.9.1.
|
|
*/
|
|
|
|
nvDmaSetStartEvoMethod(pChannel,
|
|
NVC57D_HEAD_SET_DSC_PPS_HEAD(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_NUM(C57D, _HEAD_SET_DSC_PPS_HEAD, _BYTE0, 0x00) | /* SDP ID = 0x0 */
|
|
DRF_NUM(C57D, _HEAD_SET_DSC_PPS_HEAD, _BYTE1, 0x10) | /* SDP Type = 0x10 */
|
|
DRF_NUM(C57D, _HEAD_SET_DSC_PPS_HEAD, _BYTE2, 0x7f) | /* Number of payload data bytes - 1 = 0x7F */
|
|
DRF_NUM(C57D, _HEAD_SET_DSC_PPS_HEAD, _BYTE3, 0x00)); /* Reserved */
|
|
}
|
|
|
|
static void EvoSetDscParamsC5(const NVDispEvoRec *pDispEvo,
|
|
const NvU32 head,
|
|
const NVDscInfoEvoRec *pDscInfo,
|
|
const enum nvKmsPixelDepth pixelDepth)
|
|
{
|
|
if (pDscInfo->type == NV_DSC_INFO_EVO_TYPE_HDMI) {
|
|
EvoSetHdmiDscParams(pDispEvo, head, pDscInfo, pixelDepth);
|
|
} else if (pDscInfo->type == NV_DSC_INFO_EVO_TYPE_DP) {
|
|
EvoSetDpDscParams(pDispEvo, head, pDscInfo);
|
|
} else {
|
|
NVEvoChannelPtr pChannel = pDispEvo->pDevEvo->core;
|
|
|
|
nvAssert(pDscInfo->type == NV_DSC_INFO_EVO_TYPE_DISABLED);
|
|
|
|
/* Disable DSC function */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DSC_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_CONTROL, _ENABLE, _FALSE));
|
|
|
|
/* Disable PPS SDP (Secondary-Data Packet), DP won't send out PPS SDP */
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_DSC_PPS_CONTROL(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel,
|
|
DRF_DEF(C57D, _HEAD_SET_DSC_PPS_CONTROL, _ENABLE, _FALSE));
|
|
}
|
|
}
|
|
|
|
static void
|
|
EvoEnableMidFrameAndDWCFWatermarkC5(NVDevEvoPtr pDevEvo,
|
|
NvU32 sd,
|
|
NvU32 head,
|
|
NvBool enable,
|
|
NVEvoUpdateState *pUpdateState)
|
|
{
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
|
|
if (enable) {
|
|
pDevEvo->gpus[sd].setSwSpareA[head] =
|
|
FLD_SET_DRF(C37D,
|
|
_HEAD_SET_SW_SPARE_A,
|
|
_DISABLE_MID_FRAME_AND_DWCF_WATERMARK,
|
|
_FALSE,
|
|
pDevEvo->gpus[sd].setSwSpareA[head]);
|
|
} else {
|
|
pDevEvo->gpus[sd].setSwSpareA[head] =
|
|
FLD_SET_DRF(C37D,
|
|
_HEAD_SET_SW_SPARE_A,
|
|
_DISABLE_MID_FRAME_AND_DWCF_WATERMARK,
|
|
_TRUE,
|
|
pDevEvo->gpus[sd].setSwSpareA[head]);
|
|
}
|
|
|
|
nvPushEvoSubDevMask(pDevEvo, NVBIT(sd));
|
|
|
|
nvUpdateUpdateState(pDevEvo, pUpdateState, pChannel);
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC37D_HEAD_SET_SW_SPARE_A(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, pDevEvo->gpus[sd].setSwSpareA[head]);
|
|
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
|
|
NvU32 nvEvoGetActiveViewportOffsetC3(NVDispEvoRec *pDispEvo, NvU32 head)
|
|
{
|
|
NVC372_CTRL_CMD_GET_ACTIVE_VIEWPORT_POINT_IN_PARAMS params = { };
|
|
NvU32 ret;
|
|
NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
|
|
|
|
params.base.subdeviceIndex = pDispEvo->displayOwner;
|
|
params.windowIndex = head << 1;
|
|
|
|
ret = nvRmApiControl(nvEvoGlobal.clientHandle,
|
|
pDevEvo->rmCtrlHandle,
|
|
NVC372_CTRL_CMD_GET_ACTIVE_VIEWPORT_POINT_IN,
|
|
¶ms, sizeof(params));
|
|
|
|
if (ret != NVOS_STATUS_SUCCESS) {
|
|
nvEvoLogDevDebug(pDevEvo, EVO_LOG_ERROR,
|
|
"Failed to query active viewport offset");
|
|
}
|
|
|
|
return params.activeViewportPointIn.y;
|
|
}
|
|
|
|
static NvBool EvoComputeWindowScalingTapsC3(const NVDevEvoRec *pDevEvo,
|
|
const NVEvoChannel *pChannel,
|
|
NVFlipChannelEvoHwState *pHwState)
|
|
{
|
|
NvU32 win = NV_EVO_CHANNEL_MASK_WINDOW_NUMBER(pChannel->channelMask);
|
|
const NVEvoScalerCaps *pScalerCaps =
|
|
&pDevEvo->gpus[0].capabilities.window[win].scalerCaps;
|
|
|
|
if (!nvAssignScalerTaps(pDevEvo,
|
|
pScalerCaps,
|
|
pHwState->sizeIn.width, pHwState->sizeIn.height,
|
|
pHwState->sizeOut.width, pHwState->sizeOut.height,
|
|
FALSE /* doubleScan */,
|
|
&pHwState->hTaps, &pHwState->vTaps)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NvBool nvEvoComputeWindowScalingTapsC5(const NVDevEvoRec *pDevEvo,
|
|
const NVEvoChannel *pChannel,
|
|
NVFlipChannelEvoHwState *pHwState)
|
|
{
|
|
if (!EvoComputeWindowScalingTapsC3(pDevEvo, pChannel, pHwState)) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* If scaling is enabled, CSC11 will be used by NVKMS to convert from
|
|
* linear FP16 LMS to linear FP16 RGB. As such, the user-supplied precomp
|
|
* CSC can't be programmed into CSC11 in this case.
|
|
*/
|
|
if ((pHwState->sizeIn.width != pHwState->sizeOut.width) ||
|
|
(pHwState->sizeIn.height != pHwState->sizeOut.height)) {
|
|
if (!nvIsCscMatrixIdentity(&pHwState->cscMatrix)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void EvoSetMergeModeC5(const NVDispEvoRec *pDispEvo,
|
|
const NvU32 head,
|
|
const NVEvoMergeMode mode,
|
|
NVEvoUpdateState* pUpdateState)
|
|
{
|
|
NVDevEvoRec *pDevEvo = pDispEvo->pDevEvo;
|
|
NVEvoChannelPtr pChannel = pDevEvo->core;
|
|
NvU32 data = 0x0;
|
|
|
|
nvPushEvoSubDevMask(pDevEvo, NVBIT(pDispEvo->displayOwner));
|
|
|
|
nvUpdateUpdateState(pDevEvo, pUpdateState, pChannel);
|
|
|
|
switch (mode) {
|
|
case NV_EVO_MERGE_MODE_DISABLED:
|
|
data = DRF_DEF(C57D, _HEAD_SET_RG_MERGE, _MODE, _DISABLE);
|
|
break;
|
|
case NV_EVO_MERGE_MODE_SETUP:
|
|
data = DRF_DEF(C57D, _HEAD_SET_RG_MERGE, _MODE, _SETUP);
|
|
break;
|
|
case NV_EVO_MERGE_MODE_PRIMARY:
|
|
data = DRF_DEF(C57D, _HEAD_SET_RG_MERGE, _MODE, _MASTER);
|
|
break;
|
|
case NV_EVO_MERGE_MODE_SECONDARY:
|
|
data = DRF_DEF(C57D, _HEAD_SET_RG_MERGE, _MODE, _SLAVE);
|
|
break;
|
|
}
|
|
|
|
nvDmaSetStartEvoMethod(pChannel, NVC57D_HEAD_SET_RG_MERGE(head), 1);
|
|
nvDmaSetEvoMethodData(pChannel, data);
|
|
|
|
nvPopEvoSubDevMask(pDevEvo);
|
|
}
|
|
|
|
static NvU32 EvoAllocSurfaceDescriptorC3(
|
|
NVDevEvoPtr pDevEvo, NVSurfaceDescriptor *pSurfaceDesc,
|
|
NvU32 memoryHandle, NvU32 localCtxDmaFlags,
|
|
NvU64 limit)
|
|
{
|
|
return nvCtxDmaAlloc(pDevEvo, &pSurfaceDesc->ctxDmaHandle,
|
|
memoryHandle,
|
|
localCtxDmaFlags, limit);
|
|
}
|
|
|
|
static void EvoFreeSurfaceDescriptorC3(
|
|
NVDevEvoPtr pDevEvo,
|
|
NvU32 deviceHandle,
|
|
NVSurfaceDescriptor *pSurfaceDesc)
|
|
{
|
|
nvCtxDmaFree(pDevEvo, deviceHandle, &pSurfaceDesc->ctxDmaHandle);
|
|
}
|
|
|
|
static NvU32 EvoBindSurfaceDescriptorC3(
|
|
NVDevEvoPtr pDevEvo,
|
|
NVEvoChannelPtr pChannel,
|
|
NVSurfaceDescriptor *pSurfaceDesc)
|
|
{
|
|
return nvCtxDmaBind(pDevEvo, pChannel, pSurfaceDesc->ctxDmaHandle);
|
|
}
|
|
|
|
NVEvoHAL nvEvoC3 = {
|
|
EvoSetRasterParamsC3, /* SetRasterParams */
|
|
EvoSetProcAmpC3, /* SetProcAmp */
|
|
EvoSetHeadControlC3, /* SetHeadControl */
|
|
EvoSetHeadRefClkC3, /* SetHeadRefClk */
|
|
EvoHeadSetControlORC3, /* HeadSetControlOR */
|
|
nvEvoORSetControlC3, /* ORSetControl */
|
|
EvoHeadSetDisplayIdC3, /* HeadSetDisplayId */
|
|
EvoSetUsageBoundsC3, /* SetUsageBounds */
|
|
nvEvoUpdateC3, /* Update */
|
|
nvEvoIsModePossibleC3, /* IsModePossible */
|
|
nvEvoPrePostIMPC3, /* PrePostIMP */
|
|
nvEvoSetNotifierC3, /* SetNotifier */
|
|
EvoGetCapabilitiesC3, /* GetCapabilities */
|
|
EvoFlipC3, /* Flip */
|
|
EvoFlipTransitionWARC3, /* FlipTransitionWAR */
|
|
EvoFillLUTSurfaceC3, /* FillLUTSurface */
|
|
EvoSetLUTContextDmaC3, /* SetLUTContextDma */
|
|
EvoSetOutputScalerC3, /* SetOutputScaler */
|
|
EvoSetViewportPointInC3, /* SetViewportPointIn */
|
|
EvoSetViewportInOutC3, /* SetViewportInOut */
|
|
EvoSetCursorImageC3, /* SetCursorImage */
|
|
nvEvoValidateCursorSurfaceC3, /* ValidateCursorSurface */
|
|
EvoValidateWindowFormatC3, /* ValidateWindowFormat */
|
|
nvEvoInitCompNotifierC3, /* InitCompNotifier */
|
|
nvEvoIsCompNotifierCompleteC3, /* IsCompNotifierComplete */
|
|
nvEvoWaitForCompNotifierC3, /* WaitForCompNotifier */
|
|
EvoSetDitherC3, /* SetDither */
|
|
EvoSetStallLockC3, /* SetStallLock */
|
|
EvoSetDisplayRateC3, /* SetDisplayRate */
|
|
EvoInitChannelC3, /* InitChannel */
|
|
NULL, /* InitDefaultLut */
|
|
EvoInitWindowMappingC3, /* InitWindowMapping */
|
|
nvEvoIsChannelIdleC3, /* IsChannelIdle */
|
|
nvEvoIsChannelMethodPendingC3, /* IsChannelMethodPending */
|
|
nvEvoForceIdleSatelliteChannelC3, /* ForceIdleSatelliteChannel */
|
|
nvEvoForceIdleSatelliteChannelIgnoreLockC3, /* ForceIdleSatelliteChannelIgnoreLock */
|
|
nvEvoAccelerateChannelC3, /* AccelerateChannel */
|
|
nvEvoResetChannelAcceleratorsC3, /* ResetChannelAccelerators */
|
|
nvEvoAllocRmCtrlObjectC3, /* AllocRmCtrlObject */
|
|
nvEvoFreeRmCtrlObjectC3, /* FreeRmCtrlObject */
|
|
nvEvoSetImmPointOutC3, /* SetImmPointOut */
|
|
EvoStartHeadCRC32CaptureC3, /* StartCRC32Capture */
|
|
EvoStopHeadCRC32CaptureC3, /* StopCRC32Capture */
|
|
nvEvoQueryHeadCRC32_C3, /* QueryCRC32 */
|
|
nvEvoGetScanLineC3, /* GetScanLine */
|
|
NULL, /* ConfigureVblankSyncObject */
|
|
nvEvo1SetDscParams, /* SetDscParams */
|
|
NULL, /* EnableMidFrameAndDWCFWatermark */
|
|
nvEvoGetActiveViewportOffsetC3, /* GetActiveViewportOffset */
|
|
NULL, /* ClearSurfaceUsage */
|
|
EvoComputeWindowScalingTapsC3, /* ComputeWindowScalingTaps */
|
|
nvEvoGetWindowScalingCapsC3, /* GetWindowScalingCaps */
|
|
NULL, /* SetMergeMode */
|
|
EvoAllocSurfaceDescriptorC3, /* AllocSurfaceDescriptor */
|
|
EvoFreeSurfaceDescriptorC3, /* FreeSurfaceDescriptor */
|
|
EvoBindSurfaceDescriptorC3, /* BindSurfaceDescriptor */
|
|
NULL, /* SetTmoLutSurfaceAddress */
|
|
NULL, /* SetILUTSurfaceAddress */
|
|
EvoSetISOSurfaceAddressC3, /* SetISOSurfaceAddress */
|
|
EvoSetCoreNotifierSurfaceAddressAndControlC3, /* SetCoreNotifierSurfaceAddressAndControl */
|
|
EvoSetWinNotifierSurfaceAddressAndControlC3, /* SetWinNotifierSurfaceAddressAndControl */
|
|
NULL, /* SetSemaphoreSurfaceAddressAndControl */
|
|
NULL, /* SetAcqSemaphoreSurfaceAddressAndControl */
|
|
{ /* caps */
|
|
TRUE, /* supportsNonInterlockedUsageBoundsUpdate */
|
|
TRUE, /* supportsDisplayRate */
|
|
FALSE, /* supportsFlipLockRGStatus */
|
|
FALSE, /* needDefaultLutSurface */
|
|
FALSE, /* hasUnorm10OLUT */
|
|
FALSE, /* supportsImageSharpening */
|
|
FALSE, /* supportsHDMIVRR */
|
|
FALSE, /* supportsCoreChannelSurface */
|
|
FALSE, /* supportsHDMIFRL */
|
|
TRUE, /* supportsSetStorageMemoryLayout */
|
|
FALSE, /* supportsIndependentAcqRelSemaphore */
|
|
FALSE, /* supportsCoreLut */
|
|
TRUE, /* supportsSynchronizedOverlayPositionUpdate */
|
|
FALSE, /* supportsVblankSyncObjects */
|
|
FALSE, /* requiresScalingTapsInBothDimensions */
|
|
FALSE, /* supportsMergeMode */
|
|
FALSE, /* supportsHDMI10BPC */
|
|
FALSE, /* supportsDPAudio192KHz */
|
|
NV_EVO3_SUPPORTED_DITHERING_MODES, /* supportedDitheringModes */
|
|
sizeof(NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS), /* impStructSize */
|
|
NV_EVO_SCALER_2TAPS, /* minScalerTaps */
|
|
NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C3, /* xEmulatedSurfaceMemoryFormats */
|
|
},
|
|
};
|
|
|
|
NVEvoHAL nvEvoC5 = {
|
|
EvoSetRasterParamsC5, /* SetRasterParams */
|
|
EvoSetProcAmpC5, /* SetProcAmp */
|
|
EvoSetHeadControlC3, /* SetHeadControl */
|
|
EvoSetHeadRefClkC3, /* SetHeadRefClk */
|
|
EvoHeadSetControlORC5, /* HeadSetControlOR */
|
|
nvEvoORSetControlC3, /* ORSetControl */
|
|
EvoHeadSetDisplayIdC3, /* HeadSetDisplayId */
|
|
nvEvoSetUsageBoundsC5, /* SetUsageBounds */
|
|
nvEvoUpdateC3, /* Update */
|
|
nvEvoIsModePossibleC3, /* IsModePossible */
|
|
nvEvoPrePostIMPC3, /* PrePostIMP */
|
|
nvEvoSetNotifierC3, /* SetNotifier */
|
|
EvoGetCapabilitiesC5, /* GetCapabilities */
|
|
EvoFlipC5, /* Flip */
|
|
EvoFlipTransitionWARC5, /* FlipTransitionWAR */
|
|
nvEvoFillLUTSurfaceC5, /* FillLUTSurface */
|
|
EvoSetLUTContextDmaC5, /* SetLUTContextDma */
|
|
EvoSetOutputScalerC3, /* SetOutputScaler */
|
|
EvoSetViewportPointInC3, /* SetViewportPointIn */
|
|
EvoSetViewportInOutC5, /* SetViewportInOut */
|
|
EvoSetCursorImageC3, /* SetCursorImage */
|
|
nvEvoValidateCursorSurfaceC3, /* ValidateCursorSurface */
|
|
EvoValidateWindowFormatC5, /* ValidateWindowFormat */
|
|
nvEvoInitCompNotifierC3, /* InitCompNotifier */
|
|
nvEvoIsCompNotifierCompleteC3, /* IsCompNotifierComplete */
|
|
nvEvoWaitForCompNotifierC3, /* WaitForCompNotifier */
|
|
EvoSetDitherC3, /* SetDither */
|
|
EvoSetStallLockC3, /* SetStallLock */
|
|
EvoSetDisplayRateC3, /* SetDisplayRate */
|
|
EvoInitChannelC5, /* InitChannel */
|
|
nvEvoInitDefaultLutC5, /* InitDefaultLut */
|
|
nvEvoInitWindowMappingC5, /* InitWindowMapping */
|
|
nvEvoIsChannelIdleC3, /* IsChannelIdle */
|
|
nvEvoIsChannelMethodPendingC3, /* IsChannelMethodPending */
|
|
nvEvoForceIdleSatelliteChannelC3, /* ForceIdleSatelliteChannel */
|
|
nvEvoForceIdleSatelliteChannelIgnoreLockC3, /* ForceIdleSatelliteChannelIgnoreLock */
|
|
nvEvoAccelerateChannelC3, /* AccelerateChannel */
|
|
nvEvoResetChannelAcceleratorsC3, /* ResetChannelAccelerators */
|
|
nvEvoAllocRmCtrlObjectC3, /* AllocRmCtrlObject */
|
|
nvEvoFreeRmCtrlObjectC3, /* FreeRmCtrlObject */
|
|
nvEvoSetImmPointOutC3, /* SetImmPointOut */
|
|
EvoStartHeadCRC32CaptureC3, /* StartCRC32Capture */
|
|
EvoStopHeadCRC32CaptureC3, /* StopCRC32Capture */
|
|
nvEvoQueryHeadCRC32_C3, /* QueryCRC32 */
|
|
nvEvoGetScanLineC3, /* GetScanLine */
|
|
NULL, /* ConfigureVblankSyncObject */
|
|
EvoSetDscParamsC5, /* SetDscParams */
|
|
EvoEnableMidFrameAndDWCFWatermarkC5, /* EnableMidFrameAndDWCFWatermark */
|
|
nvEvoGetActiveViewportOffsetC3, /* GetActiveViewportOffset */
|
|
NULL, /* ClearSurfaceUsage */
|
|
nvEvoComputeWindowScalingTapsC5, /* ComputeWindowScalingTaps */
|
|
nvEvoGetWindowScalingCapsC3, /* GetWindowScalingCaps */
|
|
EvoSetMergeModeC5, /* SetMergeMode */
|
|
EvoAllocSurfaceDescriptorC3, /* AllocSurfaceDescriptor */
|
|
EvoFreeSurfaceDescriptorC3, /* FreeSurfaceDescriptor */
|
|
EvoBindSurfaceDescriptorC3, /* BindSurfaceDescriptor */
|
|
EvoSetTmoLutSurfaceAddressC5, /* SetTmoLutSurfaceAddress */
|
|
EvoSetILUTSurfaceAddressC5, /* SetILUTSurfaceAddress */
|
|
EvoSetISOSurfaceAddressC3, /* SetISOSurfaceAddress */
|
|
EvoSetCoreNotifierSurfaceAddressAndControlC3, /* SetCoreNotifierSurfaceAddressAndControl */
|
|
EvoSetWinNotifierSurfaceAddressAndControlC3, /* SetWinNotifierSurfaceAddressAndControl */
|
|
NULL, /* SetSemaphoreSurfaceAddressAndControl */
|
|
NULL, /* SetAcqSemaphoreSurfaceAddressAndControl */
|
|
{ /* caps */
|
|
TRUE, /* supportsNonInterlockedUsageBoundsUpdate */
|
|
TRUE, /* supportsDisplayRate */
|
|
FALSE, /* supportsFlipLockRGStatus */
|
|
TRUE, /* needDefaultLutSurface */
|
|
TRUE, /* hasUnorm10OLUT */
|
|
FALSE, /* supportsImageSharpening */
|
|
TRUE, /* supportsHDMIVRR */
|
|
FALSE, /* supportsCoreChannelSurface */
|
|
FALSE, /* supportsHDMIFRL */
|
|
TRUE, /* supportsSetStorageMemoryLayout */
|
|
FALSE, /* supportsIndependentAcqRelSemaphore */
|
|
FALSE, /* supportsCoreLut */
|
|
TRUE, /* supportsSynchronizedOverlayPositionUpdate */
|
|
FALSE, /* supportsVblankSyncObjects */
|
|
FALSE, /* requiresScalingTapsInBothDimensions */
|
|
TRUE, /* supportsMergeMode */
|
|
FALSE, /* supportsHDMI10BPC */
|
|
FALSE, /* supportsDPAudio192KHz */
|
|
NV_EVO3_SUPPORTED_DITHERING_MODES, /* supportedDitheringModes */
|
|
sizeof(NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS), /* impStructSize */
|
|
NV_EVO_SCALER_2TAPS, /* minScalerTaps */
|
|
NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C5, /* xEmulatedSurfaceMemoryFormats */
|
|
},
|
|
};
|
|
|
|
NVEvoHAL nvEvoC6 = {
|
|
EvoSetRasterParamsC6, /* SetRasterParams */
|
|
EvoSetProcAmpC5, /* SetProcAmp */
|
|
EvoSetHeadControlC3, /* SetHeadControl */
|
|
EvoSetHeadRefClkC3, /* SetHeadRefClk */
|
|
EvoHeadSetControlORC5, /* HeadSetControlOR */
|
|
EvoORSetControlC6, /* ORSetControl */
|
|
EvoHeadSetDisplayIdC3, /* HeadSetDisplayId */
|
|
nvEvoSetUsageBoundsC5, /* SetUsageBounds */
|
|
nvEvoUpdateC3, /* Update */
|
|
nvEvoIsModePossibleC3, /* IsModePossible */
|
|
nvEvoPrePostIMPC3, /* PrePostIMP */
|
|
nvEvoSetNotifierC3, /* SetNotifier */
|
|
nvEvoGetCapabilitiesC6, /* GetCapabilities */
|
|
nvEvoFlipC6, /* Flip */
|
|
nvEvoFlipTransitionWARC6, /* FlipTransitionWAR */
|
|
nvEvoFillLUTSurfaceC5, /* FillLUTSurface */
|
|
EvoSetLUTContextDmaC5, /* SetLUTContextDma */
|
|
EvoSetOutputScalerC3, /* SetOutputScaler */
|
|
EvoSetViewportPointInC3, /* SetViewportPointIn */
|
|
EvoSetViewportInOutC5, /* SetViewportInOut */
|
|
EvoSetCursorImageC3, /* SetCursorImage */
|
|
nvEvoValidateCursorSurfaceC3, /* ValidateCursorSurface */
|
|
nvEvoValidateWindowFormatC6, /* ValidateWindowFormat */
|
|
nvEvoInitCompNotifierC3, /* InitCompNotifier */
|
|
nvEvoIsCompNotifierCompleteC3, /* IsCompNotifierComplete */
|
|
nvEvoWaitForCompNotifierC3, /* WaitForCompNotifier */
|
|
EvoSetDitherC3, /* SetDither */
|
|
EvoSetStallLockC3, /* SetStallLock */
|
|
EvoSetDisplayRateC3, /* SetDisplayRate */
|
|
EvoInitChannelC5, /* InitChannel */
|
|
nvEvoInitDefaultLutC5, /* InitDefaultLut */
|
|
nvEvoInitWindowMappingC5, /* InitWindowMapping */
|
|
nvEvoIsChannelIdleC3, /* IsChannelIdle */
|
|
nvEvoIsChannelMethodPendingC3, /* IsChannelMethodPending */
|
|
nvEvoForceIdleSatelliteChannelC3, /* ForceIdleSatelliteChannel */
|
|
nvEvoForceIdleSatelliteChannelIgnoreLockC3, /* ForceIdleSatelliteChannelIgnoreLock */
|
|
nvEvoAccelerateChannelC3, /* AccelerateChannel */
|
|
nvEvoResetChannelAcceleratorsC3, /* ResetChannelAccelerators */
|
|
nvEvoAllocRmCtrlObjectC3, /* AllocRmCtrlObject */
|
|
nvEvoFreeRmCtrlObjectC3, /* FreeRmCtrlObject */
|
|
nvEvoSetImmPointOutC3, /* SetImmPointOut */
|
|
EvoStartHeadCRC32CaptureC3, /* StartCRC32Capture */
|
|
EvoStopHeadCRC32CaptureC3, /* StopCRC32Capture */
|
|
nvEvoQueryHeadCRC32_C3, /* QueryCRC32 */
|
|
nvEvoGetScanLineC3, /* GetScanLine */
|
|
EvoConfigureVblankSyncObjectC6, /* ConfigureVblankSyncObject */
|
|
EvoSetDscParamsC5, /* SetDscParams */
|
|
NULL, /* EnableMidFrameAndDWCFWatermark */
|
|
nvEvoGetActiveViewportOffsetC3, /* GetActiveViewportOffset */
|
|
NULL, /* ClearSurfaceUsage */
|
|
nvEvoComputeWindowScalingTapsC5, /* ComputeWindowScalingTaps */
|
|
nvEvoGetWindowScalingCapsC3, /* GetWindowScalingCaps */
|
|
EvoSetMergeModeC5, /* SetMergeMode */
|
|
EvoAllocSurfaceDescriptorC3, /* AllocSurfaceDescriptor */
|
|
EvoFreeSurfaceDescriptorC3, /* FreeSurfaceDescriptor */
|
|
EvoBindSurfaceDescriptorC3, /* BindSurfaceDescriptor */
|
|
EvoSetTmoLutSurfaceAddressC5, /* SetTmoLutSurfaceAddress */
|
|
EvoSetILUTSurfaceAddressC5, /* SetILUTSurfaceAddress */
|
|
EvoSetISOSurfaceAddressC3, /* SetISOSurfaceAddress */
|
|
EvoSetCoreNotifierSurfaceAddressAndControlC3, /* SetCoreNotifierSurfaceAddressAndControl */
|
|
EvoSetWinNotifierSurfaceAddressAndControlC3, /* SetWinNotifierSurfaceAddressAndControl */
|
|
EvoSetSemaphoreSurfaceAddressAndControlC6, /* SetSemaphoreSurfaceAddressAndControl */
|
|
EvoSetAcqSemaphoreSurfaceAddressAndControlC6, /* SetAcqSemaphoreSurfaceAddressAndControl */
|
|
{ /* caps */
|
|
TRUE, /* supportsNonInterlockedUsageBoundsUpdate */
|
|
TRUE, /* supportsDisplayRate */
|
|
FALSE, /* supportsFlipLockRGStatus */
|
|
TRUE, /* needDefaultLutSurface */
|
|
TRUE, /* hasUnorm10OLUT */
|
|
FALSE, /* supportsImageSharpening */
|
|
TRUE, /* supportsHDMIVRR */
|
|
FALSE, /* supportsCoreChannelSurface */
|
|
TRUE, /* supportsHDMIFRL */
|
|
FALSE, /* supportsSetStorageMemoryLayout */
|
|
TRUE, /* supportsIndependentAcqRelSemaphore */
|
|
FALSE, /* supportsCoreLut */
|
|
TRUE, /* supportsSynchronizedOverlayPositionUpdate */
|
|
TRUE, /* supportsVblankSyncObjects */
|
|
FALSE, /* requiresScalingTapsInBothDimensions */
|
|
TRUE, /* supportsMergeMode */
|
|
TRUE, /* supportsHDMI10BPC */
|
|
FALSE, /* supportsDPAudio192KHz */
|
|
NV_EVO3_SUPPORTED_DITHERING_MODES, /* supportedDitheringModes */
|
|
sizeof(NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS), /* impStructSize */
|
|
NV_EVO_SCALER_2TAPS, /* minScalerTaps */
|
|
NV_EVO3_X_EMULATED_SURFACE_MEMORY_FORMATS_C6, /* xEmulatedSurfaceMemoryFormats */
|
|
},
|
|
};
|