/* * SPDX-FileCopyrightText: Copyright (c) 2021 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: nvhdmipkt.c * * Purpose: Provide initialization functions for HDMI library */ #include "nvlimits.h" #include "nvhdmipkt_common.h" #include "nvhdmipkt_class.h" #include "nvhdmipkt_internal.h" #include "../timing/nvt_dsc_pps.h" #include "class/cl9170.h" #include "class/cl917d.h" #include "class/cl9270.h" #include "class/cl927d.h" #include "class/cl9470.h" #include "class/cl947d.h" #include "class/cl9570.h" #include "class/cl957d.h" #include "class/clc370.h" #include "class/clc37d.h" #include "class/clc570.h" #include "class/clc57d.h" #include "class/clc670.h" #include "class/clc67d.h" // Class hierarchy structure typedef struct tagNVHDMIPKT_CLASS_HIERARCHY { NVHDMIPKT_CLASS_ID classId; NVHDMIPKT_CLASS_ID parentClassId; NvBool isRootClass; void (*initInterface)(NVHDMIPKT_CLASS*); NvBool (*constructor) (NVHDMIPKT_CLASS*); void (*destructor) (NVHDMIPKT_CLASS*); NvU32 displayClass; NvU32 coreDmaClass; } NVHDMIPKT_CLASS_HIERARCHY; /************************************************************************************************* * hierarchy structure establishes the relationship between classes. * * If isRootClass=NV_TRUE, it is a root class, else it is a child of a class. classId * * also acts as an index, and hence the order of the structure below should be maintanied. * * * * ASSUMPTION: There are two huge assumptions while creating the class relationship and * * while traversing it. 1. That of the Class ID definitaion (NVHDMIPKT_CLASS_ID), which has * * to be strictly indexed, that is 0, 1, 2... and so on. And 2. that the structure * * CLASS_HIERARCHY (above) follow that indexing. That is NVHDMIPKT_0073_CLASS is value 0 and * * the first entry in CLASS_HIERARCHY, NVHDMIPKT_9171_CLASS is value 1 and hence the second * * entry in CLASS_HIERARCHY, so on and so forth. * * * * HOW TO ADD A NEW CLASS? * * 1. Add an ID in NVHDMIPKT_CLASS_ID. * * 2. Add a source file nvhdmipkt_XXXX.c, and include it into makefiles. Makefiles of * * Mods, Windows, and Linux. * * 3. Provide initializeHdmiPktInterfaceXXXX, hdmiConstructorXXXX, and, hdmiDestructorXXXX. * * 4. Add functions that needs to be overridden in NVHDMIPKT_CLASS. * * 5. Add a relationship in hierarchy[] array. The new class can be a subclass or a root. In * * case of a root all the interfaces needs to be overridden in NVHDMIPKT_CLASS. * ************************************************************************************************/ static const NVHDMIPKT_CLASS_HIERARCHY hierarchy[] = { {// Index 0==NVHDMIPKT_0073_CLASS NVHDMIPKT_0073_CLASS, // classId NVHDMIPKT_0073_CLASS, // parentClassId NV_TRUE, // isRootClass initializeHdmiPktInterface0073, // initInterface hdmiConstructor0073, // constructor hdmiDestructor0073, // destructor 0, // displayClass 0 // coreDmaClass }, {// Index 1==NVHDMIPKT_9171_CLASS NVHDMIPKT_9171_CLASS, // classId NVHDMIPKT_9171_CLASS, // parentClassId NV_TRUE, // isRootClass initializeHdmiPktInterface9171, // initInterface hdmiConstructor9171, // constructor hdmiDestructor9171, // destructor NV9170_DISPLAY, // displayClass NV917D_CORE_CHANNEL_DMA // coreDmaClass }, {// Index 2==NVHDMIPKT_9271_CLASS NVHDMIPKT_9271_CLASS, // classId NVHDMIPKT_9171_CLASS, // parentClassId NV_FALSE, // isRootClass initializeHdmiPktInterface9271, // initInterface hdmiConstructor9271, // constructor hdmiDestructor9271, // destructor NV9270_DISPLAY, // displayClass NV927D_CORE_CHANNEL_DMA // coreDmaClass }, {// Index 3==NVHDMIPKT_9471_CLASS NVHDMIPKT_9471_CLASS, // classId NVHDMIPKT_9171_CLASS, // parentClassId NV_FALSE, // isRootClass initializeHdmiPktInterface9471, // initInterface hdmiConstructor9471, // constructor hdmiDestructor9471, // destructor NV9470_DISPLAY, // displayClass NV947D_CORE_CHANNEL_DMA // coreDmaClass }, {// Index 4==NVHDMIPKT_9571_CLASS NVHDMIPKT_9571_CLASS, // classId NVHDMIPKT_9171_CLASS, // parentClassId NV_FALSE, // isRootClass initializeHdmiPktInterface9571, // initInterface hdmiConstructor9571, // constructor hdmiDestructor9571, // destructor NV9570_DISPLAY, // displayClass NV957D_CORE_CHANNEL_DMA // coreDmaClass }, {// Index 5==NVHDMIPKT_C371_CLASS NVHDMIPKT_C371_CLASS, // classId NVHDMIPKT_9171_CLASS, // parentClassId NV_FALSE, // isRootClass initializeHdmiPktInterfaceC371, // initInterface hdmiConstructorC371, // constructor hdmiDestructorC371, // destructor NVC370_DISPLAY, // displayClass NVC37D_CORE_CHANNEL_DMA // coreDmaClass }, {// Index 6==NVHDMIPKT_C571_CLASS // Note that Turing (C57x) has a distinct displayClass and coreDmaClass, // but it inherits the _DISP_SF_USER class from Volta (C37x). We call this // NVHDMIPKT_C571_CLASS, but reuse initInterface()/constructor()/destructor() // from C371. NVHDMIPKT_C571_CLASS, NVHDMIPKT_9171_CLASS, // parentClassId NV_FALSE, // isRootClass initializeHdmiPktInterfaceC371, // initInterface hdmiConstructorC371, // constructor hdmiDestructorC371, // destructor NVC570_DISPLAY, // displayClass NVC57D_CORE_CHANNEL_DMA // coreDmaClass }, {// Index 7==NVHDMIPKT_C671_CLASS NVHDMIPKT_C671_CLASS, // classId NVHDMIPKT_9171_CLASS, // parentClassId NV_FALSE, // isRootClass initializeHdmiPktInterfaceC671, // initInterface hdmiConstructorC671, // constructor hdmiDestructorC671, // destructor NVC670_DISPLAY, // displayClass NVC67D_CORE_CHANNEL_DMA // coreDmaClass }, }; #if defined(DSC_CALLBACK_MODIFIED) // Callbacks for DSC PPS library void *hdmipktMallocCb(const void *clientHandle, NvLength size); void hdmipktFreeCb(const void *clientHandle, void *pMemPtr); void *hdmipktMallocCb(const void *clientHandle, NvLength size) { const NVHDMIPKT_CLASS *pClass = (const NVHDMIPKT_CLASS*)(clientHandle); return pClass->callback.malloc(pClass->cbHandle, size); } void hdmipktFreeCb(const void *clientHandle, void *pMemPtr) { const NVHDMIPKT_CLASS *pClass = (const NVHDMIPKT_CLASS*)(clientHandle); pClass->callback.free(pClass->cbHandle, pMemPtr); } #endif // DSC_CALLBACK_MODIFIED /********************************** HDMI Library interfaces *************************************/ /* * NvHdmiPkt_PacketCtrl */ NVHDMIPKT_RESULT NvHdmiPkt_PacketCtrl(NvHdmiPkt_Handle libHandle, NvU32 subDevice, NvU32 displayId, NvU32 head, NVHDMIPKT_TYPE packetType, NVHDMIPKT_TC transmitControl) { NVHDMIPKT_CLASS* pClass = fromHdmiPktHandle(libHandle); if (libHandle == NVHDMIPKT_INVALID_HANDLE) { return NVHDMIPKT_LIBRARY_INIT_FAIL; } return pClass->hdmiPacketCtrl(pClass, subDevice, displayId, head, packetType, transmitControl); } /* * NvHdmiPkt_PacketWrite */ NVHDMIPKT_RESULT NvHdmiPkt_PacketWrite(NvHdmiPkt_Handle libHandle, NvU32 subDevice, NvU32 displayId, NvU32 head, NVHDMIPKT_TYPE packetType, NVHDMIPKT_TC transmitControl, NvU32 packetLen, NvU8 const *const pPacket) { NVHDMIPKT_CLASS* pClass = fromHdmiPktHandle(libHandle); if (libHandle == NVHDMIPKT_INVALID_HANDLE) { return NVHDMIPKT_LIBRARY_INIT_FAIL; } return pClass->hdmiPacketWrite(pClass, subDevice, displayId, head, packetType, transmitControl, packetLen, pPacket); } NVHDMIPKT_RESULT NvHdmi_AssessLinkCapabilities(NvHdmiPkt_Handle libHandle, NvU32 subDevice, NvU32 displayId, NVT_EDID_INFO const * const pSinkEdid, HDMI_SRC_CAPS *pSrcCaps, HDMI_SINK_CAPS *pSinkCaps) { if (libHandle == NVHDMIPKT_INVALID_HANDLE) { return NVHDMIPKT_LIBRARY_INIT_FAIL; } if (!pSinkEdid || !pSrcCaps || !pSinkCaps) { return NVHDMIPKT_INVALID_ARG; } NVHDMIPKT_CLASS* pClass = fromHdmiPktHandle(libHandle); return pClass->hdmiAssessLinkCapabilities(pClass, subDevice, displayId, pSinkEdid, pSrcCaps, pSinkCaps); } /* * NvHdmi_QueryFRLConfig */ NVHDMIPKT_RESULT NvHdmi_QueryFRLConfig(NvHdmiPkt_Handle libHandle, HDMI_VIDEO_TRANSPORT_INFO const * const pVidTransInfo, HDMI_QUERY_FRL_CLIENT_CONTROL const * const pClientCtrl, HDMI_SRC_CAPS const * const pSrcCaps, HDMI_SINK_CAPS const * const pSinkCaps, HDMI_FRL_CONFIG *pFRLConfig) { if (libHandle == NVHDMIPKT_INVALID_HANDLE) { return NVHDMIPKT_LIBRARY_INIT_FAIL; } if (!pVidTransInfo || !pClientCtrl || !pSrcCaps || !pSinkCaps || !pFRLConfig) { return NVHDMIPKT_INVALID_ARG; } // if there is no FRL capability reported fail this call if (pSinkCaps->linkMaxFRLRate == HDMI_FRL_DATA_RATE_NONE) { return NVHDMIPKT_FAIL; } NVHDMIPKT_CLASS* pClass = fromHdmiPktHandle(libHandle); return pClass->hdmiQueryFRLConfig(pClass, pVidTransInfo, pClientCtrl, pSrcCaps, pSinkCaps, pFRLConfig); } /* * NvHdmi_SetFRLConfig */ NVHDMIPKT_RESULT NvHdmi_SetFRLConfig(NvHdmiPkt_Handle libHandle, NvU32 subDevice, NvU32 displayId, NvBool bFakeLt, HDMI_FRL_CONFIG *pFRLConfig) { if (libHandle == NVHDMIPKT_INVALID_HANDLE) { return NVHDMIPKT_LIBRARY_INIT_FAIL; } if (!pFRLConfig) { return NVHDMIPKT_INVALID_ARG; } NVHDMIPKT_CLASS* pClass = fromHdmiPktHandle(libHandle); return pClass->hdmiSetFRLConfig(pClass, subDevice, displayId, bFakeLt, pFRLConfig); } /* * NvHdmi_ClearFRLConfig */ NVHDMIPKT_RESULT NvHdmi_ClearFRLConfig(NvHdmiPkt_Handle libHandle, NvU32 subDevice, NvU32 displayId) { if (libHandle == NVHDMIPKT_INVALID_HANDLE) { return NVHDMIPKT_LIBRARY_INIT_FAIL; } NVHDMIPKT_CLASS* pClass = fromHdmiPktHandle(libHandle); return pClass->hdmiClearFRLConfig(pClass, subDevice, displayId); } /*************************** HDMI Library internal helper functions *****************************/ /* * NvHdmiPkt_HwClass2HdmiClass * internal function; translates display/display-dma class to hdmi class */ static NVHDMIPKT_CLASS_ID NvHdmiPkt_HwClass2HdmiClass(NvU32 const hwClass) { NVHDMIPKT_CLASS_ID hdmiClassId = NVHDMIPKT_9571_CLASS; NvU32 i = 0; for (i = 0; i < NVHDMIPKT_INVALID_CLASS; i++) { if ((hierarchy[i].displayClass == hwClass) || (hierarchy[i].coreDmaClass == hwClass)) { hdmiClassId = hierarchy[i].classId; break; } } // Assign default class 73 to pre-Kepler families if (hwClass < NV9170_DISPLAY) { hdmiClassId = NVHDMIPKT_0073_CLASS; } return hdmiClassId; } /* * NvHdmiPkt_InitInterfaces * internal function; calls class init interface functions */ static void NvHdmiPkt_InitInterfaces(NVHDMIPKT_CLASS_ID const thisClassId, NVHDMIPKT_CLASS* const pClass) { // Recurse to the root first, and then call each initInterface() method // from root to child. if (!hierarchy[thisClassId].isRootClass) { NvHdmiPkt_InitInterfaces(hierarchy[thisClassId].parentClassId, pClass); } hierarchy[thisClassId].initInterface(pClass); } static void NvHdmiPkt_CallDestructors(NVHDMIPKT_CLASS_ID const thisClassId, NVHDMIPKT_CLASS* const pClass) { // Destructor calls are made from this to root class. hierarchy[thisClassId].destructor(pClass); if (!hierarchy[thisClassId].isRootClass) { NvHdmiPkt_CallDestructors(hierarchy[thisClassId].parentClassId, pClass); } } /* * NvHdmiPkt_CallConstructors * internal function; calls class constructors and returns boolean success/failure */ static NvBool NvHdmiPkt_CallConstructors(NVHDMIPKT_CLASS_ID const thisClassId, NVHDMIPKT_CLASS* const pClass) { // Recurse to the root first, and then call each constructor // from root to child. if (!hierarchy[thisClassId].isRootClass) { if (!NvHdmiPkt_CallConstructors(hierarchy[thisClassId].parentClassId, pClass)) { return NV_FALSE; } } if (!hierarchy[thisClassId].constructor(pClass)) { if (!hierarchy[thisClassId].isRootClass) { // Backtrack on constructor failure NvHdmiPkt_CallDestructors(hierarchy[thisClassId].parentClassId, pClass); } return NV_FALSE; } return NV_TRUE; } /******************************** HDMI Library Init functions ***********************************/ /* * NvHdmiPkt_InitializeLibrary */ NvHdmiPkt_Handle NvHdmiPkt_InitializeLibrary(NvU32 const hwClass, NvU32 const numSubDevices, NvHdmiPkt_CBHandle const cbHandle, const NVHDMIPKT_CALLBACK* const pCallbacks, NvU32 const sfUserHandle, const NVHDMIPKT_RM_CLIENT_HANDLES* const pClientHandles) { NVHDMIPKT_CLASS* pClass = 0; NvU32 i = 0; NvBool result = NV_FALSE; NVHDMIPKT_CLASS_ID thisClassId = NVHDMIPKT_INVALID_CLASS; // Argument validations if (pCallbacks == 0 || numSubDevices == 0) { goto NvHdmiPkt_InitializeLibrary_exit; } // Validating RM handles/callbacks #if NVHDMIPKT_RM_CALLS_INTERNAL if (sfUserHandle == 0 || pClientHandles == 0) { goto NvHdmiPkt_InitializeLibrary_exit; } #else // !NVHDMIPKT_RM_CALLS_INTERNAL if (pCallbacks->rmGetMemoryMap == 0 || pCallbacks->rmFreeMemoryMap == 0 || pCallbacks->rmDispControl2 == 0) { goto NvHdmiPkt_InitializeLibrary_exit; } #endif // NVHDMIPKT_RM_CALLS_INTERNAL // Mandatory mutex callbacks. if (pCallbacks->acquireMutex == 0 || pCallbacks->releaseMutex == 0) { goto NvHdmiPkt_InitializeLibrary_exit; } // Mandatory memory allocation callbacks. if (pCallbacks->malloc == 0 || pCallbacks->free == 0) { goto NvHdmiPkt_InitializeLibrary_exit; } pClass = pCallbacks->malloc(cbHandle, sizeof(NVHDMIPKT_CLASS)); if (!pClass) { goto NvHdmiPkt_InitializeLibrary_exit; } // 0. Get the hdmi class ID thisClassId = NvHdmiPkt_HwClass2HdmiClass(hwClass); // Init data NVMISC_MEMSET(pClass, 0, sizeof(NVHDMIPKT_CLASS)); for (i = 0; i < NV_MAX_SUBDEVICES; i++) { pClass->memMap[i].subDevice = NVHDMIPKT_INVALID_SUBDEV; } pClass->numSubDevices = numSubDevices; pClass->cbHandle = cbHandle; pClass->thisId = thisClassId; // RM handles/callbacks #if NVHDMIPKT_RM_CALLS_INTERNAL pClass->isRMCallInternal = NV_TRUE; pClass->sfUserHandle = sfUserHandle; pClass->clientHandles.hClient = pClientHandles->hClient; pClass->clientHandles.hDevice = pClientHandles->hDevice; pClass->clientHandles.hDisplay = pClientHandles->hDisplay; for (i = 0; i < NV_MAX_SUBDEVICES; i++) { pClass->clientHandles.hSubDevices[i] = pClientHandles->hSubDevices[i]; } #else // !NVHDMIPKT_RM_CALLS_INTERNAL pClass->isRMCallInternal = NV_FALSE; pClass->callback.rmGetMemoryMap = pCallbacks->rmGetMemoryMap; pClass->callback.rmFreeMemoryMap = pCallbacks->rmFreeMemoryMap; pClass->callback.rmDispControl2 = pCallbacks->rmDispControl2; #endif // NVHDMIPKT_RM_CALLS_INTERNAL pClass->callback.acquireMutex = pCallbacks->acquireMutex; pClass->callback.releaseMutex = pCallbacks->releaseMutex; pClass->callback.malloc = pCallbacks->malloc; pClass->callback.free = pCallbacks->free; #if !defined (NVHDMIPKT_DONT_USE_TIMER) pClass->callback.setTimeout = pCallbacks->setTimeout; pClass->callback.checkTimeout = pCallbacks->checkTimeout; #endif #if defined (DEBUG) pClass->callback.print = pCallbacks->print; pClass->callback.assert = pCallbacks->assert; #endif // 1. Init interfaces NvHdmiPkt_InitInterfaces(thisClassId, pClass); // 2. Constructor calls result = NvHdmiPkt_CallConstructors(thisClassId, pClass); #if defined(DSC_CALLBACK_MODIFIED) DSC_CALLBACK callbacks; NVMISC_MEMSET(&callbacks, 0, sizeof(DSC_CALLBACK)); callbacks.clientHandle = pClass; callbacks.dscMalloc = hdmipktMallocCb; callbacks.dscFree = hdmipktFreeCb; DSC_InitializeCallback(callbacks); #endif // DSC_CALLBACK_MODIFIED NvHdmiPkt_InitializeLibrary_exit: if (result) { NvHdmiPkt_Print(pClass, "Initialize Success."); } else { if (pClass) { NvHdmiPkt_Print(pClass, "Initialize Failed."); } if (pCallbacks && pCallbacks->free) { pCallbacks->free(cbHandle, pClass); } } return (result == NV_TRUE) ? toHdmiPktHandle(pClass) : NVHDMIPKT_INVALID_HANDLE; } /* * NvHdmiPkt_DestroyLibrary */ void NvHdmiPkt_DestroyLibrary(NvHdmiPkt_Handle libHandle) { NVHDMIPKT_CLASS* pClass = fromHdmiPktHandle(libHandle); NVHDMIPKT_CLASS_ID currClassId = NVHDMIPKT_0073_CLASS; if (pClass != 0) { NvHdmiPkt_Print(pClass, "Destroy."); NvHdmiPkt_CBHandle cbHandle = pClass->cbHandle; void (*freeCb) (NvHdmiPkt_CBHandle handle, void *pMem) = pClass->callback.free; currClassId = pClass->thisId; NvHdmiPkt_CallDestructors(currClassId, pClass); freeCb(cbHandle, pClass); } }