Files
open-gpu-kernel-modules/src/common/modeset/timing/nvt_displayid20.c
Bernhard Stoeckner 54d69484da 570.86.15
2025-01-27 19:36:56 +01:00

2013 lines
80 KiB
C

//*****************************************************************************
//
// SPDX-FileCopyrightText: Copyright (c) 2024 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.
//
// File: nvt_displayid20.c
//
// Purpose: the provide displayID 2.0 related services
//
//*****************************************************************************
#include "nvBinSegment.h"
#include "nvmisc.h"
#include "nvtiming_pvt.h"
#include "displayid20.h"
PUSH_SEGMENTS
// Helper function
static NVT_STATUS getPrimaryUseCase(NvU8 product_type, NVT_DISPLAYID_PRODUCT_PRIMARY_USE_CASE *primary_use_case);
static NvU32 greatestCommonDenominator(NvU32 x, NvU32 y);
static NvU8 getExistedTimingSeqNumber(NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo, enum NVT_TIMING_TYPE);
/*
* The Second-generation version of VESA DisplayID Standard
* DisplayID v2.0
*
* @brief Parses a displayID20 section
*
* @param pDisplayId The DisplayId20 Section Block ()
* @param length Size of the displayId section Block
* @param pDisplayIdInfo Need to parse the raw data to store as NV structure
*
*/
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS NV_STDCALL
NvTiming_parseDisplayId20Info(
const NvU8 *pDisplayId,
NvU32 length,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_SECTION *pSection = NULL;
NvU32 offset = 0;
NvU32 extensionIndex = 0;
NvU32 idx = 0;
// parameter check
if ((pDisplayId == NULL) ||
(pDisplayIdInfo == NULL))
{
return NVT_STATUS_ERR;
}
pSection = (const DISPLAYID_2_0_SECTION *)pDisplayId;
if ((pSection->header.version < DISPLAYID_2_0_VERSION) ||
(DISPLAYID_2_0_SECTION_SIZE_TOTAL(pSection->header) > length))
{
return NVT_STATUS_ERR;
}
NVMISC_MEMSET(pDisplayIdInfo, 0, sizeof(NVT_DISPLAYID_2_0_INFO));
status = parseDisplayId20BaseSection(pSection, pDisplayIdInfo);
if (status != NVT_STATUS_SUCCESS)
{
return status;
}
pDisplayIdInfo->extension_count = pSection->header.extension_count;
for (extensionIndex = 0; extensionIndex < pDisplayIdInfo->extension_count; extensionIndex++)
{
// Get offset to the next section.
offset += DISPLAYID_2_0_SECTION_SIZE_TOTAL(pSection->header);
// validate the next section buffer is valid
pSection = (const DISPLAYID_2_0_SECTION *)(pDisplayId + offset);
if ((offset + DISPLAYID_2_0_SECTION_SIZE_TOTAL(pSection->header)) > length)
{
return NVT_STATUS_ERR;
}
// process the section
status = parseDisplayId20ExtensionSection(pSection, pDisplayIdInfo);
if (status != NVT_STATUS_SUCCESS)
{
return status;
}
}
for (idx = 0; idx < pDisplayIdInfo->total_timings; idx++)
{
updateColorFormatForDisplayId20Timings(pDisplayIdInfo, idx);
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NvU32
NvTiming_DisplayID2ValidationMask(
NVT_DISPLAYID_2_0_INFO *pDisplayId20Info,
NvBool bIsStrongValidation)
{
NvU32 j;
NvU32 ret = 0;
// check the DisplayId2 version and signature
if (pDisplayId20Info->version != 0x2)
{
ret |= NVT_DID2_VALIDATION_ERR_MASK(NVT_DID2_VALIDATION_ERR_VERSION);
}
if (!pDisplayId20Info->valid_data_blocks.product_id_present)
{
ret |= NVT_DID2_VALIDATION_ERR_MASK(NVT_DID2_VALIDATION_ERR_PRODUCT_IDENTIFY);
}
if (pDisplayId20Info->primary_use_case >= PRODUCT_PRIMARY_USE_GENERIC_DISPLAY &&
pDisplayId20Info->primary_use_case <= PRODUCT_PRIMARY_USE_HEAD_MOUNT_AUGMENTED_REALITY)
{
if (!(pDisplayId20Info->valid_data_blocks.parameters_present &&
pDisplayId20Info->valid_data_blocks.interface_feature_present &&
pDisplayId20Info->valid_data_blocks.type7Timing_present &&
pDisplayId20Info->total_timings))
{
ret |= NVT_DID2_VALIDATION_ERR_MASK(NVT_DID2_VALIDATION_ERR_NO_DATA_BLOCK);
}
}
// Strong validation to follow
if (bIsStrongValidation == NV_TRUE)
{
// TODO : For each of the Data Block limitation
// Type 7 Timings data block
for (j = 0; j <= pDisplayId20Info->total_timings; j++)
{
if ( NVT_PREFERRED_TIMING_IS_DISPLAYID(pDisplayId20Info->timing[j].etc.flag) &&
(pDisplayId20Info->display_param.h_pixels != 0) &&
(pDisplayId20Info->display_param.v_pixels != 0))
{
if ( pDisplayId20Info->timing[j].HVisible != pDisplayId20Info->display_param.h_pixels ||
pDisplayId20Info->timing[j].VVisible != pDisplayId20Info->display_param.v_pixels )
{
ret |= NVT_DID2_VALIDATION_ERR_MASK(NVT_DID2_VALIDATION_ERR_NO_DATA_BLOCK);
break;
}
}
}
// TODO : go on the next data block validation if it existed.
// TODO : validate extension blocks
}
return ret;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
NvTiming_DisplayID2ValidationDataBlocks(
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo,
NvBool bIsStrongValidation)
{
if (NvTiming_DisplayID2ValidationMask(pDisplayIdInfo, bIsStrongValidation) != 0)
{
return NVT_STATUS_ERR;
}
else
{
return NVT_STATUS_SUCCESS;
}
}
// DisplayID20 Entry point functions
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20BaseSection(
const DISPLAYID_2_0_SECTION *pSection,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
// validate for section checksum before processing the data block
if (computeDisplayId20SectionCheckSum((const NvU8 *)pSection, DISPLAYID_2_0_SECTION_SIZE_TOTAL(pSection->header)) != 0)
{
status |= NVT_DID2_VALIDATION_ERR_MASK(NVT_DID2_VALIDATION_ERR_CHECKSUM);
return status;
}
pDisplayIdInfo->revision = pSection->header.revision;
pDisplayIdInfo->version = pSection->header.version;
status = getPrimaryUseCase(pSection->header.product_type,
&pDisplayIdInfo->primary_use_case);
if (status != NVT_STATUS_SUCCESS)
{
return status;
}
status = parseDisplayId20SectionDataBlocks(pSection, pDisplayIdInfo);
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20ExtensionSection(
const DISPLAYID_2_0_SECTION *pSection,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
// validate for section checksum before processing the data block
if (computeDisplayId20SectionCheckSum((const NvU8 *)pSection, DISPLAYID_2_0_SECTION_SIZE_TOTAL(pSection->header)) != 0)
{
status |= NVT_DID2_VALIDATION_ERR_MASK(NVT_DID2_VALIDATION_ERR_CHECKSUM);
return status;
}
nvt_assert(pSection->header.version >= DISPLAYID_2_0_VERSION);
nvt_assert(pSection->header.extension_count == 0);
nvt_assert(pSection->header.product_type == DISPLAYID_2_0_PROD_EXTENSION);
status = parseDisplayId20SectionDataBlocks(pSection, pDisplayIdInfo);
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20SectionDataBlocks(
const DISPLAYID_2_0_SECTION *pSection,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NvU32 i = 0;
NvU32 offset = 0;
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock = NULL;
NVT_STATUS status = NVT_STATUS_SUCCESS;
while (offset < pSection->header.section_bytes)
{
// Get current block
pDataBlock = (const DISPLAYID_2_0_DATA_BLOCK_HEADER *)(pSection->data + offset);
// detected zero padding
if (pDataBlock->type == 0)
{
for (i = offset; i < pSection->header.section_bytes; i++)
{
// validate that all paddings are zeros
nvt_assert(pSection->data[i] == 0);
}
break;
}
// check data block is valid.
if ((offset + DISPLAYID_2_0_DATA_BLOCK_SIZE_TOTAL(pDataBlock)) > pSection->header.section_bytes)
{
return NVT_STATUS_ERR;
}
// parse the data block
status = parseDisplayId20DataBlock(pDataBlock, pDisplayIdInfo);
if (status != NVT_STATUS_SUCCESS)
{
return status;
}
switch (pDataBlock->type)
{
case DISPLAYID_2_0_BLOCK_TYPE_PRODUCT_IDENTITY:
pDisplayIdInfo->valid_data_blocks.product_id_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_DISPLAY_PARAM:
pDisplayIdInfo->valid_data_blocks.parameters_present = NV_TRUE;
if (pDisplayIdInfo->display_param.audio_speakers_integrated == AUDIO_SPEAKER_INTEGRATED_SUPPORTED)
{
pDisplayIdInfo->basic_caps |= NVT_DISPLAY_2_0_CAP_BASIC_AUDIO;
}
break;
case DISPLAYID_2_0_BLOCK_TYPE_TIMING_7:
pDisplayIdInfo->valid_data_blocks.type7Timing_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_TIMING_8:
pDisplayIdInfo->valid_data_blocks.type8Timing_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_TIMING_9:
pDisplayIdInfo->valid_data_blocks.type9Timing_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_RANGE_LIMITS:
pDisplayIdInfo->valid_data_blocks.dynamic_range_limit_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_INTERFACE_FEATURES:
pDisplayIdInfo->valid_data_blocks.interface_feature_present = NV_TRUE;
// Supported - Color depth is supported for all supported timings. Supported timing includes all Display-ID exposed timings
// (that is timing exposed using DisplayID timing types and CTA VICs)
if (IS_BPC_SUPPORTED_COLORFORMAT(pDisplayIdInfo->interface_features.yuv444.bpcs))
{
pDisplayIdInfo->basic_caps |= NVT_DISPLAY_2_0_CAP_YCbCr_444;
}
if (IS_BPC_SUPPORTED_COLORFORMAT(pDisplayIdInfo->interface_features.yuv422.bpcs))
{
pDisplayIdInfo->basic_caps |= NVT_DISPLAY_2_0_CAP_YCbCr_422;
}
if (pDisplayIdInfo->interface_features.audio_capability.support_48khz ||
pDisplayIdInfo->interface_features.audio_capability.support_44_1khz ||
pDisplayIdInfo->interface_features.audio_capability.support_32khz)
{
pDisplayIdInfo->basic_caps |= NVT_DISPLAY_2_0_CAP_BASIC_AUDIO;
}
break;
case DISPLAYID_2_0_BLOCK_TYPE_STEREO:
pDisplayIdInfo->valid_data_blocks.stereo_interface_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_TILED_DISPLAY:
pDisplayIdInfo->valid_data_blocks.tiled_display_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_CONTAINER_ID:
pDisplayIdInfo->valid_data_blocks.container_id_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_TIMING_10:
pDisplayIdInfo->valid_data_blocks.type10Timing_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_ADAPTIVE_SYNC:
pDisplayIdInfo->valid_data_blocks.adaptive_sync_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_ARVR_HMD:
pDisplayIdInfo->valid_data_blocks.arvr_hmd_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_ARVR_LAYER:
pDisplayIdInfo->valid_data_blocks.arvr_layer_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_BRIGHTNESS_LUMINANCE_RANGE:
pDisplayIdInfo->valid_data_blocks.brightness_luminance_range_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_VENDOR_SPEC:
pDisplayIdInfo->valid_data_blocks.vendor_specific_present = NV_TRUE;
break;
case DISPLAYID_2_0_BLOCK_TYPE_CTA_DATA:
pDisplayIdInfo->valid_data_blocks.cta_data_present = NV_TRUE;
break;
default:
status = NVT_STATUS_ERR;
}
// advance to the next block
offset += DISPLAYID_2_0_DATA_BLOCK_SIZE_TOTAL(pDataBlock);
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20DataBlock(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
switch (pDataBlock->type)
{
case DISPLAYID_2_0_BLOCK_TYPE_PRODUCT_IDENTITY:
status = parseDisplayId20ProductIdentity(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_DISPLAY_PARAM:
status = parseDisplayId20DisplayParam(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_TIMING_7:
status = parseDisplayId20Timing7(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_TIMING_8:
status = parseDisplayId20Timing8(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_TIMING_9:
status = parseDisplayId20Timing9(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_TIMING_10:
status = parseDisplayId20Timing10(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_RANGE_LIMITS:
status = parseDisplayId20RangeLimit(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_INTERFACE_FEATURES:
status = parseDisplayId20DisplayInterfaceFeatures(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_STEREO:
status = parseDisplayId20Stereo(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_TILED_DISPLAY:
status = parseDisplayId20TiledDisplay(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_CONTAINER_ID:
status = parseDisplayId20ContainerId(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_ADAPTIVE_SYNC:
status = parseDisplayId20AdaptiveSync(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_ARVR_HMD:
status = parseDisplayId20ARVRHMD(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_ARVR_LAYER:
status = parseDisplayId20ARVRLayer(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_BRIGHTNESS_LUMINANCE_RANGE:
status = parseDisplayId20BrightnessLuminanceRange(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_VENDOR_SPEC:
status = parseDisplayId20VendorSpecific(pDataBlock, pDisplayIdInfo);
break;
case DISPLAYID_2_0_BLOCK_TYPE_CTA_DATA:
status = parseDisplayId20CtaData(pDataBlock, pDisplayIdInfo);
break;
default:
status = NVT_STATUS_ERR;
}
return status;
}
// All Data Blocks Parsing
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20ProductIdentity(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
NVT_DISPLAYID_PRODUCT_IDENTITY *pProductIdentity = NULL;
const DISPLAYID_2_0_PROD_IDENTIFICATION_BLOCK *pProductIdBlock = NULL;
pProductIdBlock = (const DISPLAYID_2_0_PROD_IDENTIFICATION_BLOCK *)pDataBlock;
// add more validation if needed
if (pDisplayIdInfo == NULL) return status;
pProductIdentity = &pDisplayIdInfo->product_identity;
pProductIdentity->vendor_id = (pProductIdBlock->vendor[0] << 16) |
(pProductIdBlock->vendor[1] << 8) |
(pProductIdBlock->vendor[2]);
pProductIdentity->product_id = (pProductIdBlock->product_code[0]) |
(pProductIdBlock->product_code[1] << 8);
pProductIdentity->serial_number = (pProductIdBlock->serial_number[0]) |
(pProductIdBlock->serial_number[1] << 8) |
(pProductIdBlock->serial_number[2] << 16) |
(pProductIdBlock->serial_number[3] << 24);
pProductIdentity->week = (pProductIdBlock->model_tag >= 1 && pProductIdBlock->model_tag <= 52) ?
pProductIdBlock->model_tag : 0;
pProductIdentity->year = (pProductIdBlock->model_tag == 0xFF) ?
pProductIdBlock->model_year :
pProductIdBlock->model_year + 2000;
if (pProductIdBlock->product_name_string_size != 0)
{
NVMISC_STRNCPY((char *)pProductIdentity->product_string,
(const char *)pProductIdBlock->product_name_string,
pProductIdBlock->product_name_string_size);
}
pProductIdentity->product_string[pProductIdBlock->product_name_string_size] = '\0';
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20DisplayParam(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_DISPLAY_PARAM_BLOCK *pDisplayParamBlock = NULL;
NVT_DISPLAYID_DISPLAY_PARAMETERS *pDisplayParam = NULL;
if (pDataBlock->data_bytes != DISPLAYID_2_0_DISPLAY_PARAM_BLOCK_PAYLOAD_LENGTH)
{
return NVT_STATUS_ERR;
}
// Add more validation here if needed
if (pDisplayIdInfo == NULL) return status;
pDisplayParamBlock = (const DISPLAYID_2_0_DISPLAY_PARAM_BLOCK *)pDataBlock;
pDisplayParam = &pDisplayIdInfo->display_param;
pDisplayParam->revision = pDisplayParamBlock->header.revision;
pDisplayParam->h_image_size_micro_meter = (pDisplayParamBlock->horizontal_image_size[1] << 8 |
pDisplayParamBlock->horizontal_image_size[0]) *
(pDisplayParamBlock->header.image_size_multiplier ? 1000 : 100);
pDisplayParam->v_image_size_micro_meter = (pDisplayParamBlock->vertical_image_size[1] << 8 |
pDisplayParamBlock->vertical_image_size[0]) *
(pDisplayParamBlock->header.image_size_multiplier ? 1000 : 100);
pDisplayParam->h_pixels = pDisplayParamBlock->horizontal_pixel_count[1] << 8 |
pDisplayParamBlock->horizontal_pixel_count[0];
pDisplayParam->v_pixels = pDisplayParamBlock->vertical_pixel_count[1] << 8 |
pDisplayParamBlock->vertical_pixel_count[0];
pDisplayParam->scan_orientation = pDisplayParamBlock->feature.scan_orientation;
pDisplayParam->audio_speakers_integrated = pDisplayParamBlock->feature.audio_speaker_information ? AUDIO_SPEAKER_INTEGRATED_NOT_SUPPORTED : AUDIO_SPEAKER_INTEGRATED_SUPPORTED;
pDisplayParam->color_map_standard = pDisplayParamBlock->feature.color_information ? COLOR_MAP_CIE_1976 : COLOR_MAP_CIE_1931;
// 12 bits Binary Fraction Representations
pDisplayParam->primaries[0].x = pDisplayParamBlock->primary_color_1_chromaticity.color_bits_mid.color_x_bits_high << 8 |
pDisplayParamBlock->primary_color_1_chromaticity.color_x_bits_low;
pDisplayParam->primaries[0].y = pDisplayParamBlock->primary_color_1_chromaticity.color_y_bits_high << 4 |
pDisplayParamBlock->primary_color_1_chromaticity.color_bits_mid.color_y_bits_low;
pDisplayParam->primaries[1].x = pDisplayParamBlock->primary_color_2_chromaticity.color_bits_mid.color_x_bits_high << 8 |
pDisplayParamBlock->primary_color_2_chromaticity.color_x_bits_low;
pDisplayParam->primaries[1].y = pDisplayParamBlock->primary_color_2_chromaticity.color_y_bits_high << 4 |
pDisplayParamBlock->primary_color_2_chromaticity.color_bits_mid.color_y_bits_low;
pDisplayParam->primaries[2].x = pDisplayParamBlock->primary_color_3_chromaticity.color_bits_mid.color_x_bits_high << 8 |
pDisplayParamBlock->primary_color_3_chromaticity.color_x_bits_low;
pDisplayParam->primaries[2].y = pDisplayParamBlock->primary_color_3_chromaticity.color_y_bits_high << 4 |
pDisplayParamBlock->primary_color_3_chromaticity.color_bits_mid.color_y_bits_low;
pDisplayParam->white.x = pDisplayParamBlock->white_point_chromaticity.color_bits_mid.color_x_bits_high << 8 |
pDisplayParamBlock->white_point_chromaticity.color_x_bits_low;
pDisplayParam->white.y = pDisplayParamBlock->white_point_chromaticity.color_y_bits_high << 4 |
pDisplayParamBlock->white_point_chromaticity.color_bits_mid.color_y_bits_low;
// IEEE 754 half-precision binary floating-point format
pDisplayParam->native_max_luminance_full_coverage = pDisplayParamBlock->max_luminance_full_coverage[1] << 8 |
pDisplayParamBlock->max_luminance_full_coverage[0];
pDisplayParam->native_max_luminance_10_percent_rect_coverage = pDisplayParamBlock->max_luminance_10_percent_rectangular_coverage[1] << 8 |
pDisplayParamBlock->max_luminance_10_percent_rectangular_coverage[0];
pDisplayParam->native_min_luminance = pDisplayParamBlock->min_luminance[1] << 8 |
pDisplayParamBlock->min_luminance[0];
if (pDisplayParamBlock->feature.luminance_information == 0)
{
pDisplayParam->native_luminance_info = NATIVE_LUMINANCE_INFO_MIN_GURANTEE_VALUE;
}
else if (pDisplayParamBlock->feature.luminance_information == 1)
{
pDisplayParam->native_luminance_info = NATIVE_LUMINANCE_INFO_SOURCE_DEVICE_GUIDANCE;
}
else
{
return NVT_STATUS_ERR;
}
UPDATE_BPC_FOR_COLORFORMAT(pDisplayParam->native_color_depth,
pDisplayParamBlock->color_depth_and_device_technology.color_depth == NATIVE_COLOR_BPC_6,
pDisplayParamBlock->color_depth_and_device_technology.color_depth == NATIVE_COLOR_BPC_8,
pDisplayParamBlock->color_depth_and_device_technology.color_depth == NATIVE_COLOR_BPC_10,
pDisplayParamBlock->color_depth_and_device_technology.color_depth == NATIVE_COLOR_BPC_12,
0,
pDisplayParamBlock->color_depth_and_device_technology.color_depth == NATIVE_COLOR_BPC_16);
pDisplayParam->device_technology = pDisplayParamBlock->color_depth_and_device_technology.device_technology;
if (pDisplayParam->revision == 1)
{
pDisplayParam->device_theme_Preference = pDisplayParamBlock->color_depth_and_device_technology.device_theme_preference;
}
pDisplayParam->gamma_x100 = (pDisplayParamBlock->gamma_EOTF + 100);
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20Timing7(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_TIMING_7_BLOCK *pTiming7Block = NULL;
NvU32 descriptorCount = 0;
NvU8 revision = 0;
NvU8 i = 0;
NvU8 startSeqNumber = 0;
NVT_TIMING newTiming;
pTiming7Block = (const DISPLAYID_2_0_TIMING_7_BLOCK *)pDataBlock;
// Based on the DisplayID_2_0_E7 spec:
// the Future descriptor can be defined with more than 20 Byte per descriptor without creating a new timing type
if (pTiming7Block->header.payload_bytes_len == 0)
{
if (pDataBlock->data_bytes % sizeof(DISPLAYID_2_0_TIMING_7_DESCRIPTOR) != 0)
{
return NVT_STATUS_ERR;
}
revision = pTiming7Block->header.revision;
descriptorCount = pDataBlock->data_bytes / (sizeof(DISPLAYID_2_0_TIMING_7_DESCRIPTOR) + pTiming7Block->header.payload_bytes_len);
if (descriptorCount < 1 || descriptorCount > DISPLAYID_2_0_TIMING_7_MAX_DESCRIPTORS)
{
return NVT_STATUS_ERR;
}
if (pDisplayIdInfo != NULL)
{
startSeqNumber = getExistedTimingSeqNumber(pDisplayIdInfo, NVT_TYPE_DISPLAYID_7);
}
for (i = 0; i < descriptorCount; i++)
{
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
if (parseDisplayId20Timing7Descriptor(&pTiming7Block->descriptors[i], &newTiming, startSeqNumber+i) == NVT_STATUS_SUCCESS)
{
newTiming.etc.flag |= (revision >= DISPLAYID_2_0_TYPE7_DSC_PASSTHRU_REVISION && pTiming7Block->header.dsc_passthrough == 1) ?
NVT_FLAG_DISPLAYID_T7_DSC_PASSTHRU :
0;
if (revision >= DISPLAYID_2_0_TYPE7_YCC420_SUPPORT_REVISION)
{
newTiming.etc.flag |= pTiming7Block->descriptors[i].options.is_preferred_or_ycc420 ? NVT_FLAG_DISPLAYID_T7_T8_EXPLICT_YUV420 : 0;
if (pTiming7Block->descriptors[i].options.is_preferred_or_ycc420) // YCC 420 support
{
UPDATE_BPC_FOR_COLORFORMAT(newTiming.etc.yuv420, 0, 1, 1, 1, 0, 1);
}
}
else
{
newTiming.etc.flag |= pTiming7Block->descriptors[i].options.is_preferred_or_ycc420 ? NVT_FLAG_DISPLAYID_DTD_PREFERRED_TIMING : 0;
}
NVT_SNPRINTF((char *)newTiming.etc.name, sizeof(newTiming.etc.name), "DID20-Type7:#%2d:%dx%dx%3d.%03dHz/%s",
(int)NVT_GET_TIMING_STATUS_SEQ(newTiming.etc.status),
(int)newTiming.HVisible,
(int)((newTiming.interlaced ? 2 : 1)*newTiming.VVisible),
(int)newTiming.etc.rrx1k/1000,
(int)newTiming.etc.rrx1k%1000,
(newTiming.interlaced ? "I":"P"));
newTiming.etc.name[sizeof(newTiming.etc.name) - 1] = '\0';
newTiming.etc.rep = 0x1;
if (!assignNextAvailableDisplayId20Timing(pDisplayIdInfo, &newTiming))
{
break;
}
}
else
{
if (pDisplayIdInfo == NULL) return NVT_STATUS_ERR;
}
}
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20Timing8(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_TIMING_8_BLOCK *pTiming8Block = NULL;
NVT_TIMING newTiming;
NvU8 codeCount = 0;
NvU8 startSeqNumber = 0;
NvU8 i;
pTiming8Block = (const DISPLAYID_2_0_TIMING_8_BLOCK *)pDataBlock;
codeCount = pDataBlock->data_bytes;
if (codeCount == 0)
{
nvt_assert(0 && "No available byte code!");
return NVT_STATUS_SUCCESS;
}
if (codeCount > DISPLAYID_2_0_TIMING_8_MAX_CODES)
{
nvt_assert(0 && "one byte code is out of range!");
return NVT_STATUS_SUCCESS;
}
if (pDisplayIdInfo != NULL)
{
startSeqNumber = getExistedTimingSeqNumber(pDisplayIdInfo, NVT_TYPE_DISPLAYID_8);
}
for (i = 0; i < codeCount; i++)
{
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
if (parseDisplayId20Timing8Descriptor(&pTiming8Block->timingCode, &newTiming,
pTiming8Block->header.timing_code_type,
pTiming8Block->header.timing_code_size, i, startSeqNumber+i) == NVT_STATUS_SUCCESS)
{
newTiming.etc.flag |= ((pTiming8Block->header.revision == 1) && pTiming8Block->header.is_support_yuv420) ?
NVT_FLAG_DISPLAYID_T7_T8_EXPLICT_YUV420 :
0;
NVT_SNPRINTF((char *)newTiming.etc.name, sizeof(newTiming.etc.name), "DID20-Type8:#%3d:%dx%dx%3d.%03dHz/%s",
(int)NVT_GET_TIMING_STATUS_SEQ(newTiming.etc.status),
(int)newTiming.HVisible, (int)newTiming.VVisible,
(int)newTiming.etc.rrx1k/1000, (int)newTiming.etc.rrx1k%1000,
(newTiming.interlaced ? "I":"P"));
newTiming.etc.name[sizeof(newTiming.etc.name) - 1] = '\0';
newTiming.etc.rep = 0x1;
if (!assignNextAvailableDisplayId20Timing(pDisplayIdInfo, &newTiming))
{
break;
}
}
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20Timing9(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_TIMING_9_BLOCK *pTiming9Block = NULL;
NVT_TIMING newTiming;
NvU32 descriptorCount = 0;
NvU8 startSeqNumber = 0;
NvU8 i = 0;
descriptorCount = pDataBlock->data_bytes / sizeof(DISPLAYID_2_0_TIMING_9_DESCRIPTOR);
if (descriptorCount < 1 || descriptorCount > DISPLAYID_2_0_TIMING_9_MAX_DESCRIPTORS)
{
return NVT_STATUS_ERR;
}
pTiming9Block = (const DISPLAYID_2_0_TIMING_9_BLOCK *)pDataBlock;
if (pDisplayIdInfo != NULL)
{
startSeqNumber = getExistedTimingSeqNumber(pDisplayIdInfo, NVT_TYPE_DISPLAYID_9);
}
for (i = 0; i < descriptorCount; i++)
{
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
if (parseDisplayId20Timing9Descriptor(&pTiming9Block->descriptors[i], &newTiming, startSeqNumber+i) == NVT_STATUS_SUCCESS)
{
if (!assignNextAvailableDisplayId20Timing(pDisplayIdInfo, &newTiming))
{
break;
}
}
else
{
if (pDisplayIdInfo == NULL) return NVT_STATUS_ERR;
}
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20Timing10(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
NvU32 descriptorCount = 0;
NvU8 startSeqNumber = 0;
NvU8 i = 0;
NvU8 eachOfDescriptorsSize = sizeof(DISPLAYID_2_0_TIMING_10_6BYTES_DESCRIPTOR);
const DISPLAYID_2_0_TIMING_10_BLOCK *pTiming10Block = NULL;
const DISPLAYID_2_0_TIMING_10_6BYTES_DESCRIPTOR *p6bytesDescriptor = NULL;
NVT_TIMING newTiming;
pTiming10Block = (const DISPLAYID_2_0_TIMING_10_BLOCK *)pDataBlock;
if (pTiming10Block->header.type != DISPLAYID_2_0_BLOCK_TYPE_TIMING_10)
{
return NVT_STATUS_ERR;
}
if (pTiming10Block->header.payload_bytes_len == DISPLAYID_2_0_TIMING_10_PAYLOAD_BYTES_6)
{
descriptorCount = pDataBlock->data_bytes / sizeof(DISPLAYID_2_0_TIMING_10_6BYTES_DESCRIPTOR);
if (descriptorCount < 1 || descriptorCount > DISPLAYID_2_0_TIMING_10_MAX_6BYTES_DESCRIPTORS)
{
return NVT_STATUS_ERR;
}
}
else if (pTiming10Block->header.payload_bytes_len == DISPLAYID_2_0_TIMING_10_PAYLOAD_BYTES_7)
{
descriptorCount = pDataBlock->data_bytes / sizeof(DISPLAYID_2_0_TIMING_10_7BYTES_DESCRIPTOR);
if (descriptorCount < 1 || descriptorCount > DISPLAYID_2_0_TIMING_10_MAX_7BYTES_DESCRIPTORS)
{
return NVT_STATUS_ERR;
}
}
else if (pTiming10Block->header.payload_bytes_len == DISPLAYID_2_1_TIMING_10_PAYLOAD_BYTES_8)
{
descriptorCount = pDataBlock->data_bytes / sizeof(DISPLAYID_2_1_TIMING_10_8BYTES_DESCRIPTOR);
if (descriptorCount < 1 || descriptorCount > DISPLAYID_2_1_TIMING_10_MAX_8BYTES_DESCRIPTORS)
{
return NVT_STATUS_ERR;
}
}
eachOfDescriptorsSize += pTiming10Block->header.payload_bytes_len;
if (pDisplayIdInfo != NULL)
{
startSeqNumber = getExistedTimingSeqNumber(pDisplayIdInfo, NVT_TYPE_DISPLAYID_10);
}
for (i = 0; i < descriptorCount; i++)
{
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
if (NVT_STATUS_SUCCESS == parseDisplayId20Timing10Descriptor(&pTiming10Block->descriptors[i*eachOfDescriptorsSize],
&newTiming,
pTiming10Block->header.payload_bytes_len,
startSeqNumber+i))
{
p6bytesDescriptor = (const DISPLAYID_2_0_TIMING_10_6BYTES_DESCRIPTOR *)&pTiming10Block->descriptors[i*eachOfDescriptorsSize];
if (p6bytesDescriptor->options.ycc420_support)
{
UPDATE_BPC_FOR_COLORFORMAT(newTiming.etc.yuv420, 0, 1, 1, 1, 0, 1);
}
if (p6bytesDescriptor->options.timing_formula == DISPLAYID_2_0_TIMING_FORMULA_CVT_1_2_STANDARD_CRT_BASED)
{
NVT_SNPRINTF((char *)newTiming.etc.name, sizeof(newTiming.etc.name), "DID20-Type10:#%3d:%dx%dx%3d.%03dHz/%s",
(int)NVT_GET_TIMING_STATUS_SEQ(newTiming.etc.status),
(int)newTiming.HVisible,
(int)newTiming.VVisible,
(int)newTiming.etc.rrx1k/1000,
(int)newTiming.etc.rrx1k%1000,
(newTiming.interlaced ? "I":"P"));
}
else
{
NVT_SNPRINTF((char *)newTiming.etc.name, sizeof(newTiming.etc.name), "DID20-Type10RB%d:#%3d:%dx%dx%3d.%03dHz/%s",
p6bytesDescriptor->options.timing_formula,
(int)NVT_GET_TIMING_STATUS_SEQ(newTiming.etc.status),
(int)newTiming.HVisible,
(int)newTiming.VVisible,
(int)newTiming.etc.rrx1k/1000,
(int)newTiming.etc.rrx1k%1000,
(newTiming.interlaced ? "I":"P"));
}
newTiming.etc.name[sizeof(newTiming.etc.name) - 1] = '\0';
newTiming.etc.rep = 0x1;
if (!assignNextAvailableDisplayId20Timing(pDisplayIdInfo, &newTiming))
{
break;
}
}
else
{
if (pDisplayIdInfo == NULL)
{
return NVT_STATUS_ERR;
}
continue;
}
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20RangeLimit(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_RANGE_LIMITS_BLOCK *pRangeLimitsBlock = NULL;
NVT_DISPLAYID_RANGE_LIMITS rangeLimits = {0};
// basic sanity check
if (pDataBlock->data_bytes != DISPLAYID_2_0_RANGE_LIMITS_BLOCK_PAYLOAD_LENGTH)
{
return NVT_STATUS_ERR;
}
pRangeLimitsBlock = (const DISPLAYID_2_0_RANGE_LIMITS_BLOCK *)pDataBlock;
rangeLimits.revision = pDataBlock->revision;
rangeLimits.pclk_min = (pRangeLimitsBlock->pixel_clock_min[2] << 16 |
pRangeLimitsBlock->pixel_clock_min[1] << 8 |
pRangeLimitsBlock->pixel_clock_min[0]) + 1;
rangeLimits.pclk_max = (pRangeLimitsBlock->pixel_clock_max[2] << 16 |
pRangeLimitsBlock->pixel_clock_max[1] << 8 |
pRangeLimitsBlock->pixel_clock_max[0]) + 1;
rangeLimits.vfreq_min = pRangeLimitsBlock->vertical_frequency_min;
if (rangeLimits.revision == 1)
{
rangeLimits.vfreq_max = pRangeLimitsBlock->dynamic_video_timing_range_support.vertical_frequency_max_9_8 << 8 |
pRangeLimitsBlock->vertical_frequency_max_7_0;
}
else
{
rangeLimits.vfreq_max = pRangeLimitsBlock->vertical_frequency_max_7_0;
}
rangeLimits.seamless_dynamic_video_timing_change = pRangeLimitsBlock->dynamic_video_timing_range_support.seamless_dynamic_video_timing_change;
if (pDisplayIdInfo == NULL)
{
if (rangeLimits.vfreq_min > rangeLimits.vfreq_max || rangeLimits.pclk_min > rangeLimits.pclk_max ||
rangeLimits.vfreq_min == 0 || rangeLimits.vfreq_max == 0)
{
nvt_assert(0 && "wrong range limit");
status = NVT_STATUS_ERR;
}
return status;
}
NVMISC_MEMCPY(&pDisplayIdInfo->range_limits, &rangeLimits, sizeof(NVT_DISPLAYID_RANGE_LIMITS));
return status;
}
#define ADD_COLOR_SPACE_EOTF_COMBINATION(_pInterfaceFeatures, _color_space, _eotf) do { \
(_pInterfaceFeatures)->colorspace_eotf_combination[(_pInterfaceFeatures)->combination_count].color_space = (_color_space); \
(_pInterfaceFeatures)->colorspace_eotf_combination[(_pInterfaceFeatures)->combination_count].eotf = (_eotf); \
(_pInterfaceFeatures)->combination_count++; \
} while(0)
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20DisplayInterfaceFeatures(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
NvU32 i = 0;
const DISPLAYID_2_0_INTERFACE_FEATURES_BLOCK *pInterfaceFeaturesBlock = NULL;
NVT_DISPLAYID_INTERFACE_FEATURES *pInterfaceFeatures = NULL;
if (pDataBlock->data_bytes < DISPLAYID_2_0_INTERFACE_FEATURES_BLOCK_PAYLOAD_LENGTH_MIN)
{
return NVT_STATUS_ERR;
}
// Add more validation here if needed.
if (pDisplayIdInfo == NULL) return status;
pInterfaceFeatures = &pDisplayIdInfo->interface_features;
pInterfaceFeaturesBlock = (const DISPLAYID_2_0_INTERFACE_FEATURES_BLOCK *)pDataBlock;
pInterfaceFeatures->revision = pDataBlock->revision;
UPDATE_BPC_FOR_COLORFORMAT(pInterfaceFeatures->rgb444,
pInterfaceFeaturesBlock->interface_color_depth_rgb.bit_per_primary_6,
pInterfaceFeaturesBlock->interface_color_depth_rgb.bit_per_primary_8,
pInterfaceFeaturesBlock->interface_color_depth_rgb.bit_per_primary_10,
pInterfaceFeaturesBlock->interface_color_depth_rgb.bit_per_primary_12,
pInterfaceFeaturesBlock->interface_color_depth_rgb.bit_per_primary_14,
pInterfaceFeaturesBlock->interface_color_depth_rgb.bit_per_primary_16);
UPDATE_BPC_FOR_COLORFORMAT(pInterfaceFeatures->yuv444,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr444.bit_per_primary_6,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr444.bit_per_primary_8,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr444.bit_per_primary_10,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr444.bit_per_primary_12,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr444.bit_per_primary_14,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr444.bit_per_primary_16);
UPDATE_BPC_FOR_COLORFORMAT(pInterfaceFeatures->yuv422,
0,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr422.bit_per_primary_8,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr422.bit_per_primary_10,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr422.bit_per_primary_12,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr422.bit_per_primary_14,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr422.bit_per_primary_16);
UPDATE_BPC_FOR_COLORFORMAT(pInterfaceFeatures->yuv420,
0,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr420.bit_per_primary_8,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr420.bit_per_primary_10,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr420.bit_per_primary_12,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr420.bit_per_primary_14,
pInterfaceFeaturesBlock->interface_color_depth_ycbcr420.bit_per_primary_16);
// * 74.25MP/s
pInterfaceFeatures->yuv420_min_pclk = pInterfaceFeaturesBlock->min_pixel_rate_ycbcr420 *
7425;
pInterfaceFeatures->audio_capability.support_48khz =
pInterfaceFeaturesBlock->audio_capability.sample_rate_48_khz;
pInterfaceFeatures->audio_capability.support_44_1khz =
pInterfaceFeaturesBlock->audio_capability.sample_rate_44_1_khz;
pInterfaceFeatures->audio_capability.support_32khz =
pInterfaceFeaturesBlock->audio_capability.sample_rate_32_khz;
if (pInterfaceFeaturesBlock->color_space_and_eotf_1.color_space_srgb_eotf_srgb)
{
ADD_COLOR_SPACE_EOTF_COMBINATION(pInterfaceFeatures,
INTERFACE_COLOR_SPACE_SRGB,
INTERFACE_EOTF_SRGB);
}
if (pInterfaceFeaturesBlock->color_space_and_eotf_1.color_space_bt601_eotf_bt601)
{
ADD_COLOR_SPACE_EOTF_COMBINATION(pInterfaceFeatures,
INTERFACE_COLOR_SPACE_BT601,
INTERFACE_EOTF_BT601);
}
if (pInterfaceFeaturesBlock->color_space_and_eotf_1.color_space_bt709_eotf_bt1886)
{
ADD_COLOR_SPACE_EOTF_COMBINATION(pInterfaceFeatures,
INTERFACE_COLOR_SPACE_BT709,
INTERFACE_EOTF_BT1886);
}
if (pInterfaceFeaturesBlock->color_space_and_eotf_1.color_space_adobe_rgb_eotf_adobe_rgb)
{
ADD_COLOR_SPACE_EOTF_COMBINATION(pInterfaceFeatures,
INTERFACE_COLOR_SPACE_ADOBE_RGB,
INTERFACE_EOTF_ADOBE_RGB);
}
if (pInterfaceFeaturesBlock->color_space_and_eotf_1.color_space_dci_p3_eotf_dci_p3)
{
ADD_COLOR_SPACE_EOTF_COMBINATION(pInterfaceFeatures,
INTERFACE_COLOR_SPACE_DCI_P3,
INTERFACE_EOTF_DCI_P3);
}
if (pInterfaceFeaturesBlock->color_space_and_eotf_1.color_space_bt2020_eotf_bt2020)
{
ADD_COLOR_SPACE_EOTF_COMBINATION(pInterfaceFeatures,
INTERFACE_COLOR_SPACE_BT2020,
INTERFACE_EOTF_BT2020);
}
if (pInterfaceFeaturesBlock->color_space_and_eotf_1.color_space_bt2020_eotf_smpte_st2084)
{
ADD_COLOR_SPACE_EOTF_COMBINATION(pInterfaceFeatures,
INTERFACE_COLOR_SPACE_BT2020,
INTERFACE_EOTF_SMPTE_ST2084);
}
for (i = 0; i < pInterfaceFeaturesBlock->additional_color_space_and_eotf_count.count; i++)
{
ADD_COLOR_SPACE_EOTF_COMBINATION(pInterfaceFeatures,
pInterfaceFeaturesBlock->additional_color_space_and_eotf[i].color_space,
pInterfaceFeaturesBlock->additional_color_space_and_eotf[i].eotf);
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20Stereo(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
if (pDisplayIdInfo == NULL) return status;
// TODO: Implement the parsing here.
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20TiledDisplay(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_TILED_DISPLAY_BLOCK *pTiledDisplayBlock = NULL;
NVT_DISPLAYID_TILED_DISPLAY_TOPOLOGY *pTileTopo = NULL;
if (pDataBlock->data_bytes != DISPLAYID_2_0_TILED_DISPLAY_BLOCK_PAYLOAD_LENGTH)
{
return NVT_STATUS_ERR;
}
if (pDisplayIdInfo == NULL) return status;
pTiledDisplayBlock = (const DISPLAYID_2_0_TILED_DISPLAY_BLOCK *)pDataBlock;
pTileTopo = &pDisplayIdInfo->tile_topo;
pTileTopo->revision = pDataBlock->revision;
pTileTopo->capability.bSingleEnclosure = pTiledDisplayBlock->capability.single_enclosure;
pTileTopo->capability.bHasBezelInfo = pTiledDisplayBlock->capability.has_bezel_info;
pTileTopo->capability.multi_tile_behavior = pTiledDisplayBlock->capability.multi_tile_behavior;
pTileTopo->capability.single_tile_behavior = pTiledDisplayBlock->capability.single_tile_behavior;
pTileTopo->topology.row = ((pTiledDisplayBlock->topo_loc_high.row << 5) |
(pTiledDisplayBlock->topo_low.row)) + 1;
pTileTopo->topology.col = ((pTiledDisplayBlock->topo_loc_high.col << 5) |
(pTiledDisplayBlock->topo_low.col)) + 1;
pTileTopo->location.x = ((pTiledDisplayBlock->topo_loc_high.x << 5) |
(pTiledDisplayBlock->loc_low.x));
pTileTopo->location.y = ((pTiledDisplayBlock->topo_loc_high.y << 5) |
(pTiledDisplayBlock->loc_low.y));
pTileTopo->native_resolution.width = ((pTiledDisplayBlock->native_resolution.width_high << 8) |
pTiledDisplayBlock->native_resolution.width_low) + 1;
pTileTopo->native_resolution.height = ((pTiledDisplayBlock->native_resolution.height_high << 8) |
pTiledDisplayBlock->native_resolution.height_low) + 1;
pTileTopo->bezel_info.top = (pTiledDisplayBlock->bezel_info.top *
pTiledDisplayBlock->bezel_info.pixel_density) / 10;
pTileTopo->bezel_info.bottom = (pTiledDisplayBlock->bezel_info.bottom *
pTiledDisplayBlock->bezel_info.pixel_density) / 10;
pTileTopo->bezel_info.right = (pTiledDisplayBlock->bezel_info.right *
pTiledDisplayBlock->bezel_info.pixel_density) / 10;
pTileTopo->bezel_info.left = (pTiledDisplayBlock->bezel_info.left *
pTiledDisplayBlock->bezel_info.pixel_density) / 10;
pTileTopo->tile_topology_id.vendor_id = pTiledDisplayBlock->topo_id.vendor_id[0] << 16 |
pTiledDisplayBlock->topo_id.vendor_id[1] << 8 |
pTiledDisplayBlock->topo_id.vendor_id[2];
pTileTopo->tile_topology_id.product_id = pTiledDisplayBlock->topo_id.product_id[1] << 8 |
pTiledDisplayBlock->topo_id.product_id[0];
pTileTopo->tile_topology_id.serial_number = pTiledDisplayBlock->topo_id.serial_number[3] << 24 |
pTiledDisplayBlock->topo_id.serial_number[2] << 16 |
pTiledDisplayBlock->topo_id.serial_number[1] << 8 |
pTiledDisplayBlock->topo_id.serial_number[0];
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20ContainerId(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_CONTAINERID_BLOCK *pContainerIdBlock = NULL;
NVT_DISPLAYID_CONTAINERID *pContainerId = NULL;
if (pDataBlock->data_bytes != DISPLAYID_2_0_CONTAINERID_BLOCK_PAYLOAD_LENGTH)
{
return NVT_STATUS_ERR;
}
if (pDisplayIdInfo == NULL) return status;
pContainerIdBlock = (const DISPLAYID_2_0_CONTAINERID_BLOCK *)pDataBlock;
pContainerId = &pDisplayIdInfo->container_id;
pContainerId->revision = pDataBlock->revision;
pContainerId->data1 = pContainerIdBlock->container_id[0] << 24 |
pContainerIdBlock->container_id[1] << 16 |
pContainerIdBlock->container_id[2] << 8 |
pContainerIdBlock->container_id[3];
pContainerId->data2 = pContainerIdBlock->container_id[4] << 8 |
pContainerIdBlock->container_id[5];
pContainerId->data3 = pContainerIdBlock->container_id[6] << 8 |
pContainerIdBlock->container_id[7];
pContainerId->data4 = pContainerIdBlock->container_id[8] << 8 |
pContainerIdBlock->container_id[9];
pContainerId->data5[0] = pContainerIdBlock->container_id[10];
pContainerId->data5[1] = pContainerIdBlock->container_id[11];
pContainerId->data5[2] = pContainerIdBlock->container_id[12];
pContainerId->data5[3] = pContainerIdBlock->container_id[13];
pContainerId->data5[4] = pContainerIdBlock->container_id[14];
pContainerId->data5[5] = pContainerIdBlock->container_id[15];
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20AdaptiveSync(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_ADAPTIVE_SYNC_BLOCK *pAdaptiveSyncBlock = NULL;
NvU32 descriptorCnt = 0;
NvU8 i = 0;
NvU8 minRR = 0;
NvU16 maxRR = 0;
pAdaptiveSyncBlock = (const DISPLAYID_2_0_ADAPTIVE_SYNC_BLOCK *)pDataBlock;
if (pAdaptiveSyncBlock->header.payload_bytes_adaptive_sync_len == 0)
{
// Sanity check
if (pDataBlock->data_bytes % sizeof(DISPLAYID_2_0_ADAPTIVE_SYNC_DESCRIPTOR) != 0)
{
status = NVT_STATUS_ERR;
}
descriptorCnt = pDataBlock->data_bytes / sizeof(DISPLAYID_2_0_ADAPTIVE_SYNC_DESCRIPTOR);
if (descriptorCnt < 1) return NVT_STATUS_ERR;
if (pDisplayIdInfo == NULL)
{
for (i = 0; i < descriptorCnt; i++)
{
minRR = pAdaptiveSyncBlock->descriptors[i].min_refresh_rate;
maxRR = (pAdaptiveSyncBlock->descriptors[i].max_refresh_rate.max_rr_9_8 << 8 |
pAdaptiveSyncBlock->descriptors[i].max_refresh_rate.max_rr_7_0) + 1;
if (minRR > (maxRR + 1) || minRR == 0 || maxRR == 0)
{
status = NVT_STATUS_ERR;
}
}
return status;
}
pDisplayIdInfo->total_adaptive_sync_descriptor = descriptorCnt;
for (i = 0; i < descriptorCnt; i++)
{
// Byte 0 Adaptive-Sync Operation and Range Information
pDisplayIdInfo->adaptive_sync_descriptor[i].u.information.adaptive_sync_range = pAdaptiveSyncBlock->descriptors[i].operation_range_info.range;
pDisplayIdInfo->adaptive_sync_descriptor[i].u.information.duration_inc_flicker_perf = pAdaptiveSyncBlock->descriptors[i].operation_range_info.successive_frame_inc_tolerance;
pDisplayIdInfo->adaptive_sync_descriptor[i].u.information.modes = pAdaptiveSyncBlock->descriptors[i].operation_range_info.modes;
pDisplayIdInfo->adaptive_sync_descriptor[i].u.information.seamless_not_support = pAdaptiveSyncBlock->descriptors[i].operation_range_info.seamless_transition_not_support;
pDisplayIdInfo->adaptive_sync_descriptor[i].u.information.duration_dec_flicker_perf = pAdaptiveSyncBlock->descriptors[i].operation_range_info.successive_frame_dec_tolerance;
// 6.2 format (six integer bits and two fractional bits) a value range of 0.00 to 63.75ms
pDisplayIdInfo->adaptive_sync_descriptor[i].max_duration_inc = pAdaptiveSyncBlock->descriptors[i].max_single_frame_inc;
pDisplayIdInfo->adaptive_sync_descriptor[i].min_rr = pAdaptiveSyncBlock->descriptors[i].min_refresh_rate;
pDisplayIdInfo->adaptive_sync_descriptor[i].max_rr = (pAdaptiveSyncBlock->descriptors[i].max_refresh_rate.max_rr_9_8 << 8 |
pAdaptiveSyncBlock->descriptors[i].max_refresh_rate.max_rr_7_0) + 1;
// 6.2 format (six integer bits and two fractional bits) a value range of 0.00 to 63.75ms
pDisplayIdInfo->adaptive_sync_descriptor[i].max_duration_dec = pAdaptiveSyncBlock->descriptors[i].max_single_frame_dec;
}
}
else // all other values are RESERVED.
{
if (pDisplayIdInfo == NULL) return NVT_STATUS_ERR;
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20ARVRHMD(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
if (pDisplayIdInfo == NULL) return status;
// TODO: Implement the parsing here.
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20ARVRLayer(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
if (pDisplayIdInfo == NULL) return status;
// TODO: Implement the parsing here.
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20BrightnessLuminanceRange(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_BRIGHTNESS_LUMINANCE_RANGE_BLOCK *pBrightnessLuminanceRangeBlock = NULL;
NVT_DISPLAYID_BRIGHTNESS_LUMINANCE_RANGE *pluminanceRanges = NULL;
if ((pDataBlock == NULL) || pDataBlock->data_bytes != DISPLAYID_2_0_BRIGHTNESS_LUMINANCE_RANGE_BLOCK_PAYLOAD_LENGTH)
{
return NVT_STATUS_ERR;
}
if (pDisplayIdInfo == NULL) return status;
pBrightnessLuminanceRangeBlock = (const DISPLAYID_2_0_BRIGHTNESS_LUMINANCE_RANGE_BLOCK *)pDataBlock;
pluminanceRanges = &pDisplayIdInfo->luminance_ranges;
pluminanceRanges->revision = pDataBlock->revision;
pluminanceRanges->min_sdr_luminance = pBrightnessLuminanceRangeBlock->min_sdr_luminance;
pluminanceRanges->max_sdr_luminance = pBrightnessLuminanceRangeBlock->max_sdr_luminance;
pluminanceRanges->max_boost_sdr_luminance = pBrightnessLuminanceRangeBlock->max_boost_sdr_luminance;
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20VendorSpecific(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_VENDOR_SPECIFIC_BLOCK *block = NULL;
NVT_DISPLAYID_VENDOR_SPECIFIC *pVendorSpecific = NULL;
NvU32 ieee_oui = 0;
// Add more validation here if needed
if (pDisplayIdInfo == NULL) return status;
block = (const DISPLAYID_2_0_VENDOR_SPECIFIC_BLOCK*)pDataBlock;
pVendorSpecific = &pDisplayIdInfo->vendor_specific;
ieee_oui = (NvU32)((block->vendor_id[0] << 16) |
(block->vendor_id[1] << 8) |
(block->vendor_id[2]));
switch (ieee_oui)
{
case NVT_VESA_VENDOR_SPECIFIC_IEEE_ID:
// TODO: below parser shall be updated if DID21 changed in the future
if (pDataBlock->data_bytes == NVT_VESA_VENDOR_SPECIFIC_LENGTH)
{
pVendorSpecific->vesaVsdb.data_struct_type.type =
block->vendor_specific_data[3] & NVT_VESA_ORG_VSDB_DATA_TYPE_MASK;
pVendorSpecific->vesaVsdb.data_struct_type.color_space_and_eotf =
(block->vendor_specific_data[3] & NVT_VESA_ORG_VSDB_COLOR_SPACE_AND_EOTF_MASK) >> NVT_VESA_ORG_VSDB_COLOR_SPACE_AND_EOTF_SHIFT;
pVendorSpecific->vesaVsdb.overlapping.pixels_overlapping_count =
block->vendor_specific_data[4] & NVT_VESA_ORG_VSDB_PIXELS_OVERLAPPING_MASK;
pVendorSpecific->vesaVsdb.overlapping.multi_sst =
(block->vendor_specific_data[4] & NVT_VESA_ORG_VSDB_MULTI_SST_MODE_MASK) >> NVT_VESA_ORG_VSDB_MULTI_SST_MODE_SHIFT;
pVendorSpecific->vesaVsdb.pass_through_integer.pass_through_integer_dsc =
block->vendor_specific_data[5] & NVT_VESA_ORG_VSDB_PASS_THROUGH_INTEGER_MASK;
pVendorSpecific->vesaVsdb.pass_through_fractional.pass_through_fraction_dsc =
block->vendor_specific_data[6] & NVT_VESA_ORG_VSDB_PASS_THROUGH_FRACTIOINAL_MASK;
}
else
{
status = NVT_STATUS_ERR;
}
break;
default:
break;
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20CtaData(
const DISPLAYID_2_0_DATA_BLOCK_HEADER *pDataBlock,
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
NVT_EDID_CEA861_INFO *p861Info = NULL;
const DISPLAYID_2_0_CTA_BLOCK * ctaBlock = NULL;
NvU8 *pcta_data = NULL;
ctaBlock = (const DISPLAYID_2_0_CTA_BLOCK *)pDataBlock;
// WAR here to add a (size_t) cast for casting member from const to non-const in order to avoid Linux old compiler failed in DVS.
pcta_data = (NvU8 *)(size_t)ctaBlock->cta_data;
if (pDisplayIdInfo == NULL)
{
status = parseCta861DataBlockInfo(pcta_data, pDataBlock->data_bytes, NULL);
return status;
}
else
{
status = parseCta861DataBlockInfo(pcta_data, pDataBlock->data_bytes, &pDisplayIdInfo->cta.cta861_info);
}
if (status != NVT_STATUS_SUCCESS)
{
return status;
}
p861Info = &(pDisplayIdInfo->cta.cta861_info);
parseCta861VsdbBlocks(p861Info, pDisplayIdInfo, FROM_DISPLAYID_20_DATA_BLOCK);
parseCta861VsvdbBlocks(p861Info, pDisplayIdInfo, FROM_DISPLAYID_20_DATA_BLOCK);
parseCta861HfScdb(p861Info, pDisplayIdInfo, FROM_DISPLAYID_20_DATA_BLOCK);
// This CTA 861 function to parse 861 part
parse861bShortTiming(p861Info, pDisplayIdInfo, FROM_DISPLAYID_20_DATA_BLOCK);
// yuv420-only video
parse861bShortYuv420Timing(p861Info, pDisplayIdInfo, FROM_DISPLAYID_20_DATA_BLOCK);
if (pDisplayIdInfo->cta.cta861_info.valid.hdr_static_metadata != 0)
{
parseCta861HdrStaticMetadataDataBlock(p861Info, pDisplayIdInfo, FROM_DISPLAYID_20_DATA_BLOCK);
}
// CEA861-F at 7.5.12 section about VFPDB block.
if (p861Info->total_svr > 0)
{
parseCta861NativeOrPreferredTiming(p861Info, pDisplayIdInfo, FROM_DISPLAYID_20_DATA_BLOCK);
}
return status;
}
// Helper function
CODE_SEGMENT(PAGE_DD_CODE)
static NvU32
greatestCommonDenominator(
NvU32 x,
NvU32 y)
{
NvU32 g = 0;
while (x > 0)
{
g = x;
x = y % x;
y = g;
}
return g;
}
CODE_SEGMENT(PAGE_DD_CODE)
static NVT_STATUS
getPrimaryUseCase(
NvU8 product_type,
NVT_DISPLAYID_PRODUCT_PRIMARY_USE_CASE *primary_use_case)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
switch (product_type)
{
case DISPLAYID_2_0_PROD_TEST:
*primary_use_case = PRODUCT_PRIMARY_USE_TEST_EQUIPMENT;
break;
case DISPLAYID_2_0_PROD_GENERIC_DISPLAY:
*primary_use_case = PRODUCT_PRIMARY_USE_GENERIC_DISPLAY;
break;
case DISPLAYID_2_0_PROD_TELEVISION:
*primary_use_case = PRODUCT_PRIMARY_USE_TELEVISION;
break;
case DISPLAYID_2_0_PROD_DESKTOP_PRODUCTIVITY_DISPLAY:
*primary_use_case = PRODUCT_PRIMARY_USE_DESKTOP_PRODUCTIVITY;
break;
case DISPLAYID_2_0_PROD_DESKTOP_GAMING_DISPLAY:
*primary_use_case = PRODUCT_PRIMARY_USE_DESKTOP_GAMING;
break;
case DISPLAYID_2_0_PROD_PRESENTATION_DISPLAY:
*primary_use_case = PRODUCT_PRIMARY_USE_PRESENTATION;
break;
case DISPLAYID_2_0_PROD_HMD_VR:
*primary_use_case = PRODUCT_PRIMARY_USE_HEAD_MOUNT_VIRTUAL_REALITY;
break;
case DISPLAYID_2_0_PROD_HMD_AR:
*primary_use_case = PRODUCT_PRIMARY_USE_HEAD_MOUNT_AUGMENTED_REALITY;
break;
case DISPLAYID_2_0_PROD_EXTENSION:
status = NVT_STATUS_ERR;
break;
default:
status = NVT_STATUS_ERR;
}
return status;
}
// used in DID20 and DID20ext
CODE_SEGMENT(PAGE_DD_CODE)
NvU8
computeDisplayId20SectionCheckSum(
const NvU8 *pSectionBytes,
NvU32 length)
{
NvU32 i = 0;
NvU32 checkSum = 0;
// Each DisplayID section composed of five mandatory bytes:
// DisplayID Structure Version and Revision
// Section Size
// Product Primary Use Case
// Extension Count
// Checksum
for (i = 0, checkSum = 0; i < length; i++)
{
checkSum += pSectionBytes[i];
}
return (checkSum & 0xFF);
}
CODE_SEGMENT(PAGE_DD_CODE)
NvBool
assignNextAvailableDisplayId20Timing(
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo,
const NVT_TIMING *pTiming)
{
if (pDisplayIdInfo == NULL) return NV_TRUE;
if (pDisplayIdInfo->total_timings >= COUNT(pDisplayIdInfo->timing))
{
return NV_FALSE;
}
pDisplayIdInfo->timing[pDisplayIdInfo->total_timings] = *pTiming;
pDisplayIdInfo->total_timings++;
return NV_TRUE;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20Timing7Descriptor(
const void *pVoidDescriptor,
NVT_TIMING *pTiming,
NvU8 count)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
NvU32 gdc = 0;
const DISPLAYID_2_0_TIMING_7_DESCRIPTOR *pDescriptor = NULL;
pDescriptor = (const DISPLAYID_2_0_TIMING_7_DESCRIPTOR *)pVoidDescriptor;
// pclk is in 1Khz
pTiming->pclk1khz = ((pDescriptor->pixel_clock[2] << 16 |
pDescriptor->pixel_clock[1] << 8 |
pDescriptor->pixel_clock[0]) + 1);
pTiming->HBorder = 0;
pTiming->VBorder = 0;
pTiming->HVisible = ((pDescriptor->horizontal.active_image_pixels[1] << 8) |
(pDescriptor->horizontal.active_image_pixels[0])) + 1;
pTiming->VVisible = ((pDescriptor->vertical.active_image_lines[1] << 8) |
(pDescriptor->vertical.active_image_lines[0])) + 1;
pTiming->HTotal = (((pDescriptor->horizontal.blank_pixels[1] << 8) |
(pDescriptor->horizontal.blank_pixels[0])) + 1) +
pTiming->HVisible;
pTiming->VTotal = (((pDescriptor->vertical.blank_lines[1] << 8) |
(pDescriptor->vertical.blank_lines[0])) + 1) +
pTiming->VVisible;
pTiming->HFrontPorch = ((pDescriptor->horizontal.front_porch_pixels_high << 8) |
(pDescriptor->horizontal.front_porch_pixels_low)) + 1;
pTiming->VFrontPorch = ((pDescriptor->vertical.front_porch_lines_high << 8) |
(pDescriptor->vertical.front_porch_lines_low)) + 1;
pTiming->HSyncWidth = ((pDescriptor->horizontal.sync_width_pixels[1] << 8) |
(pDescriptor->horizontal.sync_width_pixels[0])) + 1;
pTiming->VSyncWidth = ((pDescriptor->vertical.sync_width_lines[1] << 8) |
(pDescriptor->vertical.sync_width_lines[0])) + 1;
pTiming->HSyncPol = pDescriptor->horizontal.sync_polarity ? NVT_H_SYNC_POSITIVE :
NVT_H_SYNC_NEGATIVE;
pTiming->VSyncPol = pDescriptor->vertical.sync_polarity ? NVT_V_SYNC_POSITIVE :
NVT_V_SYNC_NEGATIVE;
// EDID used in DP1.4 Compliance test had incorrect HBlank listed, leading to wrong raster sizes being set by driver (bug 2714607)
// Filter incorrect timings here. HTotal must cover sufficient blanking time
if (pTiming->HTotal < (pTiming->HVisible + pTiming->HFrontPorch + pTiming->HSyncWidth))
{
return NVT_STATUS_ERR;
}
pTiming->interlaced = pDescriptor->options.interface_frame_scanning_type;
switch (pDescriptor->options.aspect_ratio)
{
case DISPLAYID_2_0_TIMING_ASPECT_RATIO_1_1:
pTiming->etc.aspect = (1 << 16) | 1;
break;
case DISPLAYID_2_0_TIMING_ASPECT_RATIO_5_4:
pTiming->etc.aspect = (5 << 16) | 4;
break;
case DISPLAYID_2_0_TIMING_ASPECT_RATIO_4_3:
pTiming->etc.aspect = (4 << 16) | 3;
break;
case DISPLAYID_2_0_TIMING_ASPECT_RATIO_15_9:
pTiming->etc.aspect = (15 << 16) | 9;
break;
case DISPLAYID_2_0_TIMING_ASPECT_RATIO_16_9:
pTiming->etc.aspect = (16 << 16) | 9;
break;
case DISPLAYID_2_0_TIMING_ASPECT_RATIO_16_10:
pTiming->etc.aspect = (16 << 16) | 10;
break;
case DISPLAYID_2_0_TIMING_ASPECT_RATIO_64_27:
pTiming->etc.aspect = (64 << 16) | 27;
break;
case DISPLAYID_2_0_TIMING_ASPECT_RATIO_256_135:
pTiming->etc.aspect = (256 << 16) | 135;
break;
case DISPLAYID_2_0_TIMING_ASPECT_RATIO_CALCULATE:
gdc = greatestCommonDenominator(pTiming->HVisible, pTiming->VVisible);
if (gdc != 0)
{
pTiming->etc.aspect = ((pTiming->HVisible / gdc) << 16) |
(pTiming->VVisible / gdc);
}
else
{
pTiming->etc.aspect = 0;
}
break;
default:
pTiming->etc.aspect = 0;
}
pTiming->etc.rr = NvTiming_CalcRR(pTiming->pclk1khz,
pTiming->interlaced,
pTiming->HTotal,
pTiming->VTotal) / 10;
pTiming->etc.rrx1k = NvTiming_CalcRRx1k(pTiming->pclk1khz,
pTiming->interlaced,
pTiming->HTotal,
pTiming->VTotal) / 10;
// pclk change to 10kHz
pTiming->pclk = pTiming->pclk1khz / 10;
pTiming->etc.status = NVT_STATUS_DISPLAYID_7N(++count);
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20Timing8Descriptor(
const void *pVoidDescriptor,
NVT_TIMING *pTiming,
NvU8 codeType,
NvU8 codeSize,
NvU8 idx,
NvU8 count)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const NvU8 *pTimingCode = (const NvU8 *)pVoidDescriptor;
const NvU16 *pTiming2ByteCode = (const NvU16 *)pVoidDescriptor;
if (codeSize == DISPLAYID_2_0_TIMING_CODE_SIZE_1_BYTE)
{
switch (codeType)
{
case DISPLAYID_2_0_TIMING_CODE_DMT: //single-byte DMT ID Codes
status = NvTiming_EnumDMT((NvU32)(pTimingCode[idx]), pTiming);
break;
case DISPLAYID_2_0_TIMING_CODE_CTA_VIC:
status = NvTiming_EnumCEA861bTiming((NvU32)(pTimingCode[idx]), pTiming);
break;
case DISPLAYID_2_0_TIMING_CODE_HDMI_VIC:
status = NvTiming_EnumHdmiVsdbExtendedTiming((NvU32)(pTimingCode[idx]), pTiming);
break;
default:
{
nvt_assert(0 && "RESERVED timing code type");
status = NVT_STATUS_ERR;
}
break;
}
}
else if (codeSize == DISPLAYID_2_0_TIMING_CODE_SIZE_2_BYTE)
{
// Standard two-byte codes
if (codeType == DISPLAYID_2_0_TIMING_CODE_DMT)
{
status = NvTiming_EnumStdTwoBytesCode((NvU16)pTiming2ByteCode[idx], pTiming);
}
}
if (status == NVT_STATUS_SUCCESS)
{
pTiming->etc.status = NVT_STATUS_DISPLAYID_8N(++count);
return status;
}
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20Timing9Descriptor(
const void *pVoidDescriptor,
NVT_TIMING *pTiming,
NvU8 count)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_TIMING_9_DESCRIPTOR* pDescriptor = NULL;
NvU32 width = 0;
NvU32 height = 0;
NvU32 rr = 0;
pDescriptor = (const DISPLAYID_2_0_TIMING_9_DESCRIPTOR *)pVoidDescriptor;
width = (pDescriptor->horizontal_active_pixels[1] << 8 | pDescriptor->horizontal_active_pixels[0]) + 1;
height = (pDescriptor->vertical_active_lines[1] << 8 | pDescriptor->vertical_active_lines[0]) + 1;
rr = pDescriptor->refresh_rate + 1;
switch (pDescriptor->options.timing_formula)
{
case DISPLAYID_2_0_TIMING_FORMULA_CVT_1_2_STANDARD_CRT_BASED:
status = NvTiming_CalcCVT(width, height, rr, NVT_PROGRESSIVE, pTiming);
break;
case DISPLAYID_2_0_TIMING_FORMULA_CVT_1_2_REDUCED_BLANKING_1:
status = NvTiming_CalcCVT_RB(width, height, rr, NVT_PROGRESSIVE, pTiming);
break;
case DISPLAYID_2_0_TIMING_FORMULA_CVT_2_1_REDUCED_BLANKING_2:
status = NvTiming_CalcCVT_RB2(width, height, rr, pDescriptor->options.rr_1000div1001_support, NV_FALSE, pTiming);
break;
default:
{
nvt_assert(0 && "Unknown timing formula");
status = NVT_STATUS_ERR;
}
break;
}
if (status == NVT_STATUS_SUCCESS)
{
NVMISC_MEMSET(pTiming->etc.name, 0, sizeof(pTiming->etc.name));
pTiming->etc.status = NVT_STATUS_DISPLAYID_9N(++count);
if (pDescriptor->options.timing_formula == DISPLAYID_2_0_TIMING_FORMULA_CVT_1_2_STANDARD_CRT_BASED)
{
NVT_SNPRINTF((char *)pTiming->etc.name, sizeof(pTiming->etc.name), "DID20-Type9:#%3d:%dx%dx%3d.%03dHz/%s",
(int)NVT_GET_TIMING_STATUS_SEQ(pTiming->etc.status),
(int)pTiming->HVisible,
(int)pTiming->VVisible,
(int)pTiming->etc.rrx1k/1000,
(int)pTiming->etc.rrx1k%1000,
(pTiming->interlaced ? "I":"P"));
}
else
{
NVT_SNPRINTF((char *)pTiming->etc.name, sizeof(pTiming->etc.name), "DID20-Type9RB%d:#%3d:%dx%dx%3d.%03dHz/%s",
pDescriptor->options.timing_formula,
(int)NVT_GET_TIMING_STATUS_SEQ(pTiming->etc.status),
(int)pTiming->HVisible,
(int)pTiming->VVisible,
(int)pTiming->etc.rrx1k/1000,
(int)pTiming->etc.rrx1k%1000,
(pTiming->interlaced ? "I":"P"));
}
}
pTiming->etc.name[sizeof(pTiming->etc.name) - 1] = '\0';
pTiming->etc.rep = 0x1;
return status;
}
CODE_SEGMENT(PAGE_DD_CODE)
NVT_STATUS
parseDisplayId20Timing10Descriptor(
const void *pDescriptor,
NVT_TIMING *pTiming,
NvU8 payloadBytes,
NvU8 count)
{
NVT_STATUS status = NVT_STATUS_SUCCESS;
const DISPLAYID_2_0_TIMING_10_6BYTES_DESCRIPTOR* p6bytesDescriptor = NULL;
const DISPLAYID_2_0_TIMING_10_7BYTES_DESCRIPTOR* p7bytesDescriptor = NULL;
const DISPLAYID_2_1_TIMING_10_8BYTES_DESCRIPTOR* p8bytesDescriptor = NULL;
NvU32 width = 0;
NvU32 height = 0;
NvU32 rr = 0;
p6bytesDescriptor = (const DISPLAYID_2_0_TIMING_10_6BYTES_DESCRIPTOR *)pDescriptor;
width = (p6bytesDescriptor->horizontal_active_pixels[1] << 8 | p6bytesDescriptor->horizontal_active_pixels[0]) + 1;
height = (p6bytesDescriptor->vertical_active_lines[1] << 8 | p6bytesDescriptor->vertical_active_lines[0]) + 1;
rr = p6bytesDescriptor->refresh_rate + 1;
if (payloadBytes == DISPLAYID_2_0_TIMING_10_PAYLOAD_BYTES_7)
{
p7bytesDescriptor = (const DISPLAYID_2_0_TIMING_10_7BYTES_DESCRIPTOR *)pDescriptor;
rr = (p7bytesDescriptor->descriptor_6_bytes.refresh_rate | p7bytesDescriptor->refresh_rate_high << 8) + 1;
}
else if (payloadBytes == DISPLAYID_2_1_TIMING_10_PAYLOAD_BYTES_8)
{
p8bytesDescriptor = (const DISPLAYID_2_1_TIMING_10_8BYTES_DESCRIPTOR *)pDescriptor;
rr = (p8bytesDescriptor->descriptor_7_bytes.descriptor_6_bytes.refresh_rate |
p8bytesDescriptor->descriptor_7_bytes.refresh_rate_high << 8) + 1;
}
switch (p6bytesDescriptor->options.timing_formula)
{
case DISPLAYID_2_0_TIMING_FORMULA_CVT_1_2_STANDARD_CRT_BASED:
status = NvTiming_CalcCVT(width, height, rr, NVT_PROGRESSIVE, pTiming);
break;
case DISPLAYID_2_0_TIMING_FORMULA_CVT_1_2_REDUCED_BLANKING_1:
status = NvTiming_CalcCVT_RB(width, height, rr, NVT_PROGRESSIVE, pTiming);
break;
case DISPLAYID_2_0_TIMING_FORMULA_CVT_2_1_REDUCED_BLANKING_2:
if (p8bytesDescriptor != NULL)
{
status = NvTiming_CalcCVT_RB2(width, height, rr, p6bytesDescriptor->options.rr1000div1001_or_hblank, p8bytesDescriptor->additional_mini_vblank, pTiming);
}
else
{
status = NvTiming_CalcCVT_RB2(width, height, rr, p6bytesDescriptor->options.rr1000div1001_or_hblank, NV_FALSE, pTiming);
}
break;
case DISPLAYID_2_0_TIMING_FORMULA_CVT_2_1_REDUCED_BLANKING_3:
{
NvU32 deltaHBlank = 0;
NvU32 multiplier = DISPLAYID_2_0_TIMING_10_NOMINAL_MINIMUM_VBLANK;
if (p7bytesDescriptor != NULL)
{
if (p6bytesDescriptor->options.rr1000div1001_or_hblank == 0) // Horizontal Blank in Pixels = [Field Value] * 8 + 80
{
deltaHBlank = p7bytesDescriptor->delta_hblank * 8;
}
else if (p6bytesDescriptor->options.rr1000div1001_or_hblank == 1)
{
if (p7bytesDescriptor->delta_hblank <= 5)
deltaHBlank = (p7bytesDescriptor->delta_hblank * 8 + 160) - 80;
else // if 5 < Field Value <=7
deltaHBlank = (160 - ((p7bytesDescriptor->delta_hblank - 5) * 8)) - 80;
}
status = NvTiming_CalcCVT_RB3(width,
height,
rr,
deltaHBlank,
p7bytesDescriptor->additional_vblank_timing * multiplier,
0,
p6bytesDescriptor->options.early_vsync,
pTiming);
}
else if (p8bytesDescriptor != NULL)
{
if (p6bytesDescriptor->options.rr1000div1001_or_hblank == 0) // Horizontal Blank in Pixels = [Field Value] * 8 + 80
{
deltaHBlank = p8bytesDescriptor->descriptor_7_bytes.delta_hblank * 8;
}
else if (p6bytesDescriptor->options.rr1000div1001_or_hblank == 1)
{
if (p8bytesDescriptor->descriptor_7_bytes.delta_hblank <= 5)
deltaHBlank = (p8bytesDescriptor->descriptor_7_bytes.delta_hblank * 8 + 160) - 80;
else // if 5 < Field Value <=7
deltaHBlank = (160 - ((p8bytesDescriptor->descriptor_7_bytes.delta_hblank - 5) * 8)) - 80;
}
if (p8bytesDescriptor->additional_mini_vblank == 1)
multiplier = DISPLAYID_2_1_TIMING_10_ALTERNATE_MINIMUM_VBLANK;
status = NvTiming_CalcCVT_RB3(width,
height,
rr,
deltaHBlank,
p8bytesDescriptor->descriptor_7_bytes.additional_vblank_timing * multiplier,
p8bytesDescriptor->additional_mini_vblank,
p6bytesDescriptor->options.early_vsync,
pTiming);
}
else // 6 bytes descriptor
{
// just assign the HBlank 80pixel but actually HBlank is 160 in DisplayId2.1a spec
if (p6bytesDescriptor->options.rr1000div1001_or_hblank == 1)
deltaHBlank = 80;
status = NvTiming_CalcCVT_RB3(width, height, rr, deltaHBlank, 0, 0, p6bytesDescriptor->options.early_vsync, pTiming);
}
}
break;
default:
{
nvt_assert(0 && "Unknown timing formula");
status = NVT_STATUS_ERR;
}
break;
}
if (status == NVT_STATUS_SUCCESS)
{
pTiming->etc.status = NVT_STATUS_DISPLAYID_10N(++count);
return status;
}
return status;
}
// get the existed stored timing sequence number
CODE_SEGMENT(PAGE_DD_CODE)
static NvU8
getExistedTimingSeqNumber(
NVT_DISPLAYID_2_0_INFO *pDisplayIdInfo,
enum NVT_TIMING_TYPE timingType)
{
NvU8 count = 0;
NvU8 i = 0;
switch (timingType)
{
case NVT_TYPE_DISPLAYID_7:
case NVT_TYPE_DISPLAYID_8:
case NVT_TYPE_DISPLAYID_9:
case NVT_TYPE_DISPLAYID_10:
break;
default:
return count;
}
for (i = 0; i< pDisplayIdInfo->total_timings; i++)
{
if (NVT_GET_TIMING_STATUS_TYPE(pDisplayIdInfo->timing[i].etc.status) == timingType)
{
++count;
}
}
return count;
}
// get the version
CODE_SEGMENT(PAGE_DD_CODE)
NvU32 getDID2Version(NvU8 *pData, NvU32 *pVer)
{
const DISPLAYID_2_0_SECTION *pSection = (const DISPLAYID_2_0_SECTION*)pData;
*pVer = 0;
if (pSection->header.version == 0x2)
{
*pVer = (((NvU32)pSection->header.version) << 8) + ((NvU32)pSection->header.revision);
}
else
return NVT_STATUS_ERR; // un-recongnized DisplayID20 version
return NVT_STATUS_SUCCESS;
}
CODE_SEGMENT(PAGE_DD_CODE)
void
updateColorFormatForDisplayId20Timings(
NVT_DISPLAYID_2_0_INFO *pDisplayId20Info,
NvU32 timingIdx)
{
// pDisplayId20Info parsed displayID20 info
NVT_TIMING *pT= &pDisplayId20Info->timing[timingIdx];
nvt_assert(timingIdx <= COUNT(pDisplayId20Info->timing));
// rgb444 (always support 6bpc and 8bpc as per DP spec 5.1.1.1.1 RGB Colorimetry)
UPDATE_BPC_FOR_COLORFORMAT(pT->etc.rgb444, 1,
1,
pDisplayId20Info->interface_features.rgb444.bpc.bpc10,
pDisplayId20Info->interface_features.rgb444.bpc.bpc12,
pDisplayId20Info->interface_features.rgb444.bpc.bpc14,
pDisplayId20Info->interface_features.rgb444.bpc.bpc16);
// yuv444
UPDATE_BPC_FOR_COLORFORMAT(pT->etc.yuv444, 0, // yuv444 does not support 6bpc
pDisplayId20Info->interface_features.yuv444.bpc.bpc8,
pDisplayId20Info->interface_features.yuv444.bpc.bpc10,
pDisplayId20Info->interface_features.yuv444.bpc.bpc12,
pDisplayId20Info->interface_features.yuv444.bpc.bpc14,
pDisplayId20Info->interface_features.yuv444.bpc.bpc16);
// yuv422
UPDATE_BPC_FOR_COLORFORMAT(pT->etc.yuv422, 0, // yuv422 does not support 6bpc
pDisplayId20Info->interface_features.yuv422.bpc.bpc8,
pDisplayId20Info->interface_features.yuv422.bpc.bpc10,
pDisplayId20Info->interface_features.yuv422.bpc.bpc12,
pDisplayId20Info->interface_features.yuv422.bpc.bpc14,
pDisplayId20Info->interface_features.yuv422.bpc.bpc16);
if (!NVT_DID20_TIMING_IS_CTA861(pT->etc.flag, pT->etc.status))
{
// yuv420
UPDATE_BPC_FOR_COLORFORMAT(pT->etc.yuv420, 0, // yuv420 does not support 6bpc
pDisplayId20Info->interface_features.yuv420.bpc.bpc8,
pDisplayId20Info->interface_features.yuv420.bpc.bpc10,
pDisplayId20Info->interface_features.yuv420.bpc.bpc12,
pDisplayId20Info->interface_features.yuv420.bpc.bpc14,
pDisplayId20Info->interface_features.yuv420.bpc.bpc16);
}
}
POP_SEGMENTS