/*
 * SPDX-FileCopyrightText: Copyright (c) 1993-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.
 */

/******************************* List **************************************\
*                                                                           *
* Module: dp_evoadapter.h                                                   *
*    Interface for low level access to the aux bus.                         *
*    This is the synchronous version of the interface.                      *
*                                                                           *
\***************************************************************************/
#ifndef INCLUDED_DP_EVOADAPTER_H
#define INCLUDED_DP_EVOADAPTER_H

#include "dp_timer.h"
#include "dp_auxbus.h"
#include "dp_mainlink.h"
#include "dp_wardatabase.h"
#include "dp_auxdefs.h"
#include "dp_regkeydatabase.h"

#include <nvos.h>

#define HDCP_DUMMY_CN    (0x1)
#define HDCP_DUMMY_CKSV  (0xFFFFF)


namespace DisplayPort
{
    class EvoInterface
    {
    public:
        //
        //  IOCTL access to RM class DISPLAY_COMMON and NV50_DISPLAY
        //
        virtual NvU32 rmControl0073(NvU32 command, void * params, NvU32 paramSize) = 0;
        virtual NvU32 rmControl5070(NvU32 command, void * params, NvU32 paramSize) = 0;

        virtual bool getMaxLinkConfigFromUefi(NvU8 &linkRate, NvU8 &laneCount)
        {
            linkRate = 0; laneCount = 0;
            return true;
        }

        //
        // Call to tell DD that linkTraining will be performed.
        // Required when head is attached & we enter in flush mode GPUs.
        // Required to enable/disable Audio.
        //
        // Derived classes that override these functions must call down to
        // DisplayPort::EvoInterface::pre/postLinkTraining() to inherit this
        // implementation.
        //
        virtual void  preLinkTraining(NvU32 head)
        {
        }
        virtual void  postLinkTraining(NvU32 head)
        {
        }

        virtual NvU32 getSubdeviceIndex() = 0;
        virtual NvU32 getDisplayId() = 0;
        virtual NvU32 getSorIndex() = 0;
        virtual NvU32 getLinkIndex() = 0;       // Link A = 0, Link B = 1
        //
        // Query the value of a registry key.  Implementations should return 0
        // if the regkey is not set.
        //
        virtual NvU32 getRegkeyValue(const char *key)
        {
            return 0;
        }
        virtual NvU32 monitorDenylistInfo(NvU32 manufId, NvU32 productId, DpMonitorDenylistData *pDenylistData)
        {
            return 0;
        }

        virtual bool isInbandStereoSignalingSupported()
        {
            return false;
        }
    };

    MainLink * MakeEvoMainLink(EvoInterface * provider, Timer * timer);
    AuxBus   * MakeEvoAuxBus(EvoInterface * provider, Timer * timer);

    class EvoAuxBus : public AuxBus
    {
      public:
        EvoAuxBus(EvoInterface * provider, Timer * timer)
            : provider(provider),
            timer(timer),
            displayId(provider->getDisplayId()),
            subdeviceIndex(provider->getSubdeviceIndex()),
            devicePlugged(false)
        {
        }

        virtual status transaction(Action action, Type type, int address, NvU8 * buffer,
                                   unsigned sizeRequested, unsigned * sizeCompleted,
                                   unsigned * pNakReason = NULL,
                                   NvU8 offset = 0, NvU8 nWriteTransactions = 0);
        virtual unsigned transactionSize();
        virtual void setDevicePlugged(bool);

      private:
        EvoInterface * provider;
        Timer * timer;
        NvU32 displayId;
        NvU32 subdeviceIndex;
        bool devicePlugged;
    };

    class EvoMainLink : public MainLink
    {
        EvoInterface * provider;
        Timer * timer;
        NvU32 displayId;
        NvU32 subdeviceIndex;
        NvU32 _maxLinkRateSupportedGpu;
        NvU32 _maxLinkRateSupportedDfp;
        unsigned allHeadMask;
        bool _hasIncreasedWatermarkLimits;
        bool _hasMultistream;
        bool _isPC2Disabled;
        bool _isEDP;
        bool _isDP1_2Supported;
        bool _isDP1_4Supported;
        bool _isStreamCloningEnabled;
        bool _needForceRmEdid;
        bool _skipPowerdownEDPPanelWhenHeadDetach;
        bool _isDscDisabledByRegkey;
        bool _isMstDisabledByRegkey;
        bool _isFECSupported;
        bool _useDfpMaxLinkRateCaps;
        bool _applyLinkBwOverrideWarRegVal;
        bool _isDynamicMuxCapable;
        bool _enableMSAOverrideOverMST;

        bool _isLTPhyRepeaterSupported;
        //
        // LTTPR count reported by RM, it might not be the same with DPLib probe
        // For example, some Intel LTTPR might not be ready to response 0xF0000 probe
        // done by RM, but when DPLib checks the same DPCD offsets it responses
        // properly. This will cause serious LT problem.
        //
        unsigned _rmPhyRepeaterCount;

        struct DSC
        {
            bool isDscSupported;
            unsigned encoderColorFormatMask;
            unsigned lineBufferSizeKB;
            unsigned rateBufferSizeKB;
            unsigned bitsPerPixelPrecision;
            unsigned maxNumHztSlices;
            unsigned lineBufferBitDepth;
        }_DSC;

    private:
        virtual void initializeRegkeyDatabase();
        virtual void applyRegkeyOverrides();

    public:
        EvoMainLink(EvoInterface * provider, Timer * timer);

        virtual bool hasIncreasedWatermarkLimits()
        {
            return _hasIncreasedWatermarkLimits;
        }

        virtual bool hasMultistream()
        {
            return _hasMultistream;
        }

        virtual bool isPC2Disabled()
        {
            return _isPC2Disabled;
        }

        virtual bool isDP1_2Supported()
        {
            return _isDP1_2Supported;
        }
        virtual bool isDP1_4Supported()
        {
            return _isDP1_4Supported;
        }
        virtual bool isFECSupported()
        {
            return _isFECSupported;
        }

        virtual bool isStreamCloningEnabled()
        {
            return _isStreamCloningEnabled;
        }

        virtual NvU32 maxLinkRateSupported()
        {
            //
            // For cases where RM asks dplib to honor the maxLinkRate limit defined in DCB, always use
            // this as the limit. Regkey has no meaning in this case.
            // In other cases, based on regkey either honor the dcb limit or the max link rate for the
            // specific GPU architecture. This is needed to avoid regressions on existing chips.
            //
            if ((_applyLinkBwOverrideWarRegVal || _useDfpMaxLinkRateCaps) &&
                (_maxLinkRateSupportedDfp < _maxLinkRateSupportedGpu))
            {
                return _maxLinkRateSupportedDfp;
            }

            return _maxLinkRateSupportedGpu;
        }

        virtual bool isForceRmEdidRequired()
        {
            return _needForceRmEdid;
        }

        virtual bool fetchEdidByRmCtrl(NvU8* edidBuffer, NvU32 bufferSize);
        virtual bool applyEdidOverrideByRmCtrl(NvU8* edidBuffer, NvU32 bufferSize);

        virtual bool isDynamicMuxCapable()
        {
            return _isDynamicMuxCapable;
        }

        virtual bool isInternalPanelDynamicMuxCapable()
        {
            return (_isDynamicMuxCapable && _isEDP);
        }

        // Get GPU DSC capabilities
        virtual void getDscCaps(bool *pbDscSupported,
                                unsigned *pEncoderColorFormatMask,
                                unsigned *pLineBufferSizeKB,
                                unsigned *pRateBufferSizeKB,
                                unsigned *pBitsPerPixelPrecision,
                                unsigned *pMaxNumHztSlices,
                                unsigned *pLineBufferBitDepth)
        {
            if (pbDscSupported)
            {
                *pbDscSupported = _DSC.isDscSupported;
            }

            if (pEncoderColorFormatMask)
            {
                *pEncoderColorFormatMask = _DSC.encoderColorFormatMask;
            }

            if (pLineBufferSizeKB)
            {
                *pLineBufferSizeKB = _DSC.lineBufferSizeKB;
            }

            if (pRateBufferSizeKB)
            {
                *pRateBufferSizeKB = _DSC.rateBufferSizeKB;
            }

            if (pBitsPerPixelPrecision)
            {
                *pBitsPerPixelPrecision = _DSC.bitsPerPixelPrecision;
            }

            if (pMaxNumHztSlices)
            {
                *pMaxNumHztSlices = _DSC.maxNumHztSlices;
            }

            if (pLineBufferBitDepth)
            {
                *pLineBufferBitDepth = _DSC.lineBufferBitDepth;
            }
        }

        virtual NvU32 getRootDisplayId()
        {
            return this->displayId;
        }

        virtual bool isLttprSupported()
        {
            return this->_isLTPhyRepeaterSupported;
        }

        // Return the current mux state. Returns false if device is not mux capable
        bool getDynamicMuxState(NvU32 *muxState);

        virtual bool aquireSema();
        virtual void releaseSema();
        virtual bool physicalLayerSetTestPattern(PatternInfo * patternInfo);

        virtual void preLinkTraining(NvU32 head);
        virtual void postLinkTraining(NvU32 head);
        virtual NvU32 getRegkeyValue(const char *key);
        virtual const DP_REGKEY_DATABASE& getRegkeyDatabase();
        virtual NvU32 getSorIndex();
        virtual bool isInbandStereoSignalingSupported();
        virtual bool train(const LinkConfiguration & link, bool force, LinkTrainingType linkTrainingType,
                           LinkConfiguration *retLink, bool bSkipLt = false, bool isPostLtAdjRequestGranted = false,
                           unsigned phyRepeaterCount = 0);
        virtual bool retrieveRingBuffer(NvU8 dpRingBuffertype, NvU32 numRecords);
        virtual void getLinkConfig(unsigned & laneCount, NvU64 & linkRate);
        virtual bool getMaxLinkConfigFromUefi(NvU8 &linkRate, NvU8 &laneCount);
        virtual bool setDpMSAParameters(bool bStereoEnable, const NV0073_CTRL_CMD_DP_SET_MSA_PROPERTIES_PARAMS &msaparams);
        virtual bool setDpStereoMSAParameters(bool bStereoEnable, const NV0073_CTRL_CMD_DP_SET_MSA_PROPERTIES_PARAMS &msaparams);
        virtual bool setFlushMode();
        virtual void clearFlushMode(unsigned headMask, bool testMode=false);

        virtual bool dscCrcTransaction(NvBool bEnable, gpuDscCrc *data, NvU16 *headIndex);

        void triggerACT();
        void configureHDCPRenegotiate(NvU64 cN = HDCP_DUMMY_CN, NvU64 cKsv = HDCP_DUMMY_CKSV, bool bForceReAuth = false,
                                      bool bRxIDMsgPending = false);
        void configureHDCPGetHDCPState(HDCPState &hdcpState);
        bool rmUpdateDynamicDfpCache(NvU32 headIndex, RmDfpCache * dfpCache, NvBool bResetDfp);

        virtual NvU32 streamToHead(NvU32 streamId, DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID streamIdentifier = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY);

        virtual NvU32 headToStream(NvU32 head, DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID streamIdentifier = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY);

        void configureSingleStream(NvU32 head,
            NvU32 hBlankSym,
            NvU32 vBlankSym,
            bool  bEnhancedFraming,
            NvU32 tuSize,
            NvU32 waterMark,
            DP_COLORFORMAT colorFormat,
            DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID streamId = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY,
            DP_SINGLE_HEAD_MULTI_STREAM_MODE singleHeadMultistreamMode = DP_SINGLE_HEAD_MULTI_STREAM_MODE_NONE,
            bool bEnableAudioOverRightPanel = false,
            bool bEnable2Head1Or = false);

        void configureMultiStream(NvU32 head,
            NvU32 hBlankSym,
            NvU32 vBlankSym,
            NvU32 slotStart,
            NvU32 slotEnd,
            NvU32 PBN,
            NvU32 Timeslice,
            DP_COLORFORMAT colorFormat,
            DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID streamIdentifier = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY,
            DP_SINGLE_HEAD_MULTI_STREAM_MODE singleHeadMultistreamMode = DP_SINGLE_HEAD_MULTI_STREAM_MODE_NONE,
            bool bEnableAudioOverRightPanel = false,
            bool bEnable2Head1Or = false);

        void configureSingleHeadMultiStreamMode(NvU32 displayIDs[],
                                                NvU32 numStreams,
                                                NvU32 mode,
                                                bool bSetConfig,
                                                NvU8  vbiosPrimaryDispIdIndex = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY);

        void configureMsScratchRegisters(NvU32 address,
                                         NvU32 hopCount,
                                         NvU32 driverState);

        bool isActive();
        bool isEDP();
        bool skipPowerdownEdpPanelWhenHeadDetach();
        bool supportMSAOverMST();
        bool queryAndUpdateDfpParams();
        bool controlRateGoverning(NvU32 head, bool enable, bool updateNow);

        bool getDpTestPattern(NV0073_CTRL_DP_TESTPATTERN *testPattern);
        bool setDpTestPattern(NV0073_CTRL_DP_TESTPATTERN testPattern,
                              NvU8 laneMask, NV0073_CTRL_DP_CSTM cstm,
                              NvBool bIsHBR2, NvBool bSkipLaneDataOverride);
        bool getDpLaneData(NvU32 *numLanes, NvU32 *data);
        bool setDpLaneData(NvU32 numLanes, NvU32 *data);
        void configurePowerState(bool bPowerUp);
        NvU32 monitorDenylistInfo(NvU32 ManufacturerID, NvU32 ProductID, DpMonitorDenylistData *pDenylistData);
        NvU32 allocDisplayId();
        bool freeDisplayId(NvU32 displayId);
        void queryGPUCapability();
        bool getEdpPowerData(bool *panelPowerOn, bool *dpcdPowerStateD0);
        virtual bool vrrRunEnablementStage(unsigned stage, NvU32 *status);

        void configureTriggerSelect(NvU32 head,
            DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID streamIdentifier = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY);
        void configureTriggerAll(NvU32 head, bool enable);
        bool configureLinkRateTable(const NvU16 *pLinkRateTable, LinkRates *pLinkRates);
        bool configureFec(const bool bEnableFec);
    };

}

#endif //INCLUDED_DP_EVOADAPTER_H
