mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2026-03-07 14:19:50 +00:00
515.43.04
This commit is contained in:
284
src/common/displayport/inc/dp_address.h
Normal file
284
src/common/displayport/inc/dp_address.h
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_address.h *
|
||||
* Basic class for AUX Address *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_ADDRESS_H
|
||||
#define INCLUDED_DP_ADDRESS_H
|
||||
|
||||
#include "dp_internal.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
class Address
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
maxHops = 15, // update DP_MAX_ADDRESS_HOPS when changed (in displayportCommon.h)
|
||||
maxHopsHDCP = 7,
|
||||
maxPortCount = 15
|
||||
};
|
||||
|
||||
Address()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
Address(unsigned hop0)
|
||||
{
|
||||
clear();
|
||||
hop[hops++] = hop0;
|
||||
}
|
||||
|
||||
Address(unsigned hop0, unsigned hop1)
|
||||
{
|
||||
clear();
|
||||
hop[hops++] = hop0;
|
||||
hop[hops++] = hop1;
|
||||
}
|
||||
|
||||
Address(const Address & other)
|
||||
{
|
||||
clear();
|
||||
for(unsigned i = 0; i < other.size(); i++)
|
||||
{
|
||||
append(other[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
hops = 0;
|
||||
for (unsigned i = 0; i < maxHops; i++)
|
||||
{
|
||||
hop[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Address parent() const
|
||||
{
|
||||
DP_ASSERT(hops != 0);
|
||||
Address addr = *this;
|
||||
addr.hops --;
|
||||
return addr;
|
||||
}
|
||||
|
||||
unsigned tail() const
|
||||
{
|
||||
if (hops == 0)
|
||||
{
|
||||
DP_ASSERT(hops != 0);
|
||||
return 0;
|
||||
}
|
||||
return hop[hops-1];
|
||||
}
|
||||
|
||||
void append(unsigned port)
|
||||
{
|
||||
if (hops >= maxHops)
|
||||
{
|
||||
DP_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
hop[hops++] = port;
|
||||
}
|
||||
|
||||
void prepend(unsigned port)
|
||||
{
|
||||
if (hops >= maxHops)
|
||||
{
|
||||
DP_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
hops++;
|
||||
for (unsigned i = hops - 1; i > 0; i--)
|
||||
hop[i] = hop[i-1];
|
||||
hop[0] = port;
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
if (hops == 0)
|
||||
{
|
||||
DP_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
hops--;
|
||||
}
|
||||
|
||||
// Just to keep clear copy
|
||||
Address & operator = (const Address & other)
|
||||
{
|
||||
clear();
|
||||
for(unsigned i = 0; i < other.size(); i++)
|
||||
{
|
||||
append(other[i]);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator == (const Address & other) const
|
||||
{
|
||||
if (other.size() != size())
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < hops; i++)
|
||||
if (other[i] != (*this)[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Sort by size first, then "alphabetically" (lexicographical see wikipedia)
|
||||
//
|
||||
bool operator > (const Address & other) const
|
||||
{
|
||||
if (size() > other.size())
|
||||
return true;
|
||||
else if (size() < other.size())
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < hops; i++)
|
||||
{
|
||||
if ((*this)[i] > other[i])
|
||||
return true;
|
||||
else if ((*this)[i] < other[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Sort by size first, then "alphabetically" (lexicographical see wikipedia)
|
||||
//
|
||||
bool operator < (const Address & other) const
|
||||
{
|
||||
if (size() < other.size())
|
||||
return true;
|
||||
else if (size() > other.size())
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < hops; i++)
|
||||
{
|
||||
if ((*this)[i] < other[i])
|
||||
return true;
|
||||
else if ((*this)[i] > other[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator >= (const Address & other) const
|
||||
{
|
||||
return !((*this) < other);
|
||||
}
|
||||
|
||||
bool operator <= (const Address & other) const
|
||||
{
|
||||
return !((*this) > other);
|
||||
}
|
||||
|
||||
bool operator != (const Address & other) const
|
||||
{
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
unsigned size() const
|
||||
{
|
||||
return hops;
|
||||
}
|
||||
|
||||
unsigned & operator [](unsigned index)
|
||||
{
|
||||
DP_ASSERT(index < hops);
|
||||
return hop[index];
|
||||
}
|
||||
|
||||
const unsigned & operator [](unsigned index) const
|
||||
{
|
||||
DP_ASSERT(index < hops);
|
||||
return hop[index];
|
||||
}
|
||||
|
||||
bool under(const Address & root) const
|
||||
{
|
||||
if (size() < root.size())
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < root.size(); i++)
|
||||
if ((*this)[i] != root[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef char StringBuffer[maxHops*3+1];
|
||||
char * toString(StringBuffer & buffer, bool removeLeadingZero = false) const
|
||||
{
|
||||
char * p = &buffer[0];
|
||||
int hopsWritten = 0;
|
||||
for (unsigned i = 0; i < hops; i++)
|
||||
{
|
||||
if (i == 0 && hop[0] == 0 && removeLeadingZero)
|
||||
continue;
|
||||
if (hopsWritten > 0)
|
||||
*p++ = '.';
|
||||
if (hop[i] >= 10)
|
||||
*p++ = (char)(hop[i] / 10 +'0');
|
||||
*p++ = (char)(hop[i] % 10 + '0');
|
||||
hopsWritten++;
|
||||
}
|
||||
|
||||
*p++= 0;
|
||||
return (char *)&buffer[0];
|
||||
}
|
||||
|
||||
// Large enough to fit 4 hops into every NvU32
|
||||
typedef NvU32 NvU32Buffer[(maxHops-1)/4+1 < 4 ? 4 : (maxHops-1)/4+1];
|
||||
NvU32 * toNvU32Buffer(NvU32Buffer & buffer) const
|
||||
{
|
||||
for (unsigned i = 0; i < hops; i++)
|
||||
{
|
||||
buffer[i/4] |= ((NvU8) hop[i]) << (i % 4) * 8;
|
||||
}
|
||||
|
||||
return (NvU32 *)&buffer[0];
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned hop[maxHops];
|
||||
unsigned hops;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_ADDRESS_H
|
||||
80
src/common/displayport/inc/dp_auxbus.h
Normal file
80
src/common/displayport/inc/dp_auxbus.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_auxbus.h *
|
||||
* Interface for low level access to the aux bus. *
|
||||
* This is the synchronous version of the interface. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_AUXBUS_H
|
||||
#define INCLUDED_DP_AUXBUS_H
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
class AuxBus : virtual public Object
|
||||
{
|
||||
public:
|
||||
enum status
|
||||
{
|
||||
success,
|
||||
defer,
|
||||
nack,
|
||||
unSupported,
|
||||
};
|
||||
|
||||
enum Action
|
||||
{
|
||||
read,
|
||||
write,
|
||||
writeStatusUpdateRequest, // I2C only
|
||||
};
|
||||
|
||||
enum Type
|
||||
{
|
||||
native,
|
||||
i2c,
|
||||
i2cMot
|
||||
};
|
||||
|
||||
virtual status transaction(Action action, Type type, int address,
|
||||
NvU8 * buffer, unsigned sizeRequested,
|
||||
unsigned * sizeCompleted,
|
||||
unsigned * pNakReason = NULL,
|
||||
NvU8 offset = 0, NvU8 nWriteTransactions = 0) = 0;
|
||||
|
||||
virtual unsigned transactionSize() = 0;
|
||||
virtual status fecTransaction(NvU8 *fecStatus, NvU16 **fecErrorCount, NvU32 flags) { return nack; }
|
||||
virtual void setDevicePlugged(bool) {}
|
||||
virtual ~AuxBus() {}
|
||||
};
|
||||
|
||||
//
|
||||
// Wraps an auxbus interface with one that prints all the input and output
|
||||
//
|
||||
AuxBus * CreateAuxLogger(AuxBus * auxBus);
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_AUXBUS_H
|
||||
97
src/common/displayport/inc/dp_auxdefs.h
Normal file
97
src/common/displayport/inc/dp_auxdefs.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_auxdefs.h *
|
||||
* Definitions for DPCD AUX offsets *
|
||||
* Should be used sparingly (DPCD HAL preferred) *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef __DP_AUXDEFS_H__
|
||||
#define __DP_AUXDEFS_H__
|
||||
|
||||
#define DPCD_MESSAGEBOX_SIZE 48
|
||||
|
||||
//
|
||||
// This definitions are being used for orin Hdcp opensourcing. Ideally this
|
||||
// should be replaced with build flags. Bug ID: 200733434
|
||||
//
|
||||
#define DP_OPTION_HDCP_SUPPORT_ENABLE 1 /* HDCP Enable */
|
||||
|
||||
#define DP_OPTION_HDCP_12_ENABLED 1 /* DP1.2 HDCP ENABLE */
|
||||
|
||||
#define DP_OPTION_QSE_ENABLED 1 /* Remove here when QSE p4r check-in */
|
||||
|
||||
//
|
||||
// If a message is outstanding for at least 4 seconds
|
||||
// assume no reply is coming through
|
||||
//
|
||||
#define DPCD_MESSAGE_REPLY_TIMEOUT 4000
|
||||
|
||||
#define DPCD_LINK_ADDRESS_MESSAGE_RETRIES 20 // 20 retries
|
||||
#define DPCD_LINK_ADDRESS_MESSAGE_COOLDOWN 10 // 10ms between attempts
|
||||
|
||||
// pointing to the defaults for LAM settings to start with
|
||||
#define DPCD_REMOTE_DPCD_WRITE_MESSAGE_RETRIES DPCD_LINK_ADDRESS_MESSAGE_RETRIES
|
||||
#define DPCD_REMOTE_DPCD_WRITE_MESSAGE_COOLDOWN DPCD_LINK_ADDRESS_MESSAGE_COOLDOWN
|
||||
|
||||
#define DPCD_REMOTE_DPCD_READ_MESSAGE_RETRIES 7 // 7 retries
|
||||
#define DPCD_REMOTE_DPCD_READ_MESSAGE_COOLDOWN DPCD_LINK_ADDRESS_MESSAGE_COOLDOWN
|
||||
#define DPCD_REMOTE_DPCD_READ_MESSAGE_COOLDOWN_BKSV 20 // 20ms between attempts
|
||||
|
||||
#define DPCD_QUERY_STREAM_MESSAGE_RETRIES 7 // 7 retries
|
||||
#define DPCD_QUERY_STREAM_MESSAGE_COOLDOWN 20 // 20ms between attempts
|
||||
|
||||
#define MST_EDID_RETRIES 20
|
||||
#define MST_EDID_COOLDOWN 10
|
||||
|
||||
#define MST_ALLOCATE_RETRIES 10
|
||||
#define MST_ALLOCATE_COOLDOWN 10
|
||||
|
||||
#define HDCP_AUTHENTICATION_RETRIES 6 // 6 retries
|
||||
#define HDCP_CPIRQ_RXSTAUS_RETRIES 3
|
||||
#define HDCP_AUTHENTICATION_COOLDOWN 1000// 1 sec between attempts
|
||||
#define HDCP22_AUTHENTICATION_COOLDOWN 2000// 2 sec between attempts
|
||||
#define HDCP_AUTHENTICATION_COOLDOWN_HPD 3000// 3 sec for first stream Add
|
||||
#define HDCP_CPIRQ_RXSTATUS_COOLDOWN 20 // 20ms between attempts
|
||||
|
||||
// Need to re-submit Stream Validation request to falcon microcontroller after 1 sec if current request fails
|
||||
#define HDCP_STREAM_VALIDATION_RESUBMIT_COOLDOWN 1000
|
||||
|
||||
//
|
||||
// Wait till 8secs for completion of the KSV and Stream Validation, if that doesn't complete
|
||||
// then timeout.
|
||||
//
|
||||
#define HDCP_STREAM_VALIDATION_REQUEST_COOLDOWN 8000
|
||||
|
||||
#define DPCD_OUI_NVIDIA 0x00044B
|
||||
|
||||
//
|
||||
// Define maximum retry count that checking Payload ID table updated before
|
||||
// trigger ACT sequence.
|
||||
//
|
||||
#define PAYLOADIDTABLE_UPDATED_CHECK_RETRIES 300
|
||||
|
||||
#endif // __DP_AUXDEFS_H__
|
||||
181
src/common/displayport/inc/dp_auxretry.h
Normal file
181
src/common/displayport/inc/dp_auxretry.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_auxretry.h *
|
||||
* Adapter interface for friendlier AuxBus *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_AUXRETRY_H
|
||||
#define INCLUDED_DP_AUXRETRY_H
|
||||
|
||||
#include "dp_auxbus.h"
|
||||
#include "dp_timeout.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
enum
|
||||
{
|
||||
minimumRetriesOnDefer = 7
|
||||
};
|
||||
|
||||
class AuxRetry
|
||||
{
|
||||
AuxBus * aux;
|
||||
public:
|
||||
AuxRetry(AuxBus * aux = 0)
|
||||
: aux(aux)
|
||||
{
|
||||
}
|
||||
|
||||
AuxBus * getDirect()
|
||||
{
|
||||
return aux;
|
||||
}
|
||||
|
||||
enum status
|
||||
{
|
||||
ack,
|
||||
nack,
|
||||
unsupportedRegister,
|
||||
defer
|
||||
};
|
||||
|
||||
//
|
||||
// Perform an aux read transaction.
|
||||
// - Automatically handles defers up to retry limit
|
||||
// - Retries on partial read
|
||||
//
|
||||
virtual status readTransaction(int address, NvU8 * buffer, unsigned size, unsigned retries = minimumRetriesOnDefer);
|
||||
|
||||
//
|
||||
// Similar to readTransaction except that it supports reading
|
||||
// larger spans than AuxBus::transactionSize()
|
||||
//
|
||||
virtual status read(int address, NvU8 * buffer, unsigned size, unsigned retries = minimumRetriesOnDefer);
|
||||
|
||||
//
|
||||
// Perform an aux write transaction.
|
||||
// - Automatically handles defers up to retry limit
|
||||
// - Retries on partial write
|
||||
//
|
||||
virtual status writeTransaction(int address, NvU8 * buffer, unsigned size, unsigned retries = minimumRetriesOnDefer);
|
||||
|
||||
//
|
||||
// Similar to writeTransaction except that it supports writin
|
||||
// larger spans than AuxBus::transactionSize()
|
||||
//
|
||||
virtual status write(int address, NvU8 * buffer, unsigned size, unsigned retries = minimumRetriesOnDefer);
|
||||
};
|
||||
|
||||
class AuxLogger : public AuxBus
|
||||
{
|
||||
AuxBus * bus;
|
||||
char hex[256];
|
||||
char hex_body[256];
|
||||
char hint[128];
|
||||
|
||||
public:
|
||||
AuxLogger(AuxBus * bus) : bus(bus)
|
||||
{
|
||||
}
|
||||
|
||||
const char * getAction(Action action)
|
||||
{
|
||||
if (action == read)
|
||||
return "rd ";
|
||||
else if (action == write)
|
||||
return "wr ";
|
||||
else if (action == writeStatusUpdateRequest)
|
||||
return "writeStatusUpdateRequest ";
|
||||
else
|
||||
DP_ASSERT(0);
|
||||
return "???";
|
||||
}
|
||||
|
||||
const char * getType(Type typ)
|
||||
{
|
||||
if (typ == native)
|
||||
return "";
|
||||
else if (typ == i2c)
|
||||
return "i2c ";
|
||||
else if (typ == i2cMot)
|
||||
return "i2cMot ";
|
||||
else
|
||||
DP_ASSERT(0);
|
||||
return "???";
|
||||
}
|
||||
|
||||
const char * getStatus(status stat)
|
||||
{
|
||||
if (stat == success)
|
||||
return "";
|
||||
else if (stat == nack)
|
||||
return "(nack) ";
|
||||
else if (stat == defer)
|
||||
return "(defer) ";
|
||||
else
|
||||
DP_ASSERT(0);
|
||||
return "???";
|
||||
}
|
||||
|
||||
const char * getRequestId(unsigned requestIdentifier)
|
||||
{
|
||||
switch(requestIdentifier)
|
||||
{
|
||||
case 0x1: return "LINK_ADDRESS";
|
||||
case 0x4: return "CLEAR_PAT";
|
||||
case 0x10: return "ENUM_PATH";
|
||||
case 0x11: return "ALLOCATE";
|
||||
case 0x12: return "QUERY";
|
||||
case 0x20: return "DPCD_READ";
|
||||
case 0x21: return "DPCD_WRITE";
|
||||
case 0x22: return "I2C_READ";
|
||||
case 0x23: return "I2C_WRITE";
|
||||
case 0x24: return "POWER_UP_PHY";
|
||||
case 0x25: return "POWER_DOWN_PHY";
|
||||
case 0x38: return "HDCP_STATUS";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
virtual status transaction(Action action, Type type, int address,
|
||||
NvU8 * buffer, unsigned sizeRequested,
|
||||
unsigned * sizeCompleted, unsigned * pNakReason,
|
||||
NvU8 offset, NvU8 nWriteTransactions);
|
||||
|
||||
virtual unsigned transactionSize()
|
||||
{
|
||||
return bus->transactionSize();
|
||||
}
|
||||
|
||||
virtual status fecTransaction(NvU8 *fecStatus, NvU16 **fecErrorCount, NvU32 flags)
|
||||
{
|
||||
return bus->fecTransaction(fecStatus, fecErrorCount, flags);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_AUXRETRY_H
|
||||
98
src/common/displayport/inc/dp_bitstream.h
Normal file
98
src/common/displayport/inc/dp_bitstream.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_bitstream.h *
|
||||
* This is an implementation of the big endian bit stream *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_BITSTREAM_H
|
||||
#define INCLUDED_DP_BITSTREAM_H
|
||||
|
||||
#include "dp_buffer.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
//
|
||||
// Bitstream reader interface
|
||||
// - reads a packed stream of bits in Big Endian format
|
||||
// - handles alignment, buffering, and buffer bounds checking
|
||||
//
|
||||
class BitStreamReader
|
||||
{
|
||||
Buffer * sourceBuffer;
|
||||
unsigned bitsOffset;
|
||||
unsigned bitsEnd;
|
||||
|
||||
public:
|
||||
// Read 1-32 bits from the stream into *value. Returns true on success
|
||||
bool read(unsigned * value, unsigned bits);
|
||||
|
||||
// Read 1-32 bits from stream. Returns 'default' on failure.
|
||||
unsigned readOrDefault(unsigned bits, unsigned defaultValue);
|
||||
|
||||
// Skip bits until we're aligned to the power of two alignment
|
||||
bool align(unsigned align);
|
||||
|
||||
unsigned offset();
|
||||
Buffer * buffer();
|
||||
BitStreamReader(Buffer * buffer, unsigned bitsOffset, unsigned bitsCount);
|
||||
};
|
||||
|
||||
//
|
||||
// Bitstream writer interface
|
||||
//
|
||||
class BitStreamWriter
|
||||
{
|
||||
Buffer * targetBuffer;
|
||||
unsigned bitsOffset;
|
||||
public:
|
||||
//
|
||||
// Create a bitstream writer at a specific bit offset
|
||||
// into an already existing buffer
|
||||
//
|
||||
BitStreamWriter(Buffer * buffer, unsigned bitsOffset = 0);
|
||||
|
||||
//
|
||||
// Write n bits to the buffer in big endian format.
|
||||
// No buffering is performed.
|
||||
//
|
||||
bool write(unsigned value, unsigned bits);
|
||||
|
||||
//
|
||||
// Emit zero's until the offset is divisible by align.
|
||||
// CAVEAT: align must be a power of 2 (eg 8)
|
||||
//
|
||||
bool align(unsigned align);
|
||||
|
||||
//
|
||||
// Get current offset and buffer target
|
||||
//
|
||||
unsigned offset();
|
||||
Buffer * buffer();
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_BITSTREAM_H
|
||||
97
src/common/displayport/inc/dp_buffer.h
Normal file
97
src/common/displayport/inc/dp_buffer.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_buffer.h *
|
||||
* Resizable byte buffer and stream classes *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_BUFFER_H
|
||||
#define INCLUDED_DP_BUFFER_H
|
||||
|
||||
#include "dp_internal.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
class Buffer
|
||||
{
|
||||
public:
|
||||
NvU8 *data; // Data buffer
|
||||
unsigned length; // bytes used
|
||||
unsigned capacity; // size of allocation
|
||||
bool errorState; // did we lose a malloc in there?
|
||||
public:
|
||||
//
|
||||
// Write will only fail if we're unable to reallocate the buffer. In this case
|
||||
// the buffer will be reset to its empty state.
|
||||
//
|
||||
const NvU8 * getData() const { return data; }
|
||||
NvU8 * getData() { return data; }
|
||||
bool resize(unsigned newSize);
|
||||
void memZero();
|
||||
void reset();
|
||||
unsigned getLength() const { return length; }
|
||||
|
||||
// Is in error state? This happens if malloc fails. Error state is
|
||||
// held until reset is called.
|
||||
bool isError() const;
|
||||
|
||||
Buffer(const Buffer & other);
|
||||
Buffer(NvU8 * data, unsigned size);
|
||||
Buffer & operator = (const Buffer & other);
|
||||
Buffer();
|
||||
~Buffer();
|
||||
|
||||
void swap(Buffer & other) {
|
||||
swap_args(other.data, data);
|
||||
swap_args(other.length, length);
|
||||
swap_args(other.capacity, capacity);
|
||||
swap_args(other.errorState, errorState);
|
||||
}
|
||||
|
||||
bool operator== (const Buffer & other) const;
|
||||
};
|
||||
|
||||
class Stream
|
||||
{
|
||||
protected:
|
||||
Buffer * parent;
|
||||
unsigned byteOffset;
|
||||
public:
|
||||
Stream(Buffer * buffer);
|
||||
bool seek(unsigned where);
|
||||
bool read(NvU8 * buffer, unsigned size);
|
||||
bool write(NvU8 * buffer, unsigned size);
|
||||
|
||||
// returns error state of buffer
|
||||
bool isError() const;
|
||||
unsigned remaining();
|
||||
unsigned offset();
|
||||
};
|
||||
|
||||
void swapBuffers(Buffer & left, Buffer & right);
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_BUFFER_H
|
||||
535
src/common/displayport/inc/dp_configcaps.h
Normal file
535
src/common/displayport/inc/dp_configcaps.h
Normal file
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_configcaps.h *
|
||||
* Abstraction for basic caps registers *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_CONFIGCAPS_H
|
||||
#define INCLUDED_DP_CONFIGCAPS_H
|
||||
|
||||
#include "dp_connector.h"
|
||||
#include "dp_auxretry.h"
|
||||
#include "dp_linkconfig.h"
|
||||
#include "dp_regkeydatabase.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
enum PowerState
|
||||
{
|
||||
PowerStateD0 = 1,
|
||||
PowerStateD3 = 2,
|
||||
PowerStateD3AuxOn = 5
|
||||
};
|
||||
|
||||
// Extended caps = offset 0x80
|
||||
enum DwnStreamPortType
|
||||
{
|
||||
DISPLAY_PORT = 0,
|
||||
ANALOG_VGA,
|
||||
DVI,
|
||||
HDMI,
|
||||
WITHOUT_EDID,
|
||||
DISPLAY_PORT_PLUSPLUS
|
||||
} ;
|
||||
|
||||
enum DwnStreamPortAttribute
|
||||
{
|
||||
RESERVED = 0,
|
||||
IL_720_480_60HZ,
|
||||
IL_720_480_50HZ,
|
||||
IL_1920_1080_60HZ,
|
||||
IL_1920_1080_50HZ,
|
||||
PG_1280_720_60HZ,
|
||||
PG_1280_720_50_HZ,
|
||||
} ;
|
||||
|
||||
// DPCD Offset 102 enums
|
||||
enum TrainingPatternSelectType
|
||||
{
|
||||
TRAINING_DISABLED,
|
||||
TRAINING_PAT_ONE,
|
||||
TRAINING_PAT_TWO,
|
||||
TRAINING_PAT_THREE,
|
||||
};
|
||||
|
||||
enum SymbolErrorSelectType
|
||||
{
|
||||
DISPARITY_ILLEGAL_SYMBOL_ERROR,
|
||||
DISPARITY_ERROR,
|
||||
ILLEGAL_SYMBOL_ERROR,
|
||||
};
|
||||
|
||||
// DPCD Offset 1A1 enums
|
||||
enum MultistreamHotplugMode
|
||||
{
|
||||
HPD_LONG_PULSE,
|
||||
IRQ_HPD,
|
||||
};
|
||||
|
||||
// DPCD Offset 220
|
||||
enum TestPatternType
|
||||
{
|
||||
NO_PATTERN,
|
||||
COLOR_RAMPS,
|
||||
BLACK_WHITE,
|
||||
COLOR_SQUARE,
|
||||
} ;
|
||||
|
||||
// DPCD Offset 232, 233
|
||||
enum ColorFormatType
|
||||
{
|
||||
RGB,
|
||||
YCbCr_422,
|
||||
YCbCr_444,
|
||||
} ;
|
||||
|
||||
enum DynamicRangeType
|
||||
{
|
||||
VESA,
|
||||
CEA,
|
||||
} ;
|
||||
|
||||
enum YCBCRCoeffType
|
||||
{
|
||||
ITU601,
|
||||
ITU709,
|
||||
} ;
|
||||
|
||||
#define HDCP_BCAPS_SIZE (0x1)
|
||||
#define HDCP_VPRIME_SIZE (0x14)
|
||||
#define HDCP_KSV_FIFO_SIZE (0xF)
|
||||
#define HDCP_KSV_FIFO_WINDOWS_RETRY (0x3)
|
||||
#define HDCP22_BCAPS_SIZE (0x1)
|
||||
|
||||
// Bstatus DPCD offset 0x68029
|
||||
#define HDCPREADY (0x1)
|
||||
#define R0PRIME_AVAILABLE (0x2)
|
||||
#define LINK_INTEGRITY_FAILURE (0x4)
|
||||
#define REAUTHENTICATION_REQUEST (0x8)
|
||||
|
||||
struct BInfo
|
||||
{
|
||||
bool maxCascadeExceeded;
|
||||
unsigned depth;
|
||||
bool maxDevsExceeded;
|
||||
unsigned deviceCount;
|
||||
};
|
||||
|
||||
struct BCaps
|
||||
{
|
||||
bool repeater;
|
||||
bool HDCPCapable;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PHYSICAL_PORT_START = 0x0,
|
||||
PHYSICAL_PORT_END = 0x7,
|
||||
LOGICAL_PORT_START = 0x8,
|
||||
LOGICAL_PORT_END = 0xF
|
||||
};
|
||||
|
||||
class LaneStatus
|
||||
{
|
||||
public:
|
||||
//
|
||||
// Lane Status
|
||||
// CAUTION: Only updated on IRQ/HPD right now
|
||||
//
|
||||
virtual bool getLaneStatusClockRecoveryDone(int lane) = 0; // DPCD offset 202, 203
|
||||
virtual bool getLaneStatusSymbolLock(int lane)= 0;
|
||||
virtual bool getInterlaneAlignDone() = 0;
|
||||
virtual bool getDownStreamPortStatusChange() = 0;
|
||||
};
|
||||
|
||||
class TestRequest
|
||||
{
|
||||
public:
|
||||
virtual bool getPendingTestRequestTraining() = 0; // DPCD offset 218
|
||||
virtual void getTestRequestTraining(LinkRate & rate, unsigned & lanes) = 0; // DPCD offset 219, 220
|
||||
virtual bool getPendingAutomatedTestRequest() = 0; // DPCD offset 218
|
||||
virtual bool getPendingTestRequestEdidRead() = 0; // DPCD offset 218
|
||||
virtual bool getPendingTestRequestPhyCompliance() = 0; // DPCD offset 218
|
||||
virtual LinkQualityPatternType getPhyTestPattern() = 0; // DPCD offset 248
|
||||
virtual AuxRetry::status setTestResponse(bool ack, bool edidChecksumWrite = false) = 0;
|
||||
virtual AuxRetry::status setTestResponseChecksum(NvU8 checksum) = 0;
|
||||
};
|
||||
|
||||
class LegacyPort
|
||||
{
|
||||
public:
|
||||
virtual DwnStreamPortType getDownstreamPortType() = 0;
|
||||
virtual DwnStreamPortAttribute getDownstreamNonEDIDPortAttribute() = 0;
|
||||
|
||||
// For port type = HDMI
|
||||
virtual NvU64 getMaxTmdsClkRate() = 0;
|
||||
};
|
||||
|
||||
class LinkState
|
||||
{
|
||||
public:
|
||||
//
|
||||
// Link state
|
||||
//
|
||||
virtual bool isPostLtAdjustRequestSupported() = 0;
|
||||
virtual void setPostLtAdjustRequestGranted(bool bGrantPostLtRequest) = 0;
|
||||
virtual bool getIsPostLtAdjRequestInProgress() = 0; // DPCD offset 204
|
||||
virtual TrainingPatternSelectType getTrainingPatternSelect() = 0; // DPCD offset 102
|
||||
|
||||
virtual bool setTrainingMultiLaneSet(NvU8 numLanes,
|
||||
NvU8 *voltSwingSet,
|
||||
NvU8 *preEmphasisSet) = 0;
|
||||
|
||||
virtual bool readTraining(NvU8* voltageSwingLane,
|
||||
NvU8* preemphasisLane = 0,
|
||||
NvU8* trainingScoreLane = 0,
|
||||
NvU8* postCursor = 0,
|
||||
NvU8 activeLaneCount = 0) = 0;
|
||||
|
||||
virtual bool isLaneSettingsChanged(NvU8* oldVoltageSwingLane,
|
||||
NvU8* newVoltageSwingLane,
|
||||
NvU8* oldPreemphasisLane,
|
||||
NvU8* newPreemphasisLane,
|
||||
NvU8 activeLaneCount) = 0;
|
||||
|
||||
virtual AuxRetry::status setIgnoreMSATimingParamters(bool msaTimingParamIgnoreEn) = 0;
|
||||
virtual AuxRetry::status setLinkQualLaneSet(unsigned lane, LinkQualityPatternType linkQualPattern) = 0;
|
||||
virtual AuxRetry::status setLinkQualPatternSet(LinkQualityPatternType linkQualPattern, unsigned laneCount = 0) = 0;
|
||||
};
|
||||
|
||||
class LinkCapabilities
|
||||
{
|
||||
public:
|
||||
|
||||
//
|
||||
// Physical layer feature set
|
||||
//
|
||||
virtual NvU64 getMaxLinkRate() = 0; // Maximum byte-block in Hz
|
||||
virtual unsigned getMaxLaneCount() = 0; // DPCD offset 2
|
||||
virtual unsigned getMaxLaneCountSupportedAtLinkRate(LinkRate linkRate) = 0;
|
||||
virtual bool getEnhancedFraming() = 0;
|
||||
virtual bool getSupportsNoHandshakeTraining() = 0;
|
||||
virtual bool getMsaTimingparIgnored() = 0;
|
||||
virtual bool getDownstreamPort(NvU8 *portType) = 0; // DPCD offset 5
|
||||
virtual bool getSupportsMultistream() = 0; // DPCD offset 21h
|
||||
virtual bool getNoLinkTraining() = 0; // DPCD offset 330h
|
||||
virtual unsigned getPhyRepeaterCount() = 0; // DPCD offset F0002h
|
||||
};
|
||||
|
||||
class OUI
|
||||
{
|
||||
public:
|
||||
virtual bool getOuiSupported() = 0;
|
||||
virtual AuxRetry::status setOuiSource(unsigned ouiId, const char * model, size_t modelNameLength, NvU8 chipRevision) = 0;
|
||||
virtual bool getOuiSink(unsigned &ouiId, char * modelName, size_t modelNameBufferSize, NvU8 & chipRevision) = 0;
|
||||
};
|
||||
|
||||
class HDCP
|
||||
{
|
||||
public:
|
||||
virtual bool getBKSV(NvU8 *bKSV) = 0; // DPCD offset 0x68000
|
||||
virtual bool getBCaps(BCaps &bCaps, NvU8 * rawByte = 0) = 0; // DPCD offset 0x68028
|
||||
virtual bool getHdcp22BCaps(BCaps &bCaps, NvU8 * rawByte = 0) = 0; // DPCD offset 0x6921D
|
||||
virtual bool getBinfo(BInfo &bInfo) = 0; // DPCD offset 0x6802A
|
||||
|
||||
// Generic interfaces for HDCP 1.x / 2.2
|
||||
virtual bool getRxStatus(const HDCPState &hdcpState, NvU8 *data) = 0;
|
||||
};
|
||||
|
||||
class DPCDHAL :
|
||||
virtual public Object,
|
||||
public TestRequest,
|
||||
public LaneStatus,
|
||||
public LinkState,
|
||||
public LinkCapabilities,
|
||||
public OUI,
|
||||
public HDCP
|
||||
{
|
||||
public:
|
||||
//
|
||||
// Notifications of external events
|
||||
// We sent IRQ/HPD events to the HAL so that it knows
|
||||
// when to re-read the registers. All the remaining
|
||||
// calls are either accessors to cached state (caps),
|
||||
// or DPCD get/setters
|
||||
//
|
||||
virtual void notifyIRQ() = 0;
|
||||
virtual void notifyHPD(bool status, bool bSkipDPCDRead = false) = 0;
|
||||
|
||||
virtual void populateFakeDpcd() = 0;
|
||||
|
||||
// DPCD override routines
|
||||
virtual void overrideMaxLinkRate(NvU32 overrideMaxLinkRate) = 0;
|
||||
virtual void overrideMaxLaneCount(NvU32 maxLaneCount) = 0;
|
||||
virtual void skipCableBWCheck(NvU32 maxLaneAtHighRate, NvU32 maxLaneAtLowRate) = 0;
|
||||
virtual void overrideOptimalLinkCfg(LinkRate optimalLinkRate, NvU32 optimalLaneCount) = 0;
|
||||
virtual void overrideOptimalLinkRate(LinkRate optimalLinkRate) = 0;
|
||||
|
||||
virtual bool isDpcdOffline() = 0;
|
||||
virtual void setAuxBus(AuxBus * bus) = 0;
|
||||
virtual NvU32 getVideoFallbackSupported() = 0;
|
||||
//
|
||||
// Cached CAPS
|
||||
// These are only re-read when notifyHPD is called
|
||||
//
|
||||
virtual unsigned getRevisionMajor() = 0;
|
||||
virtual unsigned getRevisionMinor() = 0;
|
||||
|
||||
virtual unsigned lttprGetRevisionMajor() = 0;
|
||||
virtual unsigned lttprGetRevisionMinor() = 0;
|
||||
|
||||
virtual bool getSDPExtnForColorimetry() = 0;
|
||||
|
||||
bool isAtLeastVersion(unsigned major, unsigned minor)
|
||||
{
|
||||
if (getRevisionMajor() > major)
|
||||
return true;
|
||||
|
||||
if (getRevisionMajor() < major)
|
||||
return false;
|
||||
|
||||
return getRevisionMinor() >= minor;
|
||||
}
|
||||
|
||||
bool isVersion(unsigned major, unsigned minor)
|
||||
{
|
||||
if ((getRevisionMajor() == major) &&
|
||||
(getRevisionMinor() == minor))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lttprIsAtLeastVersion(unsigned major, unsigned minor)
|
||||
{
|
||||
if (lttprGetRevisionMajor() > major)
|
||||
return true;
|
||||
|
||||
if (lttprGetRevisionMinor() < major)
|
||||
return false;
|
||||
|
||||
return lttprGetRevisionMinor() >= minor;
|
||||
}
|
||||
|
||||
bool lttprIsVersion(unsigned major, unsigned minor)
|
||||
{
|
||||
if ((lttprGetRevisionMajor() == major) &&
|
||||
(lttprGetRevisionMinor() == minor))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert Link Bandwidth read from DPCD register to Linkrate
|
||||
NvU64 mapLinkBandiwdthToLinkrate(NvU32 linkBandwidth)
|
||||
{
|
||||
if (FLD_TEST_DRF(_DPCD, _MAX_LINK_BANDWIDTH, _VAL, _1_62_GBPS, linkBandwidth))
|
||||
return RBR;
|
||||
else if (FLD_TEST_DRF(_DPCD, _MAX_LINK_BANDWIDTH, _VAL, _2_70_GBPS, linkBandwidth))
|
||||
return HBR;
|
||||
else if (FLD_TEST_DRF(_DPCD, _MAX_LINK_BANDWIDTH, _VAL, _5_40_GBPS, linkBandwidth))
|
||||
return HBR2;
|
||||
else if (FLD_TEST_DRF(_DPCD14, _MAX_LINK_BANDWIDTH, _VAL, _8_10_GBPS, linkBandwidth))
|
||||
return HBR3;
|
||||
else
|
||||
{
|
||||
DP_ASSERT(0 && "Unknown link bandwidth. Assuming HBR");
|
||||
return HBR;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Native aux transaction size (16 for AUX)
|
||||
//
|
||||
virtual size_t getTransactionSize() = 0;
|
||||
|
||||
//
|
||||
// SST Branching device/dongle/repeater
|
||||
// - Describes downstream port limitations
|
||||
// - Not for use with MST
|
||||
// - Primarily used for dongles (look at port 0 for pclk limits)
|
||||
//
|
||||
virtual LegacyPort * getLegacyPort(unsigned index) = 0;
|
||||
virtual unsigned getLegacyPortCount() = 0;
|
||||
|
||||
virtual PCONCaps * getPCONCaps() = 0;
|
||||
|
||||
//
|
||||
// Single stream specific caps
|
||||
//
|
||||
virtual unsigned getNumberOfAudioEndpoints() = 0;
|
||||
virtual int getSinkCount() = 0;
|
||||
virtual void setSinkCount(int sinkCount) = 0;
|
||||
|
||||
//
|
||||
// MISC
|
||||
//
|
||||
virtual bool isPC2Disabled() = 0;
|
||||
virtual void setPC2Disabled(bool disabled) = 0;
|
||||
|
||||
virtual void setDPCDOffline(bool enable) = 0;
|
||||
virtual void updateDPCDOffline() = 0;
|
||||
|
||||
virtual void setSupportsESI(bool bIsESISupported) = 0;
|
||||
virtual void setLttprSupported(bool isLttprSupported) = 0;
|
||||
|
||||
//
|
||||
// Intermediate Link Rate (eDP ILR)
|
||||
//
|
||||
virtual void setIndexedLinkrateEnabled(bool newVal) = 0;
|
||||
virtual bool isIndexedLinkrateEnabled() = 0;
|
||||
virtual bool isIndexedLinkrateCapable() = 0;
|
||||
virtual NvU16 *getLinkRateTable() = 0;
|
||||
virtual bool getRawLinkRateTable(NvU8 *buffer = NULL) = 0;
|
||||
|
||||
//
|
||||
// Link power state management
|
||||
//
|
||||
virtual bool setPowerState(PowerState newState) = 0;
|
||||
virtual PowerState getPowerState() = 0;
|
||||
//
|
||||
// Multistream
|
||||
//
|
||||
virtual bool getGUID(GUID & guid) = 0; // DPCD offset 30
|
||||
virtual AuxRetry::status setGUID(GUID & guid) = 0;
|
||||
virtual AuxRetry::status setMessagingEnable(bool uprequestEnable, bool upstreamIsSource) = 0;
|
||||
virtual AuxRetry::status setMultistreamLink(bool bMultistream) = 0;
|
||||
virtual void payloadTableClearACT() = 0;
|
||||
virtual bool payloadWaitForACTReceived() = 0;
|
||||
virtual bool payloadAllocate(unsigned streamId, unsigned begin, unsigned count) = 0;
|
||||
virtual bool clearPendingMsg() = 0;
|
||||
virtual bool isMessagingEnabled() = 0;
|
||||
|
||||
//
|
||||
// If set to IRQ we'll receive CSN messages on hotplugs (which are actually easy to miss).
|
||||
// If set to HPD mode we'll always receive an HPD whenever the topology changes.
|
||||
// The library supports using both modes.
|
||||
//
|
||||
virtual AuxRetry::status setMultistreamHotplugMode(MultistreamHotplugMode notifyType) = 0;
|
||||
|
||||
//
|
||||
// Interrupts
|
||||
//
|
||||
virtual bool interruptContentProtection() = 0;
|
||||
virtual void clearInterruptContentProtection() = 0;
|
||||
|
||||
virtual bool intteruptMCCS() = 0;
|
||||
virtual void clearInterruptMCCS() = 0;
|
||||
|
||||
virtual bool interruptDownReplyReady() = 0;
|
||||
virtual void clearInterruptDownReplyReady() = 0;
|
||||
|
||||
virtual bool interruptUpRequestReady() = 0;
|
||||
virtual void clearInterruptUpRequestReady() = 0;
|
||||
|
||||
virtual bool interruptCapabilitiesChanged() = 0;
|
||||
virtual void clearInterruptCapabilitiesChanged() = 0;
|
||||
|
||||
virtual bool getLinkStatusChanged() = 0;
|
||||
virtual void clearLinkStatusChanged() = 0;
|
||||
|
||||
virtual bool getHdmiLinkStatusChanged() = 0;
|
||||
virtual void clearHdmiLinkStatusChanged() = 0;
|
||||
|
||||
virtual bool getStreamStatusChanged() = 0;
|
||||
virtual void clearStreamStatusChanged() =0;
|
||||
|
||||
virtual void setDirtyLinkStatus(bool dirty) = 0;
|
||||
virtual void refreshLinkStatus() = 0;
|
||||
virtual bool isLinkStatusValid(unsigned lanes) = 0;
|
||||
|
||||
virtual void getCustomTestPattern(NvU8 *testPattern) = 0; // DPCD offset 250 - 259
|
||||
|
||||
//
|
||||
// Message Boxes
|
||||
//
|
||||
virtual AuxRetry::status writeDownRequestMessageBox(NvU8 * data, size_t length) = 0;
|
||||
virtual size_t getDownRequestMessageBoxSize() = 0;
|
||||
|
||||
virtual AuxRetry::status writeUpReplyMessageBox(NvU8 * data, size_t length) = 0;
|
||||
virtual size_t getUpReplyMessageBoxSize() = 0;
|
||||
|
||||
virtual AuxRetry::status readDownReplyMessageBox(NvU32 offset, NvU8 * data, size_t length) = 0;
|
||||
virtual size_t getDownReplyMessageBoxSize() = 0;
|
||||
|
||||
virtual AuxRetry::status readUpRequestMessageBox(NvU32 offset, NvU8 * data, size_t length) = 0;
|
||||
virtual size_t getUpRequestMessageBoxSize() = 0;
|
||||
|
||||
// MST<->SST override
|
||||
virtual void overrideMultiStreamCap(bool mstCapable) = 0;
|
||||
virtual bool getMultiStreamCapOverride() = 0;
|
||||
|
||||
virtual bool getDpcdMultiStreamCap(void) = 0;
|
||||
|
||||
// Set GPU DP support capability
|
||||
virtual void setGpuDPSupportedVersions(bool supportDp1_2, bool supportDp1_4) = 0;
|
||||
|
||||
// Set GPU FEC support capability
|
||||
virtual void setGpuFECSupported(bool bSupportFEC) = 0;
|
||||
|
||||
virtual void applyRegkeyOverrides(const DP_REGKEY_DATABASE& dpRegkeyDatabase) = 0;
|
||||
|
||||
// PCON configuration
|
||||
|
||||
// Reset PCON (to default state)
|
||||
virtual void resetProtocolConverter() = 0;
|
||||
// Source control mode and FRL/HDMI mode selection.
|
||||
virtual bool setSourceControlMode(bool bEnableSourceControlMode, bool bEnableFRLMode) = 0;
|
||||
|
||||
virtual bool checkPCONFrlReady(bool *bFrlReady) = 0;
|
||||
virtual bool setupPCONFrlLinkAssessment(NvU32 linkBw,
|
||||
bool bEnableExtendLTMode = false,
|
||||
bool bEnableConcurrentMode = false) = 0;
|
||||
|
||||
virtual bool checkPCONFrlLinkStatus(NvU32 *frlRate) = 0;
|
||||
|
||||
virtual bool queryHdmiLinkStatus(bool *bLinkActive, bool *bLinkReady) = 0;
|
||||
virtual NvU32 restorePCONFrlLink(NvU32 linkBwMask,
|
||||
bool bEnableExtendLTMode = false,
|
||||
bool bEnableConcurrentMode = false) = 0;
|
||||
|
||||
virtual void readPsrCapabilities(vesaPsrSinkCaps *caps) = 0;
|
||||
virtual bool updatePsrConfiguration(vesaPsrConfig config) = 0;
|
||||
virtual bool readPsrConfiguration(vesaPsrConfig *config) = 0;
|
||||
virtual bool readPsrState(vesaPsrState *psrState) = 0;
|
||||
virtual bool readPsrDebugInfo(vesaPsrDebugStatus *psrDbgState) = 0;
|
||||
virtual bool writePsrErrorStatus(vesaPsrErrorStatus psrErr) = 0;
|
||||
virtual bool readPsrErrorStatus(vesaPsrErrorStatus *psrErr) = 0;
|
||||
virtual bool writePsrEvtIndicator(vesaPsrEventIndicator psrErr) = 0;
|
||||
virtual bool readPsrEvtIndicator(vesaPsrEventIndicator *psrErr) = 0;
|
||||
|
||||
virtual ~DPCDHAL() {}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Implement interface
|
||||
//
|
||||
DPCDHAL * MakeDPCDHAL(AuxBus * bus, Timer * timer);
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_CONFIGCAPS_H
|
||||
678
src/common/displayport/inc/dp_connector.h
Normal file
678
src/common/displayport/inc/dp_connector.h
Normal file
@@ -0,0 +1,678 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_connector.h *
|
||||
* This is the primary client interface. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_CONNECTOR_H
|
||||
#define INCLUDED_DP_CONNECTOR_H
|
||||
|
||||
#include "dp_auxdefs.h"
|
||||
#include "dp_object.h"
|
||||
#include "dp_mainlink.h"
|
||||
#include "dp_auxbus.h"
|
||||
#include "dp_address.h"
|
||||
#include "dp_guid.h"
|
||||
#include "dp_evoadapter.h"
|
||||
#include "dp_auxbus.h"
|
||||
#include "dp_auxretry.h"
|
||||
#include "displayport.h"
|
||||
#include "dp_vrr.h"
|
||||
#include "../../modeset/timing/nvt_dsc_pps.h"
|
||||
#include "ctrl/ctrl0073/ctrl0073dp.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
class EvoInterface;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DP_TESTMESSAGE_STATUS_SUCCESS = 0,
|
||||
DP_TESTMESSAGE_STATUS_ERROR = 0xDEADBEEF,
|
||||
DP_TESTMESSAGE_STATUS_ERROR_INSUFFICIENT_INPUT_BUFFER = 0xDEADBEED,
|
||||
DP_TESTMESSAGE_STATUS_ERROR_INVALID_PARAM = 0xDEADBEEC
|
||||
// new error code should be here
|
||||
} DP_TESTMESSAGE_STATUS;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
False = 0,
|
||||
True = 1,
|
||||
Indeterminate = 2
|
||||
} TriState;
|
||||
|
||||
enum ConnectorType
|
||||
{
|
||||
connectorDisplayPort,
|
||||
connectorHDMI,
|
||||
connectorDVI,
|
||||
connectorVGA
|
||||
};
|
||||
|
||||
typedef struct portMap
|
||||
{
|
||||
NvU16 validMap; // port i is valid = bit i is high
|
||||
NvU16 inputMap; // port i is input port = bit i is high && validMap bit i is high
|
||||
NvU16 internalMap; // port i is internal = bit i is high && validMap bit i is high
|
||||
} PortMap;
|
||||
|
||||
enum ForceDsc
|
||||
{
|
||||
DSC_DEFAULT,
|
||||
DSC_FORCE_ENABLE,
|
||||
DSC_FORCE_DISABLE
|
||||
};
|
||||
|
||||
struct DpModesetParams
|
||||
{
|
||||
unsigned headIndex;
|
||||
ModesetInfo modesetInfo;
|
||||
DP_COLORFORMAT colorFormat;
|
||||
NV0073_CTRL_CMD_DP_SET_MSA_PROPERTIES_PARAMS msaparams;
|
||||
|
||||
DpModesetParams() : headIndex(0), modesetInfo(), colorFormat(dpColorFormat_Unknown), msaparams() {}
|
||||
|
||||
DpModesetParams(unsigned newHeadIndex,
|
||||
ModesetInfo newModesetInfo,
|
||||
DP_COLORFORMAT newColorFormat = dpColorFormat_Unknown) :
|
||||
headIndex(newHeadIndex),
|
||||
modesetInfo(newModesetInfo),
|
||||
colorFormat(newColorFormat),
|
||||
msaparams() {}
|
||||
|
||||
DpModesetParams(unsigned newHeadIndex,
|
||||
ModesetInfo *newModesetInfo,
|
||||
DP_COLORFORMAT newColorFormat = dpColorFormat_Unknown) :
|
||||
headIndex(newHeadIndex),
|
||||
modesetInfo(*newModesetInfo),
|
||||
colorFormat(newColorFormat),
|
||||
msaparams() {}
|
||||
|
||||
};
|
||||
|
||||
struct DscOutParams
|
||||
{
|
||||
unsigned PPS[DSC_MAX_PPS_SIZE_DWORD]; // Out - PPS SDP data
|
||||
};
|
||||
|
||||
struct DscParams
|
||||
{
|
||||
bool bCheckWithDsc; // [IN] - Client telling DP Library to check with DSC.
|
||||
ForceDsc forceDsc; // [IN] - Client telling DP Library to force enable/disable DSC
|
||||
DSC_INFO::FORCED_DSC_PARAMS* forcedParams; // [IN] - Client telling DP Library to force certain DSC params.
|
||||
bool bEnableDsc; // [OUT] - DP Library telling client that DSC is needed for this mode.
|
||||
unsigned bitsPerPixelX16; // [IN/OUT] - Bits per pixel value multiplied by 16
|
||||
DscOutParams *pDscOutParams; // [OUT] - DSC parameters
|
||||
|
||||
DscParams() : bCheckWithDsc(false), forceDsc(DSC_DEFAULT), forcedParams(NULL), bEnableDsc(false), bitsPerPixelX16(0), pDscOutParams(NULL) {}
|
||||
};
|
||||
|
||||
class Group;
|
||||
|
||||
bool SetConfigSingleHeadMultiStreamMode(Group **targets, // Array of group pointers given for getting configured in single head multistream mode.
|
||||
NvU32 displayIDs[], // Array of displayIDs given for getting configured in single head multistream mode.
|
||||
NvU32 numStreams, // Number of streams driven out from single head.
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_MODE mode, // Configuration mode : SST or MST
|
||||
bool bSetConfig, // Set or clear the configuration.
|
||||
NvU8 vbiosPrimaryDispIdIndex = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY, // VBIOS primary display ID index in displayIDs[] array
|
||||
bool bEnableAudioOverRightPanel = false); // Audio MUX config : right or left panel
|
||||
|
||||
//
|
||||
// Device object
|
||||
// This object represents a displayport device. Devices are not reported
|
||||
// to clients until the EDID is already on file.
|
||||
//
|
||||
class Device : virtual public Object
|
||||
{
|
||||
public:
|
||||
|
||||
virtual bool isPlugged() = 0;
|
||||
virtual bool isVideoSink() = 0; // Invariant: won't change once reported
|
||||
virtual bool isAudioSink() = 0; // Invariant
|
||||
|
||||
virtual bool isLoop() = 0; // the address starts and ends at th same device
|
||||
virtual bool isRedundant() = 0;
|
||||
virtual bool isMustDisconnect() = 0; // Is this monitor's head being attach preventing
|
||||
// us from enumerating other panels?
|
||||
virtual bool isZombie() = 0; // Head is attached but we're not connected
|
||||
virtual bool isCableOk() = 0; // cable can be not ok, whenwe saw hpd, device connected, but can't talk over aux
|
||||
|
||||
virtual bool isLogical() = 0; // Is device connected to logical port
|
||||
|
||||
virtual Address getTopologyAddress() const = 0; // Invariant
|
||||
virtual bool isMultistream() = 0;
|
||||
|
||||
virtual ConnectorType getConnectorType() = 0; // Invariant
|
||||
|
||||
virtual unsigned getEDIDSize() const= 0; // Invariant
|
||||
// Copies EDID into client buffer. Fails if the buffer is too small
|
||||
virtual bool getEDID(char * buffer, unsigned size) const = 0;
|
||||
|
||||
virtual unsigned getRawEDIDSize() const= 0;
|
||||
// Copies RawEDID into client buffer. Fails if the buffer is too small
|
||||
virtual bool getRawEDID(char * buffer, unsigned size) const = 0;
|
||||
|
||||
virtual bool getPCONCaps(PCONCaps *pPCONCaps) = 0;
|
||||
|
||||
virtual bool isFallbackEdid() = 0; // is the device edid a fallback one?
|
||||
virtual GUID getGUID() const = 0; // Returns the GUID for the device
|
||||
virtual bool isPowerSuspended() = 0;
|
||||
virtual bool isActive() = 0; // Whether the device has a head attached to it
|
||||
virtual TriState hdcpAvailableHop() = 0; // Whether the device support HDCP,
|
||||
// regardless of whether the path leading to it supports HDCP.
|
||||
virtual TriState hdcpAvailable() = 0; // Whether HDCP can be enabled.
|
||||
// Note this checks that the entire path to the node support HDCP.
|
||||
|
||||
virtual PortMap getPortMap() const = 0;
|
||||
|
||||
virtual void setPanelPowerParams(bool bSinkPowerStateD0, bool bPanelPowerStateOn) = 0;
|
||||
virtual Group * getOwningGroup() = 0; // Return the group this device is currently a member of
|
||||
|
||||
virtual AuxBus * getRawAuxChannel() = 0; // No automatic retry on DEFER. See limitations in dp_auxbus.h
|
||||
virtual AuxRetry * getAuxChannel() = 0; // User friendly AUX interface
|
||||
|
||||
virtual Device * getParent() = 0;
|
||||
virtual Device * getChild(unsigned portNumber) = 0;
|
||||
|
||||
virtual void dpcdOverrides() = 0; // Apply DPCD overrides if required
|
||||
|
||||
virtual bool getDpcdRevision(unsigned * major, unsigned * minor) = 0; // get the dpcd revision (maybe cached)
|
||||
|
||||
virtual bool getSDPExtnForColorimetrySupported() = 0;
|
||||
|
||||
virtual bool getIgnoreMSACap() = 0;
|
||||
|
||||
virtual AuxRetry::status setIgnoreMSAEnable(bool msaTimingParamIgnoreEn) = 0;
|
||||
|
||||
virtual NvBool isDSCPossible() = 0;
|
||||
|
||||
virtual NvBool isDSCSupported() = 0;
|
||||
|
||||
virtual DscCaps getDscCaps() = 0;
|
||||
|
||||
//
|
||||
// This function returns the device itself or its parent device that is doing
|
||||
// DSC decompression for it.
|
||||
//
|
||||
virtual Device* getDevDoingDscDecompression() = 0;
|
||||
virtual void markDeviceForDeletion() = 0;
|
||||
|
||||
virtual bool getRawDscCaps(NvU8 *buffer, NvU32 bufferSize) = 0;
|
||||
|
||||
// This interface is still nascent. Please don't use it. Read size limit is 16 bytes.
|
||||
virtual AuxBus::status getDpcdData(unsigned offset, NvU8 * buffer,
|
||||
unsigned sizeRequested,
|
||||
unsigned * sizeCompleted,
|
||||
unsigned * pNakReason = NULL) = 0;
|
||||
|
||||
virtual AuxBus::status setDpcdData(unsigned offset, NvU8 * buffer,
|
||||
unsigned sizeRequested,
|
||||
unsigned * sizeCompleted,
|
||||
unsigned * pNakReason = NULL) = 0;
|
||||
|
||||
virtual AuxBus::status dscCrcControl(NvBool bEnable, gpuDscCrc *gpuData, sinkDscCrc *sinkData) = 0;
|
||||
virtual AuxBus::status queryFecData(NvU8 *fecStatus, NvU16 **fecErrorCount, NvU32 flags) = 0;
|
||||
|
||||
//
|
||||
// The address send here will be right shifted by the library. DD should
|
||||
// send the DDC address without the shift.
|
||||
// Parameter bForceMot in both getI2cData and setI2cData is used to forfully set
|
||||
// the MOT bit. It is needed for some special cases where the MOT bit shouldn't
|
||||
// be set but some customers need it to please their monitors.
|
||||
//
|
||||
virtual bool getI2cData(unsigned offset, NvU8 * buffer, unsigned sizeRequested, unsigned * sizeCompleted, bool bForceMot = false) = 0;
|
||||
virtual bool setI2cData(unsigned offset, NvU8 * buffer, unsigned sizeRequested, unsigned * sizeCompleted, bool bForceMot = false) = 0;
|
||||
|
||||
//
|
||||
// Calls VRR enablement implementation in dp_vrr.cpp.
|
||||
// The enablement steps include interaction over DPAux in the vendor specific
|
||||
// DPCD space.
|
||||
//
|
||||
virtual bool startVrrEnablement() = 0; // VF: calls actual enablement code.
|
||||
virtual void resetVrrEnablement() = 0; // VF: resets enablement state.
|
||||
virtual bool isVrrMonitorEnabled() = 0; // VF: gets monitor enablement state.
|
||||
virtual bool isVrrDriverEnabled() = 0; // VF: gets driver enablement state.
|
||||
|
||||
// If the sink support MSA override in MST environment.
|
||||
virtual bool isMSAOverMSTCapable() = 0;
|
||||
virtual bool isFakedMuxDevice() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~Device() {}
|
||||
|
||||
};
|
||||
|
||||
class Group : virtual public Object
|
||||
{
|
||||
public:
|
||||
|
||||
//
|
||||
// Routines for changing which panels are in a group. To move a stream to a new
|
||||
// monitor without a modeset:
|
||||
// remove(old_panel)
|
||||
// insert(new_panel)
|
||||
// The library will automatically switch over to the new configuration
|
||||
//
|
||||
virtual void insert(Device * dev) = 0;
|
||||
virtual void remove(Device * dev) = 0;
|
||||
|
||||
//
|
||||
// group->enumDevices(0) - Get first element
|
||||
// group->enumDevices(i) - Get next element
|
||||
//
|
||||
// for (Device * i = group->enumDevices(0); i; i = group->enumDevices(i))
|
||||
//
|
||||
virtual Device * enumDevices(Device * previousDevice) = 0;
|
||||
|
||||
virtual void destroy() = 0; // Destroy the group object
|
||||
|
||||
// Toggles the encryption status for the stream.
|
||||
// Returns whether encryption is currently enabled.
|
||||
virtual bool hdcpGetEncrypted() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~Group() {}
|
||||
|
||||
};
|
||||
|
||||
class Connector : virtual public Object
|
||||
{
|
||||
public:
|
||||
//
|
||||
// Normally the Connector::EventSink callsback can occur in response to the following
|
||||
// 1. Timer callbacks
|
||||
// 2. notifyLongPulse/notifyShortPulse
|
||||
//
|
||||
class EventSink
|
||||
{
|
||||
public:
|
||||
virtual void newDevice(Device * dev) = 0; // New device appears in topology
|
||||
virtual void lostDevice(Device * dev) = 0; // Lost device from topology
|
||||
// Device object ceases to exist after this call
|
||||
|
||||
virtual void notifyMustDisconnect(Group * grp) = 0; // Notification that an attached head is preventing
|
||||
// us from completing detection of a newly connected device
|
||||
|
||||
virtual void notifyDetectComplete() = 0; // Rolling call. Happens every time we've done another full
|
||||
// detect on the topology
|
||||
|
||||
virtual void bandwidthChangeNotification(Device * dev, bool isComplianceMode) = 0; // Available bandwidth to panel has changed, or panel has
|
||||
// become a zombie
|
||||
|
||||
virtual void notifyZombieStateChange(Device * dev, bool zombied) = 0; // Notification that zombie device was attached or dettached
|
||||
virtual void notifyCableOkStateChange(Device * dev, bool cableOk) = 0; // Notification that device got cable state chagne (true - cable is good, false - cables is bad)
|
||||
virtual void notifyHDCPCapDone(Device * dev, bool hdcpCap) = 0; // Notification that device's HDCP cap detection is done and get state change.
|
||||
virtual void notifyMCCSEvent(Device * dev) = 0; // Notification that an MCCS event is coming
|
||||
};
|
||||
|
||||
// Query current Device topology
|
||||
virtual Device * enumDevices(Device * previousDevice) = 0;
|
||||
|
||||
// Called before system enters an S3 state
|
||||
virtual void pause() = 0;
|
||||
|
||||
// Get maximum link configuration
|
||||
virtual LinkConfiguration getMaxLinkConfig() = 0;
|
||||
|
||||
// Get currently active link configuration
|
||||
virtual LinkConfiguration getActiveLinkConfig() = 0;
|
||||
|
||||
// Get Current link configuration
|
||||
virtual void getCurrentLinkConfig(unsigned & laneCount, NvU64 & linkRate) = 0;
|
||||
|
||||
// Get the clock calculation supported by the panel
|
||||
virtual unsigned getPanelDataClockMultiplier() = 0;
|
||||
|
||||
// Get the clock calculation supported by the GPU
|
||||
virtual unsigned getGpuDataClockMultiplier() = 0;
|
||||
|
||||
// Resume from standby/initial boot notification
|
||||
// The library is considered to start up in the suspended state. You must make this
|
||||
// API call to enable the library. None of the library APIs are functional before
|
||||
// this call.
|
||||
//
|
||||
// Returns the group representing the firmware panel if any is active.
|
||||
//
|
||||
// plugged Does RM report the root-port DisplayId in
|
||||
// its plugged connector mask
|
||||
//
|
||||
// firmwareLinkHandsOff RM does NOT report the rootport displayId as active,
|
||||
// but one of the active panels shares the same SOR.
|
||||
//
|
||||
// firmwareDPActive RM reports the rootport displayId in the active device list
|
||||
// but display-driver hasn't yet performed its first modeset.
|
||||
//
|
||||
// isUefiSystem DD tells the library whether this system is a UEFI based
|
||||
// one so that the library can get the current and max link config
|
||||
// from RM/UEFI instead of trying to determine them on its own.
|
||||
//
|
||||
// firmwareHead Head being used to drive the firmware
|
||||
// display, if firmwareDPActive is true.
|
||||
//
|
||||
// bFirmwareLinkUseMultistream
|
||||
// Specifies whether the firmware connector is being driven in SST
|
||||
// (false) or MST (true) mode.
|
||||
//
|
||||
// bDisableVbiosScratchRegisterUpdate
|
||||
// Disables update of
|
||||
// NV_PDISP_SOR_DP_SCRATCH_RAD/MISC scratch
|
||||
// pad registers with last lit up display
|
||||
// address. This address is used by VBIOS in
|
||||
// case of driver unload or BSOD.
|
||||
//
|
||||
// bAllowMST Allow/Disallow Multi-streaming
|
||||
//
|
||||
virtual Group * resume(bool firmwareLinkHandsOff,
|
||||
bool firmwareDPActive,
|
||||
bool plugged,
|
||||
bool isUefiSystem = false,
|
||||
unsigned firmwareHead = 0,
|
||||
bool bFirmwareLinkUseMultistream = false,
|
||||
bool bDisableVbiosScratchRegisterUpdate = false,
|
||||
bool bAllowMST = true) = 0;
|
||||
|
||||
// The display-driver should enable hands off mode when attempting
|
||||
// to use a shared resource (such as the SOR) in a non-DP configuration.
|
||||
virtual void enableLinkHandsOff() = 0;
|
||||
virtual void releaseLinkHandsOff() = 0;
|
||||
|
||||
// Usage scenario:
|
||||
// beginCompoundQuery()
|
||||
// compoundQueryAttach(1280x1024)
|
||||
// compoundQueryAttach(1920x1080)
|
||||
// .endCompoundQuery()
|
||||
// Will tell you if you have sufficient bandwidth to operate
|
||||
// two panels at 1920x1080 and 1280x1024 assuming all currently
|
||||
// attached panels are detached.
|
||||
virtual void beginCompoundQuery() = 0;
|
||||
|
||||
//
|
||||
// twoChannelAudioHz
|
||||
// If you need 192khz stereo specify 192000 here.
|
||||
//
|
||||
// eightChannelAudioHz
|
||||
// Same setting for multi channel audio.
|
||||
// DisplayPort encodes 3-8 channel streams as 8 channel
|
||||
//
|
||||
// pixelClockHz
|
||||
// Requested pixel clock for the mode
|
||||
//
|
||||
// depth
|
||||
// Requested color depth
|
||||
//
|
||||
virtual bool compoundQueryAttach(Group * target,
|
||||
unsigned twoChannelAudioHz,
|
||||
unsigned eightChannelAudioHz,
|
||||
NvU64 pixelClockHz,
|
||||
unsigned rasterWidth,
|
||||
unsigned rasterHeight,
|
||||
unsigned rasterBlankStartX,
|
||||
unsigned rasterBlankEndX,
|
||||
unsigned depth) = 0;
|
||||
|
||||
virtual bool compoundQueryAttach(Group * target,
|
||||
const DpModesetParams &modesetParams, // Modeset info
|
||||
DscParams *pDscParams) = 0; // DSC parameters
|
||||
|
||||
virtual bool endCompoundQuery() = 0;
|
||||
|
||||
// Interface to indicate if clients need to perform a head shutdown before a modeset
|
||||
virtual bool isHeadShutDownNeeded(Group * target, // Group of panels we're attaching to this head
|
||||
unsigned headIndex,
|
||||
unsigned twoChannelAudioHz, // if you need 192khz stereo specify 192000 here
|
||||
unsigned eightChannelAudioHz, // Same setting for multi channel audio.
|
||||
// DisplayPort encodes 3-8 channel streams as 8 channel
|
||||
NvU64 pixelClockHz, // Requested pixel clock for the mode
|
||||
unsigned rasterWidth,
|
||||
unsigned rasterHeight,
|
||||
unsigned rasterBlankStartX,
|
||||
unsigned rasterBlankEndX,
|
||||
unsigned depth) = 0;
|
||||
|
||||
// Interface to indicate if clients need to perform a head shutdown before a modeset
|
||||
virtual bool isHeadShutDownNeeded(Group * target, // Group of panels we're attaching to this head
|
||||
unsigned headIndex,
|
||||
ModesetInfo modesetInfo) = 0; // Modeset info relevant DSC data
|
||||
|
||||
//
|
||||
// Interface for clients to query library if the link is going to be trained during notifyAttachBegin(modeset).
|
||||
// Note: This API is not intended to know if a link training will be performed during assessment of the link.
|
||||
// This API is added to see if library can avoid link training during modeset so that client can take necessary decision
|
||||
// to avoid a destructive modeset from UEFI mode at post to a GPU driver detected mode
|
||||
// (thus prevent a visible glitch - i.e. Smooth Transition)
|
||||
//
|
||||
// How isLinkTrainingNeededForModeset API is different from isHeadShutDownNeeded API -
|
||||
// In case of MST : we always shutdown head and link train if link is inactive, so both APIs return TRUE
|
||||
// In case of SST :
|
||||
// - If requested link config < active link config, we shutdown head to prevent overflow
|
||||
// as head will still be driving at higher mode during link training to lower mode
|
||||
// So both APIs return TRUE
|
||||
// - If requested link config >= active link config, we don't need a head shutdown since
|
||||
// SOR clocks can be changed by entering flush mode but will need to link train for mode change
|
||||
// So isHeadShutDownNeeded returns FALSE and isLinkTrainingNeededForModeset returns TRUE
|
||||
//
|
||||
virtual bool isLinkTrainingNeededForModeset (ModesetInfo modesetInfo) = 0;
|
||||
|
||||
// Notify library before/after modeset (update)
|
||||
virtual bool notifyAttachBegin(Group * target, // Group of panels we're attaching to this head
|
||||
unsigned headIndex,
|
||||
unsigned twoChannelAudioHz, // if you need 192khz stereo specify 192000 here
|
||||
unsigned eightChannelAudioHz, // Same setting for multi channel audio.
|
||||
// DisplayPort encodes 3-8 channel streams as 8 channel
|
||||
NvU64 pixelClockHz, // Requested pixel clock for the mode
|
||||
unsigned rasterWidth,
|
||||
unsigned rasterHeight,
|
||||
unsigned rasterBlankStartX,
|
||||
unsigned rasterBlankEndX,
|
||||
unsigned depth) = 0;
|
||||
|
||||
// Group of panels we're attaching to this head
|
||||
virtual bool notifyAttachBegin(Group * target, const DpModesetParams &modesetParams) = 0;
|
||||
|
||||
virtual void readRemoteHdcpCaps() = 0;
|
||||
|
||||
// modeset might be cancelled when NAB failed
|
||||
virtual void notifyAttachEnd(bool modesetCancelled) = 0;
|
||||
|
||||
//
|
||||
// Client needs to be notified about the SST<->MST transition,
|
||||
// based on which null modeset will be sent.
|
||||
//
|
||||
virtual bool isLinkAwaitingTransition() = 0;
|
||||
|
||||
virtual void resetLinkTrainingCounter() = 0;
|
||||
|
||||
// Notify library before/after shutdown (update)
|
||||
virtual void notifyDetachBegin(Group * target) = 0;
|
||||
virtual void notifyDetachEnd(bool bKeepOdAlive = false) = 0;
|
||||
|
||||
// Notify library to assess PCON link capability
|
||||
virtual bool assessPCONLinkCapability(PCONLinkControl *params) = 0;
|
||||
|
||||
// Notify library of hotplug/IRQ
|
||||
virtual void notifyLongPulse(bool statusConnected) = 0;
|
||||
virtual void notifyShortPulse() = 0;
|
||||
|
||||
// Notify Library when ACPI initialization is done
|
||||
virtual void notifyAcpiInitDone() = 0;
|
||||
|
||||
// Notify Library when GPU capability changes. Usually because power event.
|
||||
virtual void notifyGPUCapabilityChange() = 0;
|
||||
virtual void notifyHBR2WAREngage() = 0;
|
||||
|
||||
// Create a new Group. Note that if you wish to do a modeset but send the
|
||||
// stream nowhere, you may do a modeset with an EMPTY group. This is expected
|
||||
// to be the mechanism by which monitor faking is implemented.
|
||||
virtual Group * newGroup() = 0;
|
||||
|
||||
// Shutdown and the destroy the connector manager
|
||||
virtual void destroy() = 0;
|
||||
|
||||
virtual void createFakeMuxDevice(const NvU8 *buffer, NvU32 bufferSize) = 0;
|
||||
virtual void deleteFakeMuxDevice() = 0;
|
||||
virtual bool getRawDscCaps(NvU8 *buffer, NvU32 bufferSize) = 0;
|
||||
|
||||
//
|
||||
// OS Modeset Order mitigation causes the library to delay the reporting
|
||||
// of new devices until they can be safely turned on.
|
||||
// When enabled the library client will not see connection events until
|
||||
// MustDisconnect messages are processed.
|
||||
//
|
||||
// Policy state should be set before the library is brought out of
|
||||
// the suspended state.
|
||||
//
|
||||
// Important Note: This option changes the definition of QueryMode.
|
||||
// Without OS order mitigation query mode assumes that you will
|
||||
// deatach all of the heads from any zombied monitors *before*
|
||||
// activating the new panel. If your driver cannot guarantee
|
||||
// this invariant, then it must enable order mitigation.
|
||||
//
|
||||
virtual void setPolicyModesetOrderMitigation(bool enabled) = 0;
|
||||
|
||||
//
|
||||
// force LT at NAB for compliance test (Power Management) in Win10 RS2+ (WDDM 2.2)
|
||||
//
|
||||
// RS2 no longer sends an explicit call for setPanelPowerParams during the Resume.
|
||||
// It does that by specifying an additional flag during the call to SetTimings. Due to
|
||||
// this DP lib doesn't get chance to perform this transition from setPanelPowerParams
|
||||
// and since it was already skipping LT in NAB/modeswitch, so LT get missed out on the
|
||||
// compliance device during resume from S3/S4.
|
||||
//
|
||||
virtual void setPolicyForceLTAtNAB(bool enabled) = 0;
|
||||
|
||||
//
|
||||
// There are cases where OS does not detach heads from connector immediately after hot-unplug,
|
||||
// on next hot-plug there is no guarantee that newly connected sink is capable to drive existing
|
||||
// raster timings. Flush mode has following restriction
|
||||
// When exiting flush mode S/W should ensure that the final
|
||||
// link clock & lane count should be able to support existing raster.
|
||||
// If we run into this situation and use flush mode then that will cause display engine to hang.
|
||||
// This variable ensures to assess link safely in this situation and instead of using flush mode ask
|
||||
// DD to detach/reattach heads for link training.
|
||||
//
|
||||
virtual void setPolicyAssessLinkSafely(bool enabled) = 0;
|
||||
|
||||
//
|
||||
// These interfaces are meant to be used *ONLY* for tool purposes.
|
||||
// Clients should *NOT* use them for their own implementation.
|
||||
//
|
||||
// Sets the preferred link config which the tool has requested to train to.
|
||||
// Each set call should be paired with a reset call. Also, preferred link configs won't persist across HPDs.
|
||||
// It is advisable to do compound queries before setting a mode on a preferred config.
|
||||
// Compound queries and notify attaches(link train) would use the preferred link config unless it is reset again.
|
||||
// (not advisable to leave a preferred link config always ON).
|
||||
//
|
||||
virtual bool setPreferredLinkConfig(LinkConfiguration & lc, bool commit,
|
||||
bool force = false,
|
||||
LinkTrainingType forceTrainType = NORMAL_LINK_TRAINING) = 0;
|
||||
|
||||
//
|
||||
// Resets the preferred link config and lets the library go back to default LT policy.
|
||||
// Should follow a previous set call.
|
||||
//
|
||||
virtual bool resetPreferredLinkConfig(bool force=false) = 0;
|
||||
|
||||
//
|
||||
// These interfaces are used by client to allow/disallow
|
||||
// Multi-streaming.
|
||||
//
|
||||
// If connected sink is MST capable then:
|
||||
// Client should detach all active MST video/audio streams before
|
||||
// disallowing MST, vise-versa client should detach active SST
|
||||
// stream before allowing MST.
|
||||
//
|
||||
virtual void setAllowMultiStreaming(bool bAllowMST) = 0;
|
||||
virtual bool getAllowMultiStreaming(void) = 0;
|
||||
|
||||
// This function reads sink MST capability from DPCD register(s).
|
||||
virtual bool getSinkMultiStreamCap(void) = 0;
|
||||
|
||||
// These interfaces are Deprecated, use setAllowMultiStreaming()
|
||||
virtual void setDp11ProtocolForced() = 0;
|
||||
virtual void resetDp11ProtocolForced() = 0;
|
||||
virtual bool isDp11ProtocolForced() = 0;
|
||||
|
||||
virtual bool getHDCPAbortCodesDP12(NvU32 &hdcpAbortCodesDP12) = 0;
|
||||
|
||||
virtual bool getOuiSink(unsigned &ouiId, char * modelName,
|
||||
size_t modelNameBufferSize, NvU8 & chipRevision) = 0;
|
||||
|
||||
virtual bool getIgnoreSourceOuiHandshake() = 0;
|
||||
virtual void setIgnoreSourceOuiHandshake(bool bIgnore) = 0;
|
||||
|
||||
//
|
||||
// The following function is to be used to get the capability bit that tells the client whether the connector
|
||||
// can do multistream.
|
||||
//
|
||||
virtual bool isMultiStreamCapable() = 0;
|
||||
virtual bool isFlushSupported() = 0;
|
||||
virtual bool isStreamCloningEnabled() = 0;
|
||||
virtual NvU32 maxLinkRateSupported() = 0;
|
||||
virtual bool isFECSupported() = 0;
|
||||
virtual bool isFECCapable() = 0;
|
||||
|
||||
// Following APIs are for link test/config for DP Test Utility
|
||||
virtual bool getTestPattern(NV0073_CTRL_DP_TESTPATTERN *pTestPattern) = 0;
|
||||
virtual bool setTestPattern(NV0073_CTRL_DP_TESTPATTERN testPattern,
|
||||
NvU8 laneMask, NV0073_CTRL_DP_CSTM cstm,
|
||||
NvBool bIsHBR2, NvBool bSkipLaneDataOverride) = 0;
|
||||
// "data" is an array of NV0073_CTRL_MAX_LANES unsigned ints
|
||||
virtual bool getLaneConfig(NvU32 *numLanes, NvU32 *data) = 0;
|
||||
// "data" is an array of NV0073_CTRL_MAX_LANES unsigned ints
|
||||
virtual bool setLaneConfig(NvU32 numLanes, NvU32 *data) = 0;
|
||||
|
||||
virtual DP_TESTMESSAGE_STATUS sendDPTestMessage(void *pBuffer,
|
||||
NvU32 requestSize,
|
||||
NvU32 *pDpStatus) = 0;
|
||||
|
||||
virtual DP_TESTMESSAGE_STATUS getStreamIDs(NvU32 *pStreamIDs, NvU32 *pCount) = 0;
|
||||
// Function to configure power up/down for DP Main Link
|
||||
virtual void configurePowerState(bool bPowerUp) = 0;
|
||||
|
||||
virtual void readPsrCapabilities(vesaPsrSinkCaps *caps) = 0;
|
||||
virtual bool updatePsrConfiguration(vesaPsrConfig config) = 0;
|
||||
virtual bool readPsrConfiguration(vesaPsrConfig *config) = 0;
|
||||
virtual bool readPsrState(vesaPsrState *psrState) = 0;
|
||||
virtual bool readPsrDebugInfo(vesaPsrDebugStatus *psrDbgState) = 0;
|
||||
virtual bool writePsrErrorStatus(vesaPsrErrorStatus psrErr) = 0;
|
||||
virtual bool readPsrErrorStatus(vesaPsrErrorStatus *psrErr) = 0;
|
||||
virtual bool writePsrEvtIndicator(vesaPsrEventIndicator psrErr) = 0;
|
||||
virtual bool readPsrEvtIndicator(vesaPsrEventIndicator *psrErr) = 0;
|
||||
virtual bool updatePsrLinkState(bool bTrainLink) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~Connector() {}
|
||||
};
|
||||
|
||||
//
|
||||
// Library routine to create primary port interface
|
||||
// (Not intended to be used by display driver)
|
||||
Connector * createConnector(MainLink * mainInterface, // DisplayDriver implemented MainLink object
|
||||
AuxBus * auxInterface, // DisplayDriver implemented AuxRetry wrapper
|
||||
Timer * timerInterface, // DisplayDriver provided Timer services
|
||||
Connector::EventSink * sink); // Interface to notify DisplayDriver of events
|
||||
}
|
||||
#endif //INCLUDED_DP_CONNECTOR_H
|
||||
632
src/common/displayport/inc/dp_connectorimpl.h
Normal file
632
src/common/displayport/inc/dp_connectorimpl.h
Normal file
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_connectorimpl.cpp *
|
||||
* DP connector implementation *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_CONNECTORIMPL_H
|
||||
#define INCLUDED_DP_CONNECTORIMPL_H
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_guid.h"
|
||||
#include "dp_connector.h"
|
||||
#include "dp_configcaps.h"
|
||||
#include "dp_list.h"
|
||||
#include "dp_buffer.h"
|
||||
#include "dp_auxdefs.h"
|
||||
#include "dp_watermark.h"
|
||||
#include "dp_edid.h"
|
||||
#include "dp_discovery.h"
|
||||
#include "dp_groupimpl.h"
|
||||
#include "dp_deviceimpl.h"
|
||||
#include "./dptestutil/dp_testmessage.h"
|
||||
|
||||
// HDCP abort codes
|
||||
#define HDCP_FLAGS_ABORT_DEVICE_REVOKED 0x00000800 // Abort due to a revoked device in DP1.2 topology
|
||||
#define HDCP_FLAGS_ABORT_DEVICE_INVALID 0x00080000 // Abort due to an invalid device in DP1.2 topology
|
||||
#define HDCP_FLAGS_ABORT_HOP_LIMIT_EXCEEDED 0x80000000 // Abort, number of devices in DP1.2 topology exceeds supported limit
|
||||
|
||||
static inline unsigned getDataClockMultiplier(NvU64 linkRate, NvU64 laneCount)
|
||||
{
|
||||
//
|
||||
// To get the clock multiplier:
|
||||
// - Convert the linkRate from Hz to 10kHz by dividing it by 10000.
|
||||
// - Multiply the 10kHz linkRate by the laneCount.
|
||||
// - Multiply by 10.0/8, to account for the 8b/10b encoding overhead in the DP protocol layer.
|
||||
//
|
||||
// Avoid floating point in the arithmetic in the calculation
|
||||
// through the following conversions:
|
||||
// linkRate/10000.0 * laneCount * 10.0/8
|
||||
// (linkRate * laneCount * 10) / (10000 * 8)
|
||||
// (linkRate * laneCount) / (1000 * 8)
|
||||
//
|
||||
return (unsigned) DisplayPort::axb_div_c_64(linkRate, laneCount, 8000);
|
||||
}
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DP_TRANSPORT_MODE_INIT = 0,
|
||||
DP_TRANSPORT_MODE_SINGLE_STREAM = 1,
|
||||
DP_TRANSPORT_MODE_MULTI_STREAM = 2,
|
||||
} DP_TRANSPORT_MODE;
|
||||
|
||||
struct ConnectorImpl : public Connector, DiscoveryManager::DiscoveryManagerEventSink, Timer::TimerCallback, MessageManager::MessageReceiver::MessageReceiverEventSink
|
||||
{
|
||||
// DPCD HAL Layer - We should use this in place of direct register accesses
|
||||
DPCDHAL * hal;
|
||||
|
||||
MainLink * main; // Main link controls
|
||||
AuxBus * auxBus;
|
||||
|
||||
TestMessage testMessage; // TestMessage instance
|
||||
|
||||
Timer * timer; // OS provided timer services
|
||||
Connector::EventSink * sink; // Event Sink
|
||||
|
||||
unsigned ouiId; // Sink ouiId
|
||||
char modelName[NV_DPCD_SOURCE_DEV_ID_STRING__SIZE + 1]; // Device Model-name
|
||||
bool bIgnoreSrcOuiHandshake; // Skip writing source OUI
|
||||
|
||||
LinkPolicy linkPolicy;
|
||||
bool linkGuessed; // True when link was "guessed" during HPD in TMDS mode
|
||||
bool isLinkQuiesced; // True when link was set to quiet mode by TMDS modeset
|
||||
|
||||
bool bNoLtDoneAfterHeadDetach; // True when head is disconnected in NDE
|
||||
|
||||
bool isDP12AuthCap; // To tell whether this DP1.2 connector/ upmost device has the authentication Cap.
|
||||
bool isHDCPAuthOn; // To tell whether this connector has the authentication on.
|
||||
bool isHDCPReAuthPending; // To tell whether HDCP Auth is pending (at every stream addition and cleared at handler).
|
||||
bool isHDCPAuthTriggered; // To tell whether HDCP Auth is triggered and only cleared at unplug/device detach for MST.
|
||||
bool isHopLimitExceeded; // To tell the current topology is over limitation.
|
||||
bool bIsDiscoveryDetectActive; // To tell device discovery is active ( isDiscoveryDetectComplete is also used as DD notify and not want to impacts that. )
|
||||
bool isDiscoveryDetectComplete; // To tell device discovery is finished.
|
||||
bool bDeferNotifyLostDevice; // To tell if we should defer notify lost device event to client.
|
||||
|
||||
HDCPValidateData hdcpValidateData; // Cache the HDCP ValidateData.
|
||||
unsigned authRetries; // Retry counter for the authentication.
|
||||
unsigned retryLT; // Retry counter for link training in case of link lost in PostLQA
|
||||
unsigned hdcpCapsRetries; // Retry counter for Hdcp Caps read.
|
||||
unsigned hdcpCpIrqRxStatusRetries; // Retry counter for CPIRQ RxStatus read.
|
||||
bool bLTPhyRepeater; // Link Train PHY Repeaters between Source and Sink
|
||||
bool bFromResumeToNAB; // True if from resume to NAB, WAR flag for unblocking GA1.5
|
||||
bool bAttachOnResume; // True if notifyLongPulse is called for resume (reboot/S3/S4)
|
||||
bool bSkipAssessLinkForEDP; // Skip assessLink() for eDP. Assuming max is reachable.
|
||||
bool bPConConnected; // HDMI2.1-Protocol Converter (Support SRC control mode) connected.
|
||||
bool bSkipAssessLinkForPCon; // Skip assessLink() for PCON. DD will call assessFRLLink later.
|
||||
bool bHdcpAuthOnlyOnDemand; // True if only initiate Hdcp authentication on demand and MST won't auto-trigger authenticate at device attach.
|
||||
|
||||
bool constructorFailed;
|
||||
|
||||
//
|
||||
// OS Modeset Order mitigation causes the library to delay the reporting
|
||||
// of new devices until they can be safely turned on.
|
||||
// When enabled the library client will not see connection events until
|
||||
// MustDisconnect messages are processed.
|
||||
//
|
||||
// Policy state should be set before the library is brought out of
|
||||
// the suspended state.
|
||||
//
|
||||
bool policyModesetOrderMitigation;
|
||||
|
||||
//
|
||||
// force LT at NAB for compliance test (Power Management) in Win10 RS2+ (WDDM 2.2)
|
||||
//
|
||||
// RS2 no longer sends an explicit call for setPanelPowerParams during the Resume.
|
||||
// It does that by specifying an additional flag during the call to SetTimings. Due to
|
||||
// this DP lib doesn't get chance to perform this transition from setPanelPowerParams
|
||||
// and since it was already skipping LT in NAB/modeswitch, so LT get missed out on the
|
||||
// compliance device during resume from S3/S4.
|
||||
//
|
||||
bool policyForceLTAtNAB;
|
||||
|
||||
//
|
||||
// There are cases where OS does not detach heads from connector immediately after hot-unplug,
|
||||
// on next hot-plug there is no guarantee that newly connected sink is capable to drive existing
|
||||
// raster timings. Flush mode has following restriction
|
||||
// When exiting flush mode S/W should ensure that the final
|
||||
// link clock & lane count should be able to support existing raster.
|
||||
// If we run into this situation and use flush mode then that will cause display engine to hang.
|
||||
// This variable ensures to assess link safely in this situation: if newly connected sink is
|
||||
// not capable to drive existing raster then just restore link configuration which was there
|
||||
// before enabling flush mode, through fake link training.
|
||||
//
|
||||
bool policyAssessLinkSafely;
|
||||
|
||||
bool bDisableVbiosScratchRegisterUpdate;
|
||||
|
||||
// Only works when policyModesetOrderMitigation is true.
|
||||
// To record if we should report newDevice.
|
||||
bool modesetOrderMitigation;
|
||||
|
||||
List deviceList;
|
||||
List activeGroups;
|
||||
LinkedList<GroupImpl> intransitionGroups;
|
||||
LinkedList<GroupImpl> addStreamMSTIntransitionGroups;
|
||||
List inactiveGroups;
|
||||
|
||||
// Compound query
|
||||
bool compoundQueryActive;
|
||||
bool compoundQueryResult;
|
||||
unsigned compoundQueryCount;
|
||||
unsigned compoundQueryLocalLinkPBN;
|
||||
|
||||
unsigned freeSlots, maximumSlots;
|
||||
|
||||
// Multistream messaging
|
||||
MessageManager * messageManager;
|
||||
DiscoveryManager * discoveryManager;
|
||||
|
||||
// Multistream timeslot management (on local link)
|
||||
LinkConfiguration highestAssessedLC; // As of last assess, the highest possible link configuration
|
||||
|
||||
LinkConfiguration activeLinkConfig; // Current link config.
|
||||
|
||||
// this is the link config requested by a client.
|
||||
// can be set and reset by the client for a given operation.
|
||||
LinkConfiguration preferredLinkConfig;
|
||||
|
||||
//
|
||||
// Desired link configuration of single head multiple sst secondary connector.
|
||||
//
|
||||
LinkConfiguration oneHeadSSTSecPrefLnkCfg;
|
||||
|
||||
// All possible link configs
|
||||
LinkConfiguration * allPossibleLinkCfgs;
|
||||
unsigned numPossibleLnkCfg;
|
||||
|
||||
PCONLinkControl activePConLinkControl;
|
||||
|
||||
//
|
||||
// We're waiting for an MST<->SST transition
|
||||
// The transition cannot be made without the DD
|
||||
// disconnecting all heads. All devices are reported
|
||||
// as must_disconnect. Once the last device blocking
|
||||
// the transition is deattached from a head - we transition.
|
||||
//
|
||||
bool linkAwaitingTransition;
|
||||
|
||||
// Unless we're awaiting transition this is identical to hal->getSupportsMultistream()
|
||||
DP_TRANSPORT_MODE linkState;
|
||||
|
||||
bool bAudioOverRightPanel;
|
||||
|
||||
bool previousPlugged;
|
||||
bool connectorActive; // Keep track of if connector is active to serve any IRQ
|
||||
|
||||
Group * firmwareGroup; // The group used for book-keeping when we're in firmware mode
|
||||
|
||||
List pendingEdidReads; // List of DevicePendingEDIDRead structures.
|
||||
// This list tracks the currently in progress MST Edid Reads
|
||||
|
||||
Device * lastDeviceSetForVbios;
|
||||
|
||||
// Flag which gets set when ACPI init is done. DD calls notifyAcpiInitDone to tell client that ACPI init is completed
|
||||
// & client can now initiate DDC EDID read for a device which supports EDID through SBIOS
|
||||
bool bAcpiInitDone;
|
||||
|
||||
// Flag to check if the system is UEFI.
|
||||
bool bIsUefiSystem;
|
||||
|
||||
// Flag to check if LT should be skipped.
|
||||
bool bSkipLt;
|
||||
|
||||
// Flag to make sure that zombie gets triggred when a powerChange event happens
|
||||
bool bMitigateZombie;
|
||||
|
||||
//
|
||||
// HP Valor QHD+ N15P-Q3 EDP needs 50ms delay after D3
|
||||
// during trainLinkOptimized to come up on S4
|
||||
//
|
||||
bool bDelayAfterD3;
|
||||
|
||||
//
|
||||
// ASUS and Samsung monitors have inconsistent behavior when
|
||||
// DPCD 0x600 updated to D3. Skip D3 only in case these monitors
|
||||
// are driven in SST config
|
||||
//
|
||||
bool bKeepLinkAlive;
|
||||
|
||||
//
|
||||
// HP Trump dock link training is unstable during S4 resume, which causes
|
||||
// system to hang. Keep the link alive to increase stability.
|
||||
// See Bug 2109823.
|
||||
//
|
||||
bool bKeepLinkAliveMST;
|
||||
|
||||
// Keep the link alive when connector is in SST
|
||||
bool bKeepLinkAliveSST;
|
||||
|
||||
//
|
||||
// HTC Vive Link box is not happy when we power down the link
|
||||
// during link training when there is no stream present. It requests
|
||||
// for a link retraining pulse which is not required.
|
||||
// WAR to address this - NV Bug# 1793084
|
||||
//
|
||||
bool bKeepOptLinkAlive;
|
||||
|
||||
// Keep both DP and FRL link alive to save time.
|
||||
bool bKeepLinkAliveForPCON;
|
||||
|
||||
//
|
||||
// Remote HDCP DCPD access should be D0 but won't introduce extra Dx
|
||||
// state toggle. Use the counter to avoid powerdownlink when HDCP probe.
|
||||
//
|
||||
unsigned pendingRemoteHdcpDetections;
|
||||
|
||||
//
|
||||
// ASUS PQ 321 tiled monitor sometimes loses link while assessing link
|
||||
// or link training .So if we lower config from HBR2 to HBR and when
|
||||
// we retrain the link , we see black screen.
|
||||
// So WAR is to retry link training with same config for 3 times before
|
||||
// lowering link config. NV Bug #1846925
|
||||
//
|
||||
bool bNoFallbackInPostLQA;
|
||||
|
||||
bool bReportDeviceLostBeforeNew;
|
||||
bool bEnableAudioBeyond48K;
|
||||
bool bDisableSSC;
|
||||
bool bEnableFastLT;
|
||||
NvU32 maxLinkRateFromRegkey;
|
||||
|
||||
//
|
||||
// Latency(ms) to apply between link-train and FEC enable for bug
|
||||
// 2561206.
|
||||
//
|
||||
NvU32 LT2FecLatencyMs;
|
||||
|
||||
//
|
||||
// Dual SST Partner connector object pointer
|
||||
ConnectorImpl *pCoupledConnector;
|
||||
|
||||
// Set to true when a DSC mode is requested.
|
||||
bool bFECEnable;
|
||||
|
||||
// Save link config before entering PSR.
|
||||
LinkConfiguration psrLinkConfig;
|
||||
|
||||
//
|
||||
// Apply MST DSC caps WAR based on OUI ID of sink
|
||||
//
|
||||
bool bDscMstCapBug3143315;
|
||||
|
||||
//
|
||||
// Enable DSC Pass through support in driver based on regkey.
|
||||
//
|
||||
bool bDscMstEnablePassThrough;
|
||||
|
||||
//
|
||||
// Synaptics branch device doesn't support Virtual Peer Devices so DSC
|
||||
// capability of downstream device should be decided based on device's own
|
||||
// and its parent's DSC capability
|
||||
//
|
||||
bool bDscCapBasedOnParent;
|
||||
|
||||
void sharedInit();
|
||||
ConnectorImpl(MainLink * main, AuxBus * auxBus, Timer * timer, Connector::EventSink * sink);
|
||||
|
||||
void setPolicyModesetOrderMitigation(bool enabled);
|
||||
void setPolicyForceLTAtNAB(bool enabled);
|
||||
void setPolicyAssessLinkSafely(bool enabled);
|
||||
|
||||
void discoveryDetectComplete();
|
||||
void discoveryNewDevice(const DiscoveryManager::Device & device);
|
||||
void discoveryLostDevice(const Address & address);
|
||||
void processNewDevice(const DiscoveryManager::Device & device,
|
||||
const Edid & edid,
|
||||
bool isMultistream,
|
||||
DwnStreamPortType portType,
|
||||
DwnStreamPortAttribute portAttribute,
|
||||
bool isCompliance = false);
|
||||
|
||||
void applyEdidWARs(Edid & edid, DiscoveryManager::Device device);
|
||||
void applyRegkeyOverrides(const DP_REGKEY_DATABASE& dpRegkeyDatabase);
|
||||
|
||||
ResStatusNotifyMessage ResStatus;
|
||||
|
||||
void messageProcessed(MessageManager::MessageReceiver * from);
|
||||
|
||||
~ConnectorImpl();
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
virtual void hardwareWasReset();
|
||||
virtual LinkConfiguration getMaxLinkConfig();
|
||||
virtual LinkConfiguration getActiveLinkConfig();
|
||||
virtual void powerdownLink(bool bPowerdownPanel = false);
|
||||
|
||||
GroupImpl * getActiveGroupForSST();
|
||||
bool detectSinkCountChange();
|
||||
bool handlePhyPatternRequest();
|
||||
void applyOuiWARs();
|
||||
bool linkUseMultistream()
|
||||
{
|
||||
return (linkState == DP_TRANSPORT_MODE_MULTI_STREAM);
|
||||
}
|
||||
|
||||
void populateAllDpConfigs();
|
||||
|
||||
//
|
||||
// Suspend resume API
|
||||
//
|
||||
virtual Group * resume(bool firmwareLinkHandsOff,
|
||||
bool firmwareDPActive,
|
||||
bool plugged,
|
||||
bool isUefiSystem = false,
|
||||
unsigned firmwareHead = 0,
|
||||
bool bFirmwareLinkUseMultistream = false,
|
||||
bool bDisableVbiosScratchRegisterUpdate = false,
|
||||
bool bAllowMST = true);
|
||||
virtual void pause();
|
||||
|
||||
virtual Device * enumDevices(Device * previousDevice) ;
|
||||
|
||||
|
||||
virtual void beginCompoundQuery() ;
|
||||
virtual bool compoundQueryAttach(Group * target,
|
||||
unsigned twoChannelAudioHz, // if you need 192khz stereo specify 192000 here
|
||||
unsigned eightChannelAudioHz, // Same setting for multi channel audio.
|
||||
// DisplayPort encodes 3-8 channel streams as 8 channel
|
||||
NvU64 pixelClockHz, // Requested pixel clock for the mode
|
||||
unsigned rasterWidth,
|
||||
unsigned rasterHeight,
|
||||
unsigned rasterBlankStartX,
|
||||
unsigned rasterBlankEndX,
|
||||
unsigned depth);
|
||||
|
||||
virtual bool compoundQueryAttach(Group * target,
|
||||
const DpModesetParams &modesetParams, // Modeset info
|
||||
DscParams *pDscParams = NULL); // DSC parameters
|
||||
|
||||
virtual bool endCompoundQuery();
|
||||
|
||||
//
|
||||
// Timer callback tags.
|
||||
// (we pass the address of these variables as context to ::expired)
|
||||
char tagFireEvents;
|
||||
char tagDelayedLinkTrain;
|
||||
char tagHDCPReauthentication;
|
||||
char tagDelayedHdcpCapRead;
|
||||
char tagDelayedHDCPCPIrqHandling;
|
||||
|
||||
//
|
||||
// Enable disable TMDS mode
|
||||
//
|
||||
virtual void enableLinkHandsOff();
|
||||
virtual void releaseLinkHandsOff();
|
||||
|
||||
//
|
||||
// Timer callback for event management
|
||||
// Uses: fireEvents()
|
||||
virtual void expired(const void * tag);
|
||||
|
||||
// Generate Events.
|
||||
// useTimer specifies whether we fire the events on the timer
|
||||
// context, or this context.
|
||||
void fireEvents();
|
||||
|
||||
// returns the number of pending notifications.
|
||||
void fireEventsInternal();
|
||||
|
||||
virtual bool isHeadShutDownNeeded(Group * target, // Group of panels we're attaching to this head
|
||||
unsigned headIndex,
|
||||
ModesetInfo modesetInfo);
|
||||
|
||||
virtual bool isLinkTrainingNeededForModeset(ModesetInfo modesetInfo);
|
||||
|
||||
virtual bool notifyAttachBegin(Group * target, // Group of panels we're attaching to this head
|
||||
const DpModesetParams &modesetParams);
|
||||
|
||||
virtual bool isHeadShutDownNeeded(Group * target, // Group of panels we're attaching to this head
|
||||
unsigned headIndex,
|
||||
unsigned twoChannelAudioHz, // if you need 192khz stereo specify 192000 here
|
||||
unsigned eightChannelAudioHz, // Same setting for multi channel audio. DisplayPort encodes 3-8 channel streams as 8 channel
|
||||
NvU64 pixelClockHz, // Requested pixel clock for the mode
|
||||
unsigned rasterWidth,
|
||||
unsigned rasterHeight,
|
||||
unsigned rasterBlankStartX,
|
||||
unsigned rasterBlankEndX,
|
||||
unsigned depth) ;
|
||||
|
||||
virtual bool notifyAttachBegin(Group * target, // Group of panels we're attaching to this head
|
||||
unsigned headIndex,
|
||||
unsigned twoChannelAudioHz, // if you need 192khz stereo specify 192000 here
|
||||
unsigned eightChannelAudioHz, // Same setting for multi channel audio.
|
||||
// DisplayPort encodes 3-8 channel streams as 8 channel
|
||||
NvU64 pixelClockHz, // Requested pixel clock for the mode
|
||||
unsigned rasterWidth,
|
||||
unsigned rasterHeight,
|
||||
unsigned rasterBlankStartX,
|
||||
unsigned rasterBlankEndX,
|
||||
unsigned depth) ;
|
||||
|
||||
virtual void readRemoteHdcpCaps();
|
||||
virtual void notifyAttachEnd(bool modesetCancelled);
|
||||
virtual void notifyDetachBegin(Group * target);
|
||||
virtual void notifyDetachEnd(bool bKeepOdAlive = false);
|
||||
|
||||
bool performIeeeOuiHandshake();
|
||||
void setIgnoreSourceOuiHandshake(bool bIgnore);
|
||||
bool getIgnoreSourceOuiHandshake();
|
||||
bool willLinkSupportModeSST(const LinkConfiguration & linkConfig, const ModesetInfo & modesetInfo);
|
||||
void forceLinkTraining();
|
||||
|
||||
void assessLink(LinkTrainingType trainType = NORMAL_LINK_TRAINING);
|
||||
|
||||
bool isLinkInD3();
|
||||
bool isLinkActive();
|
||||
bool isLinkLost();
|
||||
bool trainSingleHeadMultipleSSTLinkNotAlive(GroupImpl *pGroupAttached);
|
||||
bool isLinkAwaitingTransition();
|
||||
bool isNoActiveStreamAndPowerdown();
|
||||
void incPendingRemoteHdcpDetection()
|
||||
{
|
||||
pendingRemoteHdcpDetections++;
|
||||
}
|
||||
void decPendingRemoteHdcpDetection()
|
||||
{
|
||||
if (pendingRemoteHdcpDetections > 0)
|
||||
{
|
||||
pendingRemoteHdcpDetections--;
|
||||
}
|
||||
}
|
||||
bool trainLinkOptimized(LinkConfiguration lConfig);
|
||||
bool trainLinkOptimizedSingleHeadMultipleSST(GroupImpl * group);
|
||||
bool getValidLowestLinkConfig(LinkConfiguration & lConfig, LinkConfiguration & lowestSelected, ModesetInfo queryModesetInfo);
|
||||
bool postLTAdjustment(const LinkConfiguration &, bool force);
|
||||
void populateUpdatedLaneSettings(NvU8* voltageSwingLane, NvU8* preemphasisLane, NvU32 *data);
|
||||
void populateDscCaps(DSC_INFO* dscInfo, DeviceImpl * dev, DSC_INFO::FORCED_DSC_PARAMS* forcedParams);
|
||||
void populateDscGpuCaps(DSC_INFO* dscInfo);
|
||||
void populateForcedDscParams(DSC_INFO* dscInfo, DSC_INFO::FORCED_DSC_PARAMS* forcedParams);
|
||||
void populateDscSinkCaps(DSC_INFO* dscInfo, DeviceImpl * dev);
|
||||
void populateDscModesetInfo(MODESET_INFO * pModesetInfo, const DpModesetParams * pModesetParams);
|
||||
|
||||
bool train(const LinkConfiguration & lConfig, bool force, LinkTrainingType trainType = NORMAL_LINK_TRAINING);
|
||||
bool validateLinkConfiguration(const LinkConfiguration & lConfig);
|
||||
|
||||
virtual bool assessPCONLinkCapability(PCONLinkControl *params);
|
||||
bool trainPCONFrlLink(PCONLinkControl *pConControl);
|
||||
|
||||
// Set Device DSC state based on current DSC state of all active devices on this connector
|
||||
bool setDeviceDscState(Device * dev, bool bEnableDsc);
|
||||
|
||||
// the lowest level function(nearest to the hal) for the connector.
|
||||
bool rawTrain(const LinkConfiguration & lConfig, bool force, LinkTrainingType linkTrainingType);
|
||||
|
||||
bool enableFlush();
|
||||
bool beforeAddStream(GroupImpl * group, bool force=false, bool forFlushMode = false);
|
||||
void afterAddStream(GroupImpl * group);
|
||||
void beforeDeleteStream(GroupImpl * group, bool forFlushMode = false);
|
||||
void afterDeleteStream(GroupImpl * group);
|
||||
void disableFlush(bool test=false);
|
||||
|
||||
bool beforeAddStreamMST(GroupImpl * group, bool force = false, bool forFlushMode = false);
|
||||
|
||||
bool deleteAllVirtualChannels();
|
||||
void clearTimeslices();
|
||||
bool allocateTimeslice(GroupImpl * targetGroup);
|
||||
void freeTimeslice(GroupImpl * targetGroup);
|
||||
void flushTimeslotsToHardware();
|
||||
bool getHDCPAbortCodesDP12(NvU32 &hdcpAbortCodesDP12);
|
||||
bool getOuiSink(unsigned &ouiId, char * modelName, size_t modelNameBufferSize, NvU8 & chipRevision);
|
||||
bool hdcpValidateKsv(const NvU8 *ksv, NvU32 Size);
|
||||
void cancelHdcpCallbacks();
|
||||
bool handleCPIRQ();
|
||||
void handleSSC();
|
||||
void handleMCCSIRQ();
|
||||
void handleHdmiLinkStatusChanged();
|
||||
void sortActiveGroups(bool ascending);
|
||||
void configInit();
|
||||
|
||||
virtual DeviceImpl* findDeviceInList(const Address & address);
|
||||
virtual void disconnectDeviceList();
|
||||
void notifyLongPulseInternal(bool statusConnected);
|
||||
virtual void notifyLongPulse(bool status);
|
||||
virtual void notifyShortPulse();
|
||||
virtual Group * newGroup() ;
|
||||
virtual void destroy();
|
||||
virtual void createFakeMuxDevice(const NvU8 *buffer, NvU32 bufferSize);
|
||||
virtual void deleteFakeMuxDevice();
|
||||
virtual bool getRawDscCaps(NvU8 *buffer, NvU32 bufferSize);
|
||||
virtual bool isMultiStreamCapable();
|
||||
virtual bool isFlushSupported();
|
||||
virtual bool isStreamCloningEnabled();
|
||||
virtual bool isFECSupported();
|
||||
virtual bool isFECCapable();
|
||||
virtual NvU32 maxLinkRateSupported();
|
||||
virtual bool setPreferredLinkConfig(LinkConfiguration & lc, bool commit, bool force = false, LinkTrainingType trainType = NORMAL_LINK_TRAINING);
|
||||
virtual bool resetPreferredLinkConfig(bool force = false);
|
||||
virtual void setAllowMultiStreaming(bool bAllowMST);
|
||||
virtual bool getAllowMultiStreaming(void);
|
||||
virtual bool getSinkMultiStreamCap(void);
|
||||
virtual void setDp11ProtocolForced();
|
||||
virtual void resetDp11ProtocolForced();
|
||||
virtual bool isDp11ProtocolForced();
|
||||
bool isAcpiInitDone();
|
||||
virtual void notifyAcpiInitDone();
|
||||
Group * createFirmwareGroup();
|
||||
virtual void notifyGPUCapabilityChange();
|
||||
virtual void notifyHBR2WAREngage();
|
||||
|
||||
bool getTestPattern(NV0073_CTRL_DP_TESTPATTERN *testPattern);
|
||||
bool setTestPattern(NV0073_CTRL_DP_TESTPATTERN testPattern, NvU8 laneMask, NV0073_CTRL_DP_CSTM cstm, NvBool bIsHBR2, NvBool bSkipLaneDataOverride = false);
|
||||
bool getLaneConfig(NvU32 *numLanes, NvU32 *data); // "data" is an array of NV0073_CTRL_MAX_LANES unsigned ints
|
||||
bool setLaneConfig(NvU32 numLanes, NvU32 *data); // "data" is an array of NV0073_CTRL_MAX_LANES unsigned ints
|
||||
void getCurrentLinkConfig(unsigned & laneCount, NvU64 & linkRate); // CurrentLink Configuration
|
||||
unsigned getPanelDataClockMultiplier();
|
||||
unsigned getGpuDataClockMultiplier();
|
||||
void configurePowerState(bool bPowerUp);
|
||||
virtual void readPsrCapabilities(vesaPsrSinkCaps *caps);
|
||||
virtual bool updatePsrConfiguration(vesaPsrConfig config);
|
||||
virtual bool readPsrConfiguration(vesaPsrConfig *config);
|
||||
virtual bool readPsrDebugInfo(vesaPsrDebugStatus *psrDbgState);
|
||||
virtual bool writePsrErrorStatus(vesaPsrErrorStatus psrErr);
|
||||
virtual bool readPsrErrorStatus(vesaPsrErrorStatus *psrErr);
|
||||
virtual bool writePsrEvtIndicator(vesaPsrEventIndicator psrErr);
|
||||
virtual bool readPsrEvtIndicator(vesaPsrEventIndicator *psrErr);
|
||||
virtual bool readPsrState(vesaPsrState *psrState);
|
||||
virtual bool updatePsrLinkState(bool bTrainLink);
|
||||
|
||||
// for dp test utility. pBuffer is the request buffer of type DP_STATUS_REQUEST_xxxx
|
||||
DP_TESTMESSAGE_STATUS sendDPTestMessage(void *pBuffer,
|
||||
NvU32 requestSize,
|
||||
NvU32 *pDpStatus);
|
||||
|
||||
DP_TESTMESSAGE_STATUS getStreamIDs(NvU32 *pStreamIDs, NvU32 *pCount); // for dp test utility, called by DD
|
||||
|
||||
// Reset link training counter for the active link configuration.
|
||||
virtual void resetLinkTrainingCounter()
|
||||
{
|
||||
activeLinkConfig.setLTCounter(0);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// New devices do not get a DeviceImpl created until after
|
||||
// the EDID read has completed. This object is used
|
||||
// to track the necessary state.
|
||||
//
|
||||
struct DevicePendingEDIDRead : protected EdidReadMultistream::EdidReadMultistreamEventSink, public ListElement
|
||||
{
|
||||
EdidReadMultistream reader;
|
||||
DiscoveryManager::Device device;
|
||||
ConnectorImpl * parent;
|
||||
|
||||
void mstEdidCompleted(EdidReadMultistream * from);
|
||||
void mstEdidReadFailed(EdidReadMultistream * from);
|
||||
|
||||
public:
|
||||
DevicePendingEDIDRead(ConnectorImpl * _parent, MessageManager * manager, DiscoveryManager::Device dev)
|
||||
: reader(_parent->timer, manager, this, dev.address), device(dev), parent(_parent)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_CONNECTORIMPL_H
|
||||
41
src/common/displayport/inc/dp_crc.h
Normal file
41
src/common/displayport/inc/dp_crc.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_crc.h *
|
||||
* CRC Algorithms for the messaging subsystem. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_CRC_H
|
||||
#define INCLUDED_DP_CRC_H
|
||||
|
||||
#include "dp_bitstream.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
unsigned dpCalculateHeaderCRC(BitStreamReader * reader);
|
||||
unsigned dpCalculateBodyCRC(BitStreamReader * writer);
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_CRC_H
|
||||
498
src/common/displayport/inc/dp_deviceimpl.h
Normal file
498
src/common/displayport/inc/dp_deviceimpl.h
Normal file
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort*********************************\
|
||||
* *
|
||||
* Module: dp_connector.cpp *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_DEVICEIMPL_H
|
||||
#define INCLUDED_DP_DEVICEIMPL_H
|
||||
|
||||
#include "dp_connector.h"
|
||||
#include "dp_internal.h"
|
||||
#include "dp_edid.h"
|
||||
#include "dp_list.h"
|
||||
#include "dp_auxdefs.h"
|
||||
#include "dp_vrr.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
#define PREDEFINED_DSC_MST_BPPX16 160;
|
||||
#define HDCP_BCAPS_DDC_OFFSET 0x40
|
||||
#define HDCP_BCAPS_DDC_EN_BIT 0x80
|
||||
#define HDCP_BCAPS_DP_EN_BIT 0x01
|
||||
#define HDCP_I2C_CLIENT_ADDR 0x74
|
||||
|
||||
struct GroupImpl;
|
||||
struct ConnectorImpl;
|
||||
class DeviceHDCPDetection;
|
||||
class VrrEnablement;
|
||||
|
||||
struct DeviceImpl : public Device,
|
||||
public AuxBus,
|
||||
public ListElement
|
||||
{
|
||||
//
|
||||
// Shadow state: This is the last state delivered to DD.
|
||||
// see the ConnectorImpl::fireEvents() function for handling.
|
||||
//
|
||||
// State is double buffered to allow for allow announces
|
||||
// to happen at the end of the state updates. We assume
|
||||
// the DD can call any Connector API in response to the
|
||||
// event.
|
||||
//
|
||||
struct Shadow
|
||||
{
|
||||
bool plugged;
|
||||
bool zombie;
|
||||
bool cableOk;
|
||||
bool mustDisconnect;
|
||||
bool hdcpCapDone;
|
||||
LinkConfiguration highestAssessedLC;
|
||||
} shadow;
|
||||
|
||||
struct BandWidth
|
||||
{
|
||||
struct _Enum_Path
|
||||
{
|
||||
unsigned total, free;
|
||||
bool bPathFECCapable;
|
||||
bool dataValid; // Is the cache valid?
|
||||
} enum_path;
|
||||
|
||||
struct Compound_Query_State
|
||||
{
|
||||
unsigned totalTimeSlots; // Total timeslots available for allocation across this node
|
||||
|
||||
unsigned timeslots_used_by_query; // Timeslots accounted for.
|
||||
|
||||
unsigned bandwidthAllocatedForIndex; // Compound query is compromised of several
|
||||
// qeuery attaches. These query attaches
|
||||
// may have more than one device associated.
|
||||
// this mask keeps track of which queryAttach's
|
||||
// have already had the stream "rounted" past
|
||||
// this node.
|
||||
} compound_query_state;
|
||||
|
||||
LinkConfiguration lastHopLinkConfig; // inferred from enum_path.total
|
||||
|
||||
} bandwidth;
|
||||
|
||||
enum rawEprState
|
||||
{
|
||||
software,
|
||||
hardware
|
||||
};
|
||||
|
||||
void resetCacheInferredLink();
|
||||
LinkConfiguration * inferLeafLink(unsigned * totalLinkSlots);
|
||||
|
||||
|
||||
DeviceImpl * parent; // Upstream parent device
|
||||
DeviceImpl * children[16];
|
||||
PortMap portMap;
|
||||
|
||||
Edid rawEDID;
|
||||
Edid processedEdid;
|
||||
Edid ddcEdid;
|
||||
DPCDHAL * hal;
|
||||
GroupImpl * activeGroup;
|
||||
ConnectorImpl * connector;
|
||||
ConnectorType connectorType;
|
||||
Address address;
|
||||
GUID guid;
|
||||
NvU8 peerDevice;
|
||||
NvU8 dpcdRevisionMajor;
|
||||
NvU8 dpcdRevisionMinor;
|
||||
bool multistream;
|
||||
bool videoSink, audioSink;
|
||||
bool plugged;
|
||||
|
||||
|
||||
AuxRetry friendlyAux;
|
||||
bool payloadAllocated; // did the allocate payload go through?
|
||||
|
||||
unsigned char BCAPS[HDCP_BCAPS_SIZE]; // Hdcp1.x bCaps raw data
|
||||
unsigned char BKSV[HDCP_KSV_SIZE]; // Hdcp1.x bKsv raw data
|
||||
unsigned char nvBCaps[HDCP_BCAPS_SIZE]; // NV generic HDCP BCAPS including 1.x, 2.2, ...
|
||||
NvU64 maxTmdsClkRate;
|
||||
|
||||
|
||||
bool isPendingNewDevice();
|
||||
bool isPendingLostDevice();
|
||||
bool isPendingZombie();
|
||||
bool isPendingCableOk();
|
||||
bool isPendingBandwidthChange();
|
||||
bool isPendingHDCPCapDone();
|
||||
|
||||
TriState isHDCPCap;
|
||||
bool isDeviceHDCPDetectionAlive;
|
||||
DeviceHDCPDetection * deviceHDCPDetection;
|
||||
|
||||
PCONCaps pconCaps;
|
||||
|
||||
// this flag signifies that the compliance device has requested EDID read test and may follow
|
||||
// hidden and lazy zombie policy.
|
||||
bool complianceDeviceEdidReadTest;
|
||||
|
||||
bool lazyExitNow;
|
||||
|
||||
// VRR Enablement structure
|
||||
VrrEnablement *vrrEnablement;
|
||||
|
||||
// DSC fields
|
||||
NvU8 rawDscCaps[16];
|
||||
DscCaps dscCaps;
|
||||
|
||||
// Panel replay Caps
|
||||
PanelReplayCaps prCaps;
|
||||
|
||||
bool bIsFakedMuxDevice;
|
||||
bool bIsPreviouslyFakedMuxDevice;
|
||||
bool bisMarkedForDeletion;
|
||||
|
||||
//
|
||||
// Device doing the DSC decompression for this device. This could be device itself
|
||||
// or its parent
|
||||
//
|
||||
DeviceImpl* devDoingDscDecompression;
|
||||
//
|
||||
// If DSC stream can be sent to this device or not. Either device itself or it's
|
||||
// parent can do DSC decompression
|
||||
//
|
||||
bool bDSCPossible;
|
||||
|
||||
bool bFECSupported;
|
||||
bool bFECUncorrectedSupported;
|
||||
bool bFECCorrectedSupported;
|
||||
bool bFECBitSupported;
|
||||
bool bFECParityBlockSupported;
|
||||
bool bFECParitySupported;
|
||||
|
||||
TriState bSdpExtCapable;
|
||||
bool bMSAOverMSTCapable;
|
||||
|
||||
DeviceImpl(DPCDHAL * hal, ConnectorImpl * connector, DeviceImpl * parent);
|
||||
~DeviceImpl();
|
||||
|
||||
virtual bool isCableOk();
|
||||
virtual bool isLogical();
|
||||
virtual bool isZombie();
|
||||
|
||||
virtual unsigned getEDIDSize() const;
|
||||
virtual bool getEDID(char * buffer, unsigned size) const;
|
||||
virtual unsigned getRawEDIDSize() const;
|
||||
virtual bool getRawEDID(char * buffer, unsigned size) const;
|
||||
|
||||
virtual bool getPCONCaps(PCONCaps *pPCONCaps);
|
||||
|
||||
virtual Group * getOwningGroup()
|
||||
{
|
||||
return (Group *)activeGroup;
|
||||
}
|
||||
|
||||
bool isActive();
|
||||
|
||||
void applyOUIOverrides();
|
||||
|
||||
virtual Device * getParent()
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
virtual Device * getChild(unsigned portNumber)
|
||||
{
|
||||
return children[portNumber];
|
||||
}
|
||||
|
||||
virtual bool isMultistream() // Sink supports multistream, remember we can have 1.1 targets
|
||||
{
|
||||
return address.size() != 0;
|
||||
}
|
||||
|
||||
virtual bool isNativeDPCD()
|
||||
{
|
||||
return (address.size() < 2);
|
||||
}
|
||||
|
||||
virtual bool isVideoSink()
|
||||
{
|
||||
return videoSink;
|
||||
}
|
||||
|
||||
virtual bool isAudioSink()
|
||||
{
|
||||
return audioSink;
|
||||
}
|
||||
|
||||
virtual bool isLoop()
|
||||
{
|
||||
DP_LOG(("isLoop implementation is pending (bug 791059)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool isRedundant()
|
||||
{
|
||||
DP_LOG(("isRedundant implementation is pending (bug 791059)"));
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool isMustDisconnect();
|
||||
|
||||
virtual bool isPlugged()
|
||||
{
|
||||
return plugged;
|
||||
}
|
||||
|
||||
virtual Address getTopologyAddress() const
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
virtual ConnectorType getConnectorType()
|
||||
{
|
||||
return connectorType;
|
||||
}
|
||||
|
||||
virtual bool isFallbackEdid()
|
||||
{
|
||||
return this->processedEdid.isFallbackEdid();
|
||||
}
|
||||
|
||||
virtual GUID getGUID() const
|
||||
{
|
||||
return guid;
|
||||
}
|
||||
|
||||
virtual PortMap getPortMap() const
|
||||
{
|
||||
return portMap;
|
||||
}
|
||||
|
||||
virtual TriState hdcpAvailableHop();
|
||||
virtual TriState hdcpAvailable();
|
||||
|
||||
virtual bool isMSAOverMSTCapable()
|
||||
{
|
||||
return bMSAOverMSTCapable;
|
||||
}
|
||||
|
||||
virtual bool isFakedMuxDevice();
|
||||
virtual bool isPreviouslyFakedMuxDevice();
|
||||
|
||||
bool bypassDpcdPowerOff()
|
||||
{
|
||||
return processedEdid.WARFlags.disableDpcdPowerOff;
|
||||
}
|
||||
|
||||
bool powerOnMonitorBeforeLt()
|
||||
{
|
||||
return processedEdid.WARFlags.powerOnBeforeLt;
|
||||
}
|
||||
|
||||
bool forceMaxLinkConfig()
|
||||
{
|
||||
return processedEdid.WARFlags.forceMaxLinkConfig;
|
||||
}
|
||||
|
||||
bool skipRedundantLt()
|
||||
{
|
||||
return processedEdid.WARFlags.skipRedundantLt;
|
||||
}
|
||||
|
||||
bool ignoreRedundantHotplug()
|
||||
{
|
||||
return processedEdid.WARFlags.ignoreRedundantHotplug;
|
||||
}
|
||||
|
||||
bool isOptimalLinkConfigOverridden()
|
||||
{
|
||||
return processedEdid.WARFlags.overrideOptimalLinkCfg;
|
||||
}
|
||||
|
||||
// Apply DPCD overrides if required
|
||||
void dpcdOverrides();
|
||||
|
||||
bool getDpcdRevision(unsigned * major, unsigned * minor)
|
||||
{
|
||||
if (!major || !minor)
|
||||
{
|
||||
DP_ASSERT(0 && "Null pointers passed in.");
|
||||
return false;
|
||||
}
|
||||
|
||||
*major = this->dpcdRevisionMajor;
|
||||
*minor = this->dpcdRevisionMinor;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getIgnoreMSACap()
|
||||
{
|
||||
return hal->getMsaTimingparIgnored();
|
||||
}
|
||||
|
||||
AuxRetry::status setIgnoreMSAEnable(bool msaTimingParamIgnoreEn)
|
||||
{
|
||||
return hal->setIgnoreMSATimingParamters(msaTimingParamIgnoreEn);
|
||||
}
|
||||
|
||||
virtual bool getSDPExtnForColorimetrySupported();
|
||||
|
||||
virtual bool isPowerSuspended();
|
||||
|
||||
virtual void setPanelPowerParams(bool bSinkPowerStateD0, bool bPanelPowerStateOn);
|
||||
|
||||
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();
|
||||
// default behaviour is querying first three registers for every lane --> flags = 0x7
|
||||
virtual status fecTransaction(NvU8 *fecStatus, NvU16 **fecErrorCount, NvU32 flags = NV_DP_FEC_FLAGS_SELECT_ALL);
|
||||
virtual AuxBus * getRawAuxChannel() { return this; }
|
||||
virtual AuxRetry * getAuxChannel() { return &friendlyAux; }
|
||||
virtual AuxBus::status getDpcdData(unsigned offset, NvU8 * buffer,
|
||||
unsigned sizeRequested,
|
||||
unsigned * sizeCompleted,
|
||||
unsigned * pNakReason=NULL);
|
||||
virtual AuxBus::status setDpcdData(unsigned offset, NvU8 * buffer,
|
||||
unsigned sizeRequested,
|
||||
unsigned * sizeCompleted,
|
||||
unsigned * pNakReason=NULL);
|
||||
virtual AuxBus::status queryFecData(NvU8 *fecStatus, NvU16 **fecErrorCount, NvU32 flags);
|
||||
|
||||
virtual DscCaps getDscCaps();
|
||||
|
||||
//
|
||||
// This function returns the device itself or its parent device that is doing
|
||||
// DSC decompression for it.
|
||||
//
|
||||
virtual Device* getDevDoingDscDecompression();
|
||||
virtual void markDeviceForDeletion() {bisMarkedForDeletion = true;};
|
||||
virtual bool isMarkedForDeletion() {return bisMarkedForDeletion;};
|
||||
virtual bool getRawDscCaps(NvU8 *buffer, NvU32 bufferSize);
|
||||
|
||||
virtual AuxBus::status dscCrcControl(NvBool bEnable, gpuDscCrc *dataGpu, sinkDscCrc *dataSink);
|
||||
|
||||
//
|
||||
// Parameter bForceMot in both getI2cData and setI2cData is used to forfully set
|
||||
// the MOT bit. It is needed for some special cases where the MOT bit shouldn't
|
||||
// be set but some customers need it to please their monitors.
|
||||
//
|
||||
virtual bool getI2cData(unsigned offset, NvU8 * buffer, unsigned sizeRequested, unsigned * sizeCompleted, bool bForceMot = false);
|
||||
virtual bool setI2cData(unsigned offset, NvU8 * buffer, unsigned sizeRequested, unsigned * sizeCompleted, bool bForceMot = false);
|
||||
virtual bool getRawEpr(unsigned * totalEpr, unsigned * freeEpr, rawEprState eprState);
|
||||
|
||||
void switchToComplianceFallback();
|
||||
|
||||
// VRR Display Enablement Functions
|
||||
bool startVrrEnablement(void);
|
||||
void resetVrrEnablement(void);
|
||||
bool isVrrMonitorEnabled(void);
|
||||
bool isVrrDriverEnabled(void);
|
||||
|
||||
// Panel replay related functions
|
||||
bool isPanelReplaySupported(void);
|
||||
void getPanelReplayCaps(void);
|
||||
bool setPanelReplayConfig(panelReplayConfig prcfg);
|
||||
|
||||
NvBool getDSCSupport();
|
||||
bool getFECSupport();
|
||||
NvBool isDSCPassThroughSupported();
|
||||
NvBool isDSCSupported();
|
||||
NvBool isDSCPossible();
|
||||
bool isFECSupported();
|
||||
bool readAndParseDSCCaps();
|
||||
bool parseDscCaps(const NvU8 *buffer, NvU32 bufferSize);
|
||||
bool setDscEnable(bool enable);
|
||||
bool getDscEnable(bool *pEnable);
|
||||
unsigned getDscVersionMajor();
|
||||
unsigned getDscVersionMinor();
|
||||
unsigned getDscRcBufferSize();
|
||||
unsigned getDscRcBufferBlockSize();
|
||||
unsigned getDscMaxSlicesPerSink();
|
||||
unsigned getDscLineBufferBitDepth();
|
||||
NvBool isDscBlockPredictionSupported();
|
||||
unsigned getDscMaxBitsPerPixel();
|
||||
NvBool isDscRgbSupported();
|
||||
NvBool isDscYCbCr444Supported();
|
||||
NvBool isDscYCbCrSimple422Supported();
|
||||
NvBool isDscYCbCr422NativeSupported();
|
||||
NvBool isDscYCbCr420NativeSupported();
|
||||
unsigned getDscPeakThroughputMode0();
|
||||
unsigned getDscPeakThroughputModel();
|
||||
unsigned getDscMaxSliceWidth();
|
||||
unsigned getDscDecoderColorDepthSupportMask();
|
||||
};
|
||||
class DeviceHDCPDetection : public Object, MessageManager::Message::MessageEventSink, Timer::TimerCallback
|
||||
{
|
||||
DeviceImpl* parent;
|
||||
RemoteDpcdReadMessage remoteBKSVReadMessage;
|
||||
RemoteDpcdReadMessage remoteBCapsReadMessage;
|
||||
RemoteDpcdReadMessage remote22BCapsReadMessage;
|
||||
MessageManager * messageManager; // For transmit and receive
|
||||
Timer * timer;
|
||||
bool bksvReadCompleted;
|
||||
bool bCapsReadCompleted;
|
||||
bool isValidBKSV;
|
||||
bool isBCapsHDCP;
|
||||
unsigned retriesRemoteBKSVReadMessage;
|
||||
unsigned retriesRemoteBCapsReadMessage;
|
||||
unsigned retriesRemote22BCapsReadMessage;
|
||||
bool retryRemoteBKSVReadMessage;
|
||||
bool retryRemoteBCapsReadMessage;
|
||||
bool retryRemote22BCapsReadMessage;
|
||||
bool bBKSVReadMessagePending;
|
||||
bool bBCapsReadMessagePending;
|
||||
|
||||
public:
|
||||
|
||||
DeviceHDCPDetection(DeviceImpl * parent, MessageManager * messageManager, Timer * timer)
|
||||
: bksvReadCompleted(false),bCapsReadCompleted(false),isValidBKSV(false),
|
||||
isBCapsHDCP(false), retriesRemoteBKSVReadMessage(0), retriesRemoteBCapsReadMessage(0),
|
||||
retriesRemote22BCapsReadMessage(0), retryRemoteBKSVReadMessage(false),
|
||||
retryRemoteBCapsReadMessage(false), retryRemote22BCapsReadMessage(false),
|
||||
bBKSVReadMessagePending(false), bBCapsReadMessagePending(false)
|
||||
|
||||
{
|
||||
this->parent = parent;
|
||||
this->messageManager = messageManager;
|
||||
this->timer = timer;
|
||||
}
|
||||
|
||||
~DeviceHDCPDetection();
|
||||
void expired(const void * tag);
|
||||
void start();
|
||||
void waivePendingHDCPCapDoneNotification();
|
||||
|
||||
bool hdcpValidateKsv(const NvU8 *ksv, NvU32 Size);
|
||||
void handleRemoteDpcdReadDownReply(MessageManager::Message * from);
|
||||
void messageFailed(MessageManager::Message * from, NakData * nakData);
|
||||
void messageCompleted(MessageManager::Message * from);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_DEVICEIMPL_H
|
||||
|
||||
328
src/common/displayport/inc/dp_discovery.h
Normal file
328
src/common/displayport/inc/dp_discovery.h
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_discovery.h *
|
||||
* Class definition for discovery manager. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_DISCOVERY_H
|
||||
#define INCLUDED_DP_DISCOVERY_H
|
||||
|
||||
#include "dp_address.h"
|
||||
#include "dp_list.h"
|
||||
#include "dp_messages.h"
|
||||
#include "dp_messagecodings.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
class DiscoveryManager : virtual public Object
|
||||
{
|
||||
public:
|
||||
struct Device
|
||||
{
|
||||
Address address; // direct topology address
|
||||
|
||||
bool legacy; // legacy (NON DP) device emulated on this port
|
||||
bool branch; // DP 1.2 style branching device
|
||||
PeerDevice peerDevice; // connector type of the device on this port
|
||||
unsigned dpcdRevisionMajor;
|
||||
unsigned dpcdRevisionMinor;
|
||||
GUID peerGuid; // device guid
|
||||
unsigned SDPStreams; // maximum number of audio streams supported
|
||||
unsigned SDPStreamSinks; // number of outputs to select from
|
||||
bool dirty; // got updates for the same device
|
||||
PortMap portMap;
|
||||
bool videoSink; // Should be true when a video sink is supported
|
||||
NvU64 maxTmdsClkRate;
|
||||
|
||||
Device():peerDevice(None),SDPStreams(0),SDPStreamSinks(0),dirty(false),videoSink(false)
|
||||
{
|
||||
portMap.validMap = portMap.inputMap = portMap.internalMap = 0;
|
||||
}
|
||||
|
||||
~Device(){}
|
||||
|
||||
};
|
||||
|
||||
struct ReceiverSink :
|
||||
virtual public Object,
|
||||
public MessageManager::MessageReceiver::MessageReceiverEventSink
|
||||
{
|
||||
DiscoveryManager * parent;
|
||||
|
||||
// will handle CSN (up_req) and generate a up_reply for it.
|
||||
virtual void messageProcessed(MessageManager::MessageReceiver * from);
|
||||
void handleCSN(MessageManager::MessageReceiver * from);
|
||||
|
||||
ReceiverSink(DiscoveryManager * parent)
|
||||
:parent(parent)
|
||||
{}
|
||||
|
||||
virtual ~ReceiverSink()
|
||||
{}
|
||||
};
|
||||
|
||||
// This will account for upreplies and their failures/retries.
|
||||
struct CsnUpReplyContainer : ListElement, Timer::TimerCallback, MessageManager::Message::MessageEventSink
|
||||
{
|
||||
struct CsnUpReply: public GenericUpReplyMessage
|
||||
{
|
||||
CsnUpReplyContainer * container;
|
||||
|
||||
CsnUpReply(CsnUpReplyContainer * container, const Address & target)
|
||||
: GenericUpReplyMessage(target, 0x2), container(container)
|
||||
{}
|
||||
|
||||
~CsnUpReply()
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
DiscoveryManager * parent;
|
||||
CsnUpReply upReplyMessage;
|
||||
unsigned delayInUsec;
|
||||
unsigned retries;
|
||||
Address target;
|
||||
|
||||
virtual void messageFailed(MessageManager::Message * from, NakData * nakData)
|
||||
{
|
||||
// if reason of failure is not timeout or defer; just forget trying again.
|
||||
if (!(nakData->reason == NakDefer || nakData->reason == NakTimeout))
|
||||
{
|
||||
messageCompleted(from);
|
||||
return;
|
||||
}
|
||||
|
||||
// queue a callback to reset and send again
|
||||
queueUpReply();
|
||||
return;
|
||||
}
|
||||
|
||||
virtual void messageCompleted(MessageManager::Message * from)
|
||||
{
|
||||
// don't delete now. Queue callback to delete later
|
||||
retries = 0;
|
||||
parent->timer->queueCallback(this, "CSNF", 5000);
|
||||
}
|
||||
|
||||
void queueUpReply()
|
||||
{
|
||||
parent->timer->queueCallback(this, "CSNF", delayInUsec/1000);
|
||||
}
|
||||
|
||||
void postUpReply()
|
||||
{
|
||||
upReplyMessage.set(target);
|
||||
parent->messageManager->postReply(&this->upReplyMessage, this);
|
||||
}
|
||||
|
||||
virtual void expired(const void * tag)
|
||||
{
|
||||
if (retries)
|
||||
retries--;
|
||||
|
||||
if (retries)
|
||||
postUpReply();
|
||||
else
|
||||
{
|
||||
// enough retries. wrap up.
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
CsnUpReplyContainer(DiscoveryManager * parent)
|
||||
:parent(parent), upReplyMessage(this, target), delayInUsec(200000), retries(4), target(Address(0))
|
||||
{}
|
||||
|
||||
virtual ~CsnUpReplyContainer()
|
||||
{
|
||||
// remove self from queue and delete
|
||||
// cancel all pending callbacks
|
||||
parent->timer->cancelCallbacks(this);
|
||||
parent->pendingCsnUpReplies.remove(this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ReceiverSink receiverSink;
|
||||
|
||||
ConnStatusNotifyMessage connectionStatusNotifyProcessor;
|
||||
|
||||
GUIDBuilder guidBuilder;
|
||||
|
||||
List pendingCsnUpReplies;
|
||||
|
||||
public:
|
||||
|
||||
struct DiscoveryManagerEventSink
|
||||
{
|
||||
virtual void discoveryDetectComplete() = 0; // reply to processDetect
|
||||
virtual void discoveryNewDevice(const DiscoveryManager::Device & device) = 0; // these can go out anytime
|
||||
virtual void discoveryLostDevice(const Address & address) = 0;
|
||||
};
|
||||
|
||||
enum {
|
||||
maximumTopologyNodes = 128
|
||||
};
|
||||
|
||||
Device currentDevices[maximumTopologyNodes];
|
||||
unsigned currentDevicesCount;
|
||||
|
||||
Device * findDevice(const Address & address);
|
||||
Device * findDevice(GUID & guid);
|
||||
void addDevice(const Device & device);
|
||||
void removeDevice(Device * device);
|
||||
void removeDeviceTree(const Address & prefix);
|
||||
Device * findChildDeviceForBranchWithGuid(GUID guid, unsigned port, Address & childAddr);
|
||||
|
||||
//
|
||||
// This is responsible for a "complete" detection of a sink. Specifically using remote dpcd reads and writes
|
||||
//
|
||||
struct SinkDetection : MessageManager::Message::MessageEventSink, ListElement, Timer::TimerCallback
|
||||
{
|
||||
Device device;
|
||||
Address address;
|
||||
RemoteDpcdWriteMessage remoteDpcdWriteMessage;
|
||||
RemoteDpcdReadMessage remoteDpcdReadMessage;
|
||||
PowerUpPhyMessage powerUpPhyMessage;
|
||||
LinkAddressMessage linkAddressMessage;
|
||||
DiscoveryManager * parent;
|
||||
bool completed;
|
||||
unsigned retriesRemoteDpcdWriteMessage;
|
||||
bool retryRemoteDpcdWriteMessage;
|
||||
unsigned retriesRemoteDpcdReadMessage;
|
||||
bool retryRemoteDpcdReadMessage;
|
||||
unsigned retriesLinkAddressMessage;
|
||||
bool retryLinkAddressMessage;
|
||||
|
||||
bool bFromCSN;
|
||||
|
||||
SinkDetection(DiscoveryManager * parent, const Device & device, bool bFromCSN)
|
||||
: device(device), address(device.address), parent(parent), completed(false),
|
||||
retriesRemoteDpcdWriteMessage(0), retryRemoteDpcdWriteMessage(false),
|
||||
retriesRemoteDpcdReadMessage(0), retryRemoteDpcdReadMessage(false),
|
||||
bFromCSN(bFromCSN)
|
||||
{}
|
||||
|
||||
~SinkDetection();
|
||||
void expired(const void * tag);
|
||||
void start();
|
||||
|
||||
void detectCompleted(bool passed);
|
||||
void messageFailed(MessageManager::Message * from, NakData * nakData);
|
||||
void handleRemoteDpcdReadDownReply();
|
||||
void handleRemoteDpcdWriteDownReply();
|
||||
void handleLinkAddressDownReply();
|
||||
|
||||
void messageCompleted(MessageManager::Message * from);
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// This object represents an address in some stage of detection
|
||||
//
|
||||
struct BranchDetection : MessageManager::Message::MessageEventSink, ListElement, Timer::TimerCallback
|
||||
{
|
||||
Device parentDevice;
|
||||
Address address;
|
||||
LinkAddressMessage::Result child[16];
|
||||
unsigned childCount;
|
||||
|
||||
LinkAddressMessage linkAddressMessage;
|
||||
RemoteDpcdWriteMessage remoteDpcdWriteMessage;
|
||||
|
||||
DiscoveryManager * parent;
|
||||
bool completed;
|
||||
bool retryLinkAddressMessage;
|
||||
unsigned retriesLinkAddressMessage;
|
||||
unsigned retriesRemoteDpcdWriteMessage;
|
||||
bool retryRemoteDpcdWriteMessage;
|
||||
|
||||
BranchDetection(DiscoveryManager * parent, const Device & device)
|
||||
: parentDevice(device), address(parentDevice.address),
|
||||
parent(parent), completed(false),
|
||||
retryLinkAddressMessage(false), retriesLinkAddressMessage(0),
|
||||
retriesRemoteDpcdWriteMessage(0), retryRemoteDpcdWriteMessage(false)
|
||||
{}
|
||||
|
||||
void expired(const void * tag);
|
||||
void start();
|
||||
~BranchDetection();
|
||||
|
||||
void detectCompleted(bool present);
|
||||
void messageFailed(MessageManager::Message * from, NakData * nakData) ;
|
||||
void handleLinkAddressDownReply();
|
||||
void handleRemoteDpcdReadDownReply();
|
||||
void messageCompleted(MessageManager::Message * from);
|
||||
};
|
||||
|
||||
void detect(const Address & address);
|
||||
void detectBranch(Device device);
|
||||
void detectSink(Device newDevice, bool bFromCSN);
|
||||
|
||||
public:
|
||||
|
||||
List outstandingBranchDetections;
|
||||
List outstandingSinkDetections;
|
||||
DiscoveryManagerEventSink * sink; // To call NotifyDetectComplete()
|
||||
MessageManager * messageManager; // For transmit and receive
|
||||
Timer * timer;
|
||||
DPCDHAL * hal;
|
||||
|
||||
DiscoveryManager(MessageManager * messageManager, DiscoveryManagerEventSink * sink, Timer * timer, DPCDHAL * hal)
|
||||
: receiverSink(this),
|
||||
connectionStatusNotifyProcessor(&receiverSink),
|
||||
guidBuilder(timer, 0x10DE9070),
|
||||
currentDevicesCount(0),
|
||||
sink(sink),
|
||||
messageManager(messageManager),
|
||||
timer(timer),
|
||||
hal(hal)
|
||||
{
|
||||
|
||||
//
|
||||
// Register to filter all the upmessages. We want to know when
|
||||
// connection status notify events are on their way.
|
||||
//
|
||||
messageManager->registerReceiver(&connectionStatusNotifyProcessor);
|
||||
}
|
||||
|
||||
~DiscoveryManager()
|
||||
{
|
||||
while (!this->outstandingBranchDetections.isEmpty())
|
||||
delete this->outstandingBranchDetections.front();
|
||||
|
||||
while (!this->outstandingSinkDetections.isEmpty())
|
||||
delete this->outstandingSinkDetections.front();
|
||||
|
||||
while (!this->pendingCsnUpReplies.isEmpty())
|
||||
delete this->pendingCsnUpReplies.front();
|
||||
}
|
||||
|
||||
void notifyLongPulse(bool status);
|
||||
|
||||
};
|
||||
}
|
||||
#endif //INCLUDED_DP_DISCOVERY_H
|
||||
321
src/common/displayport/inc/dp_edid.h
Normal file
321
src/common/displayport/inc/dp_edid.h
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_edid.h *
|
||||
* reading EDID from SST/MST Device *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_EDID_H
|
||||
#define INCLUDED_DP_EDID_H
|
||||
|
||||
#include "dp_buffer.h"
|
||||
#include "dp_auxbus.h"
|
||||
#include "dp_address.h"
|
||||
#include "dp_messages.h"
|
||||
#include "dp_messagecodings.h"
|
||||
#include "dp_timer.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
class Edid;
|
||||
|
||||
//
|
||||
// Shared utility object for MST/SST edid reading.
|
||||
// This object handles the retry, CRC validating,
|
||||
// identification of EDID length, DDC ping, etc.
|
||||
//
|
||||
// It's designed as an asynchronous state machine
|
||||
// because of the way MST EDID reads are built.
|
||||
//
|
||||
class EdidAssembler
|
||||
{
|
||||
public:
|
||||
EdidAssembler(Edid * const edid, bool bPatchCrc = false);
|
||||
|
||||
//
|
||||
// returns false - when existing data in Edid is invalid
|
||||
// returns seg - segment from which to read next block
|
||||
// returns offset - offset within block from which to start reading next block
|
||||
//
|
||||
bool readNextRequest(NvU8 & seg, NvU8 & offset);
|
||||
|
||||
// returns false when Edid read is completed
|
||||
void postReply(const Buffer & buffer, unsigned sizeCompleted, bool success);
|
||||
void postReply(unsigned char * data, unsigned sizeCompleted, bool success);
|
||||
|
||||
// returns true when it read all the required blocks
|
||||
bool readIsComplete();
|
||||
void reset();
|
||||
private:
|
||||
Edid * edid;
|
||||
Stream stream;
|
||||
|
||||
NvU8 oldBlockChecksum;
|
||||
unsigned blocksRead;
|
||||
unsigned totalBlockCnt;
|
||||
unsigned retriesCount;
|
||||
bool bPatchCrc;
|
||||
};
|
||||
|
||||
//
|
||||
// EDID
|
||||
//
|
||||
class Edid
|
||||
{
|
||||
public:
|
||||
Edid();
|
||||
~Edid();
|
||||
|
||||
Buffer * getBuffer() const { return &buffer; }
|
||||
NvU8 getFirstPageChecksum(); // Get checksum byte
|
||||
NvU8 getLastPageChecksum(); // Get checksum byte for last block
|
||||
|
||||
bool verifyCRC();
|
||||
unsigned getEdidVersion();
|
||||
unsigned getBlockCount();
|
||||
const char * getName() const;
|
||||
unsigned getEdidSize() const;
|
||||
bool isChecksumValid() const;
|
||||
bool isJunkEdid() const;
|
||||
bool isFallbackEdid() const;
|
||||
void swap(Edid & right);
|
||||
void applyEdidWorkArounds(NvU32 warFlag, const DpMonitorDenylistData *pDenylistData);
|
||||
void patchCrc();
|
||||
void setForcedEdidChecksum(bool set)
|
||||
{
|
||||
this->forcedCheckSum = set;
|
||||
}
|
||||
|
||||
void setFallbackFlag(bool set)
|
||||
{
|
||||
this->fallbackEdid = set;
|
||||
}
|
||||
|
||||
void setPatchedChecksum(bool set)
|
||||
{
|
||||
this->patchedChecksum = set;
|
||||
}
|
||||
|
||||
bool isPatchedChecksum() const
|
||||
{
|
||||
return this->patchedChecksum;
|
||||
}
|
||||
|
||||
bool isValidHeader() const
|
||||
{
|
||||
NvU8 validHeaderData[8] = {
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x00};
|
||||
|
||||
if (buffer.getLength() < 0x8)
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < 8; i++)
|
||||
{
|
||||
if (buffer.data[i] != validHeaderData[i])
|
||||
{
|
||||
DP_LOG(("DP-EDID> Invalid EDID Header"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned getManufId() const
|
||||
{
|
||||
if (buffer.getLength() < 0xa)
|
||||
return 0;
|
||||
|
||||
return ((buffer.data[0x9] << 8) | (buffer.data[0x8]));
|
||||
}
|
||||
|
||||
unsigned getProductId() const
|
||||
{
|
||||
if (buffer.getLength() < 0xc)
|
||||
return 0;
|
||||
|
||||
return ((buffer.data[0xb] << 8) | (buffer.data[0xa]));
|
||||
}
|
||||
|
||||
unsigned getYearWeek() const
|
||||
{
|
||||
if (buffer.getLength() < 0x12)
|
||||
return 0;
|
||||
|
||||
return ((buffer.data[0x11] << 8) | (buffer.data[0x10]));
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool extensionCountDisabled;
|
||||
bool dataForced;
|
||||
bool disableDpcdPowerOff;
|
||||
bool forceMaxLinkConfig;
|
||||
bool powerOnBeforeLt;
|
||||
bool skipRedundantLt;
|
||||
bool skipCableBWCheck;
|
||||
bool overrideOptimalLinkCfg;
|
||||
bool overrideMaxLaneCount;
|
||||
bool ignoreRedundantHotplug;
|
||||
bool delayAfterD3;
|
||||
bool keepLinkAlive;
|
||||
bool useLegacyAddress;
|
||||
bool reassessMaxLink;
|
||||
bool bIgnoreDscCap; // Ignore DSC even if sink reports DSC capability
|
||||
}_WARFlags;
|
||||
|
||||
_WARFlags WARFlags;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned maxLaneCount; // Max lane count value to override
|
||||
unsigned maxLaneAtHighRate; // Max lane count supported at HBR
|
||||
unsigned maxLaneAtLowRate; // Max lane count supported at RBR
|
||||
unsigned optimalLinkRate; // Optimal link rate value to override
|
||||
unsigned optimalLaneCount; // Optimal lane count value to override
|
||||
}_WARData;
|
||||
|
||||
_WARData WARData;
|
||||
|
||||
void resetData()
|
||||
{
|
||||
buffer.reset();
|
||||
checkSumValid = false;
|
||||
forcedCheckSum = false;
|
||||
fallbackEdid = false;
|
||||
// clear the WARFlags
|
||||
_WARFlags temp = {0};
|
||||
WARFlags = temp;
|
||||
}
|
||||
|
||||
bool operator== (const Edid & other)
|
||||
{
|
||||
return (buffer == other.buffer);
|
||||
}
|
||||
|
||||
bool operator!= (const Edid & other)
|
||||
{
|
||||
return !(buffer == other.buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
void validateCheckSum();
|
||||
|
||||
mutable Buffer buffer;
|
||||
bool checkSumValid;
|
||||
bool forcedCheckSum;
|
||||
bool fallbackEdid;
|
||||
bool patchedChecksum;
|
||||
};
|
||||
|
||||
//
|
||||
// SST EDID Read API
|
||||
//
|
||||
bool EdidReadSST(Edid & edid, AuxBus * aux, Timer * timer, bool pendingTestRequestEdidRead = false, bool bBypassAssembler = false, MainLink *main = NULL);
|
||||
|
||||
enum EDID_DDC
|
||||
{
|
||||
EDID_DDC_NONE = 0x00,
|
||||
EDID_DDC_ADR0 = 0xA0,
|
||||
EDID_DDC_ADR1 = 0xA2,
|
||||
EDID_DDC_ADR2 = 0xA6,
|
||||
EDID_SEG_SELECTOR_OFFSET = 0x60,
|
||||
};
|
||||
EDID_DDC sstDDCPing(AuxBus & dpAux);
|
||||
|
||||
//
|
||||
// MST EDID Read API
|
||||
//
|
||||
|
||||
class EdidReadMultistream : public Object, protected MessageManager::Message::MessageEventSink, Timer::TimerCallback
|
||||
{
|
||||
public:
|
||||
class EdidReadMultistreamEventSink // Connector will inherit from this
|
||||
{
|
||||
public:
|
||||
virtual void mstEdidCompleted(EdidReadMultistream * from) = 0;
|
||||
virtual void mstEdidReadFailed(EdidReadMultistream * from) = 0;
|
||||
};
|
||||
|
||||
EdidReadMultistream(Timer * timer, MessageManager * manager, EdidReadMultistream::EdidReadMultistreamEventSink * sink, Address topologyAddress)
|
||||
: topologyAddress(topologyAddress), manager(manager), edidReaderManager(&edid), ddcIndex(0),
|
||||
retries(0), timer(timer), sink(sink)
|
||||
{
|
||||
startReadingEdid();
|
||||
}
|
||||
|
||||
Edid edid;
|
||||
Address topologyAddress;
|
||||
~EdidReadMultistream();
|
||||
|
||||
private:
|
||||
void startReadingEdid();
|
||||
|
||||
MessageManager * manager;
|
||||
RemoteI2cReadMessage remoteI2cRead;
|
||||
EdidAssembler edidReaderManager; // come up another word besides edidReaderManager eg Manager
|
||||
NvU8 DDCAddress;
|
||||
NvU8 ddcIndex;
|
||||
unsigned retries;
|
||||
Timer * timer;
|
||||
|
||||
void readNextBlock(NvU8 seg, NvU8 offset);
|
||||
void failedToReadEdid();
|
||||
void expired(const void * tag);
|
||||
|
||||
EdidReadMultistreamEventSink * sink;
|
||||
|
||||
virtual void messageFailed(MessageManager::Message * from, NakData * nakData);
|
||||
virtual void messageCompleted(MessageManager::Message * from);
|
||||
void edidAttemptDone(bool succeeded);
|
||||
};
|
||||
|
||||
//
|
||||
// Useful defines
|
||||
//
|
||||
enum
|
||||
{
|
||||
EDID_BLOCK_SIZE = 0x80,
|
||||
EDID_SEGMENT_SIZE = 2*EDID_BLOCK_SIZE,
|
||||
EDID_POLICY_BLOCK_READ_MAX_RETRY_COUNT = 3,
|
||||
// DID EDID CTS v1.3 d12 currently outlines that Source shall support up to 16 blocks of EDID data.
|
||||
EDID_MAX_BLOCK_COUNT = 16,
|
||||
};
|
||||
|
||||
static const NvU8 ddcAddrList[] = {EDID_DDC_ADR0, EDID_DDC_ADR1, EDID_DDC_ADR2};
|
||||
const NvU8 ddcAddrListSize = sizeof(ddcAddrList)/sizeof(NvU8);
|
||||
const NvU8 EDID_READ_MAX_RETRY_COUNT = 6;
|
||||
const NvU8 EDID_MAX_AUX_RETRIES = 10;
|
||||
const NvU8 EDID_AUX_WAIT_TIME = 1;
|
||||
NvU8 getEDIDBlockChecksum(const Buffer &);
|
||||
|
||||
void makeEdidFallback(Edid & edid, NvU32 fallbackFormatSupported = 0);
|
||||
void makeEdidFallbackVGA(Edid & edid);
|
||||
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_EDID_H
|
||||
410
src/common/displayport/inc/dp_evoadapter.h
Normal file
410
src/common/displayport/inc/dp_evoadapter.h
Normal file
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* 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
|
||||
121
src/common/displayport/inc/dp_groupimpl.h
Normal file
121
src/common/displayport/inc/dp_groupimpl.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_groupimpl.h *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_GROUPIMPL_H
|
||||
#define INCLUDED_DP_GROUPIMPL_H
|
||||
|
||||
#include "dp_connector.h"
|
||||
#include "dp_deviceimpl.h"
|
||||
#include "dp_linkedlist.h"
|
||||
#include "dp_watermark.h"
|
||||
#include "dp_auxdefs.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
|
||||
struct GroupImpl : public Group, ListElement, Timer::TimerCallback
|
||||
{
|
||||
ConnectorImpl * parent;
|
||||
LinkedList<Device> members;
|
||||
List elements;
|
||||
unsigned headIndex;
|
||||
unsigned streamIndex;
|
||||
bool streamValidationDone;
|
||||
bool headInFirmware; // Set if this is a firmware run mode. If set lastModesetInfo is NOT valid
|
||||
bool bIsHeadShutdownNeeded; // Set if head shutdown is requested during modeset
|
||||
bool hdcpEnabled;
|
||||
bool hdcpPreviousStatus;
|
||||
bool bWaitForDeAllocACT;
|
||||
bool bDeferredPayloadAlloc;
|
||||
ModesetInfo lastModesetInfo;
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID singleHeadMultiStreamID;
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_MODE singleHeadMultiStreamMode;
|
||||
DP_COLORFORMAT colorFormat;
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned PBN;
|
||||
int count;
|
||||
int begin;
|
||||
bool hardwareDirty; // Does the configureStream need to be called again?
|
||||
Watermark watermarks; // Cached watermark calculations
|
||||
} timeslot;
|
||||
|
||||
bool bIsCurrentModesetGroup; // Group that is getting attached
|
||||
|
||||
GroupImpl(ConnectorImpl * parent, bool isFirmwareGroup = false)
|
||||
: parent(parent),
|
||||
streamValidationDone(true),
|
||||
headInFirmware(false),
|
||||
bIsHeadShutdownNeeded(true),
|
||||
hdcpEnabled(false),
|
||||
hdcpPreviousStatus(false),
|
||||
bWaitForDeAllocACT(false),
|
||||
singleHeadMultiStreamID(DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY),
|
||||
singleHeadMultiStreamMode(DP_SINGLE_HEAD_MULTI_STREAM_MODE_NONE),
|
||||
bIsCurrentModesetGroup(false),
|
||||
headAttached(false)
|
||||
{
|
||||
timeslot.count = 0;
|
||||
}
|
||||
|
||||
~GroupImpl()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void insert(Device * dev);
|
||||
virtual void remove(Device * dev);
|
||||
void update(Device * dev, bool allocationState); // send the allocatepayload/deallocatepayload message
|
||||
bool contains(Device * dev) { return members.contains(dev); }
|
||||
virtual Device * enumDevices(Device * previousDevice);
|
||||
|
||||
void updateVbiosScratchRegister(Device * lastDevice); // Update the VBIOS scratch register with last lit display
|
||||
|
||||
//
|
||||
// Timer callback tags.
|
||||
// (we pass the address of these variables as context to ::expired)
|
||||
//
|
||||
char tagHDCPReauthentication;
|
||||
char tagStreamValidation;
|
||||
|
||||
unsigned authRetries; // Retry counter for the authentication.
|
||||
|
||||
virtual void expired(const void * tag);
|
||||
virtual bool hdcpGetEncrypted();
|
||||
virtual void destroy();
|
||||
void cancelHdcpCallbacks();
|
||||
|
||||
bool isHeadAttached() { return headAttached; }
|
||||
void setHeadAttached(bool attached);
|
||||
|
||||
private:
|
||||
bool headAttached; // True if modeset started (during NAB). Sets back to False during NDE
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_GROUPIMPL_H
|
||||
120
src/common/displayport/inc/dp_guid.h
Normal file
120
src/common/displayport/inc/dp_guid.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_guid.h *
|
||||
* GUID struct and builder class *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_GUID_H
|
||||
#define INCLUDED_DP_GUID_H
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_timer.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
#define DPCD_GUID_SIZE 16
|
||||
|
||||
struct GUID
|
||||
{
|
||||
NvU8 data[DPCD_GUID_SIZE];
|
||||
|
||||
GUID()
|
||||
{
|
||||
dpMemZero(&data, sizeof(data));
|
||||
}
|
||||
|
||||
bool isGuidZero()
|
||||
{
|
||||
for (unsigned i = 0 ; i < DPCD_GUID_SIZE; i++)
|
||||
if (data[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator == (const GUID & other) const
|
||||
{
|
||||
for (unsigned i = 0 ; i < DPCD_GUID_SIZE; i++)
|
||||
if (data[i] != other.data[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator != (const GUID & other) const
|
||||
{
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
void copyFrom(const NvU8 * buffer)
|
||||
{
|
||||
dpMemCopy(&this->data[0], buffer, sizeof data);
|
||||
}
|
||||
|
||||
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
// Two Xs per byte, plus four dashes and a NUL byte.
|
||||
typedef char StringBuffer[DPCD_GUID_SIZE*2 + 5];
|
||||
char * toString(StringBuffer & buffer) const
|
||||
{
|
||||
char *p = &buffer[0];
|
||||
|
||||
for (unsigned i = 0; i < DPCD_GUID_SIZE; i++) {
|
||||
dpByteToHexChar(p, data[i]);
|
||||
p += 2;
|
||||
if (i == 3 || i == 5 || i == 7 || i == 9)
|
||||
*p++ = '-';
|
||||
}
|
||||
|
||||
*p++ = '\0';
|
||||
|
||||
DP_ASSERT(p == buffer + sizeof(buffer));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
};
|
||||
|
||||
class GUIDBuilder
|
||||
{
|
||||
NvU32 salt;
|
||||
NvU32 previousRandom;
|
||||
Timer * source;
|
||||
|
||||
|
||||
//
|
||||
// Linear congruential random number generator
|
||||
// Seed values chosen from numerical methods
|
||||
//
|
||||
NvU32 random();
|
||||
|
||||
public:
|
||||
GUIDBuilder(Timer * source, NvU32 salt);
|
||||
|
||||
void makeGuid(GUID & guid);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_GUID_H
|
||||
55
src/common/displayport/inc/dp_hostimp.h
Normal file
55
src/common/displayport/inc/dp_hostimp.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2015-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_hostimp.h *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_HOSTIMP_H
|
||||
#define INCLUDED_DP_HOSTIMP_H
|
||||
|
||||
#include "nvtypes.h"
|
||||
#include "dp_tracing.h"
|
||||
|
||||
extern "C" void * dpMalloc(NvLength size);
|
||||
extern "C" void dpFree(void * ptr);
|
||||
extern "C" void dpDebugBreakpoint();
|
||||
// Note: dpPrint() implementations are expected to append a newline themselves.
|
||||
extern "C" void dpPrint(const char * formatter, ...);
|
||||
extern "C" void dpTraceEvent(NV_DP_TRACING_EVENT event,
|
||||
NV_DP_TRACING_PRIORITY priority, NvU32 numArgs, ...);
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
#define NV_DP_ASSERT_ENABLED 1
|
||||
#else
|
||||
#define NV_DP_ASSERT_ENABLED 0
|
||||
#endif
|
||||
|
||||
#if NV_DP_ASSERT_ENABLED
|
||||
extern "C" void dpAssert(const char *expression, const char *file,
|
||||
const char *function, int line);
|
||||
#endif
|
||||
|
||||
#endif // INCLUDED_DP_HOSTIMP_H
|
||||
139
src/common/displayport/inc/dp_internal.h
Normal file
139
src/common/displayport/inc/dp_internal.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_internal.h *
|
||||
* RM stubs to allow unit testing. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_INTERNAL_H
|
||||
#define INCLUDED_DP_INTERNAL_H
|
||||
|
||||
//
|
||||
// Clients should not include this file
|
||||
// This file provides the private malloc implementation.
|
||||
//
|
||||
|
||||
#include <nvtypes.h>
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
#include "dp_object.h"
|
||||
#include "dp_ringbuffer.h"
|
||||
|
||||
static inline void dpByteToHexChar(char *output, NvU8 c)
|
||||
{
|
||||
char dig = (c>>4) & 0xF;
|
||||
output[0] = dig < 10 ? dig + '0' : dig + 'A' - 10;
|
||||
dig = c & 0xF;
|
||||
output[1] = dig < 10 ? dig + '0' : dig + 'A' - 10;
|
||||
}
|
||||
|
||||
static inline void dpHexDump(char * output, unsigned outSize, NvU8 * buffer, unsigned size)
|
||||
{
|
||||
char * tail = output;
|
||||
if (outSize < size * 3 + 1)
|
||||
return;
|
||||
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
{
|
||||
dpByteToHexChar(tail, buffer[i]);
|
||||
tail += 2;
|
||||
*tail++ = ' ';
|
||||
}
|
||||
*tail = 0;
|
||||
}
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
template <class T>
|
||||
inline void swap_args(T & left, T & right)
|
||||
{
|
||||
T temp = left;
|
||||
left = right;
|
||||
right = temp;
|
||||
}
|
||||
|
||||
inline NvU64 divide_ceil(NvU64 a, NvU64 b)
|
||||
{
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
inline NvU64 divide_floor(NvU64 a, NvU64 b)
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
|
||||
inline NvU64 axb_div_c_64(NvU64 a, NvU64 b, NvU64 c)
|
||||
{
|
||||
// NvU64 arithmetic to keep precision and avoid floats
|
||||
// a*b/c = (a/c)*b + ((a%c)*b + c/2)/c
|
||||
return ((a/c)*b + ((a%c)*b + c/2)/c);
|
||||
}
|
||||
}
|
||||
|
||||
#define DP_MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#define DP_MAX(x,y) ((x)<(y)?(y):(x))
|
||||
|
||||
//
|
||||
// Macro to suppress unused local variable
|
||||
//
|
||||
template <class T> void dp_used(const T & /*x*/) {}
|
||||
#define DP_USED(x) dp_used(x)
|
||||
|
||||
|
||||
//
|
||||
// Basic debug logging facility
|
||||
//
|
||||
|
||||
#if NV_DP_ASSERT_ENABLED
|
||||
#define DP_LOG(x) \
|
||||
do \
|
||||
{ \
|
||||
dpPrint x; \
|
||||
addDpLogRecord x; \
|
||||
}while (false)
|
||||
|
||||
#define DP_ASSERT(x) \
|
||||
if (!(x)) \
|
||||
{ \
|
||||
addDpAssertRecord(); \
|
||||
dpAssert(#x, __FILE__, __FUNCTION__, __LINE__); \
|
||||
dpDebugBreakpoint(); \
|
||||
}
|
||||
#else
|
||||
|
||||
#define DP_LOG(x)
|
||||
|
||||
#define DP_ASSERT(x) \
|
||||
{ \
|
||||
DP_USED(x); \
|
||||
if (!(x)) \
|
||||
{ \
|
||||
addDpAssertRecord(); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //INCLUDED_DP_INTERNAL_H
|
||||
449
src/common/displayport/inc/dp_linkconfig.h
Normal file
449
src/common/displayport/inc/dp_linkconfig.h
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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_linkconfig.h *
|
||||
* Link Configuration object implementation *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_LINKCONFIG_H
|
||||
#define INCLUDED_DP_LINKCONFIG_H
|
||||
|
||||
#include "dp_auxdefs.h"
|
||||
#include "dp_internal.h"
|
||||
#include "dp_watermark.h"
|
||||
#include "ctrl/ctrl0073/ctrl0073specific.h" // NV0073_CTRL_HDCP_VPRIME_SIZE
|
||||
#include "displayport.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
typedef NvU64 LinkRate;
|
||||
|
||||
class LinkRates : virtual public Object
|
||||
{
|
||||
public:
|
||||
// Store link rate in multipler of 270MBPS to save space
|
||||
NvU8 element[NV_DPCD_SUPPORTED_LINK_RATES__SIZE];
|
||||
NvU8 entries;
|
||||
|
||||
LinkRates() : entries(0) {}
|
||||
|
||||
void clear()
|
||||
{
|
||||
entries = 0;
|
||||
for (int i = 0; i < NV_DPCD_SUPPORTED_LINK_RATES__SIZE; i++)
|
||||
{
|
||||
element[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool import(NvU8 linkBw)
|
||||
{
|
||||
if (entries < NV_DPCD_SUPPORTED_LINK_RATES__SIZE)
|
||||
{
|
||||
element[entries] = linkBw;
|
||||
entries++;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
NvU8 getNumLinkRates()
|
||||
{
|
||||
return entries;
|
||||
}
|
||||
|
||||
LinkRate getLowerRate(LinkRate rate)
|
||||
{
|
||||
int i;
|
||||
NvU8 linkBw = (NvU8)(rate / DP_LINK_BW_FREQ_MULTI_MBPS);
|
||||
|
||||
if ((entries == 0) || (linkBw <= element[0]))
|
||||
return 0;
|
||||
|
||||
for (i = entries - 1; i > 0; i--)
|
||||
{
|
||||
if (linkBw > element[i])
|
||||
break;
|
||||
}
|
||||
|
||||
rate = (LinkRate)element[i] * DP_LINK_BW_FREQ_MULTI_MBPS;
|
||||
return rate;
|
||||
}
|
||||
|
||||
LinkRate getMaxRate()
|
||||
{
|
||||
LinkRate rate = 0;
|
||||
if ((entries > 0) &&
|
||||
(entries <= NV_DPCD_SUPPORTED_LINK_RATES__SIZE))
|
||||
{
|
||||
rate = (LinkRate)element[entries - 1] * DP_LINK_BW_FREQ_MULTI_MBPS;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
};
|
||||
|
||||
class LinkPolicy : virtual public Object
|
||||
{
|
||||
bool bNoFallback; // No fallback when LT fails
|
||||
LinkRates linkRates;
|
||||
|
||||
public:
|
||||
LinkPolicy() : bNoFallback(false)
|
||||
{
|
||||
}
|
||||
bool skipFallback()
|
||||
{
|
||||
return bNoFallback;
|
||||
}
|
||||
void setSkipFallBack(bool bSkipFallback)
|
||||
{
|
||||
bNoFallback = bSkipFallback;
|
||||
}
|
||||
|
||||
LinkRates *getLinkRates()
|
||||
{
|
||||
return &linkRates;
|
||||
}
|
||||
};
|
||||
enum
|
||||
{
|
||||
totalTimeslots = 64,
|
||||
totalUsableTimeslots = totalTimeslots - 1
|
||||
};
|
||||
|
||||
// in MBps
|
||||
enum
|
||||
{
|
||||
RBR = 162000000,
|
||||
EDP_2_16GHZ = 216000000,
|
||||
EDP_2_43GHZ = 243000000,
|
||||
HBR = 270000000,
|
||||
EDP_3_24GHZ = 324000000,
|
||||
EDP_4_32GHZ = 432000000,
|
||||
HBR2 = 540000000,
|
||||
HBR3 = 810000000
|
||||
};
|
||||
|
||||
struct HDCPState
|
||||
{
|
||||
bool HDCP_State_Encryption;
|
||||
bool HDCP_State_1X_Capable;
|
||||
bool HDCP_State_22_Capable;
|
||||
bool HDCP_State_Authenticated;
|
||||
bool HDCP_State_Repeater_Capable;
|
||||
};
|
||||
|
||||
struct HDCPValidateData
|
||||
{
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_MODE_NONE,
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_MODE_SST,
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_MODE_MST,
|
||||
}DP_SINGLE_HEAD_MULTI_STREAM_MODE;
|
||||
|
||||
#define HEAD_INVALID_STREAMS 0
|
||||
#define HEAD_DEFAULT_STREAMS 1
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY = 0,
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_SECONDARY = 1,
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_MAX = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_SECONDARY,
|
||||
} DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID;
|
||||
|
||||
#define DP_INVALID_SOR_INDEX 0xFFFFFFFF
|
||||
#define DSC_DEPTH_FACTOR 16
|
||||
|
||||
|
||||
class LinkConfiguration : virtual public Object
|
||||
{
|
||||
public:
|
||||
LinkPolicy policy;
|
||||
unsigned lanes;
|
||||
LinkRate peakRatePossible;
|
||||
LinkRate peakRate;
|
||||
LinkRate minRate;
|
||||
bool enhancedFraming;
|
||||
bool multistream;
|
||||
bool disablePostLTRequest;
|
||||
bool bEnableFEC;
|
||||
bool bDisableLTTPR;
|
||||
|
||||
//
|
||||
// The counter to record how many times link training happens.
|
||||
// Client can reset the counter by calling setLTCounter(0)
|
||||
//
|
||||
unsigned linkTrainCounter;
|
||||
|
||||
LinkConfiguration() :
|
||||
lanes(0), peakRatePossible(0), peakRate(0), minRate(0),
|
||||
enhancedFraming(false), multistream(false), disablePostLTRequest(false),
|
||||
bEnableFEC(false), bDisableLTTPR(false), linkTrainCounter(0) {};
|
||||
|
||||
LinkConfiguration(LinkPolicy * p, unsigned lanes, LinkRate peakRate,
|
||||
bool enhancedFraming, bool MST, bool disablePostLTRequest = false,
|
||||
bool bEnableFEC = false, bool bDisableLTTPR = false) :
|
||||
lanes(lanes), peakRatePossible(peakRate), peakRate(peakRate),
|
||||
enhancedFraming(enhancedFraming), multistream(MST),
|
||||
disablePostLTRequest(disablePostLTRequest),
|
||||
bEnableFEC(bEnableFEC), bDisableLTTPR(bDisableLTTPR),
|
||||
linkTrainCounter(0)
|
||||
{
|
||||
// downrate for spread and FEC
|
||||
minRate = linkOverhead(peakRate);
|
||||
if (p)
|
||||
{
|
||||
policy = *p;
|
||||
}
|
||||
}
|
||||
|
||||
void setLTCounter(unsigned counter)
|
||||
{
|
||||
linkTrainCounter = counter;
|
||||
}
|
||||
|
||||
unsigned getLTCounter()
|
||||
{
|
||||
return linkTrainCounter;
|
||||
}
|
||||
|
||||
NvU64 linkOverhead(NvU64 rate)
|
||||
{
|
||||
if(bEnableFEC)
|
||||
{
|
||||
|
||||
// if FEC is enabled, we have to account for 3% overhead
|
||||
// for FEC+downspread according to DP 1.4 spec
|
||||
|
||||
return rate - 3 * rate/ 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if FEC is not enabled, link overhead comprises only of
|
||||
// 0.05% downspread.
|
||||
return rate - 5 * rate/ 1000;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void enableFEC(bool setFEC)
|
||||
{
|
||||
bEnableFEC = setFEC;
|
||||
|
||||
// If FEC is enabled, update minRate with FEC+downspread overhead.
|
||||
minRate = linkOverhead(peakRate);
|
||||
}
|
||||
|
||||
LinkConfiguration(unsigned long TotalLinkPBN)
|
||||
: enhancedFraming(true),
|
||||
multistream(true),
|
||||
disablePostLTRequest(false),
|
||||
bEnableFEC(false),
|
||||
bDisableLTTPR(false),
|
||||
linkTrainCounter(0)
|
||||
{
|
||||
// Reverse engineer a link configuration from Total TotalLinkPBN
|
||||
// Note that HBR2 twice HBR. The table below treats HBR2x1 and HBRx2, etc.
|
||||
|
||||
//
|
||||
// BW Effective Lanes Total TotalLinkPBN
|
||||
// 165 1 195.5555556
|
||||
// 165 2 391.1111111
|
||||
// 165 4 782.2222222
|
||||
// 270 1 320
|
||||
// 270 2 640
|
||||
// 270 4 1280
|
||||
// 270 8 2560
|
||||
//
|
||||
|
||||
if (TotalLinkPBN <= 90)
|
||||
peakRatePossible = peakRate = RBR, minRate = linkOverhead(RBR), lanes=0; // FAIL
|
||||
if (TotalLinkPBN <= 195)
|
||||
peakRatePossible = peakRate = RBR, minRate = linkOverhead(RBR), lanes=1;
|
||||
else if (TotalLinkPBN <= 320)
|
||||
peakRatePossible = peakRate = HBR, minRate=linkOverhead(HBR), lanes = 1;
|
||||
else if (TotalLinkPBN <= 391)
|
||||
peakRatePossible = peakRate = RBR, minRate=linkOverhead(RBR), lanes = 2;
|
||||
else if (TotalLinkPBN <= 640)
|
||||
peakRatePossible = peakRate = HBR, minRate=linkOverhead(HBR), lanes = 2; // could be HBR2x1, but TotalLinkPBN works out same
|
||||
else if (TotalLinkPBN <= 782)
|
||||
peakRatePossible = peakRate = RBR, minRate=linkOverhead(RBR), lanes = 4;
|
||||
else if (TotalLinkPBN <= 960)
|
||||
peakRatePossible = peakRate = HBR3, minRate=linkOverhead(HBR3), lanes = 1;
|
||||
else if (TotalLinkPBN <= 1280)
|
||||
peakRatePossible = peakRate = HBR, minRate=linkOverhead(HBR), lanes = 4; // could be HBR2x2
|
||||
else if (TotalLinkPBN <= 1920)
|
||||
peakRatePossible = peakRate = HBR3, minRate=linkOverhead(HBR3), lanes = 2; // could be HBR2x
|
||||
else if (TotalLinkPBN <= 2560)
|
||||
peakRatePossible = peakRate = HBR2, minRate=linkOverhead(HBR2), lanes = 4;
|
||||
else if (TotalLinkPBN <= 3840)
|
||||
peakRatePossible = peakRate = HBR3, minRate=linkOverhead(HBR3), lanes = 4;
|
||||
else {
|
||||
peakRatePossible = peakRate = RBR, minRate = linkOverhead(RBR), lanes = 0; // FAIL
|
||||
DP_ASSERT(0 && "Unknown configuration");
|
||||
}
|
||||
}
|
||||
|
||||
void setEnhancedFraming(bool newEnhancedFraming)
|
||||
{
|
||||
enhancedFraming = newEnhancedFraming;
|
||||
}
|
||||
|
||||
bool isValid()
|
||||
{
|
||||
return lanes != laneCount_0;
|
||||
}
|
||||
|
||||
bool lowerConfig(bool bReduceLaneCnt = false)
|
||||
{
|
||||
//
|
||||
// TODO: bReduceLaneCnt is set to fallback to 4 lanes with lower
|
||||
// valid link rate. But we should reset to max lane count
|
||||
// sink supports instead.
|
||||
//
|
||||
|
||||
LinkRate lowerRate = policy.getLinkRates()->getLowerRate(peakRate);
|
||||
|
||||
if(bReduceLaneCnt)
|
||||
{
|
||||
// Reduce laneCount before reducing linkRate
|
||||
if(lanes == laneCount_1)
|
||||
{
|
||||
if (lowerRate)
|
||||
{
|
||||
lanes = laneCount_4;
|
||||
peakRate = lowerRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
lanes = laneCount_0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lanes /= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reduce the link rate instead of lane count
|
||||
if (lowerRate)
|
||||
{
|
||||
peakRate = lowerRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
lanes /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
minRate = linkOverhead(peakRate);
|
||||
|
||||
return lanes != laneCount_0;
|
||||
}
|
||||
|
||||
void setLaneRate(LinkRate newRate, unsigned newLanes)
|
||||
{
|
||||
peakRate = newRate;
|
||||
lanes = newLanes;
|
||||
minRate = linkOverhead(peakRate);
|
||||
}
|
||||
|
||||
unsigned pbnTotal()
|
||||
{
|
||||
return PBNForSlots(totalUsableTimeslots);
|
||||
}
|
||||
|
||||
void pbnRequired(const ModesetInfo & modesetInfo, unsigned & base_pbn, unsigned & slots, unsigned & slots_pbn)
|
||||
{
|
||||
base_pbn = pbnForMode(modesetInfo);
|
||||
slots = slotsForPBN(base_pbn);
|
||||
slots_pbn = PBNForSlots(slots);
|
||||
}
|
||||
|
||||
NvU32 slotsForPBN(NvU32 allocatedPBN, bool usable = false)
|
||||
{
|
||||
NvU64 bytes_per_pbn = 54 * 1000000 / 64; // this comes out exact
|
||||
NvU64 bytes_per_timeslot = peakRate * lanes / 64;
|
||||
|
||||
if (bytes_per_timeslot == 0)
|
||||
return (NvU32)-1;
|
||||
|
||||
if (usable)
|
||||
{
|
||||
// round down to find the usable integral slots for a given value of PBN.
|
||||
NvU32 slots = (NvU32)divide_floor(allocatedPBN * bytes_per_pbn, bytes_per_timeslot);
|
||||
DP_ASSERT(slots <= 64);
|
||||
|
||||
return slots;
|
||||
}
|
||||
else
|
||||
return (NvU32)divide_ceil(allocatedPBN * bytes_per_pbn, bytes_per_timeslot);
|
||||
}
|
||||
|
||||
NvU32 PBNForSlots(NvU32 slots) // Rounded down
|
||||
{
|
||||
NvU64 bytes_per_pbn = 54 * 1000000 / 64; // this comes out exact
|
||||
NvU64 bytes_per_timeslot = peakRate * lanes / 64;
|
||||
|
||||
return (NvU32)(bytes_per_timeslot * slots/ bytes_per_pbn);
|
||||
}
|
||||
|
||||
bool operator!= (const LinkConfiguration & right) const
|
||||
{
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
bool operator== (const LinkConfiguration & right) const
|
||||
{
|
||||
return (this->lanes == right.lanes &&
|
||||
this->peakRate == right.peakRate &&
|
||||
this->enhancedFraming == right.enhancedFraming &&
|
||||
this->multistream == right.multistream &&
|
||||
this->bEnableFEC == right.bEnableFEC);
|
||||
}
|
||||
|
||||
bool operator< (const LinkConfiguration & right) const
|
||||
{
|
||||
NvU64 leftMKBps = peakRate * lanes;
|
||||
NvU64 rightMKBps = right.peakRate * right.lanes;
|
||||
|
||||
if (leftMKBps == rightMKBps)
|
||||
{
|
||||
return (lanes < right.lanes);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (leftMKBps < rightMKBps);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif //INCLUDED_DP_LINKCONFIG_H
|
||||
143
src/common/displayport/inc/dp_linkedlist.h
Normal file
143
src/common/displayport/inc/dp_linkedlist.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2015-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.
|
||||
*/
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_linkedlist.h *
|
||||
* A linked list that uses DislayPort::List as a backend, but which *
|
||||
* allocates the list backbone dynamically. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_LINKEDLIST_H
|
||||
#define INCLUDED_DP_LINKEDLIST_H
|
||||
|
||||
#include "dp_list.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
template<typename T>
|
||||
class LinkedList : public Object
|
||||
{
|
||||
// The Element class forms the list backbone and contains pointers to
|
||||
// each item in the list.
|
||||
class Element : public ListElement
|
||||
{
|
||||
public:
|
||||
Element(T *item) : item(item) { }
|
||||
T *item;
|
||||
};
|
||||
|
||||
List list;
|
||||
|
||||
// No public copy constructor.
|
||||
LinkedList(LinkedList &other) { }
|
||||
|
||||
// Find the Element containing an item.
|
||||
Element *containing(T *item)
|
||||
{
|
||||
for (ListElement *le = list.begin(); le != list.end(); le = le->next)
|
||||
{
|
||||
Element *e = static_cast<Element *>(le);
|
||||
if (e->item == item)
|
||||
return e;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
// The list starts out empty.
|
||||
LinkedList() { }
|
||||
|
||||
// Insert an item at the front of the list.
|
||||
void insertFront(T *item)
|
||||
{
|
||||
// Construct an element and add it to the list.
|
||||
Element *e = new Element(item);
|
||||
DP_ASSERT(e);
|
||||
if (e)
|
||||
{
|
||||
list.insertFront(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove an item from the list.
|
||||
// O(n) to find the item to remove.
|
||||
// It is an error to try to remove an item that is not in the list.
|
||||
void remove(T *item)
|
||||
{
|
||||
Element *e = containing(item);
|
||||
DP_ASSERT(e && "Item was not a member of the list");
|
||||
delete e;
|
||||
}
|
||||
|
||||
// Find the next item in the list after the specified item. If item is
|
||||
// NULL, this returns the first item.
|
||||
T *next(T *prev)
|
||||
{
|
||||
if (list.isEmpty())
|
||||
return NULL;
|
||||
|
||||
// If prev is NULL or not in the list, return the first item.
|
||||
Element *e = containing(prev);
|
||||
if (!e)
|
||||
{
|
||||
e = static_cast<Element *>(list.begin());
|
||||
return e->item;
|
||||
}
|
||||
else if (e->next != list.end())
|
||||
{
|
||||
e = static_cast<Element *>(e->next);
|
||||
return e->item;
|
||||
}
|
||||
else
|
||||
{
|
||||
// prev was the last element in the list.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Query whether an item is a member of the list.
|
||||
// O(n)
|
||||
bool contains(T *item)
|
||||
{
|
||||
Element *e = containing(item);
|
||||
return e != NULL;
|
||||
}
|
||||
|
||||
bool isEmpty()
|
||||
{
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
T *pop()
|
||||
{
|
||||
DP_ASSERT(!list.isEmpty());
|
||||
Element *e = static_cast<Element *>(list.last());
|
||||
T *item = e->item;
|
||||
delete e;
|
||||
return item;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_DP_LINKEDLIST_H
|
||||
84
src/common/displayport/inc/dp_list.h
Normal file
84
src/common/displayport/inc/dp_list.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_list.h *
|
||||
* Simple doubly linked list queue *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_LIST_H
|
||||
#define INCLUDED_DP_LIST_H
|
||||
|
||||
#include "dp_object.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
//
|
||||
// List is an intrusive container, it may
|
||||
// only contain elements that derive from ListElement
|
||||
//
|
||||
// NOTE! Deleting an element automatically unlinks it
|
||||
// from the enclosing container.
|
||||
//
|
||||
struct ListElement : virtual public Object
|
||||
{
|
||||
ListElement * next, * prev;
|
||||
|
||||
ListElement();
|
||||
virtual ~ListElement();
|
||||
};
|
||||
|
||||
|
||||
class List : public ListElement
|
||||
{
|
||||
public:
|
||||
bool isEmpty();
|
||||
void insertFront(ListElement * item);
|
||||
void insertBack(ListElement * item);
|
||||
void insertBefore(ListElement * insertBeforeThis, ListElement * item);
|
||||
void clear();
|
||||
ListElement* front();
|
||||
ListElement* last();
|
||||
|
||||
ListElement* begin() { return this->next; }
|
||||
ListElement* end() { return this; }
|
||||
|
||||
static ListElement * remove(ListElement * item); // Removes but does not delete
|
||||
bool contains(ListElement * item);
|
||||
ListElement * replace(ListElement * replacement, ListElement * replacee);
|
||||
List();
|
||||
~List();
|
||||
|
||||
unsigned size()
|
||||
{
|
||||
unsigned count = 0;
|
||||
for (ListElement * i = begin(); i!=end(); i = i->next)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_LIST_H
|
||||
265
src/common/displayport/inc/dp_mainlink.h
Normal file
265
src/common/displayport/inc/dp_mainlink.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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_mainlink.h *
|
||||
* Mainlink interface implemented by client. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_MAINLINK_H
|
||||
#define INCLUDED_DP_MAINLINK_H
|
||||
|
||||
#include "dp_linkconfig.h"
|
||||
#include "dp_vrr.h"
|
||||
#include "dp_wardatabase.h"
|
||||
#include "dp_auxdefs.h"
|
||||
#include "displayport.h"
|
||||
#include "ctrl/ctrl0073/ctrl0073dp.h"
|
||||
#include "dp_regkeydatabase.h"
|
||||
|
||||
#define HDCP_DUMMY_CN (0x1)
|
||||
#define HDCP_DUMMY_CKSV (0xFFFFF)
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
typedef enum
|
||||
{
|
||||
NONE, //Abort it manually
|
||||
UNTRUST, //Abort due to Kp mismatch
|
||||
UNRELBL, //Abort due to repeated link failure
|
||||
KSV_LEN, //Abort due to KSV length
|
||||
KSV_SIG, //Abort due to KSV signature
|
||||
SRM_SIG, //Abort due to SRM signature
|
||||
SRM_REV, //Abort due to SRM revocation
|
||||
NORDY, //Abort due to repeater not ready
|
||||
KSVTOP, //Abort due to KSV topology error
|
||||
BADBKSV //Abort due to invalid Bksv
|
||||
}AbortAuthReason;
|
||||
|
||||
// This is also used for DPCD offset 10B. 249
|
||||
enum LinkQualityPatternType
|
||||
{
|
||||
LINK_QUAL_DISABLED,
|
||||
LINK_QUAL_D10_2,
|
||||
LINK_QUAL_SYM_ERROR,
|
||||
LINK_QUAL_PRBS7,
|
||||
LINK_QUAL_80BIT_CUST,
|
||||
LINK_QUAL_HBR2_COMPLIANCE_EYE,
|
||||
LINK_QUAL_CP2520PAT3,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LinkQualityPatternType lqsPattern;
|
||||
|
||||
//
|
||||
// 80 bits DP CSTM Test Pattern data;
|
||||
// ctsmLower takes bits 31:0 (lowest 32 bits)
|
||||
// ctsmMiddle takes bits 63:32 (middle 32 bits)
|
||||
// ctsmUpper takes bits 79:64 (highest 16 bits)
|
||||
//
|
||||
int ctsmLower;
|
||||
int ctsmMiddle;
|
||||
int ctsmUpper;
|
||||
} PatternInfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char bcaps;
|
||||
unsigned char bksv[5];
|
||||
bool hdcpCapable;
|
||||
unsigned char updMask;
|
||||
}RmDfpCache;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NORMAL_LINK_TRAINING, // full LT
|
||||
NO_LINK_TRAINING,
|
||||
FAST_LINK_TRAINING,
|
||||
}LinkTrainingType;
|
||||
|
||||
class MainLink : virtual public Object
|
||||
{
|
||||
private:
|
||||
virtual void initializeRegkeyDatabase() = 0;
|
||||
virtual void applyRegkeyOverrides() = 0;
|
||||
|
||||
public:
|
||||
virtual bool physicalLayerSetTestPattern(PatternInfo * patternInfo) = 0;
|
||||
|
||||
//
|
||||
// Wrappers for existing link training RM control calls
|
||||
//
|
||||
virtual bool train(const LinkConfiguration & link, bool force, LinkTrainingType linkTrainingType,
|
||||
LinkConfiguration *retLink, bool bSkipLt = false, bool isPostLtAdjRequestGranted = false,
|
||||
unsigned phyRepeaterCount = 0) = 0;
|
||||
|
||||
// RM control call to retrieve buffer from RM for DP Library to dump logs
|
||||
virtual bool retrieveRingBuffer(NvU8 dpRingBuffertype, NvU32 numRecords) = 0;
|
||||
|
||||
//
|
||||
// Requests to DD to perform pre & post link training steps
|
||||
// which may disconnect and later reconnect the head (For Pre-gf119 GPUs)
|
||||
//
|
||||
virtual void preLinkTraining(NvU32 head) = 0;
|
||||
virtual void postLinkTraining(NvU32 head) = 0;
|
||||
virtual NvU32 getRegkeyValue(const char *key) = 0;
|
||||
virtual const DP_REGKEY_DATABASE& getRegkeyDatabase() = 0;
|
||||
virtual NvU32 getSorIndex() = 0;
|
||||
virtual bool isInbandStereoSignalingSupported() = 0;
|
||||
|
||||
|
||||
virtual bool isEDP() = 0;
|
||||
virtual bool supportMSAOverMST() = 0;
|
||||
virtual bool isForceRmEdidRequired() = 0;
|
||||
virtual bool fetchEdidByRmCtrl(NvU8* edidBuffer, NvU32 bufferSize) = 0;
|
||||
virtual bool applyEdidOverrideByRmCtrl(NvU8* edidBuffer, NvU32 bufferSize) = 0;
|
||||
|
||||
// Return if Panel is Dynamic MUX capable
|
||||
virtual bool isDynamicMuxCapable() = 0;
|
||||
|
||||
// Return the current mux state. Returns false if not mux capable
|
||||
virtual bool getDynamicMuxState(NvU32 *muxState) = 0;
|
||||
|
||||
// Return if Internal panel is Dynamic Mux capable
|
||||
virtual bool isInternalPanelDynamicMuxCapable() = 0;
|
||||
|
||||
// Check if we should skip power down eDP when head detached.
|
||||
virtual bool skipPowerdownEdpPanelWhenHeadDetach() = 0;
|
||||
|
||||
// Get GPU DSC capabilities
|
||||
virtual void getDscCaps(bool *pbDscSupported = NULL,
|
||||
unsigned *pEncoderColorFormatMask = NULL,
|
||||
unsigned *pLineBufferSizeKB = NULL,
|
||||
unsigned *pRateBufferSizeKB = NULL,
|
||||
unsigned *pBitsPerPixelPrecision = NULL,
|
||||
unsigned *pMaxNumHztSlices = NULL,
|
||||
unsigned *pLineBufferBitDepth = NULL) = 0;
|
||||
|
||||
//
|
||||
// Get the current link config.
|
||||
// (Used for the boot case where EFI/VBIOS may have already trained
|
||||
// the link. We need this to confirm the programming since
|
||||
// we cannot rely on the DPCD registers being correct or sane)
|
||||
//
|
||||
virtual void getLinkConfig(unsigned &laneCount, NvU64 & linkRate) = 0;
|
||||
|
||||
// Get the max link config from UEFI.
|
||||
virtual bool getMaxLinkConfigFromUefi(NvU8 &linkRate, NvU8 &laneCount) = 0;
|
||||
//
|
||||
// Query if a head is attached to this DisplayId
|
||||
//
|
||||
virtual bool isActive() = 0;
|
||||
|
||||
virtual bool hasIncreasedWatermarkLimits() = 0;
|
||||
virtual bool hasMultistream() = 0;
|
||||
virtual bool isPC2Disabled() = 0;
|
||||
virtual bool isDP1_2Supported() = 0;
|
||||
virtual bool isDP1_4Supported() = 0;
|
||||
virtual bool isStreamCloningEnabled() = 0;
|
||||
virtual NvU32 maxLinkRateSupported() = 0;
|
||||
virtual bool isLttprSupported() = 0;
|
||||
virtual bool isFECSupported() = 0;
|
||||
|
||||
virtual bool setDpMSAParameters(bool bStereoEnable, const NV0073_CTRL_CMD_DP_SET_MSA_PROPERTIES_PARAMS &msaparams) = 0;
|
||||
virtual bool setDpStereoMSAParameters(bool bStereoEnable, const NV0073_CTRL_CMD_DP_SET_MSA_PROPERTIES_PARAMS &msaparams) = 0;
|
||||
virtual bool setFlushMode() = 0;
|
||||
virtual void clearFlushMode(unsigned headMask, bool testMode=false) = 0;
|
||||
|
||||
//
|
||||
// HDCP Renegotiate and trigger ACT.
|
||||
//
|
||||
virtual void configureHDCPRenegotiate(NvU64 cN = HDCP_DUMMY_CN, NvU64 cKsv = HDCP_DUMMY_CKSV, bool bForceReAuth = false, bool bRxIDMsgPending = false) = 0;
|
||||
virtual void triggerACT() = 0;
|
||||
virtual void configureHDCPGetHDCPState(HDCPState &hdcpState) = 0;
|
||||
|
||||
virtual NvU32 streamToHead(NvU32 streamId,
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID streamIdentifier = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY) = 0;
|
||||
virtual NvU32 headToStream(NvU32 head,
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID streamIdentifier = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY) = 0;
|
||||
|
||||
virtual 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)= 0;
|
||||
|
||||
virtual 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)= 0;
|
||||
|
||||
virtual void configureSingleHeadMultiStreamMode(NvU32 displayIDs[],
|
||||
NvU32 numStreams,
|
||||
NvU32 mode,
|
||||
bool bSetConfig,
|
||||
NvU8 vbiosPrimaryDispIdIndex = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY)= 0;
|
||||
|
||||
virtual void configureMsScratchRegisters(NvU32 address,
|
||||
NvU32 hopCount,
|
||||
NvU32 driverState) = 0;
|
||||
|
||||
virtual bool controlRateGoverning(NvU32 head, bool enable, bool updateNow = true) = 0;
|
||||
virtual bool getDpTestPattern(NV0073_CTRL_DP_TESTPATTERN * testPattern) = 0;
|
||||
virtual bool setDpTestPattern(NV0073_CTRL_DP_TESTPATTERN testPattern,
|
||||
NvU8 laneMask, NV0073_CTRL_DP_CSTM cstm,
|
||||
NvBool bIsHBR2, NvBool bSkipLaneDataOverride = false) = 0;
|
||||
virtual bool getDpLaneData(NvU32 *numLanes, NvU32 *data) = 0;
|
||||
virtual bool setDpLaneData(NvU32 numLanes, NvU32 *data) = 0;
|
||||
virtual bool rmUpdateDynamicDfpCache(NvU32 headIndex, RmDfpCache * dfpCache, NvBool bResetDfp) = 0;
|
||||
virtual void configurePowerState(bool bPowerUp) = 0;
|
||||
virtual NvU32 monitorDenylistInfo(NvU32 ManufacturerID, NvU32 ProductID, DpMonitorDenylistData *pDenylistData) = 0;
|
||||
virtual NvU32 getRootDisplayId() = 0;
|
||||
virtual NvU32 allocDisplayId() = 0;
|
||||
virtual bool freeDisplayId(NvU32 displayId) = 0;
|
||||
virtual void queryGPUCapability() = 0;
|
||||
virtual bool queryAndUpdateDfpParams() = 0;
|
||||
virtual bool getEdpPowerData(bool *panelPowerOn, bool *bDPCDPowerStateD0) = 0;
|
||||
virtual bool vrrRunEnablementStage(unsigned stage, NvU32 *status) = 0;
|
||||
|
||||
virtual void configureTriggerSelect(NvU32 head,
|
||||
DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID streamIdentifier = DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY) = 0;
|
||||
|
||||
virtual void configureTriggerAll(NvU32 head, bool enable) = 0;
|
||||
virtual bool dscCrcTransaction(NvBool bEnable, gpuDscCrc *data, NvU16 *headIndex){ return false; }
|
||||
virtual bool configureLinkRateTable(const NvU16 *pLinkRateTable, LinkRates *pLinkRates) = 0;
|
||||
virtual bool configureFec(const bool bEnableFec) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_MAINLINK_H
|
||||
148
src/common/displayport/inc/dp_merger.h
Normal file
148
src/common/displayport/inc/dp_merger.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_merger.h *
|
||||
* Asynchronous Message merger *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_MERGER_H
|
||||
#define INCLUDED_DP_MERGER_H
|
||||
|
||||
#include "dp_list.h"
|
||||
#include "dp_auxretry.h"
|
||||
#include "dp_timer.h"
|
||||
#include "dp_bitstream.h"
|
||||
#include "dp_address.h"
|
||||
#include "dp_messageheader.h"
|
||||
#include "dp_configcaps.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
// after 4 secs delete dead transactions
|
||||
#define DP_INCOMPLETE_MESSAGE_TIMEOUT_USEC 4000000
|
||||
struct EncodedMessage;
|
||||
|
||||
class MessageTransactionMerger : virtual public Object
|
||||
{
|
||||
class IncompleteMessage : public ListElement
|
||||
{
|
||||
public:
|
||||
EncodedMessage message;
|
||||
NvU64 lastUpdated;
|
||||
|
||||
};
|
||||
|
||||
List incompleteMessages;
|
||||
Timer * timer;
|
||||
NvU64 incompleteMessageTimeoutMs;
|
||||
IncompleteMessage * freeOnNextCall; // we don't need to delete it on destruct
|
||||
// since this is ALSO a member of the list we own
|
||||
|
||||
IncompleteMessage * getTransactionRecord(const Address & address, unsigned messageNumber);
|
||||
public:
|
||||
MessageTransactionMerger(Timer * timer, unsigned incompleteMessageTimeoutMs)
|
||||
: timer(timer), incompleteMessageTimeoutMs(incompleteMessageTimeoutMs), freeOnNextCall(0)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// Pushes data into the queue and returns an encoded
|
||||
// message if an entire message is assembled.
|
||||
//
|
||||
EncodedMessage * pushTransaction(MessageHeader * header, Buffer * data);
|
||||
};
|
||||
|
||||
class IncomingTransactionManager : virtual public Object
|
||||
{
|
||||
public:
|
||||
class IncomingTransactionManagerEventSink
|
||||
{
|
||||
public:
|
||||
virtual void messagedReceived(IncomingTransactionManager * from, EncodedMessage * message) = 0;
|
||||
};
|
||||
|
||||
void mailboxInterrupt();
|
||||
|
||||
//
|
||||
// Create a message merger object
|
||||
// - sink is called whenever a new message is received
|
||||
// Callback::fired is passed an IncompleteMessage as the data arg.
|
||||
//
|
||||
IncomingTransactionManager(Timer * timerInterface, const Address & addressPrefix, IncomingTransactionManagerEventSink * sink);
|
||||
virtual ~IncomingTransactionManager();
|
||||
|
||||
protected:
|
||||
virtual AuxRetry::status readMessageBox(NvU32 offset, NvU8 * data, size_t length) = 0;
|
||||
virtual size_t getMessageBoxSize() = 0;
|
||||
virtual size_t getTransactionSize() = 0;
|
||||
virtual void clearMessageBoxInterrupt() = 0;
|
||||
private:
|
||||
MessageTransactionMerger incompleteMessages; // List<IncompleteMessage>
|
||||
|
||||
Buffer localWindow;
|
||||
Timer * timer;
|
||||
IncomingTransactionManagerEventSink * sink;
|
||||
Address addressPrefix; // This is the aux address of the downstream port
|
||||
// This field will be prepended to the address decoded.
|
||||
};
|
||||
|
||||
class DownReplyManager : public IncomingTransactionManager
|
||||
{
|
||||
public:
|
||||
DownReplyManager(DPCDHAL * hal, Timer * timer, const Address & addressPrefix, IncomingTransactionManagerEventSink * sink)
|
||||
: IncomingTransactionManager(timer, addressPrefix, sink), hal(hal)
|
||||
{
|
||||
}
|
||||
virtual ~DownReplyManager() {}
|
||||
|
||||
protected:
|
||||
DPCDHAL * hal;
|
||||
|
||||
virtual AuxRetry::status readMessageBox(NvU32 offset, NvU8 * data, size_t length);
|
||||
virtual size_t getMessageBoxSize();
|
||||
virtual size_t getTransactionSize();
|
||||
virtual void clearMessageBoxInterrupt();
|
||||
};
|
||||
|
||||
class UpRequestManager : public IncomingTransactionManager
|
||||
{
|
||||
public:
|
||||
UpRequestManager(DPCDHAL * hal, Timer * timer, const Address & addressPrefix, IncomingTransactionManagerEventSink * sink)
|
||||
: IncomingTransactionManager(timer, addressPrefix, sink), hal(hal)
|
||||
{
|
||||
}
|
||||
virtual ~UpRequestManager() {}
|
||||
protected:
|
||||
DPCDHAL * hal;
|
||||
|
||||
virtual AuxRetry::status readMessageBox(NvU32 offset, NvU8 * data, size_t length);
|
||||
virtual size_t getMessageBoxSize();
|
||||
virtual size_t getTransactionSize();
|
||||
virtual void clearMessageBoxInterrupt();
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_MERGER_H
|
||||
559
src/common/displayport/inc/dp_messagecodings.h
Normal file
559
src/common/displayport/inc/dp_messagecodings.h
Normal file
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_messagecodings.h *
|
||||
* Encoding routines for various messages. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_MESSAGECODINGS_H
|
||||
#define INCLUDED_DP_MESSAGECODINGS_H
|
||||
|
||||
#include "dp_messages.h"
|
||||
#include "displayport.h"
|
||||
#include "dp_auxdefs.h"
|
||||
|
||||
/* Fields for the HDCP stream status */
|
||||
#define NV_DP_HDCP_STREAM_STATE 1:0
|
||||
#define NV_DP_HDCP_STREAM_STATE_NO_EXIST (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_STATE_NOT_ACTIVE (0x00000001)
|
||||
#define NV_DP_HDCP_STREAM_STATE_ACTIVE (0x00000002)
|
||||
#define NV_DP_HDCP_STREAM_STATE_ERROR (0x00000003)
|
||||
#define NV_DP_HDCP_STREAM_REPEATER 2:2
|
||||
#define NV_DP_HDCP_STREAM_REPEATER_SIMPLE (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_REPEATER_REPEATER (0x00000001)
|
||||
#define NV_DP_HDCP_STREAM_ENCRYPTION 3:3
|
||||
#define NV_DP_HDCP_STREAM_ENCRYPTION_OFF (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_ENCRYPTION_ON (0x00000001)
|
||||
#define NV_DP_HDCP_STREAM_AUTHENTICATION 4:4
|
||||
#define NV_DP_HDCP_STREAM_AUTHENTICATION_OFF (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_AUTHENTICATION_IP (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_AUTHENTICATION_ON (0x00000001)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_SINK_LEGACY 8:8
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_SINK_LEGACY_NO (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_SINK_LEGACY_YES (0x00000001)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_SINK_NON_DP1_2_CP 9:9
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_SINK_NON_DP1_2_CP_NO (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_SINK_NON_DP1_2_CP_YES (0x00000001)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_SINK_MULTI 10:10
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_SINK_MULTI_NO (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_SINK_MULTI_YES (0x00000001)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_CP_TYPE_HDCP1X 11:11
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_CP_TYPE_HDCP1X_NO (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_CP_TYPE_HDCP1X_YES (0x00000001)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_CP_TYPE_HDCP2X 12:12
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_CP_TYPE_HDCP2X_NO (0x00000000)
|
||||
#define NV_DP_HDCP_STREAM_OUTPUT_CP_TYPE_HDCP2X_YES (0x00000001)
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
typedef NakData Message_NakData;
|
||||
|
||||
enum
|
||||
{
|
||||
REMOTE_READ_BUFFER_SIZE = 128,
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
None,
|
||||
UpstreamSourceOrSSTBranch,
|
||||
DownstreamBranch,
|
||||
DownstreamSink,
|
||||
Dongle
|
||||
}PeerDevice;
|
||||
|
||||
struct I2cWriteTransaction
|
||||
{
|
||||
I2cWriteTransaction(unsigned WriteI2cDeviceId, unsigned NumBytes,
|
||||
unsigned char * buffer, bool NoStopBit = false,
|
||||
unsigned I2cTransactionDelay = 0);
|
||||
I2cWriteTransaction();
|
||||
unsigned WriteI2cDeviceId;
|
||||
unsigned NumBytes;
|
||||
unsigned char *I2cData;
|
||||
bool NoStopBit;
|
||||
unsigned I2cTransactionDelay;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DoesNotExist = 0,
|
||||
NotActive = 1,
|
||||
Active = 2,
|
||||
}StreamState;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CP_IRQ_ON = 0,
|
||||
No_EVENT = 1
|
||||
}StreamEvent;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STREAM_BEHAVIOUR_MASK_OFF = 0,
|
||||
STREAM_BEHAVIOUR_MASK_ON = 1
|
||||
}StreamBehaviorMask;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STREAM_EVENT_MASK_OFF = 0,
|
||||
STREAM_EVENT_MASK_ON = 1
|
||||
}StreamEventMask;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
Force_Reauth = 0,
|
||||
BlockFlow = 1
|
||||
}StreamBehavior;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
StreamUnconnected = 0,
|
||||
NonAuthLegacyDevice = 1, // TV or CRT
|
||||
DP_MST = 4
|
||||
}OutputSinkType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HDCP1x = 1,
|
||||
HDCP2x = 2
|
||||
}OutputCPType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SinkEvent0,
|
||||
SinkEvent255 = 0xFF
|
||||
}SinkEvent;
|
||||
|
||||
//
|
||||
// LINK_ADDRESS 0x1
|
||||
//
|
||||
class LinkAddressMessage : public MessageManager::Message
|
||||
{
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
bool isInputPort;
|
||||
PeerDevice peerDeviceType;
|
||||
unsigned portNumber;
|
||||
bool hasMessaging;
|
||||
bool dpPlugged;
|
||||
|
||||
bool legacyPlugged;
|
||||
unsigned dpcdRevisionMajor;
|
||||
unsigned dpcdRevisionMinor;
|
||||
GUID peerGUID;
|
||||
unsigned SDPStreams;
|
||||
unsigned SDPStreamSinks;
|
||||
};
|
||||
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
|
||||
private:
|
||||
struct
|
||||
{
|
||||
GUID guid; // originating branch device
|
||||
unsigned numberOfPorts;
|
||||
Result res[16];
|
||||
} reply;
|
||||
|
||||
public:
|
||||
LinkAddressMessage() : Message(NV_DP_SBMSG_REQUEST_ID_LINK_ADDRESS,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_2)
|
||||
{
|
||||
dpMemZero(&reply, sizeof(reply));
|
||||
}
|
||||
|
||||
// Second stage init kept separate from constructor (reusable message)
|
||||
void set(const Address & target);
|
||||
|
||||
void getGUID(GUID & guid){guid = reply.guid;}
|
||||
|
||||
// Number of ports described
|
||||
unsigned resultCount(){return reply.numberOfPorts;}
|
||||
const Result * result(unsigned index)
|
||||
{
|
||||
return &reply.res[index];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// CONNECTION_STATUS_NOTIFY 0x2
|
||||
//
|
||||
class ConnStatusNotifyMessage : public MessageManager::MessageReceiver
|
||||
{
|
||||
public:
|
||||
typedef struct
|
||||
{
|
||||
GUID guid;
|
||||
unsigned port;
|
||||
bool legacyPlugged;
|
||||
bool devicePlugged;
|
||||
bool messagingCapability;
|
||||
bool isInputPort;
|
||||
PeerDevice peerDeviceType;
|
||||
}Request;
|
||||
|
||||
protected:
|
||||
Request request;
|
||||
|
||||
public:
|
||||
Request * getUpRequestData(){ return &request; }
|
||||
virtual bool processByType(EncodedMessage * message, BitStreamReader * reader);
|
||||
ConnStatusNotifyMessage(MessageReceiverEventSink * sink);
|
||||
};
|
||||
|
||||
//
|
||||
// GENERIC_UP_REPLY 0xnn
|
||||
//
|
||||
class GenericUpReplyMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
|
||||
virtual void expired(const void * tag)
|
||||
{ }
|
||||
|
||||
public:
|
||||
GenericUpReplyMessage(const Address & target, unsigned requestId,
|
||||
bool bReplyIsNack = false, bool bBroadcast = true,
|
||||
bool bPath = false);
|
||||
GenericUpReplyMessage(unsigned requestId, bool bReplyIsNack,
|
||||
bool bBroadcast, bool bPath);
|
||||
void set(const Address & target, bool bReplyIsNack = false,
|
||||
bool bBroadcast = true, bool bPath = false);
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// CLEAR_PAYLOAD_ID_TABLE 0x14
|
||||
//
|
||||
class ClearPayloadIdTableMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
virtual ParseResponseStatus parseResponse(EncodedMessage * message);
|
||||
public:
|
||||
ClearPayloadIdTableMessage();
|
||||
};
|
||||
|
||||
//
|
||||
// ENUM_PATH_RESOURCES 0x10
|
||||
//
|
||||
class EnumPathResMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
|
||||
public:
|
||||
struct
|
||||
{
|
||||
unsigned portNumber;
|
||||
bool bFECCapability;
|
||||
unsigned TotalPBN;
|
||||
unsigned FreePBN;
|
||||
} reply;
|
||||
EnumPathResMessage(const Address & target, unsigned port, bool point);
|
||||
};
|
||||
|
||||
//
|
||||
// ALLOCATE_PAYLOAD 0x11
|
||||
//
|
||||
class AllocatePayloadMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
|
||||
private:
|
||||
struct
|
||||
{
|
||||
unsigned portNumber;
|
||||
unsigned PBN;
|
||||
unsigned virtualChannelPayloadId;
|
||||
}reply;
|
||||
|
||||
public:
|
||||
|
||||
AllocatePayloadMessage() : Message(NV_DP_SBMSG_REQUEST_ID_ALLOCATE_PAYLOAD,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_4)
|
||||
{
|
||||
dpMemZero(&reply, sizeof(reply));
|
||||
}
|
||||
|
||||
void set(const Address & target,
|
||||
unsigned port,
|
||||
unsigned nSDPStreams,
|
||||
unsigned vcPayloadId,
|
||||
unsigned PBN,
|
||||
unsigned* SDPStreamSink,
|
||||
bool entirePath);
|
||||
|
||||
unsigned replyPortNumber(){return reply.portNumber;}
|
||||
unsigned replyPBN(){return reply.PBN;}
|
||||
unsigned replyVirtualChannelPayloadId(){return reply.virtualChannelPayloadId;}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// QUERY_PAYLOAD 0x12
|
||||
//
|
||||
class QueryPayloadMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
|
||||
private:
|
||||
struct
|
||||
{
|
||||
unsigned portNumber;
|
||||
unsigned allocatedPBN;
|
||||
} reply;
|
||||
|
||||
public:
|
||||
QueryPayloadMessage(const Address & target,
|
||||
unsigned port,
|
||||
unsigned vcPayloadId);
|
||||
|
||||
unsigned replyPortNumber() {return reply.portNumber;}
|
||||
unsigned replyAllocatedPBN() {return reply.allocatedPBN;}
|
||||
};
|
||||
|
||||
//
|
||||
// RESOURCE_STATUS_NOTIFY 0x13
|
||||
//
|
||||
class ResStatusNotifyMessage : public MessageManager::MessageReceiver
|
||||
{
|
||||
virtual bool processByType(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
public:
|
||||
struct
|
||||
{
|
||||
unsigned port;
|
||||
GUID guid;
|
||||
unsigned PBN;
|
||||
} request;
|
||||
|
||||
public:
|
||||
ResStatusNotifyMessage(MessageReceiverEventSink * sink);
|
||||
};
|
||||
|
||||
//
|
||||
// REMOTE_DPCD_READ 0x20
|
||||
//
|
||||
class RemoteDpcdReadMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
|
||||
private:
|
||||
struct
|
||||
{
|
||||
unsigned portNumber;
|
||||
unsigned numBytesReadDPCD;
|
||||
unsigned char readData[REMOTE_READ_BUFFER_SIZE]; // Buffer
|
||||
} reply;
|
||||
|
||||
public:
|
||||
void set(const Address & target,
|
||||
unsigned port,
|
||||
unsigned dpcdAddress,
|
||||
unsigned nBytesToRead);
|
||||
|
||||
RemoteDpcdReadMessage() : Message(NV_DP_SBMSG_REQUEST_ID_REMOTE_DPCD_READ,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_3)
|
||||
{
|
||||
dpMemZero(&reply, sizeof(reply));
|
||||
}
|
||||
|
||||
|
||||
unsigned replyPortNumber(){return reply.portNumber;}
|
||||
unsigned replyNumOfBytesReadDPCD(){return reply.numBytesReadDPCD;}
|
||||
|
||||
const NvU8 * replyGetData()
|
||||
{
|
||||
return reply.readData;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// REMOTE_DPCD_WRITE 0x21
|
||||
//
|
||||
class RemoteDpcdWriteMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
public:
|
||||
void set(const Address & target,
|
||||
unsigned port,
|
||||
unsigned dpcdAddress,
|
||||
unsigned nBytesToWrite,
|
||||
const NvU8 * writeData);
|
||||
|
||||
RemoteDpcdWriteMessage() : Message(NV_DP_SBMSG_REQUEST_ID_REMOTE_DPCD_WRITE,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_3) {}
|
||||
};
|
||||
|
||||
//
|
||||
// REMOTE_I2C_READ 0x22
|
||||
//
|
||||
class RemoteI2cReadMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
private:
|
||||
struct
|
||||
{
|
||||
unsigned portNumber;
|
||||
unsigned numBytesReadI2C;
|
||||
unsigned char readData[REMOTE_READ_BUFFER_SIZE];
|
||||
} reply;
|
||||
|
||||
public:
|
||||
|
||||
RemoteI2cReadMessage() : Message(NV_DP_SBMSG_REQUEST_ID_REMOTE_I2C_READ,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_3)
|
||||
{
|
||||
dpMemZero(&reply, sizeof(reply));
|
||||
}
|
||||
|
||||
void set(const Address & target,
|
||||
unsigned nWriteTransactions,
|
||||
unsigned port,
|
||||
I2cWriteTransaction* transactions,
|
||||
unsigned readI2cDeviceId,
|
||||
unsigned nBytesToRead);
|
||||
|
||||
unsigned replyPortNumber(){return reply.portNumber;}
|
||||
unsigned replyNumOfBytesReadI2C(){return reply.numBytesReadI2C;}
|
||||
unsigned char* replyGetI2CData(unsigned* numBytes)
|
||||
{
|
||||
*numBytes = this->replyNumOfBytesReadI2C();
|
||||
return reply.readData;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// REMOTE_I2C_WRITE 0x23
|
||||
//
|
||||
class RemoteI2cWriteMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
private:
|
||||
struct
|
||||
{
|
||||
unsigned portNumber;
|
||||
} reply;
|
||||
|
||||
public:
|
||||
|
||||
RemoteI2cWriteMessage() : Message(NV_DP_SBMSG_REQUEST_ID_REMOTE_I2C_WRITE,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_3)
|
||||
{
|
||||
dpMemZero(&reply, sizeof(reply));
|
||||
}
|
||||
|
||||
void set(const Address & target,
|
||||
unsigned port,
|
||||
unsigned writeI2cDeviceId,
|
||||
unsigned nBytesToWrite,
|
||||
unsigned char* writeData);
|
||||
|
||||
unsigned replyPortNumber() {return reply.portNumber;}
|
||||
};
|
||||
|
||||
//
|
||||
// POWER_UP_PHY 0x24
|
||||
//
|
||||
class PowerUpPhyMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
|
||||
private:
|
||||
struct
|
||||
{
|
||||
unsigned portNumber;
|
||||
} reply;
|
||||
|
||||
public:
|
||||
PowerUpPhyMessage() : Message(NV_DP_SBMSG_REQUEST_ID_POWER_UP_PHY,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_3)
|
||||
{
|
||||
dpMemZero(&reply, sizeof(reply));
|
||||
}
|
||||
|
||||
void set(const Address & target,
|
||||
unsigned port,
|
||||
bool entirePath);
|
||||
|
||||
unsigned replyPortNumber(){return reply.portNumber;}
|
||||
};
|
||||
|
||||
//
|
||||
// POWER_DOWN_PHY 0x25
|
||||
//
|
||||
class PowerDownPhyMessage : public MessageManager::Message
|
||||
{
|
||||
virtual ParseResponseStatus parseResponseAck(EncodedMessage * message,
|
||||
BitStreamReader * reader);
|
||||
|
||||
private:
|
||||
struct
|
||||
{
|
||||
unsigned portNumber;
|
||||
} reply;
|
||||
|
||||
public:
|
||||
PowerDownPhyMessage() : Message(NV_DP_SBMSG_REQUEST_ID_POWER_DOWN_PHY,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_3)
|
||||
{
|
||||
dpMemZero(&reply, sizeof(reply));
|
||||
}
|
||||
|
||||
void set(const Address & target,
|
||||
unsigned port,
|
||||
bool entirePath);
|
||||
|
||||
unsigned replyPortNumber(){return reply.portNumber;}
|
||||
};
|
||||
|
||||
//
|
||||
// SINK_EVENT_NOTIFY 0x30
|
||||
//
|
||||
class SinkEventNotifyMessage : public MessageManager::MessageReceiver
|
||||
{
|
||||
virtual bool processByType(EncodedMessage * message, BitStreamReader * reader);
|
||||
|
||||
public:
|
||||
SinkEventNotifyMessage(MessageReceiverEventSink * sink, unsigned requestId);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_MESSAGECODINGS_H
|
||||
94
src/common/displayport/inc/dp_messageheader.h
Normal file
94
src/common/displayport/inc/dp_messageheader.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_messageheader.h *
|
||||
* DP message header parser *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_MESSAGEHEADER_H
|
||||
#define INCLUDED_DP_MESSAGEHEADER_H
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_list.h"
|
||||
#include "dp_auxretry.h"
|
||||
#include "dp_timer.h"
|
||||
#include "dp_bitstream.h"
|
||||
#include "dp_address.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
//
|
||||
// User filled message structure
|
||||
//
|
||||
#define MAX_MESSAGE_SIZE 64
|
||||
struct EncodedMessage : public Object
|
||||
{
|
||||
unsigned messageNumber; // 0 or 1
|
||||
Address address; // target device for message (source for reply)
|
||||
Buffer buffer;
|
||||
bool isBroadcast;
|
||||
bool isPathMessage;
|
||||
|
||||
EncodedMessage()
|
||||
: messageNumber(0), isBroadcast(false), isPathMessage(false)
|
||||
{}
|
||||
|
||||
void swap(EncodedMessage & other)
|
||||
{
|
||||
swap_args(messageNumber, other.messageNumber);
|
||||
swap_args(address, other.address);
|
||||
swap_args(isBroadcast, other.isBroadcast);
|
||||
swap_args(isPathMessage, other.isPathMessage);
|
||||
buffer.swap(other.buffer);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Decoded message header
|
||||
//
|
||||
struct MessageHeader
|
||||
{
|
||||
Address address;
|
||||
unsigned messageNumber;
|
||||
unsigned payloadBytes;
|
||||
bool isBroadcast;
|
||||
bool isPathMessage;
|
||||
bool isTransactionStart;
|
||||
bool isTransactionEnd;
|
||||
unsigned headerSizeBits;
|
||||
};
|
||||
|
||||
bool decodeHeader(BitStreamReader * reader, MessageHeader * header, const Address & address);
|
||||
|
||||
//
|
||||
// Routines for maintaining a list of partially complete messages
|
||||
//
|
||||
|
||||
// after 4 secs delete dead transactions
|
||||
#define DP_INCOMPLETE_MESSAGE_TIMEOUT_USEC 4000000
|
||||
|
||||
}
|
||||
#endif //INCLUDED_DP_MESSAGEHEADER_H
|
||||
322
src/common/displayport/inc/dp_messages.h
Normal file
322
src/common/displayport/inc/dp_messages.h
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_messages.h *
|
||||
* Encoding routines for aux common messages. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_MESSAGES_H
|
||||
#define INCLUDED_DP_MESSAGES_H
|
||||
|
||||
#include "dp_address.h"
|
||||
#include "dp_bitstream.h"
|
||||
#include "dp_splitter.h"
|
||||
#include "dp_merger.h"
|
||||
#include "dp_crc.h"
|
||||
#include "dp_list.h"
|
||||
#include "dp_connector.h"
|
||||
#include "dp_messageheader.h"
|
||||
#include "dp_auxdefs.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
bool extractGUID(BitStreamReader * reader, GUID * guid);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NakUndefined,
|
||||
NakWriteFailure,
|
||||
NakInvalidRAD,
|
||||
NakCrcFailure,
|
||||
NakBadParam,
|
||||
NakDefer,
|
||||
NakLinkFailure,
|
||||
NakNoResources,
|
||||
NakDpcdFail,
|
||||
NakI2cNak,
|
||||
NakAllocateFail,
|
||||
|
||||
// Extensions
|
||||
NakTimeout = 0x100 // Message was unable to be transmitted
|
||||
|
||||
} NakReason;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GUID guid;
|
||||
NakReason reason;
|
||||
unsigned nak_data;
|
||||
} NakData;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ParseResponseSuccess,
|
||||
ParseResponseFailed,
|
||||
ParseResponseWrong
|
||||
} ParseResponseStatus;
|
||||
|
||||
//
|
||||
// Priority levels are defined to prioritize SBMs for DP1.4 (Highest Priority - LEVEL1, Lowest Priority - DEFAULT)
|
||||
// Current implementation has the following priority levels
|
||||
// CLEAR_PAYLOAD_ID_TABLE = NV_DP_SBMSG_PRIORITY_LEVEL_1
|
||||
// LINK_ADDRESS = NV_DP_SBMSG_PRIORITY_LEVEL_2
|
||||
// REMOTE_DPCD_READ, REMOTE_DPCD_WRITE = NV_DP_SBMSG_PRIORITY_LEVEL_3
|
||||
// REMOTE_I2C_READ, REMOTE_I2C_WRITE = NV_DP_SBMSG_PRIORITY_LEVEL_3
|
||||
// POWER_UP_PHY, POWER_DOWN_PHY = NV_DP_SBMSG_PRIORITY_LEVEL_3
|
||||
// ENUM_PATH_RESOURCES, ALLOCATE_PAYLOAD = NV_DP_SBMSG_PRIORITY_LEVEL_4
|
||||
// All other messages = NV_DP_SBMSG_PRIORITY_LEVEL_DEFAULT
|
||||
//
|
||||
// However, Message::setMessagePriority can be used to override this priority levels, if required.
|
||||
//
|
||||
typedef enum
|
||||
{
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_DEFAULT,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_4,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_3,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_2,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_1,
|
||||
} DPSideBandMessagePriority;
|
||||
|
||||
//
|
||||
// CLASS: MessageManager
|
||||
//
|
||||
class MessageManager :
|
||||
virtual public Object,
|
||||
IncomingTransactionManager::IncomingTransactionManagerEventSink
|
||||
{
|
||||
|
||||
Timer * timer;
|
||||
DPCDHAL * hal;
|
||||
DownRequestManager splitterDownRequest;
|
||||
UpReplyManager splitterUpReply;
|
||||
UpRequestManager mergerUpRequest;
|
||||
DownReplyManager mergerDownReply;
|
||||
bool isBeingDestroyed;
|
||||
bool isPaused;
|
||||
|
||||
List messageReceivers;
|
||||
List notYetSentDownRequest; // Down Messages yet to be processed
|
||||
List notYetSentUpReply; // Up Reply Messages yet to be processed
|
||||
List awaitingReplyDownRequest; // Transmitted, Split, but not yet replied to
|
||||
|
||||
void onUpRequestReceived(bool status, EncodedMessage * message);
|
||||
void onDownReplyReceived(bool status, EncodedMessage * message);
|
||||
void transmitAwaitingDownRequests();
|
||||
void transmitAwaitingUpReplies();
|
||||
|
||||
// IncomingTransactionManager
|
||||
void messagedReceived(IncomingTransactionManager * from, EncodedMessage * message);
|
||||
|
||||
public:
|
||||
class Message;
|
||||
void cancelAllByType(unsigned type);
|
||||
void cancelAll(Message * message);
|
||||
|
||||
void pause()
|
||||
{
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
void clearPendingMsg()
|
||||
{
|
||||
hal->clearPendingMsg();
|
||||
}
|
||||
void IRQUpReqest()
|
||||
{
|
||||
mergerUpRequest.mailboxInterrupt();
|
||||
}
|
||||
|
||||
void IRQDownReply()
|
||||
{
|
||||
mergerDownReply.mailboxInterrupt();
|
||||
}
|
||||
|
||||
MessageManager(DPCDHAL * hal, Timer * timer)
|
||||
: timer(timer), hal(hal),
|
||||
splitterDownRequest(hal, timer),
|
||||
splitterUpReply(hal, timer),
|
||||
mergerUpRequest(hal, timer, Address(0), this),
|
||||
mergerDownReply(hal, timer, Address(0), this),
|
||||
isBeingDestroyed(false)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// CLASS: MessageReceiver
|
||||
//
|
||||
class MessageReceiver : public ListElement, OutgoingTransactionManager::OutgoingTransactionManagerEventSink
|
||||
{
|
||||
public:
|
||||
class MessageReceiverEventSink
|
||||
{
|
||||
public:
|
||||
virtual void messageProcessed(MessageReceiver * from) = 0;
|
||||
};
|
||||
|
||||
// Returns false if the message should be passed to the next receiver
|
||||
virtual bool process(EncodedMessage * message);
|
||||
|
||||
// per message type should implement this
|
||||
virtual bool processByType(EncodedMessage * message, BitStreamReader * reader) = 0;
|
||||
|
||||
unsigned getRequestId() {return requestId;}
|
||||
Address & getAddress() {return address;}
|
||||
|
||||
MessageReceiver(MessageReceiverEventSink* sink, unsigned requestId)
|
||||
: sink(sink),
|
||||
requestId(requestId),
|
||||
bProcessed(true),
|
||||
address(0) // 0 to start with
|
||||
{}
|
||||
|
||||
virtual void splitterFailed(OutgoingTransactionManager * from)
|
||||
{
|
||||
DP_ASSERT(0 && "why did we send a reply");
|
||||
}
|
||||
|
||||
virtual void splitterTransmitted(OutgoingTransactionManager * from)
|
||||
{
|
||||
DP_ASSERT(0 && "why did we send a reply");
|
||||
}
|
||||
|
||||
protected:
|
||||
MessageReceiverEventSink * sink;
|
||||
unsigned requestId;
|
||||
bool bProcessed;
|
||||
Address address;
|
||||
MessageManager * parent;
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// CLASS: Message
|
||||
//
|
||||
class Message : public ListElement,
|
||||
OutgoingTransactionManager::OutgoingTransactionManagerEventSink,
|
||||
Timer::TimerCallback /* countdown timer for reply */
|
||||
{
|
||||
public:
|
||||
class MessageEventSink
|
||||
{
|
||||
public:
|
||||
virtual void messageFailed(Message * from, NakData * nakData) = 0;
|
||||
virtual void messageCompleted(Message * from) = 0;
|
||||
};
|
||||
unsigned getMsgType() {return requestIdentifier;}
|
||||
unsigned getSinkPort() {return sinkPort;}
|
||||
protected:
|
||||
// Encoded message body (set in dp_messagecodings)
|
||||
// this data structure is invalidated on post
|
||||
// as the data gets swapped into the transmit buffer.
|
||||
EncodedMessage encodedMessage;
|
||||
MessageEventSink * sink;
|
||||
|
||||
MessageManager * parent;
|
||||
bool transmitReply;
|
||||
bool bTransmitted;
|
||||
unsigned requestIdentifier;
|
||||
unsigned messagePriority;
|
||||
unsigned sinkPort;
|
||||
|
||||
// State updated by post operation
|
||||
struct {
|
||||
unsigned messageNumber;
|
||||
Address target;
|
||||
} state;
|
||||
|
||||
virtual ParseResponseStatus parseResponseAck(
|
||||
EncodedMessage * message, BitStreamReader * reader) = 0;
|
||||
virtual ParseResponseStatus parseResponse(EncodedMessage * message);
|
||||
virtual void splitterFailed(OutgoingTransactionManager * from);
|
||||
virtual void expired(const void * tag);
|
||||
virtual void splitterTransmitted(OutgoingTransactionManager * from);
|
||||
|
||||
public:
|
||||
friend class MessageManager;
|
||||
|
||||
Message(int requestIdentifier, int messagePriority)
|
||||
: sink(0),
|
||||
parent(0),
|
||||
transmitReply(false),
|
||||
bTransmitted(false),
|
||||
requestIdentifier(requestIdentifier),
|
||||
messagePriority(messagePriority),
|
||||
sinkPort(0xFF)
|
||||
{
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (parent) {
|
||||
parent->timer->cancelCallbacks(this);
|
||||
parent->splitterDownRequest.cancel(this);
|
||||
}
|
||||
|
||||
parent = 0;
|
||||
List::remove(this);
|
||||
encodedMessage.buffer.reset();
|
||||
}
|
||||
|
||||
// This function can be used to override the already set priority of the message from it's constructor.
|
||||
void setMessagePriority(DPSideBandMessagePriority priorityLevel)
|
||||
{
|
||||
this->messagePriority = priorityLevel;
|
||||
return;
|
||||
}
|
||||
|
||||
protected:
|
||||
~Message()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Register new receiver for unpair messages
|
||||
// (eg. broadcast messages or sink->source messages)
|
||||
//
|
||||
void registerReceiver(MessageReceiver * receiver);
|
||||
|
||||
// Post a message to be asynchronously transmitted
|
||||
void post(Message * message, Message::MessageEventSink * sink, bool isReply = false);
|
||||
void postReply(Message * message, Message::MessageEventSink * sink);
|
||||
void cancel(Message * message);
|
||||
|
||||
bool send(Message * message, NakData & nakData);
|
||||
friend class Message;
|
||||
~MessageManager();
|
||||
};
|
||||
struct GenericMessageCompletion : public MessageManager::Message::MessageEventSink
|
||||
{
|
||||
bool failed;
|
||||
bool completed;
|
||||
NakData nakData;
|
||||
GenericMessageCompletion();
|
||||
void messageFailed(MessageManager::Message * from, NakData * data);
|
||||
void messageCompleted(MessageManager::Message * from);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_MESSAGES_H
|
||||
132
src/common/displayport/inc/dp_object.h
Normal file
132
src/common/displayport/inc/dp_object.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2011-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_object.h *
|
||||
* This is the object from which all other dynamically-allocated objects *
|
||||
* must inherit. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_OBJECT_H
|
||||
#define INCLUDED_DP_OBJECT_H
|
||||
|
||||
#include "nvtypes.h"
|
||||
#include "stddef.h"
|
||||
#include "dp_hostimp.h"
|
||||
|
||||
static inline void dpMemCopy(void * target, const void * source, size_t len)
|
||||
{
|
||||
NvU8 * t = (NvU8 *)target;
|
||||
const NvU8 * s = (const NvU8 *)source;
|
||||
|
||||
while (len--)
|
||||
*t++=*s++;
|
||||
}
|
||||
|
||||
static inline void dpMemZero(void * target, size_t len)
|
||||
{
|
||||
NvU8 * t = (NvU8 *)target;
|
||||
|
||||
while (len--)
|
||||
*t++=0;
|
||||
}
|
||||
|
||||
static inline bool dpMemCmp(void *pvBuf1, void *pvBuf2, size_t size)
|
||||
{
|
||||
NvU8 *pBuf1 = (NvU8 *)pvBuf1;
|
||||
NvU8 *pBuf2 = (NvU8 *)pvBuf2;
|
||||
|
||||
if(!pBuf1 || !pBuf2 || !size)
|
||||
return false;
|
||||
|
||||
do
|
||||
{
|
||||
if(*pBuf1++ == *pBuf2++)
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}while(--size);
|
||||
|
||||
if(!size)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
//
|
||||
// Any object allocated through "new" must virtually inherit from this type.
|
||||
// This guarantees that the memory allocation goes through dpMalloc/dpFree.
|
||||
// Leak detection is implemented only on allocations of this type. Data
|
||||
// structures may assume 0 initialization if allocated off the heap.
|
||||
//
|
||||
// You must use virtual inheritance because objects that inherit from
|
||||
// multiple Object-derived classes would otherwise cause ambiguity when
|
||||
// someone tries to use new or delete on them.
|
||||
//
|
||||
struct Object
|
||||
{
|
||||
virtual ~Object() {}
|
||||
|
||||
void *operator new(size_t sz)
|
||||
{
|
||||
void * block = dpMalloc(sz);
|
||||
if (block)
|
||||
{
|
||||
dpMemZero(block, sz);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
void *operator new[](size_t sz)
|
||||
{
|
||||
void * block = dpMalloc(sz);
|
||||
if (block)
|
||||
{
|
||||
dpMemZero(block, sz);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
void operator delete(void * ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
dpFree(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void operator delete[](void * ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
dpFree(ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_DP_OBJECT_H
|
||||
108
src/common/displayport/inc/dp_regkeydatabase.h
Normal file
108
src/common/displayport/inc/dp_regkeydatabase.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2020-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_regkeydatabase.h *
|
||||
* Definition of the DP_REGKEY_DATABASE *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_REGKEYDATABASE_H
|
||||
#define INCLUDED_DP_REGKEYDATABASE_H
|
||||
|
||||
#include "dp_auxdefs.h"
|
||||
|
||||
// Regkey Names
|
||||
#define NV_DP_REGKEY_ENABLE_AUDIO_BEYOND_48K "ENABLE_AUDIO_BEYOND48K"
|
||||
#define NV_DP_REGKEY_OVERRIDE_DPCD_REV "OVERRIDE_DPCD_REV"
|
||||
#define NV_DP_REGKEY_DISABLE_SSC "DISABLE_SSC"
|
||||
#define NV_DP_REGKEY_ENABLE_FAST_LINK_TRAINING "ENABLE_FAST_LINK_TRAINING"
|
||||
#define NV_DP_REGKEY_DISABLE_MST "DISABLE_MST"
|
||||
#define NV_DP_REGKEY_ENABLE_INBAND_STEREO_SIGNALING "ENABLE_INBAND_STEREO_SIGNALING"
|
||||
#define NV_DP_REGKEY_SKIP_POWEROFF_EDP_IN_HEAD_DETACH "SKIP_POWEROFF_EDP_IN_HEAD_DETACH"
|
||||
#define NV_DP_REGKEY_ENABLE_OCA_LOGGING "ENABLE_OCA_LOGGING"
|
||||
#define NV_DP_REGKEY_REPORT_DEVICE_LOST_BEFORE_NEW "HP_WAR_1707690"
|
||||
#define NV_DP_REGKEY_APPLY_LINK_BW_OVERRIDE_WAR "APPLY_LINK_BW_OVERRIDE_WAR"
|
||||
#define NV_DP_REGKEY_APPLY_MAX_LINK_RATE_OVERRIDES "APPLY_OVERRIDES_FOR_BUG_2489143"
|
||||
#define NV_DP_REGKEY_DISABLE_DSC "DISABLE_DSC"
|
||||
#define NV_DP_REGKEY_SKIP_ASSESSLINK_FOR_EDP "HP_WAR_2189772"
|
||||
#define NV_DP_REGKEY_HDCP_AUTH_ONLY_ON_DEMAND "DP_HDCP_AUTH_ONLY_ON_DEMAND"
|
||||
#define NV_DP_REGKEY_ENABLE_MSA_OVER_MST "ENABLE_MSA_OVER_MST"
|
||||
|
||||
// Keep link alive for SST and MST
|
||||
#define NV_DP_REGKEY_KEEP_OPT_LINK_ALIVE "DP_KEEP_OPT_LINK_ALIVE"
|
||||
// Keep link alive when connector is in MST
|
||||
#define NV_DP_REGKEY_KEEP_OPT_LINK_ALIVE_MST "DP_KEEP_OPT_LINK_ALIVE_MST"
|
||||
// Keep link alive when connector is in SST
|
||||
#define NV_DP_REGKEY_KEEP_OPT_LINK_ALIVE_SST "DP_KEEP_OPT_LINK_ALIVE_SST"
|
||||
|
||||
#define NV_DP_REGKEY_FORCE_EDP_ILR "DP_BYPASS_EDP_ILR_REV_CHECK"
|
||||
|
||||
//
|
||||
// DSC capability of downstream device should be decided based on device's own
|
||||
// and its parent's DSC capability.
|
||||
//
|
||||
#define NV_DP_DSC_MST_CAP_BUG_3143315 "DP_DSC_MST_CAP_BUG_3143315"
|
||||
|
||||
//
|
||||
// Enable DSC Pass through support in MST mode.
|
||||
//
|
||||
#define NV_DP_DSC_MST_ENABLE_PASS_THROUGH "DP_DSC_MST_ENABLE_PASS_THROUGH"
|
||||
|
||||
//
|
||||
// Data Base used to store all the regkey values.
|
||||
// The actual data base is declared statically in dp_evoadapter.cpp.
|
||||
// All entries set to 0 before initialized by the first EvoMainLink constructor.
|
||||
// The first EvoMainLink constructor will populate that data base.
|
||||
// Later EvoMainLink will use values from that data base.
|
||||
//
|
||||
struct DP_REGKEY_DATABASE
|
||||
{
|
||||
bool bInitialized; // set to true after the first EvoMainLink instance is constructed
|
||||
// Below are regkey values
|
||||
bool bAudioBeyond48kEnabled;
|
||||
NvU32 dpcdRevOveride;
|
||||
bool bSscDisabled;
|
||||
bool bFastLinkTrainingEnabled;
|
||||
bool bMstDisabled;
|
||||
bool bInbandStereoSignalingEnabled;
|
||||
bool bPoweroffEdpInHeadDetachSkipped;
|
||||
bool bOcaLoggingEnabled;
|
||||
bool bReportDeviceLostBeforeNew;
|
||||
bool bLinkBwOverrideWarApplied;
|
||||
NvU32 applyMaxLinkRateOverrides;
|
||||
bool bDscDisabled;
|
||||
bool bAssesslinkForEdpSkipped;
|
||||
bool bHdcpAuthOnlyOnDemand;
|
||||
bool bMsaOverMstEnabled;
|
||||
bool bOptLinkKeptAlive;
|
||||
bool bOptLinkKeptAliveMst;
|
||||
bool bOptLinkKeptAliveSst;
|
||||
bool bBypassEDPRevCheck;
|
||||
bool bDscMstCapBug3143315;
|
||||
bool bDscMstEnablePassThrough;
|
||||
};
|
||||
|
||||
#endif //INCLUDED_DP_REGKEYDATABASE_H
|
||||
|
||||
33
src/common/displayport/inc/dp_ringbuffer.h
Normal file
33
src/common/displayport/inc/dp_ringbuffer.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <nvtypes.h>
|
||||
#include <dpringbuffertypes.h>
|
||||
#include "dp_object.h"
|
||||
|
||||
#define addToRingBufferCollection(x) {}
|
||||
#define addDpLogRecord(x, ...) {}
|
||||
#define addDpAssertRecord() {}
|
||||
#define queryDpLogRecords(a, b, c) {}
|
||||
#define resetDpAssertRingBuffer() {}
|
||||
|
||||
156
src/common/displayport/inc/dp_splitter.h
Normal file
156
src/common/displayport/inc/dp_splitter.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_splitter.h *
|
||||
* Asynchronous Message splitter *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_SPLITTER_H
|
||||
#define INCLUDED_DP_SPLITTER_H
|
||||
|
||||
#include "dp_list.h"
|
||||
#include "dp_auxretry.h"
|
||||
#include "dp_timer.h"
|
||||
#include "dp_auxdefs.h"
|
||||
#include "dp_messageheader.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
|
||||
struct EncodedMessage;
|
||||
class DPCDHAL;
|
||||
|
||||
class MessageTransactionSplitter
|
||||
{
|
||||
EncodedMessage * messageOutstanding; // If set we've pulled an item out of the downQueue queue.
|
||||
// One or more transactions have been sent as a result
|
||||
// messageOutstanding->messageOffset show how far into
|
||||
// the message we are.
|
||||
unsigned assemblyTransmitted;
|
||||
public:
|
||||
void set(EncodedMessage * messageOutstanding)
|
||||
{
|
||||
this->messageOutstanding = messageOutstanding;
|
||||
assemblyTransmitted = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Encode the next transaction.
|
||||
// returns false if there are no more transactions
|
||||
//
|
||||
bool get(Buffer & assemblyBuffer);
|
||||
|
||||
MessageTransactionSplitter()
|
||||
{}
|
||||
};
|
||||
|
||||
class OutgoingTransactionManager:
|
||||
virtual public Object,
|
||||
private Timer::TimerCallback
|
||||
{
|
||||
public:
|
||||
class OutgoingTransactionManagerEventSink
|
||||
{
|
||||
public:
|
||||
virtual void splitterFailed(OutgoingTransactionManager * from) = 0; // Sink DEFER the writes
|
||||
virtual void splitterTransmitted(OutgoingTransactionManager * from) = 0; // message was sent (may NACK later)
|
||||
};
|
||||
|
||||
// Send the encoded message. This call is destructive to the EncodedMessage
|
||||
// passed in
|
||||
bool send( EncodedMessage & payload, OutgoingTransactionManagerEventSink * sink);
|
||||
|
||||
OutgoingTransactionManager(Timer * timer);
|
||||
virtual ~OutgoingTransactionManager() { timer->cancelCallbacks(this); }
|
||||
|
||||
// Do not make any calls to the event sink
|
||||
void cancel(OutgoingTransactionManagerEventSink * sink);
|
||||
|
||||
protected:
|
||||
virtual AuxRetry::status writeMessageBox(NvU8 * data, size_t length) = 0;
|
||||
virtual size_t getMessageBoxSize() = 0;
|
||||
private:
|
||||
void writeToWindow( bool firstAttempt);
|
||||
void split();
|
||||
void expired(const void * tag); // timer callback
|
||||
|
||||
unsigned retriesLeft;
|
||||
|
||||
Buffer assemblyBuffer;
|
||||
MessageTransactionSplitter transactionSplitter;
|
||||
|
||||
//
|
||||
// List of outgoing messages
|
||||
//
|
||||
struct OutgoingMessage : ListElement
|
||||
{
|
||||
OutgoingTransactionManagerEventSink* eventSink;
|
||||
EncodedMessage message;
|
||||
};
|
||||
|
||||
List queuedMessages;
|
||||
|
||||
//
|
||||
// Message currently assembled in transactionSplitter
|
||||
// (if any)
|
||||
//
|
||||
OutgoingMessage * activeMessage;
|
||||
Timer * timer;
|
||||
};
|
||||
|
||||
|
||||
class DownRequestManager : public OutgoingTransactionManager
|
||||
{
|
||||
public:
|
||||
DownRequestManager(DPCDHAL * hal, Timer * timer)
|
||||
: OutgoingTransactionManager(timer), hal(hal)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~DownRequestManager() {}
|
||||
protected:
|
||||
DPCDHAL * hal;
|
||||
|
||||
virtual AuxRetry::status writeMessageBox(NvU8 * data, size_t length);
|
||||
virtual size_t getMessageBoxSize();
|
||||
};
|
||||
|
||||
class UpReplyManager : public OutgoingTransactionManager
|
||||
{
|
||||
public:
|
||||
UpReplyManager(DPCDHAL * hal, Timer * timer)
|
||||
: OutgoingTransactionManager(timer), hal(hal)
|
||||
{
|
||||
}
|
||||
virtual ~UpReplyManager() {}
|
||||
protected:
|
||||
DPCDHAL * hal;
|
||||
|
||||
virtual AuxRetry::status writeMessageBox(NvU8 * data, size_t length);
|
||||
virtual size_t getMessageBoxSize();
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_SPLITTER_H
|
||||
74
src/common/displayport/inc/dp_timeout.h
Normal file
74
src/common/displayport/inc/dp_timeout.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_timeout.h *
|
||||
* Local timeout management *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_TIMEOUT_H
|
||||
#define INCLUDED_DP_TIMEOUT_H
|
||||
|
||||
#include "dp_timer.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
//
|
||||
// Timeout management
|
||||
//
|
||||
class Timeout : virtual public Object
|
||||
{
|
||||
Timer * timer;
|
||||
NvU64 timeoutTime; // What time to trigger the timeout at
|
||||
|
||||
public:
|
||||
|
||||
Timeout(Timer * _timer, int timeoutMilliseconds)
|
||||
: timer(_timer), timeoutTime(_timer->getTimeUs() + timeoutMilliseconds*1000 + 1 /* counter could be about to roll */)
|
||||
{
|
||||
}
|
||||
|
||||
NvS64 remainingUs()
|
||||
{
|
||||
NvS64 remaining = (NvS64)(timeoutTime - timer->getTimeUs());
|
||||
|
||||
// Rollover check
|
||||
if (remaining < 0)
|
||||
{
|
||||
remaining = 0;
|
||||
}
|
||||
|
||||
DP_ASSERT(remaining < ((NvS64)1000000*3600) && "Timeout remaining over an hour");
|
||||
|
||||
return remaining;
|
||||
}
|
||||
|
||||
bool valid()
|
||||
{
|
||||
return remainingUs() > 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_TIMEOUT_H
|
||||
104
src/common/displayport/inc/dp_timer.h
Normal file
104
src/common/displayport/inc/dp_timer.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_timer.h *
|
||||
* Local timer interface *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_TIMER_H
|
||||
#define INCLUDED_DP_TIMER_H
|
||||
|
||||
#include "dp_list.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
//
|
||||
// RawTimer
|
||||
// This API is expected to be implemented by the
|
||||
// library client.
|
||||
//
|
||||
class RawTimer : virtual public Object
|
||||
{
|
||||
public:
|
||||
struct Callback : virtual public Object
|
||||
{
|
||||
virtual void expired() = 0;
|
||||
};
|
||||
virtual void queueCallback(Callback * callback, int milliseconds) = 0;
|
||||
virtual NvU64 getTimeUs() = 0;
|
||||
virtual void sleep(int milliseconds) = 0;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Timer
|
||||
//
|
||||
class Timer : public RawTimer::Callback
|
||||
{
|
||||
public:
|
||||
struct TimerCallback
|
||||
{
|
||||
virtual void expired(const void * context) = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
RawTimer * raw;
|
||||
NvU64 nextTimestamp;
|
||||
List pending;
|
||||
struct PendingCallback : ListElement
|
||||
{
|
||||
TimerCallback * target;
|
||||
const void * context;
|
||||
NvU64 timestamp; // in usec
|
||||
bool executeInSleep;
|
||||
|
||||
};
|
||||
|
||||
virtual void expired();
|
||||
unsigned fire(bool fromSleep);
|
||||
|
||||
void _pump(unsigned milliseconds, bool fromSleep);
|
||||
public:
|
||||
Timer(RawTimer * raw) : raw(raw) {}
|
||||
virtual ~Timer() {}
|
||||
|
||||
//
|
||||
// Queue a timer callback.
|
||||
// Unless the dont-execute-in-sleep flag is
|
||||
//
|
||||
void queueCallback(Timer::TimerCallback * target, const void * context, unsigned milliseconds, bool executeInSleep = true);
|
||||
NvU64 getTimeUs();
|
||||
void sleep(unsigned milliseconds);
|
||||
void cancelCallbacks(Timer::TimerCallback * to);
|
||||
|
||||
void cancelCallback(Timer::TimerCallback * to, const void * context);
|
||||
void queueCallbackInOrder(Timer::TimerCallback * target, const void * context, unsigned milliseconds, bool executeInSleep);
|
||||
void cancelCallbacksWithoutContext(const void * context);
|
||||
void cancelAllCallbacks();
|
||||
bool checkCallbacksOfSameContext(const void * context);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_TIMER_H
|
||||
128
src/common/displayport/inc/dp_tracing.h
Normal file
128
src/common/displayport/inc/dp_tracing.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2018-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.
|
||||
*/
|
||||
/******************************* DisplayPort ******************************\
|
||||
* *
|
||||
* Module: dp_tracing.h *
|
||||
* Header file for support of tracing, implemented by a host provider *
|
||||
* Because this is platform-agnostic, the tracing API *
|
||||
* is left up to the host interface. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_TRACING_H
|
||||
#define INCLUDED_DP_TRACING_H
|
||||
|
||||
#include "nvtypes.h"
|
||||
|
||||
typedef enum NV_DP_TRACING_EVENT
|
||||
{
|
||||
TRACE_DP_ID_HOTPLUG,
|
||||
TRACE_DP_ID_NEW_SINK_DETECTED,
|
||||
TRACE_DP_ID_NEW_SINK_REPORTED,
|
||||
TRACE_DP_ID_NEW_MST_DEVICE,
|
||||
TRACE_DP_ID_LOST_DEVICE,
|
||||
TRACE_DP_ID_LINK_ASSESSMENT,
|
||||
TRACE_DP_ID_LINK_TRAINING_START,
|
||||
TRACE_DP_ID_LINK_TRAINING_DONE,
|
||||
TRACE_DP_ID_NOTIFY_ATTACH_BEGIN,
|
||||
TRACE_DP_ID_NOTIFY_ATTACH_BEGIN_STATUS,
|
||||
TRACE_DP_ID_NOTIFY_ATTACH_END,
|
||||
TRACE_DP_ID_NOTIFY_DETACH_BEGIN,
|
||||
TRACE_DP_ID_NOTIFY_DETACH_END,
|
||||
TRACE_DP_ID_MESSAGE_EXPIRED
|
||||
} NV_DP_TRACING_EVENT;
|
||||
|
||||
typedef enum NV_DP_TRACING_PRIORITY
|
||||
{
|
||||
TRACE_DP_PRIORITY_ERROR,
|
||||
TRACE_DP_PRIORITY_WARNING,
|
||||
TRACE_DP_PRIORITY_INFO
|
||||
} NV_DP_TRACING_PRIORITY;
|
||||
|
||||
#define NV_DPTRACE_MAX_PARAMS 8
|
||||
|
||||
#define _NV_DPTRACE_EXPAND_HELPER(x) x
|
||||
#define _NV_DPTRACE_EXPAND(x) _NV_DPTRACE_EXPAND_HELPER(x)
|
||||
|
||||
//
|
||||
// _COUNT_ARGS: Counts the size of an argument list.
|
||||
//
|
||||
// For example, if the argument list is two-arguments "A, B", then call it like this:
|
||||
// _COUNT_ARGS(_placeholder, A, B, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
//
|
||||
// which maps to the argument names like this:
|
||||
// _COUNT_ARGS(_0=_placeholder, _1=A, _2=B, _3=9, _4=8, _5=7, _6=6, _7=5, _8=4,, _9=3, _10=2, ...)
|
||||
//
|
||||
// and thus _COUNT_ARGS will return 2, the correct size of the argument list.
|
||||
//
|
||||
#define _NV_DPTRACE_COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) _10
|
||||
|
||||
#define NV_DPTRACE_ERROR(...) NV_DPTRACE_EVENT(TRACE_DP_PRIORITY_ERROR, __VA_ARGS__)
|
||||
#define NV_DPTRACE_WARNING(...) NV_DPTRACE_EVENT(TRACE_DP_PRIORITY_WARNING, __VA_ARGS__)
|
||||
#define NV_DPTRACE_INFO(...) NV_DPTRACE_EVENT(TRACE_DP_PRIORITY_INFO, __VA_ARGS__)
|
||||
|
||||
//
|
||||
// When ##__VA_ARGS__ is used, it will delete a preceding comma (',') when
|
||||
// __VA_ARGS__ is blank (i.e. zero-length argument list). This allows
|
||||
// the zero-argument case to work without resulting in a syntax error.
|
||||
//
|
||||
// We have a placeholder argument as the first parameter to _COUNT_ARGS
|
||||
// so that we can take advantage of this comma-deleting behavior.
|
||||
//
|
||||
// However, there shouldn't be a zero-arg case as of now, because the first arg is the event.
|
||||
//
|
||||
#define NV_DPTRACE_EVENT(priority, ...) \
|
||||
_NV_DPTRACE_SEND(priority, _NV_DPTRACE_EXPAND(_NV_DPTRACE_COUNT_ARGS(_0, ##__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)), __VA_ARGS__)
|
||||
|
||||
#define _NV_DPTRACE_SEND(priority, argc, ...) _NV_DPTRACE_EXPAND(_NV_DPTRACE_SEND_N(priority, argc, __VA_ARGS__))
|
||||
#define _NV_DPTRACE_SEND_N(priority, argc, ...) _NV_DPTRACE_EXPAND(_NV_DPTRACE_##argc(priority, __VA_ARGS__))
|
||||
|
||||
// The first argument is the event - macro number is one higher than num args passed to dpTraceEvent
|
||||
#define _NV_DPTRACE_1(priority, event) \
|
||||
dpTraceEvent(TRACE_DP_ID_##event, priority, 0);
|
||||
|
||||
#define _NV_DPTRACE_2(priority, event, p1) \
|
||||
dpTraceEvent(TRACE_DP_ID_##event, priority, 1, p1);
|
||||
|
||||
#define _NV_DPTRACE_3(priority, event, p1, p2) \
|
||||
dpTraceEvent(TRACE_DP_ID_##event, priority, 2, p1, p2);
|
||||
|
||||
#define _NV_DPTRACE_4(priority, event, p1, p2, p3) \
|
||||
dpTraceEvent(TRACE_DP_ID_##event, priority, 3, p1, p2, p3);
|
||||
|
||||
#define _NV_DPTRACE_5(priority, event, p1, p2, p3, p4) \
|
||||
dpTraceEvent(TRACE_DP_ID_##event, priority, 4, p1, p2, p3, p4);
|
||||
|
||||
#define _NV_DPTRACE_6(priority, event, p1, p2, p3, p4, p5) \
|
||||
dpTraceEvent(TRACE_DP_ID_##event, priority, 5, p1, p2, p3, p4, p5);
|
||||
|
||||
#define _NV_DPTRACE_7(priority, event, p1, p2, p3, p4, p5, p6) \
|
||||
dpTraceEvent(TRACE_DP_ID_##event, priority, 6, p1, p2, p3, p4, p5, p6);
|
||||
|
||||
#define _NV_DPTRACE_8(priority, event, p1, p2, p3, p4, p5, p6, p7) \
|
||||
dpTraceEvent(TRACE_DP_ID_##event, priority, 7, p1, p2, p3, p4, p5, p6, p7);
|
||||
|
||||
#define _NV_DPTRACE_9(priority, event, p1, p2, p3, p4, p5, p6, p7, p8) \
|
||||
dpTraceEvent(TRACE_DP_ID_##event, priority, 8, p1, p2, p3, p4, p5, p6, p7, p8);
|
||||
|
||||
#endif // INCLUDED_DP_TRACING_H
|
||||
95
src/common/displayport/inc/dp_vrr.h
Normal file
95
src/common/displayport/inc/dp_vrr.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_vrr.h *
|
||||
* Prototypes and definitions related to VRR enablement *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_VRR_H
|
||||
#define INCLUDED_DP_VRR_H
|
||||
|
||||
#include "dp_object.h"
|
||||
|
||||
// Worstcase VRR enablement handshake timeout of 600ms (40x15ms)
|
||||
#define VRR_ENABLE_STATUS_TIMEOUT_THRESHOLD 40
|
||||
#define VRR_ENABLE_STATUS_TIMEOUT_INTERVAL_MS 15
|
||||
|
||||
// Retry enablement threshold in notifyShortPulse()
|
||||
#define VRR_MAX_RETRIES 3
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
enum VrrEnableStage
|
||||
{
|
||||
VRR_ENABLE_STAGE_MONITOR_ENABLE_BEGIN,
|
||||
VRR_ENABLE_STAGE_MONITOR_ENABLE_CHALLENGE,
|
||||
VRR_ENABLE_STAGE_MONITOR_ENABLE_CHECK,
|
||||
VRR_ENABLE_STAGE_DRIVER_ENABLE_BEGIN,
|
||||
VRR_ENABLE_STAGE_DRIVER_ENABLE_CHALLENGE,
|
||||
VRR_ENABLE_STAGE_DRIVER_ENABLE_CHECK,
|
||||
VRR_ENABLE_STAGE_RESET_MONITOR,
|
||||
VRR_ENABLE_STAGE_INIT_PUBLIC_INFO,
|
||||
VRR_ENABLE_STAGE_GET_PUBLIC_INFO,
|
||||
VRR_ENABLE_STAGE_STATUS_CHECK,
|
||||
};
|
||||
|
||||
struct DeviceImpl;
|
||||
|
||||
class VrrEnablement : virtual public Object
|
||||
{
|
||||
private:
|
||||
DeviceImpl *parent;
|
||||
bool bMonitorEnabled;
|
||||
|
||||
bool vrrGetPublicInfo(void);
|
||||
bool vrrWaitOnEnableStatus(void);
|
||||
bool vrrEnableMonitor(void);
|
||||
bool vrrEnableDriver(void);
|
||||
|
||||
public:
|
||||
|
||||
VrrEnablement(DeviceImpl *parent)
|
||||
: parent(parent)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
~VrrEnablement()
|
||||
{
|
||||
parent = NULL;
|
||||
reset();
|
||||
}
|
||||
|
||||
bool start(void);
|
||||
void reset(void)
|
||||
{
|
||||
bMonitorEnabled = false;
|
||||
}
|
||||
bool isMonitorEnabled(void);
|
||||
bool isDriverEnabled(void);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
75
src/common/displayport/inc/dp_wardatabase.h
Normal file
75
src/common/displayport/inc/dp_wardatabase.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_wardatabase.h *
|
||||
* EDID and OUI based workarounds for panel/TCON issues *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#ifndef INCLUDED_DP_WARDATABASE_H
|
||||
#define INCLUDED_DP_WARDATABASE_H
|
||||
|
||||
#include "dp_object.h"
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
#define WAR_MAX_REASSESS_ATTEMPT 3
|
||||
#define WAR_MAX_RETRAIN_ATTEMPT 3
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DP_MONITOR_CAPABILITY_DP_SKIP_REDUNDANT_LT = (1 << 0), // Do not train if the link B/W and lane count are already set to the desired quantities
|
||||
DP_MONITOR_CAPABILITY_DP_SKIP_CABLE_BW_CHECK = (1 << 1), // Skip the link training attempts to test cable bandwidth in CheckDpLink
|
||||
DP_MONITOR_CAPABILITY_DP_MULTI_WRITE_DPCD_0x600 = (1 << 2), // Repeatedly write 0x1 to 0x600 with extra delays until the read verifies the write
|
||||
DP_MONITOR_CAPABILITY_DP_WRITE_0x600_BEFORE_LT = (1 << 3), // Power on a monitor before every link training
|
||||
DP_MONITOR_CAPABILITY_DP_OVERRIDE_OPTIMAL_LINK_CONFIG = (1 << 4), // Override optimal link config
|
||||
DP_MONITOR_CAPABILITY_DP_OVERRIDE_MAX_LANE_COUNT = (1 << 5), // WAR for some DP monitors which claims more lane count than it really supports. It may generate interrupt storm if unsupported lane count is applied
|
||||
DP_MONITOR_CAPABILITY_DP_AVOID_UPDATE_POWER_STATE = (1 << 6), // Don't update panel power state when head detach or lid closed
|
||||
} DP_MONITOR_CAPABILITY;
|
||||
|
||||
struct DpMonitorDenylistData: virtual public Object
|
||||
{
|
||||
// Max lane count supported override value
|
||||
unsigned int dpMaxLaneCountOverride;
|
||||
|
||||
// Link rate and Lane count value overrides
|
||||
// when we need to skip BW check
|
||||
struct
|
||||
{
|
||||
unsigned int maxLaneAtHighRate;
|
||||
unsigned int maxLaneAtLowRate;
|
||||
} dpSkipCheckLink;
|
||||
|
||||
// Link rate and Lane count value overrides
|
||||
// when we need to force optimal link config
|
||||
struct
|
||||
{
|
||||
unsigned int linkRate;
|
||||
unsigned int laneCount;
|
||||
} dpOverrideOptimalLinkConfig;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDED_DP_WARDATABASE_H
|
||||
134
src/common/displayport/inc/dp_watermark.h
Normal file
134
src/common/displayport/inc/dp_watermark.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_watermark.h *
|
||||
* DP watermark IsModePossible calculations. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_WATERMARK_H
|
||||
#define INCLUDED_DP_WATERMARK_H
|
||||
|
||||
#include "displayport.h"
|
||||
|
||||
#define WAR_AUDIOCLAMPING_FREQ 48000 // Audio freq. more than 48KHz are currently clamped due to bug 925211
|
||||
|
||||
namespace DisplayPort
|
||||
{
|
||||
class LinkConfiguration;
|
||||
|
||||
struct ModesetInfo
|
||||
{
|
||||
unsigned twoChannelAudioHz; // if you need 192khz stereo specify 192000 here
|
||||
unsigned eightChannelAudioHz; // Same setting for multi channel audio.
|
||||
// DisplayPort encodes 3-8 channel streams as 8 channel
|
||||
NvU64 pixelClockHz; // Requested pixel clock for the mode
|
||||
unsigned rasterWidth;
|
||||
unsigned rasterHeight;
|
||||
unsigned surfaceWidth; // RasterBlankStartX - newRasterBlankEndX
|
||||
unsigned surfaceHeight; // Active region height
|
||||
unsigned depth;
|
||||
unsigned rasterBlankStartX;
|
||||
unsigned rasterBlankEndX;
|
||||
unsigned bitsPerComponent; // Bits per component
|
||||
bool bEnableDsc; // bEnableDsc=1 indicates DSC would be enabled for the mode
|
||||
DSC_MODE mode; // DSC Mode
|
||||
|
||||
ModesetInfo(): twoChannelAudioHz(0),
|
||||
eightChannelAudioHz(0),
|
||||
pixelClockHz(0),
|
||||
rasterWidth(0),
|
||||
rasterHeight(0),
|
||||
surfaceWidth(0),
|
||||
surfaceHeight(0),
|
||||
depth(0),
|
||||
rasterBlankStartX(0),
|
||||
rasterBlankEndX(0),
|
||||
bitsPerComponent(0),
|
||||
bEnableDsc(false),
|
||||
mode(DSC_SINGLE) {}
|
||||
|
||||
ModesetInfo(unsigned newTwoChannelAudioHz, unsigned newEightChannelAudioHz, NvU64 newPixelClockHz,
|
||||
unsigned newRasterWidth, unsigned newRasterHeight,
|
||||
unsigned newSurfaceWidth, unsigned newSurfaceHeight, unsigned newDepth,
|
||||
unsigned newRasterBlankStartX=0, unsigned newRasterBlankEndX=0, bool newBEnableDsc = false,
|
||||
DSC_MODE newMode = DSC_SINGLE):
|
||||
twoChannelAudioHz(newTwoChannelAudioHz),
|
||||
eightChannelAudioHz(newEightChannelAudioHz),
|
||||
pixelClockHz(newPixelClockHz),
|
||||
rasterWidth(newRasterWidth),
|
||||
rasterHeight(newRasterHeight),
|
||||
surfaceWidth(newSurfaceWidth),
|
||||
surfaceHeight(newSurfaceHeight),
|
||||
depth(newDepth),
|
||||
rasterBlankStartX(newRasterBlankStartX),
|
||||
rasterBlankEndX(newRasterBlankEndX),
|
||||
bitsPerComponent(0),
|
||||
bEnableDsc(newBEnableDsc),
|
||||
mode(newMode){}
|
||||
};
|
||||
|
||||
struct Watermark
|
||||
{
|
||||
unsigned waterMark;
|
||||
unsigned tuSize;
|
||||
unsigned hBlankSym;
|
||||
unsigned vBlankSym;
|
||||
};
|
||||
|
||||
bool isModePossibleSST
|
||||
(
|
||||
const LinkConfiguration & linkConfig,
|
||||
const ModesetInfo & modesetInfo,
|
||||
Watermark * dpInfo,
|
||||
bool bUseIncreasedWatermarkLimits = false
|
||||
);
|
||||
|
||||
bool isModePossibleMST
|
||||
(
|
||||
const LinkConfiguration & linkConfig,
|
||||
const ModesetInfo & modesetInfo,
|
||||
Watermark * dpInfo
|
||||
);
|
||||
|
||||
bool isModePossibleSSTWithFEC
|
||||
(
|
||||
const LinkConfiguration & linkConfig,
|
||||
const ModesetInfo & modesetInfo,
|
||||
Watermark * dpInfo,
|
||||
bool bUseIncreasedWatermarkLimits = false
|
||||
);
|
||||
|
||||
bool isModePossibleMSTWithFEC
|
||||
(
|
||||
const LinkConfiguration & linkConfig,
|
||||
const ModesetInfo & modesetInfo,
|
||||
Watermark * dpInfo
|
||||
);
|
||||
|
||||
// Return Payload Bandwidth Number(PBN)for requested mode
|
||||
unsigned pbnForMode(const ModesetInfo & modesetInfo);
|
||||
}
|
||||
|
||||
#endif //INCLUDED_DP_WATERMARK_H
|
||||
122
src/common/displayport/inc/dptestutil/dp_testmessage.h
Normal file
122
src/common/displayport/inc/dptestutil/dp_testmessage.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort*********************************\
|
||||
* *
|
||||
* Module: dp_testmessage.h *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#ifndef INCLUDED_DP_TESTMESSAGE_H
|
||||
#define INCLUDED_DP_TESTMESSAGE_H
|
||||
|
||||
#include "dp_auxdefs.h"
|
||||
|
||||
#include "dp_connector.h"
|
||||
|
||||
#define DP_LPRIME_SIZE 20
|
||||
namespace DisplayPort
|
||||
{
|
||||
// test request status, for DP and nvapi
|
||||
typedef enum
|
||||
{
|
||||
DP_TESTMESSAGE_REQUEST_STATUS_PENDING = 0, // the request is still be processing
|
||||
DP_TESTMESSAGE_REQUEST_STATUS_DONE = 1, // request has been processed
|
||||
DP_TESTMESSAGE_REQUEST_STATUS_ERROR = 2, // error, Dp lib busy with other request
|
||||
DP_TESTMESSAGE_REQUEST_STATUS_NEWREQUEST = 3, // new request for user
|
||||
} DP_TESTMESSAGE_REQUEST_STATUS;
|
||||
|
||||
// Request type enum.
|
||||
typedef enum
|
||||
{
|
||||
} DP_TESTMESSAGE_REQUEST_TYPE;
|
||||
|
||||
class TestMessage;
|
||||
struct ConnectorImpl;
|
||||
|
||||
struct DPTestMessageCompletion : public MessageManager::Message::MessageEventSink
|
||||
{
|
||||
TestMessage *parent;
|
||||
|
||||
public:
|
||||
void setParent(TestMessage *parent)
|
||||
{
|
||||
this->parent = parent;
|
||||
}
|
||||
// call back function if message fails, the status of the dp lib(testMessageStatus)
|
||||
// need to be set to DONE
|
||||
void messageFailed(MessageManager::Message * from, NakData * data);
|
||||
|
||||
// call back function if message complete, the status of the dp lib(testMessageStatus)
|
||||
// need to be set to DONE.
|
||||
// If a message has a reply, it is necessary to record the reply in the dp lib to
|
||||
// send back to user later
|
||||
void messageCompleted(MessageManager::Message * from);
|
||||
|
||||
};
|
||||
|
||||
class TestMessage : virtual public Object
|
||||
{
|
||||
private:
|
||||
ConnectorImpl *pConnector;
|
||||
// check if the user provided request struct is of valid size
|
||||
inline bool isValidStruct(DP_TESTMESSAGE_REQUEST_TYPE requestType, NvU32 structSize)
|
||||
{
|
||||
switch (requestType)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
MessageManager *pMsgManager;
|
||||
DPTestMessageCompletion diagCompl;
|
||||
|
||||
// Data Structure for Generic Message.
|
||||
NvU32 replyBytes;
|
||||
|
||||
public:
|
||||
|
||||
DP_TESTMESSAGE_REQUEST_STATUS testMessageStatus;
|
||||
|
||||
TestMessage() : testMessageStatus(DP_TESTMESSAGE_REQUEST_STATUS_DONE)
|
||||
{
|
||||
diagCompl.setParent(this);
|
||||
pConnector = 0;
|
||||
pMsgManager = 0;
|
||||
replyBytes = 0;
|
||||
}
|
||||
DP_TESTMESSAGE_STATUS sendDPTestMessage(void *pBuffer,
|
||||
NvU32 requestSize,
|
||||
NvU32 *pDpStatus);
|
||||
MessageManager * getMessageManager();
|
||||
void setupTestMessage(MessageManager *msgManager, ConnectorImpl *connector)
|
||||
{
|
||||
pMsgManager = msgManager;
|
||||
pConnector = connector;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
315
src/common/displayport/src/dp_auxretry.cpp
Normal file
315
src/common/displayport/src/dp_auxretry.cpp
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* Module: dp_auxretry.cpp *
|
||||
* Interface implemented by library client. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_auxbus.h"
|
||||
#include "dp_auxretry.h"
|
||||
#include "dp_messageheader.h"
|
||||
|
||||
#include "displayport.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
//
|
||||
// Read a DPCD address.
|
||||
// - allows size greater than single transaction/burst size
|
||||
// - handles defer retries
|
||||
// - handles nacks with incomplete data
|
||||
//
|
||||
AuxRetry::status AuxRetry::readTransaction(int address, NvU8 * buffer, unsigned size, unsigned retries)
|
||||
{
|
||||
unsigned completed;
|
||||
AuxBus::status s;
|
||||
|
||||
DP_ASSERT( size <= aux->transactionSize() );
|
||||
|
||||
do
|
||||
{
|
||||
s = aux->transaction(AuxBus::read, AuxBus::native, address, buffer, size, &completed);
|
||||
|
||||
//
|
||||
// Got success & requested data. Also size of returned data is
|
||||
// expected & non zero.
|
||||
//
|
||||
if ((s == AuxBus::success) && (completed == size) && (completed != 0))
|
||||
{
|
||||
return ack;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Handle defer case with a simple retry
|
||||
//
|
||||
if (s == AuxBus::defer)
|
||||
{
|
||||
if (retries)
|
||||
{
|
||||
--retries;
|
||||
continue;
|
||||
}
|
||||
|
||||
return defer;
|
||||
}
|
||||
|
||||
//
|
||||
// Nack shouldn't happen in general. Unsupported registers
|
||||
// are supposed to ACK with size of 0.
|
||||
//
|
||||
if ( s == AuxBus::nack )
|
||||
{
|
||||
return nack;
|
||||
}
|
||||
|
||||
if ( completed == 0 )
|
||||
{
|
||||
return unsupportedRegister;
|
||||
}
|
||||
|
||||
//
|
||||
// We got less data back than we requested...
|
||||
// It's unclear when this might happen in the spec.
|
||||
// We can either
|
||||
// 1. Split the read into multiple pieces
|
||||
// (Dangerous since we may receive non-atomic updates)
|
||||
// 2. Retry
|
||||
//
|
||||
if ( completed < size )
|
||||
{
|
||||
//
|
||||
// Retry
|
||||
//
|
||||
if (retries)
|
||||
{
|
||||
--retries;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Closest approximation is a defer
|
||||
return defer;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(retries);
|
||||
|
||||
if ((s == AuxBus::defer) || (completed < size))
|
||||
{
|
||||
return defer;
|
||||
}
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
//
|
||||
// Write a DPCD address.
|
||||
// - allows size greater than single transaction/burst size
|
||||
// - handles defer retries
|
||||
// - handles nacks with incomplete data
|
||||
//
|
||||
AuxRetry::status AuxRetry::writeTransaction(int address, NvU8 * buffer, unsigned size, unsigned retries)
|
||||
{
|
||||
unsigned completed;
|
||||
AuxBus::status s;
|
||||
|
||||
DP_ASSERT( size <= aux->transactionSize() );
|
||||
|
||||
do
|
||||
{
|
||||
s = aux->transaction(AuxBus::write, AuxBus::native, address, buffer, size, &completed);
|
||||
|
||||
//
|
||||
// Got success & requested data. Also size of returned data is
|
||||
// expected & non zero.
|
||||
//
|
||||
if ((s == AuxBus::success) && (completed == size) && (completed != 0))
|
||||
{
|
||||
return ack;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Handle defer case with a simple retry
|
||||
//
|
||||
if (s == AuxBus::defer)
|
||||
{
|
||||
if (retries)
|
||||
{
|
||||
--retries;
|
||||
continue;
|
||||
}
|
||||
|
||||
return defer;
|
||||
}
|
||||
|
||||
//
|
||||
// Nack shouldn't happen in general. Unsupported registers
|
||||
// are supposed to ACK with size of 0.
|
||||
//
|
||||
if ( s == AuxBus::nack )
|
||||
{
|
||||
return nack;
|
||||
}
|
||||
|
||||
DP_ASSERT( s == AuxBus::success);
|
||||
|
||||
if ( completed == 0 )
|
||||
{
|
||||
return unsupportedRegister;
|
||||
}
|
||||
|
||||
//
|
||||
// Incomplete write?
|
||||
// Shouldn't happen. Just retry if it does
|
||||
//
|
||||
if ( completed < size )
|
||||
{
|
||||
//
|
||||
// Retry
|
||||
//
|
||||
if (retries)
|
||||
{
|
||||
--retries;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Closest approximation is a defer
|
||||
return defer;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(retries);
|
||||
|
||||
if ((s == AuxBus::defer) || (completed < size))
|
||||
{
|
||||
return defer;
|
||||
}
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
//
|
||||
// Similar to readTransaction except that it supports reading
|
||||
// larger spans than AuxBus::transactionSize()
|
||||
//
|
||||
AuxRetry::status AuxRetry::read(int address, NvU8 * buffer, unsigned size, unsigned retries)
|
||||
{
|
||||
for (unsigned i = 0 ; i < size; )
|
||||
{
|
||||
int todo = DP_MIN(size - i, aux->transactionSize());
|
||||
status s = readTransaction(address+i, buffer+i, todo, retries);
|
||||
|
||||
if (s != ack)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
i += todo;
|
||||
}
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
//
|
||||
// Similar to writeTransaction except that it supports writing
|
||||
// larger spans than AuxBus::transactionSize()
|
||||
//
|
||||
AuxRetry::status AuxRetry::write(int address, NvU8 * buffer, unsigned size, unsigned retries)
|
||||
{
|
||||
for (unsigned i = 0 ; i < size; )
|
||||
{
|
||||
int todo = DP_MIN(size - i, aux->transactionSize());
|
||||
status s = writeTransaction(address+i, buffer+i, todo, retries);
|
||||
|
||||
if (s != ack)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
i += todo;
|
||||
}
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
AuxBus::status AuxLogger::transaction(Action action, Type type, int address,
|
||||
NvU8 * buffer, unsigned sizeRequested,
|
||||
unsigned * sizeCompleted, unsigned * pNakReason,
|
||||
NvU8 offset, NvU8 nWriteTransactions)
|
||||
{
|
||||
AuxBus::status result = bus->transaction(action, type, address, buffer, sizeRequested, sizeCompleted);
|
||||
hint[0] = 0;
|
||||
//
|
||||
// Do the hex dump.
|
||||
// - We can't make library calls
|
||||
// - We need to do this in one printf
|
||||
if (result == success)
|
||||
{
|
||||
if (type == native)
|
||||
if (address == NV_DPCD_MBOX_DOWN_REQ || address == NV_DPCD_MBOX_UP_REP ||
|
||||
address == NV_DPCD_MBOX_DOWN_REP || address == NV_DPCD_MBOX_UP_REQ)
|
||||
{
|
||||
unsigned len = *sizeCompleted;
|
||||
Buffer storage(buffer, len);
|
||||
BitStreamReader reader(&storage, 0, len*8);
|
||||
MessageHeader header;
|
||||
DisplayPort::decodeHeader(&reader, &header, Address(1));
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
dpHexDump(&hex[0], sizeof(hex), buffer, header.headerSizeBits/8);
|
||||
dpHexDump(&hex_body[0], sizeof(hex), buffer + header.headerSizeBits/8, len - header.headerSizeBits/8);
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
const char * name = "";
|
||||
if (header.isTransactionStart && action==write && len > header.headerSizeBits/8)
|
||||
name = getRequestId(buffer[header.headerSizeBits/8]);
|
||||
|
||||
DP_LOG(("DP-AUX> %s%s%s%s%04Xh hint(to:%s %s%s %s #%d) { %s| %s}",
|
||||
sizeRequested == *sizeCompleted ? "" : "INCOMPLETE ", getStatus(result),
|
||||
getAction(action), getType(type), address,
|
||||
header.address.toString(sb), header.isTransactionStart ? "S" : "",
|
||||
header.isTransactionEnd ? "E" : "", name, header.messageNumber,
|
||||
hex, hex_body));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
hex[0] = 0;
|
||||
|
||||
dpHexDump(&hex[0], sizeof(hex), buffer, *sizeCompleted);
|
||||
DP_LOG(("DP-AUX> %s%s%s%s%04Xh { %s }", sizeRequested == *sizeCompleted ? "" : "INCOMPLETE ",
|
||||
getStatus(result), getAction(action), getType(type), address, hex));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AuxBus * DisplayPort::CreateAuxLogger(AuxBus * auxBus)
|
||||
{
|
||||
return new AuxLogger(auxBus);
|
||||
}
|
||||
204
src/common/displayport/src/dp_bitstream.cpp
Normal file
204
src/common/displayport/src/dp_bitstream.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_bitstream.c *
|
||||
* Implementation of Big Endian bit streams. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_bitstream.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
bool BitStreamReader::read(unsigned * value, unsigned bits)
|
||||
{
|
||||
unsigned topbit = (7- (this->bitsOffset & 7));
|
||||
|
||||
if (this->bitsOffset + bits > this->bitsEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// We're filling the byte down from 'topbit' towards 0.
|
||||
// Can we fit all of the bits starting at topbit before
|
||||
// overflowing to the next byte?
|
||||
//
|
||||
if (bits <= (topbit+1))
|
||||
{
|
||||
int bottombit = topbit - (bits-1);
|
||||
*value = (this->buffer()->data[this->bitsOffset / 8] >> bottombit) & ((1 << bits)-1);
|
||||
|
||||
this->bitsOffset+=bits;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// We're either reading too many bits or we're straddling
|
||||
// a byte boundary. Serialize bit by bit.
|
||||
// NOTE: This scenario is entire unlikely. Don't optimize.
|
||||
//
|
||||
|
||||
*value = 0;
|
||||
while (bits)
|
||||
{
|
||||
unsigned bit;
|
||||
if (!read(&bit, 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*value = *value * 2 + bit;
|
||||
bits--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned BitStreamReader::readOrDefault(unsigned bits, unsigned defaultValue)
|
||||
{
|
||||
unsigned value;
|
||||
|
||||
if (read(&value, bits))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool BitStreamReader::align(unsigned align)
|
||||
{
|
||||
// Verify alignment is a power of two
|
||||
if (!(align && ((align & (align - 1)) == 0)))
|
||||
{
|
||||
DP_ASSERT(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this->bitsOffset & (align - 1))
|
||||
{
|
||||
this->bitsOffset = (this->bitsOffset + align) &~ (align - 1);
|
||||
}
|
||||
}
|
||||
return this->bitsOffset <= this->bitsEnd;
|
||||
}
|
||||
|
||||
bool BitStreamWriter::write(unsigned value, unsigned bits)
|
||||
{
|
||||
DP_ASSERT((value < (1ULL << bits)) && "Value out of range");
|
||||
unsigned topbit = (7- (this->bitsOffset & 7));
|
||||
|
||||
if (this->bitsOffset + bits > this->buffer()->length * 8)
|
||||
{
|
||||
this->buffer()->resize((this->bitsOffset + bits+7)/8);
|
||||
}
|
||||
|
||||
//
|
||||
// We're filling the byte down from 'topbit' towards 0.
|
||||
// Can we fit all of the bits starting at topbit before
|
||||
// overflowing to the next byte?
|
||||
//
|
||||
if (bits <= (topbit+1))
|
||||
{
|
||||
int bottombit = topbit - (bits-1);
|
||||
NvU8 clearmask = ((1 << bits)-1) << bottombit;
|
||||
|
||||
this->buffer()->data[this->bitsOffset / 8] = (NvU8)((this->buffer()->data[this->bitsOffset / 8] &~ clearmask) | (value << bottombit));
|
||||
|
||||
this->bitsOffset+=bits;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// We're either writing too many bits or we're straddling
|
||||
// a byte boundary. Serialize bit by bit.
|
||||
// NOTE: This scenario is entire unlikely. Don't optimize.
|
||||
//
|
||||
|
||||
while (bits)
|
||||
{
|
||||
bits --;
|
||||
if (!write( (value >> bits) & 1, 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitStreamWriter::align(unsigned align)
|
||||
{
|
||||
// Verify alignment is a power of two
|
||||
if (!(align && ((align & (align - 1)) == 0)))
|
||||
{
|
||||
DP_ASSERT(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this->bitsOffset & (align - 1))
|
||||
return this->write(0, align - (this->bitsOffset & (align - 1)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned BitStreamReader::offset()
|
||||
{
|
||||
return this->bitsOffset;
|
||||
}
|
||||
|
||||
unsigned BitStreamWriter::offset()
|
||||
{
|
||||
return this->bitsOffset;
|
||||
}
|
||||
|
||||
Buffer * BitStreamWriter::buffer()
|
||||
{
|
||||
return this->targetBuffer;
|
||||
}
|
||||
|
||||
Buffer * BitStreamReader::buffer()
|
||||
{
|
||||
return this->sourceBuffer;
|
||||
}
|
||||
|
||||
|
||||
BitStreamWriter::BitStreamWriter(Buffer * buffer, unsigned bitsOffset)
|
||||
{
|
||||
this->targetBuffer = buffer;
|
||||
this->bitsOffset = bitsOffset;
|
||||
}
|
||||
|
||||
|
||||
BitStreamReader::BitStreamReader(Buffer * buffer, unsigned bitsOffset, unsigned bitsCount)
|
||||
{
|
||||
this->sourceBuffer = buffer;
|
||||
this->bitsOffset = bitsOffset;
|
||||
this->bitsEnd = bitsCount + bitsOffset;
|
||||
}
|
||||
267
src/common/displayport/src/dp_buffer.cpp
Normal file
267
src/common/displayport/src/dp_buffer.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_buffer.cpp *
|
||||
* Resizable byte buffer and stream operations *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_buffer.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
void DisplayPort::swapBuffers(Buffer & left, Buffer & right)
|
||||
{
|
||||
NvU8 *tmpData = left.data;
|
||||
unsigned tmpLength = left.length;
|
||||
unsigned tmpCapacity = left.capacity;
|
||||
bool tmpErrorState = left.errorState;
|
||||
|
||||
left.data = right.data;
|
||||
left.length = right.length;
|
||||
left.capacity = right.capacity;
|
||||
left.errorState = right.errorState;
|
||||
|
||||
right.data = tmpData;
|
||||
right.length = tmpLength;
|
||||
right.capacity = tmpCapacity;
|
||||
right.errorState= tmpErrorState;
|
||||
}
|
||||
|
||||
|
||||
bool Stream::seek(unsigned where)
|
||||
{
|
||||
//
|
||||
// Allow seek to any position in the file INCLUDING
|
||||
// the first byte past the end of the file.
|
||||
//
|
||||
if (where > this->parent->length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->byteOffset = where;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Stream::read(NvU8 * buffer, unsigned size)
|
||||
{
|
||||
unsigned stopReadAt = this->byteOffset + size;
|
||||
|
||||
if (stopReadAt > this->parent->length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dpMemCopy(buffer, this->parent->data + this->byteOffset, size);
|
||||
this->byteOffset = stopReadAt;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Buffer::resize(unsigned stopWriteAt)
|
||||
{
|
||||
bool mustIncrease = stopWriteAt > this->capacity;
|
||||
|
||||
if (mustIncrease || (stopWriteAt * 4 < this->capacity) )
|
||||
{
|
||||
unsigned newCapacity;
|
||||
NvU8 * newBuffer;
|
||||
|
||||
newCapacity = 32;
|
||||
|
||||
while (newCapacity <= stopWriteAt)
|
||||
{
|
||||
newCapacity *= 2;
|
||||
}
|
||||
|
||||
if (newCapacity == this->capacity) {
|
||||
this->length = stopWriteAt;
|
||||
return true;
|
||||
}
|
||||
|
||||
newBuffer = (NvU8 *)dpMalloc(sizeof(NvU8) * newCapacity);
|
||||
|
||||
if (!newBuffer)
|
||||
{
|
||||
if (mustIncrease)
|
||||
{
|
||||
if (this->data)
|
||||
{
|
||||
dpFree(this->data);
|
||||
}
|
||||
|
||||
this->errorState = true;
|
||||
this->data = 0;
|
||||
this->capacity = 0;
|
||||
this->length = 0;
|
||||
}
|
||||
else
|
||||
newCapacity = this->capacity;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->data)
|
||||
{
|
||||
dpMemCopy(newBuffer, this->data, DP_MIN(newCapacity, this->length));
|
||||
dpFree(this->data);
|
||||
}
|
||||
|
||||
this->data = newBuffer;
|
||||
this->capacity = newCapacity;
|
||||
|
||||
}
|
||||
|
||||
this->length = stopWriteAt;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Buffer::memZero()
|
||||
{
|
||||
if (this->data)
|
||||
dpMemZero(this->data, this->length);
|
||||
}
|
||||
|
||||
bool Stream::write(NvU8 * buffer, unsigned size)
|
||||
{
|
||||
unsigned stopWriteAt = this->byteOffset + size;
|
||||
|
||||
if (stopWriteAt > this->parent->length)
|
||||
{
|
||||
this->parent->resize(stopWriteAt);
|
||||
}
|
||||
|
||||
if (isError())
|
||||
return false;
|
||||
|
||||
dpMemCopy( this->parent->data + this->byteOffset, buffer, size);
|
||||
this->byteOffset = stopWriteAt;
|
||||
this->parent->length = DP_MAX(this->parent->length, stopWriteAt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned Stream::remaining()
|
||||
{
|
||||
return this->parent->length - this->byteOffset;
|
||||
}
|
||||
|
||||
unsigned Stream::offset()
|
||||
{
|
||||
return this->byteOffset;
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void Buffer::reset()
|
||||
{
|
||||
if (this->data)
|
||||
{
|
||||
dpFree(this->data);
|
||||
}
|
||||
|
||||
length = 0;
|
||||
capacity = 0;
|
||||
data = 0;
|
||||
errorState = false;
|
||||
}
|
||||
|
||||
bool Buffer::isError() const
|
||||
{
|
||||
return this->errorState;
|
||||
}
|
||||
|
||||
|
||||
Stream::Stream(Buffer * buffer)
|
||||
: parent(buffer), byteOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool Stream::isError() const
|
||||
{
|
||||
return this->parent->errorState;
|
||||
}
|
||||
|
||||
Buffer::Buffer()
|
||||
: data(0), length(0), capacity(0), errorState(false)
|
||||
{
|
||||
}
|
||||
|
||||
Buffer::Buffer(NvU8 * src, unsigned size)
|
||||
: data(0), length(0), capacity(0), errorState(false)
|
||||
{
|
||||
if (src && size && resize(size) && data)
|
||||
dpMemCopy(data, src, size);
|
||||
}
|
||||
|
||||
Buffer::Buffer(const Buffer & other)
|
||||
: data(0), length(0), capacity(0), errorState(false)
|
||||
{
|
||||
if (other.isError())
|
||||
{
|
||||
errorState = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (resize(other.getLength()) && other.getData())
|
||||
dpMemCopy(getData(), other.getData(), getLength());
|
||||
}
|
||||
}
|
||||
|
||||
Buffer & Buffer::operator = (const Buffer & other)
|
||||
{
|
||||
if (other.isError())
|
||||
{
|
||||
errorState = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (resize(other.getLength()))
|
||||
dpMemCopy(getData(), other.getData(), getLength());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool Buffer::operator== (const Buffer & other) const
|
||||
{
|
||||
if (length != other.length)
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
{
|
||||
if (data[i] != other.data[i])
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
3170
src/common/displayport/src/dp_configcaps.cpp
Normal file
3170
src/common/displayport/src/dp_configcaps.cpp
Normal file
File diff suppressed because it is too large
Load Diff
6889
src/common/displayport/src/dp_connectorimpl.cpp
Normal file
6889
src/common/displayport/src/dp_connectorimpl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
93
src/common/displayport/src/dp_crc.cpp
Normal file
93
src/common/displayport/src/dp_crc.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort*********************************\
|
||||
* *
|
||||
* Module: dp_crc.cpp *
|
||||
* CRC Algorithms for the messaging subsystem. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#include "dp_internal.h"
|
||||
#include "dp_bitstream.h"
|
||||
#include "dp_crc.h"
|
||||
using namespace DisplayPort;
|
||||
|
||||
//
|
||||
// DP CRC for transactions headers
|
||||
//
|
||||
unsigned DisplayPort::dpCalculateHeaderCRC(BitStreamReader * reader)
|
||||
{
|
||||
unsigned remainder = 0;
|
||||
unsigned bit, i;
|
||||
|
||||
while (reader->read(&bit, 1))
|
||||
{
|
||||
remainder <<= 1;
|
||||
remainder |= bit;
|
||||
if ((remainder & 0x10) == 0x10)
|
||||
{
|
||||
remainder ^= 0x13;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 4; i != 0; i--)
|
||||
{
|
||||
remainder <<= 1;
|
||||
if ((remainder & 0x10) != 0)
|
||||
{
|
||||
remainder ^= 0x13;
|
||||
}
|
||||
}
|
||||
|
||||
return remainder & 0xF;
|
||||
}
|
||||
|
||||
//
|
||||
// DP CRC for body
|
||||
//
|
||||
unsigned DisplayPort::dpCalculateBodyCRC(BitStreamReader * reader)
|
||||
{
|
||||
unsigned remainder = 0;
|
||||
unsigned bit, i;
|
||||
|
||||
while (reader->read(&bit, 1))
|
||||
{
|
||||
remainder <<= 1;
|
||||
remainder |= bit;
|
||||
if ((remainder & 0x100) == 0x100)
|
||||
{
|
||||
remainder ^= 0xD5;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 8; i != 0; i--)
|
||||
{
|
||||
remainder <<= 1;
|
||||
if ((remainder & 0x100) != 0)
|
||||
{
|
||||
remainder ^= 0xD5;
|
||||
}
|
||||
}
|
||||
|
||||
return remainder & 0xFF;
|
||||
}
|
||||
2552
src/common/displayport/src/dp_deviceimpl.cpp
Normal file
2552
src/common/displayport/src/dp_deviceimpl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
928
src/common/displayport/src/dp_discovery.cpp
Normal file
928
src/common/displayport/src/dp_discovery.cpp
Normal file
@@ -0,0 +1,928 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_discovery.cpp *
|
||||
* The DP MST discovery manager. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_discovery.h"
|
||||
#include "dp_messages.h"
|
||||
#include "dp_tracing.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
void DiscoveryManager::notifyLongPulse(bool status)
|
||||
{
|
||||
if (status)
|
||||
{
|
||||
Device device;
|
||||
device.address = Address(0);
|
||||
device.branch = hal->getSupportsMultistream();
|
||||
device.legacy = false;
|
||||
|
||||
detectBranch(device);
|
||||
}
|
||||
else if (!status)
|
||||
{
|
||||
removeDeviceTree(Address());
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryManager::detectBranch(Device device)
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
|
||||
//
|
||||
// 1. Create a LINK_ADDRESS_MESSAGE to send to this target so that we can find who he is
|
||||
// 2. Create a REMOTE_DPCD_WRITE to set the GUID for this target
|
||||
// *alternatively* we may have to use the local DPCD HAL to write this
|
||||
// 3. Enumerate any children that we may wish to queue detect on.
|
||||
//
|
||||
DP_LOG(("%s(): target = %s", __FUNCTION__, device.address.toString(sb)));
|
||||
|
||||
BranchDetection * branchDetection = new BranchDetection(this, device);
|
||||
outstandingBranchDetections.insertBack(branchDetection);
|
||||
branchDetection->start();
|
||||
}
|
||||
|
||||
void DiscoveryManager::detectSink(DiscoveryManager::Device device, bool bFromCSN)
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
|
||||
DP_LOG(("%s(): target = %s", __FUNCTION__, device.address.toString(sb)));
|
||||
SinkDetection * sinkDetection = new SinkDetection(this, device, bFromCSN);
|
||||
sinkDetection->start();
|
||||
}
|
||||
|
||||
DiscoveryManager::Device * DiscoveryManager::findDevice(const Address & address)
|
||||
{
|
||||
for (unsigned i = 0; i < currentDevicesCount; i++)
|
||||
if (currentDevices[i].address == address)
|
||||
{
|
||||
if (currentDevices[i].peerGuid.isGuidZero() && currentDevices[i].peerDevice != Dongle &&
|
||||
(currentDevices[i].dpcdRevisionMajor >= 1 && currentDevices[i].dpcdRevisionMinor >= 2))
|
||||
{
|
||||
DP_ASSERT(0 && "Zero guid for device even though its not a dongle type.");
|
||||
}
|
||||
return ¤tDevices[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DiscoveryManager::Device * DiscoveryManager::findDevice(GUID & guid)
|
||||
{
|
||||
if (guid.isGuidZero())
|
||||
{
|
||||
DP_ASSERT(0 && "zero guid search");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < currentDevicesCount; i++)
|
||||
{
|
||||
if (currentDevices[i].dpcdRevisionMajor <= 1 && currentDevices[i].dpcdRevisionMinor < 2)
|
||||
continue;
|
||||
|
||||
if (currentDevices[i].peerGuid == guid)
|
||||
return ¤tDevices[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DiscoveryManager::addDevice(const DiscoveryManager::Device & device)
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
|
||||
GUID guid = device.peerGuid;
|
||||
if (guid.isGuidZero() &&
|
||||
(device.peerDevice != Dongle) &&
|
||||
(device.dpcdRevisionMajor >= 1 && device.dpcdRevisionMinor >= 2))
|
||||
{
|
||||
DP_ASSERT(0 && "GUID missing for the device");
|
||||
}
|
||||
DP_ASSERT(!findDevice(device.address) && "Redundant add");
|
||||
sink->discoveryNewDevice(device);
|
||||
|
||||
DP_LOG(("DP-DM> New device '%s' %s %s %s", device.address.toString(sb),
|
||||
device.branch ? "Branch" : "", device.legacy ? "Legacy" : "",
|
||||
device.peerDevice == Dongle ? "Dongle" :
|
||||
device.peerDevice == DownstreamSink ? "DownstreamSink" : ""));
|
||||
|
||||
Address::NvU32Buffer addrBuffer;
|
||||
dpMemZero(addrBuffer, sizeof(addrBuffer));
|
||||
device.address.toNvU32Buffer(addrBuffer);
|
||||
NV_DPTRACE_INFO(NEW_MST_DEVICE, device.address.size(), addrBuffer[0], addrBuffer[1],
|
||||
addrBuffer[2], addrBuffer[3], device.branch, device.legacy, device.peerDevice);
|
||||
|
||||
if (currentDevicesCount < maximumTopologyNodes)
|
||||
{
|
||||
currentDevices[currentDevicesCount++] = device;
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryManager::removeDevice(Device * device)
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
|
||||
DP_LOG(("DP-DM> Lost device '%s' %s %s %s", device->address.toString(sb),
|
||||
device->branch ? "Branch" : "", device->legacy ? "Legacy" : "",
|
||||
device->peerDevice == Dongle ? "Dongle" :
|
||||
device->peerDevice == DownstreamSink ? "DownstreamSink" : ""));
|
||||
|
||||
sink->discoveryLostDevice(device->address);
|
||||
|
||||
for (unsigned i = (unsigned)(device-¤tDevices[0]); i < currentDevicesCount - 1; i++)
|
||||
currentDevices[i] = currentDevices[i+1];
|
||||
currentDevicesCount--;
|
||||
}
|
||||
|
||||
void DiscoveryManager::removeDeviceTree(const Address & prefix)
|
||||
{
|
||||
for (unsigned i = 0; i < currentDevicesCount;)
|
||||
if (currentDevices[i].address.under(prefix))
|
||||
removeDevice(¤tDevices[i]);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
|
||||
DiscoveryManager::Device * DiscoveryManager::findChildDeviceForBranchWithGuid
|
||||
(
|
||||
GUID guid,
|
||||
unsigned port,
|
||||
Address & childAddr
|
||||
)
|
||||
{
|
||||
// Find it in relevant parent's device list
|
||||
DiscoveryManager::Device * parentDevice = findDevice(guid);
|
||||
if (!parentDevice)
|
||||
{
|
||||
DP_LOG(("DM> No Parent present for the device in DB."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
childAddr = parentDevice->address;
|
||||
childAddr.append(port);
|
||||
return (findDevice(childAddr));
|
||||
}
|
||||
|
||||
void DiscoveryManager::SinkDetection::detectCompleted(bool passed)
|
||||
{
|
||||
// we could not read or write the guid
|
||||
if (!passed)
|
||||
{
|
||||
//
|
||||
// DP1.2 monitors that do not support GUID get filtered and dropped as 'not present'.
|
||||
// Instead we demote such monitors to DP1.1 and continue sink detection so that end
|
||||
// user at least gets active display scanout on such monitors (albeit reduced to DP1.1).
|
||||
//
|
||||
if (device.dpcdRevisionMajor > 1 || device.dpcdRevisionMinor >= 2)
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> sink at '%s' failed GUID identification, demote to 1.1 sink.",
|
||||
address.toString(sb)));
|
||||
device.dpcdRevisionMajor = 1;
|
||||
device.dpcdRevisionMinor = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Had it previously been reported as present?
|
||||
if (Device * device = parent->findDevice(address))
|
||||
parent->removeDevice(device);
|
||||
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// at this point we are sure that we have a device GUID.
|
||||
// We need to check whether the device is new to the DB.
|
||||
// Had we previously reported the device?
|
||||
|
||||
Device * oldDevice = parent->findDevice(device.address);
|
||||
|
||||
if (!oldDevice)
|
||||
{
|
||||
// completely new device
|
||||
parent->addDevice(device);
|
||||
}
|
||||
// If it was a branch and now isn't.. delete the tree of devices under it
|
||||
else if (oldDevice && oldDevice->branch && !device.branch)
|
||||
{
|
||||
parent->removeDeviceTree(device.address);
|
||||
}
|
||||
// It changed, delete the previously reported
|
||||
else if (oldDevice && (oldDevice->legacy != device.legacy ||
|
||||
oldDevice->dpcdRevisionMajor!= device.dpcdRevisionMajor ||
|
||||
oldDevice->dpcdRevisionMinor!= device.dpcdRevisionMinor ||
|
||||
oldDevice->peerDevice != device.peerDevice||
|
||||
oldDevice->peerGuid != device.peerGuid ||
|
||||
oldDevice->SDPStreams != device.SDPStreams||
|
||||
oldDevice->SDPStreamSinks != device.SDPStreamSinks ||
|
||||
oldDevice->videoSink != device.videoSink))
|
||||
{
|
||||
parent->removeDevice(oldDevice);
|
||||
}
|
||||
|
||||
// otherwise.. it already existed, and still does
|
||||
|
||||
// We're done
|
||||
completed = true;
|
||||
delete this;
|
||||
}
|
||||
|
||||
void DiscoveryManager::BranchDetection::detectCompleted(bool present)
|
||||
{
|
||||
//
|
||||
// Handle device not present
|
||||
//
|
||||
if (!present)
|
||||
{
|
||||
// Had it previously been reported as present?
|
||||
if (Device * device = parent->findDevice(address))
|
||||
parent->removeDevice(device);
|
||||
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// We've got a linkAddressMessage and we were able to program the GUID!
|
||||
// Report the branch and queue any children that were enumerated for detection
|
||||
//
|
||||
parent->addDevice(parentDevice);
|
||||
|
||||
unsigned portsToDelete = (1 << (Address::maxPortCount+1)) - 1; // 16 ports
|
||||
for (unsigned i = 0; i < childCount; i++)
|
||||
{
|
||||
Device newDevice;
|
||||
newDevice.address = address;
|
||||
newDevice.address.append(child[i].portNumber);
|
||||
|
||||
//
|
||||
// Input port? Nothing plugged in? Delete the tree of all devices under this one
|
||||
// DP 1.2 Spec : 2.11.9.5.x
|
||||
//
|
||||
if (child[i].isInputPort || !child[i].dpPlugged) {
|
||||
continue;
|
||||
}
|
||||
|
||||
portsToDelete &= ~(1 << child[i].portNumber);
|
||||
|
||||
newDevice.peerDevice = child[i].peerDeviceType;
|
||||
newDevice.legacy = child[i].legacyPlugged && (newDevice.peerDevice == Dongle);
|
||||
newDevice.dpcdRevisionMajor = child[i].dpcdRevisionMajor;
|
||||
newDevice.dpcdRevisionMinor = child[i].dpcdRevisionMinor;
|
||||
// if internal device; use parent's GUID which we ourselves generated or got from the LAM.
|
||||
if (child[i].portNumber > PHYSICAL_PORT_END)
|
||||
newDevice.peerGuid = parentDevice.peerGuid;
|
||||
else
|
||||
newDevice.peerGuid = child[i].peerGUID;
|
||||
|
||||
newDevice.SDPStreams = child[i].SDPStreams;
|
||||
newDevice.SDPStreamSinks = child[i].SDPStreamSinks;
|
||||
|
||||
if (child[i].peerDeviceType == DownstreamBranch &&
|
||||
child[i].hasMessaging)
|
||||
{
|
||||
newDevice.branch = true;
|
||||
newDevice.videoSink = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
newDevice.branch = false;
|
||||
newDevice.videoSink = ((child[i].peerDeviceType == Dongle) ?
|
||||
child[i].legacyPlugged : true);
|
||||
}
|
||||
|
||||
//
|
||||
// Had we previously reported the device?
|
||||
//
|
||||
Device * oldDevice = parent->findDevice(newDevice.address);
|
||||
|
||||
// If it was a branch and now isn't.. delete the tree of devices under it
|
||||
if (oldDevice && oldDevice->branch && !newDevice.branch)
|
||||
{
|
||||
parent->removeDeviceTree(newDevice.address);
|
||||
}
|
||||
// It changed, delete
|
||||
else if (oldDevice && (oldDevice->legacy != newDevice.legacy ||
|
||||
oldDevice->dpcdRevisionMajor!= newDevice.dpcdRevisionMajor ||
|
||||
oldDevice->dpcdRevisionMinor!= newDevice.dpcdRevisionMinor ||
|
||||
oldDevice->peerDevice != newDevice.peerDevice||
|
||||
oldDevice->peerGuid != newDevice.peerGuid ||
|
||||
oldDevice->SDPStreams != newDevice.SDPStreams||
|
||||
oldDevice->SDPStreamSinks != newDevice.SDPStreamSinks ||
|
||||
oldDevice->videoSink != newDevice.videoSink))
|
||||
{
|
||||
parent->removeDevice(oldDevice);
|
||||
}
|
||||
|
||||
// otherwise.. it already existed, and still does
|
||||
if (newDevice.branch)
|
||||
{
|
||||
parent->detectBranch(newDevice);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the new device is a sink. It may or may not have a guid.
|
||||
// write the guid if needed.
|
||||
parent->detectSink(newDevice, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i <= Address::maxPortCount; i++)
|
||||
if ((portsToDelete >> i) & 1)
|
||||
{
|
||||
Address a = address;
|
||||
a.append(i);
|
||||
parent->removeDeviceTree(a);
|
||||
}
|
||||
|
||||
// We're done
|
||||
completed = true;
|
||||
delete this;
|
||||
}
|
||||
|
||||
void DiscoveryManager::BranchDetection::expired(const void * tag)
|
||||
{
|
||||
if (retryLinkAddressMessage)
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> Requeing LINK_ADDRESS_MESSAGE to %s", address.toString(sb)));
|
||||
|
||||
retryLinkAddressMessage = false;
|
||||
linkAddressMessage.set(address);
|
||||
parent->messageManager->post(&linkAddressMessage, this);
|
||||
}
|
||||
else if (retryRemoteDpcdWriteMessage)
|
||||
{
|
||||
Address parentAddress = address;
|
||||
parentAddress.pop();
|
||||
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> Requeing REMOTE_DPCD_WRITE_MESSAGE to %s", parentAddress.toString(sb)));
|
||||
|
||||
retryRemoteDpcdWriteMessage = false;
|
||||
remoteDpcdWriteMessage.set(parentAddress, parentAddress.tail(), NV_DPCD_GUID, sizeof(GUID), (NvU8 *)&parentDevice.peerGuid);
|
||||
DP_LOG(("DP-DM> Setting GUID (remotely) for '%s' sent REMOTE_DPCD_WRITE {%p}", address.toString(sb), &remoteDpcdWriteMessage));
|
||||
|
||||
parent->messageManager->post(&remoteDpcdWriteMessage, this);
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryManager::SinkDetection::expired(const void * tag)
|
||||
{
|
||||
if (retryLinkAddressMessage)
|
||||
{
|
||||
Address parentAddress = address;
|
||||
parentAddress.pop();
|
||||
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> Requeueing LAM message to %s", parentAddress.toString(sb)));
|
||||
|
||||
retryLinkAddressMessage = false;
|
||||
linkAddressMessage.set(parentAddress);
|
||||
|
||||
parent->messageManager->post(&linkAddressMessage, this);
|
||||
}
|
||||
else if (retryRemoteDpcdReadMessage)
|
||||
{
|
||||
Address parentAddress = address;
|
||||
parentAddress.pop();
|
||||
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> Requeueing REMOTE_DPCD_READ_MESSAGE to %s", parentAddress.toString(sb)));
|
||||
|
||||
retryRemoteDpcdReadMessage = false;
|
||||
remoteDpcdReadMessage.set(parentAddress, parentAddress.tail(), NV_DPCD_GUID, sizeof(GUID));
|
||||
DP_LOG(("DP-DM> Setting GUID (remotely) for '%s' sent REMOTE_DPCD_READ {%p}", address.toString(sb), &remoteDpcdReadMessage));
|
||||
|
||||
parent->messageManager->post(&remoteDpcdReadMessage, this);
|
||||
}
|
||||
else if (retryRemoteDpcdWriteMessage)
|
||||
{
|
||||
Address parentAddress = address;
|
||||
parentAddress.pop();
|
||||
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> Requeueing REMOTE_DPCD_WRITE_MESSAGE to %s", parentAddress.toString(sb)));
|
||||
|
||||
retryRemoteDpcdWriteMessage = false;
|
||||
remoteDpcdWriteMessage.set(parentAddress,
|
||||
parentAddress.tail(),
|
||||
NV_DPCD_GUID, sizeof(GUID),
|
||||
(NvU8 *)&device.peerGuid);
|
||||
DP_LOG(("DP-DM> Setting GUID (remotely) for '%s' sent REMOTE_DPCD_WRITE {%p}", address.toString(sb), &remoteDpcdWriteMessage));
|
||||
|
||||
parent->messageManager->post(&remoteDpcdWriteMessage, this);
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryManager::BranchDetection::messageFailed(MessageManager::Message * from, NakData * nakData)
|
||||
{
|
||||
//
|
||||
// If any of our messages fail, we've completed detection on this buzzard.
|
||||
// The only exception is if we get a DEFER - then we retry indefinitely
|
||||
//
|
||||
if (from == &linkAddressMessage)
|
||||
{
|
||||
if (retriesLinkAddressMessage < DPCD_LINK_ADDRESS_MESSAGE_RETRIES &&
|
||||
(nakData->reason == NakDefer || nakData->reason == NakTimeout))
|
||||
{
|
||||
retriesLinkAddressMessage++;
|
||||
retryLinkAddressMessage = true;
|
||||
parent->timer->queueCallback(this, "DISC", DPCD_LINK_ADDRESS_MESSAGE_COOLDOWN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (from == &remoteDpcdWriteMessage)
|
||||
{
|
||||
if ((retriesRemoteDpcdWriteMessage < DPCD_REMOTE_DPCD_WRITE_MESSAGE_RETRIES) &&
|
||||
(nakData->reason == NakDefer || nakData->reason == NakTimeout))
|
||||
{
|
||||
retriesRemoteDpcdWriteMessage++;
|
||||
retryRemoteDpcdWriteMessage = true;
|
||||
parent->timer->queueCallback(this, "DISC", DPCD_REMOTE_DPCD_WRITE_MESSAGE_COOLDOWN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> Message %s {%p} at '%s' failed. Device marked not present.",
|
||||
from == &linkAddressMessage ? "LINK_ADDRESS_MESSAGE" :
|
||||
from == &remoteDpcdWriteMessage ? "REMOTE_DPCD_WRITE(GUID)" : "???",
|
||||
from, address.toString(sb)));
|
||||
|
||||
|
||||
//
|
||||
// Detection is done and branch doesn't exist.
|
||||
// (Note this automatically removes self from any list we're in)
|
||||
//
|
||||
detectCompleted(false);
|
||||
}
|
||||
|
||||
void DiscoveryManager::SinkDetection::messageFailed(MessageManager::Message * from, NakData * nakData)
|
||||
{
|
||||
if (from == &remoteDpcdReadMessage)
|
||||
{
|
||||
if ((retriesRemoteDpcdReadMessage < DPCD_REMOTE_DPCD_READ_MESSAGE_RETRIES) &&
|
||||
(nakData->reason == NakDefer || nakData->reason == NakTimeout))
|
||||
{
|
||||
retriesRemoteDpcdReadMessage++;
|
||||
retryRemoteDpcdReadMessage = true;
|
||||
parent->timer->queueCallback(this, "DISC", DPCD_REMOTE_DPCD_READ_MESSAGE_COOLDOWN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (from == &remoteDpcdWriteMessage)
|
||||
{
|
||||
if ((retriesRemoteDpcdWriteMessage < DPCD_REMOTE_DPCD_WRITE_MESSAGE_RETRIES) &&
|
||||
(nakData->reason == NakDefer || nakData->reason == NakTimeout))
|
||||
{
|
||||
retriesRemoteDpcdWriteMessage++;
|
||||
retryRemoteDpcdWriteMessage = true;
|
||||
parent->timer->queueCallback(this, "DISC", DPCD_REMOTE_DPCD_WRITE_MESSAGE_COOLDOWN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (from == &linkAddressMessage)
|
||||
{
|
||||
if ((retriesLinkAddressMessage < DPCD_LINK_ADDRESS_MESSAGE_RETRIES) &&
|
||||
(nakData->reason == NakDefer || nakData->reason == NakTimeout))
|
||||
{
|
||||
retriesLinkAddressMessage++;
|
||||
retryLinkAddressMessage = true;
|
||||
parent->timer->queueCallback(this, "DISC", DPCD_LINK_ADDRESS_MESSAGE_COOLDOWN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> Message %s {%p} at '%s' failed.",
|
||||
from == &remoteDpcdWriteMessage ? "REMOTE_DPCD_WRITE(GUID)" :
|
||||
from == &remoteDpcdReadMessage ? "REMOTE_DPCD_READ(GUID)" :
|
||||
from == &linkAddressMessage ? "LINK_ADDRESS_MESSAGE" : "???",
|
||||
from, address.toString(sb)));
|
||||
|
||||
detectCompleted(false);
|
||||
}
|
||||
|
||||
void DiscoveryManager::SinkDetection::handleLinkAddressDownReply()
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
LinkAddressMessage::Result child;
|
||||
child = *linkAddressMessage.result(address.tail());
|
||||
|
||||
device.peerDevice = child.peerDeviceType;
|
||||
device.dpcdRevisionMajor = child.dpcdRevisionMajor;
|
||||
device.dpcdRevisionMinor = child.dpcdRevisionMinor;
|
||||
|
||||
if (device.dpcdRevisionMajor == 0)
|
||||
{
|
||||
device.dpcdRevisionMajor = 1;
|
||||
device.dpcdRevisionMinor = 1;
|
||||
}
|
||||
device.portMap.inputMap |= (1 << child.portNumber);
|
||||
|
||||
DP_LOG(("DP-DM> handleLinkAddressDownReply for sink device on '%s': DPCD Rev = %d.%d",
|
||||
address.toString(sb), device.dpcdRevisionMajor, device.dpcdRevisionMinor));
|
||||
|
||||
// Check if the device already has a GUID
|
||||
// or it is a dongle or on a logical port ; in which case no GUID is required.
|
||||
if ((!device.peerGuid.isGuidZero()) ||
|
||||
(device.peerDevice == Dongle) ||
|
||||
(device.dpcdRevisionMajor <= 1 && device.dpcdRevisionMinor < 2) ||
|
||||
(device.address.tail() > PHYSICAL_PORT_END))
|
||||
{
|
||||
parent->addDevice(device);
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
Address parentAddress = address.parent();
|
||||
remoteDpcdReadMessage.set(parentAddress, address.tail(), NV_DPCD_GUID, sizeof(GUID));
|
||||
|
||||
parent->messageManager->post(&remoteDpcdReadMessage, this);
|
||||
|
||||
}
|
||||
|
||||
void DiscoveryManager::SinkDetection::handleRemoteDpcdReadDownReply()
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> REMOTE_DPCD_READ {%p} at '%s' completed",
|
||||
(MessageManager::Message *)&remoteDpcdReadMessage,
|
||||
address.toString(sb)));
|
||||
if (remoteDpcdReadMessage.replyNumOfBytesReadDPCD() != sizeof(GUID))
|
||||
{
|
||||
DP_ASSERT(0 && "Incomplete GUID in remote DPCD read message");
|
||||
detectCompleted(false);
|
||||
return;
|
||||
}
|
||||
|
||||
DP_ASSERT(remoteDpcdReadMessage.replyPortNumber() == address.tail());
|
||||
device.peerGuid.copyFrom(remoteDpcdReadMessage.replyGetData());
|
||||
|
||||
if (!device.peerGuid.isGuidZero())
|
||||
{
|
||||
// we got the GUID ... handle device add/remove
|
||||
detectCompleted(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// We need to give ourselves a non-zero GUID!
|
||||
//
|
||||
parent->guidBuilder.makeGuid(device.peerGuid);
|
||||
|
||||
Address parentAddress = address.parent();
|
||||
remoteDpcdWriteMessage.set(parentAddress,
|
||||
address.tail(),
|
||||
NV_DPCD_GUID, sizeof(GUID),
|
||||
(NvU8 *)&device.peerGuid);
|
||||
|
||||
DP_LOG(("DP-DM> Setting GUID (remotely) for '%s' sent REMOTE_DPCD_WRITE {%p}",
|
||||
address.toString(sb), &remoteDpcdWriteMessage));
|
||||
|
||||
parent->messageManager->post(&remoteDpcdWriteMessage, this);
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryManager::BranchDetection::handleLinkAddressDownReply()
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
|
||||
//
|
||||
// Copy link address results out of the structure
|
||||
// - We cannot process the contents until after
|
||||
// we've programmed the GUID. The reasoning is
|
||||
// that we need to make sure we do not enumerate
|
||||
// devices not yet in a usable state.
|
||||
//
|
||||
childCount = linkAddressMessage.resultCount();
|
||||
for (unsigned i = 0; i < childCount; i++)
|
||||
{
|
||||
child[i] = *linkAddressMessage.result(i);
|
||||
|
||||
// also update the portmap
|
||||
parentDevice.portMap.internalMap = 0xFF00; // ports 0x8 to 0xF are internal
|
||||
parentDevice.portMap.validMap |= (1 << child[i].portNumber);
|
||||
if (child[i].isInputPort)
|
||||
{
|
||||
parentDevice.peerDevice = child[i].peerDeviceType;
|
||||
parentDevice.dpcdRevisionMajor = child[i].dpcdRevisionMajor;
|
||||
parentDevice.dpcdRevisionMinor = child[i].dpcdRevisionMinor;
|
||||
parentDevice.portMap.inputMap |= (1 << child[i].portNumber);
|
||||
}
|
||||
}
|
||||
|
||||
linkAddressMessage.getGUID(parentDevice.peerGuid);
|
||||
if (parentDevice.peerGuid.isGuidZero())
|
||||
{
|
||||
//
|
||||
// We need to give ourselves a non-zero GUID!
|
||||
//
|
||||
parent->guidBuilder.makeGuid(parentDevice.peerGuid);
|
||||
|
||||
if (address == Address(0))
|
||||
{
|
||||
DP_LOG(("DP-DM> Setting GUID (locally) for '%s'", address.toString(sb)));
|
||||
//
|
||||
// We're locally connected, use the DPCD HAL to write the new GUID
|
||||
//
|
||||
if (AuxRetry::ack != parent->hal->setGUID(parentDevice.peerGuid))
|
||||
{
|
||||
detectCompleted(false);
|
||||
return;
|
||||
}
|
||||
|
||||
detectCompleted(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Let's build a remote DPCD request. Remember the target is the *parent*
|
||||
// of the device we want to talk to
|
||||
//
|
||||
Address parentAddress = address;
|
||||
parentAddress.pop();
|
||||
remoteDpcdWriteMessage.set(parentAddress, address.tail(),
|
||||
NV_DPCD_GUID, sizeof(GUID),
|
||||
(NvU8 *)&parentDevice.peerGuid);
|
||||
|
||||
DP_LOG(("DP-DM> Setting GUID (remotely) for '%s' sent REMOTE_DPCD_WRITE {%p}",
|
||||
address.toString(sb), &remoteDpcdWriteMessage));
|
||||
|
||||
parent->messageManager->post(&remoteDpcdWriteMessage, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Already had a GUID
|
||||
//
|
||||
detectCompleted(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DiscoveryManager::BranchDetection::messageCompleted(MessageManager::Message * from)
|
||||
{
|
||||
if (from == &linkAddressMessage)
|
||||
handleLinkAddressDownReply();
|
||||
else if (from == &remoteDpcdWriteMessage)
|
||||
detectCompleted(true);
|
||||
}
|
||||
|
||||
void DiscoveryManager::SinkDetection::messageCompleted(MessageManager::Message * from)
|
||||
{
|
||||
if (from == &remoteDpcdReadMessage)
|
||||
handleRemoteDpcdReadDownReply();
|
||||
else if (from == &linkAddressMessage)
|
||||
handleLinkAddressDownReply();
|
||||
else if (from == &remoteDpcdWriteMessage)
|
||||
detectCompleted(true);
|
||||
}
|
||||
|
||||
void DiscoveryManager::BranchDetection::start()
|
||||
{
|
||||
//
|
||||
// 1. Create a LINK_ADDRESS_MESSAGE to send to this target so that we can find who he is
|
||||
// 2. Create a REMOTE_DPCD_WRITE to set the GUID for this target
|
||||
// *alternatively* we may have to use the local DPCD HAL to write this
|
||||
// 3. Enumerate any children that we may wish to queue detect on.
|
||||
//
|
||||
linkAddressMessage.set(address);
|
||||
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
DP_LOG(("DP-DM> Detecting '%s' (sending LINK_ADDRESS_MESSAGE {%p})",
|
||||
address.toString(sb),
|
||||
(MessageManager::Message *)&linkAddressMessage));
|
||||
|
||||
parent->messageManager->post(&linkAddressMessage, this);
|
||||
}
|
||||
|
||||
void DiscoveryManager::SinkDetection::start()
|
||||
{
|
||||
//
|
||||
// Per DP1.4 requirement:
|
||||
// Send PowerUpPhy message first, to make sure device is ready to work
|
||||
//
|
||||
NakData nakData;
|
||||
powerUpPhyMessage.set(address.parent(), address.tail(), NV_TRUE);
|
||||
parent->messageManager->send(&powerUpPhyMessage, nakData);
|
||||
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
|
||||
// The sink is found in CSN, missing dpcd revision
|
||||
if (bFromCSN)
|
||||
{
|
||||
parent->outstandingSinkDetections.insertBack(this);
|
||||
// Create a LINK_ADDRESS_MESSAGE to send to parent of this target
|
||||
linkAddressMessage.set(address.parent());
|
||||
|
||||
DP_LOG(("DP-DM> Detecting '%s' (sending LINK_ADDRESS_MESSAGE {%p})",
|
||||
address.toString(sb),
|
||||
(MessageManager::Message *)&linkAddressMessage));
|
||||
parent->messageManager->post(&linkAddressMessage, this);
|
||||
}
|
||||
else // The sink is found in LAM sent for branch, and with DPCD rev.
|
||||
{
|
||||
// Check if the device already has a GUID
|
||||
// or it is a dongle or on a logical port ; in which case no GUID is required.
|
||||
if ((!device.peerGuid.isGuidZero()) ||
|
||||
(device.peerDevice == Dongle) ||
|
||||
(device.dpcdRevisionMajor <= 1 && device.dpcdRevisionMinor < 2) ||
|
||||
(device.address.tail() > PHYSICAL_PORT_END))
|
||||
{
|
||||
parent->addDevice(device);
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
parent->outstandingSinkDetections.insertBack(this);
|
||||
Address parentAddress = address.parent();
|
||||
remoteDpcdReadMessage.set(parentAddress, address.tail(), NV_DPCD_GUID, sizeof(GUID));
|
||||
|
||||
parent->messageManager->post(&remoteDpcdReadMessage, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DiscoveryManager::BranchDetection::~BranchDetection()
|
||||
{
|
||||
List::remove(this);
|
||||
|
||||
if (parent->outstandingSinkDetections.isEmpty() &&
|
||||
parent->outstandingBranchDetections.isEmpty())
|
||||
parent->sink->discoveryDetectComplete();
|
||||
|
||||
parent->timer->cancelCallbacks(this);
|
||||
}
|
||||
|
||||
DiscoveryManager::SinkDetection::~SinkDetection()
|
||||
{
|
||||
List::remove(this);
|
||||
|
||||
if (parent->outstandingSinkDetections.isEmpty() &&
|
||||
parent->outstandingBranchDetections.isEmpty())
|
||||
parent->sink->discoveryDetectComplete();
|
||||
|
||||
parent->timer->cancelCallbacks(this);
|
||||
}
|
||||
|
||||
void DiscoveryManager::ReceiverSink::messageProcessed(MessageManager::MessageReceiver * from)
|
||||
{
|
||||
DP_ASSERT((from->getRequestId() == 0x2) && "This receiver is only meant for CSNs");
|
||||
|
||||
// CSNs are broadcast messages. So replies will always go to immediate downstream branch
|
||||
CsnUpReplyContainer * csnReplyContainer = new CsnUpReplyContainer(parent);
|
||||
parent->pendingCsnUpReplies.insertBack(csnReplyContainer);
|
||||
|
||||
//Send acknowledgement to the CSN sender.
|
||||
csnReplyContainer->postUpReply();
|
||||
|
||||
ConnStatusNotifyMessage* csnMessage = static_cast<ConnStatusNotifyMessage*>(from);
|
||||
|
||||
if (csnMessage->getUpRequestData()->isInputPort)
|
||||
{
|
||||
DP_LOG(("Concentrator?? Got CSN for an upstream port!"));
|
||||
return;
|
||||
}
|
||||
|
||||
Address childAddr;
|
||||
DiscoveryManager::Device * oldDevice = parent->findChildDeviceForBranchWithGuid(csnMessage->getUpRequestData()->guid,
|
||||
csnMessage->getUpRequestData()->port, childAddr);
|
||||
if (!csnMessage->getUpRequestData()->devicePlugged) // some device was unplugged or powered off
|
||||
{
|
||||
if (oldDevice)
|
||||
parent->removeDeviceTree(childAddr);
|
||||
return;
|
||||
}
|
||||
|
||||
handleCSN(from);
|
||||
}
|
||||
|
||||
void DiscoveryManager::ReceiverSink::handleCSN(MessageManager::MessageReceiver * from)
|
||||
{
|
||||
ConnStatusNotifyMessage* csnMessage = static_cast<ConnStatusNotifyMessage*>(from);
|
||||
|
||||
// There is no point in serving an upRequest when no device is present.
|
||||
if (parent->currentDevicesCount == 0)
|
||||
{
|
||||
DP_ASSERT(0 && "DM> No Device in the Topology");
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Check for non-zero GUID in CSN message. It is mandatory to find respective parent
|
||||
// Branch should not send CSN with Zero GUID as a unique GUID is set before CSN
|
||||
//
|
||||
if ((csnMessage->getUpRequestData()->guid).isGuidZero())
|
||||
{
|
||||
DP_ASSERT(0 && "Ignoring CSN. Invalid parent device due to zero-GUID.");
|
||||
return;
|
||||
}
|
||||
|
||||
Address childAddr;
|
||||
unsigned port = csnMessage->getUpRequestData()->port;
|
||||
DiscoveryManager::Device * oldDevice =
|
||||
parent->findChildDeviceForBranchWithGuid(csnMessage->getUpRequestData()->guid,
|
||||
port,
|
||||
childAddr);
|
||||
|
||||
// Check if we already have a device
|
||||
if (oldDevice)
|
||||
{
|
||||
oldDevice->dirty = true;
|
||||
|
||||
// Set the videoSink status of oldDevice again as old device might be a legacy dongle
|
||||
// and a video sink is now added with it
|
||||
oldDevice->videoSink = ((csnMessage->getUpRequestData()->peerDeviceType == Dongle) ?
|
||||
csnMessage->getUpRequestData()->legacyPlugged : true);
|
||||
|
||||
parent->sink->discoveryNewDevice(*oldDevice);
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit if no valid address matched for further detection.
|
||||
if ((childAddr.size() == 0) ||
|
||||
(childAddr.size() > Address::maxHops))
|
||||
{
|
||||
DP_ASSERT(0 && "Ignoring CSN. Invalid parent device due to GUID not found in discovered topology");
|
||||
return;
|
||||
}
|
||||
|
||||
DiscoveryManager::Device newDevice;
|
||||
newDevice.address = childAddr;
|
||||
newDevice.branch = (csnMessage->getUpRequestData()->messagingCapability == true) &&
|
||||
(csnMessage->getUpRequestData()->peerDeviceType == DownstreamBranch);
|
||||
|
||||
newDevice.peerDevice = csnMessage->getUpRequestData()->peerDeviceType;
|
||||
newDevice.legacy = csnMessage->getUpRequestData()->legacyPlugged == true;
|
||||
newDevice.SDPStreams = newDevice.SDPStreamSinks = 0;
|
||||
|
||||
if (csnMessage->getUpRequestData()->devicePlugged) // Check for a new device only if it's plugged
|
||||
{
|
||||
if (newDevice.branch)
|
||||
{
|
||||
newDevice.videoSink = false;
|
||||
// send a LAM and the whole nine yards
|
||||
DP_ASSERT(newDevice.legacy == false);
|
||||
parent->detectBranch(newDevice);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
newDevice.SDPStreams = newDevice.SDPStreamSinks = 1;
|
||||
newDevice.videoSink = ((csnMessage->getUpRequestData()->peerDeviceType == Dongle) ?
|
||||
csnMessage->getUpRequestData()->legacyPlugged : true);
|
||||
|
||||
parent->detectSink(newDevice, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
625
src/common/displayport/src/dp_edid.cpp
Normal file
625
src/common/displayport/src/dp_edid.cpp
Normal file
@@ -0,0 +1,625 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_edid.c *
|
||||
* Implementation of SST/MST EDID reader *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_buffer.h"
|
||||
#include "dp_internal.h"
|
||||
#include "dp_edid.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
EdidAssembler::EdidAssembler(Edid * const edid, bool bPatchCrc):
|
||||
edid(edid), stream(edid->getBuffer()), oldBlockChecksum(0x00),
|
||||
blocksRead(0), totalBlockCnt(0), retriesCount(0),
|
||||
bPatchCrc(bPatchCrc) {}
|
||||
|
||||
|
||||
bool EdidAssembler::readIsComplete()
|
||||
{
|
||||
return (blocksRead > 0 && blocksRead == totalBlockCnt);
|
||||
}
|
||||
|
||||
void EdidAssembler::reset()
|
||||
{
|
||||
oldBlockChecksum = 0x00;
|
||||
blocksRead = 0;
|
||||
totalBlockCnt = 0;
|
||||
retriesCount = 0;
|
||||
stream.seek(0);
|
||||
}
|
||||
|
||||
void EdidAssembler::postReply(const Buffer & buffer, unsigned sizeCompleted, bool success)
|
||||
{
|
||||
if (!success || buffer.isError())
|
||||
{
|
||||
retriesCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// For SST:
|
||||
// Check the Checksum Error Per Block reading, mark the EDID as "patched" if
|
||||
// CRC is wrong. DPLib will return fallback EDID.
|
||||
//
|
||||
blocksRead++;
|
||||
stream.write(buffer.data, sizeCompleted);
|
||||
if (getEDIDBlockChecksum(buffer))
|
||||
{
|
||||
if (bPatchCrc)
|
||||
edid->patchCrc();
|
||||
edid->setPatchedChecksum(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void EdidAssembler::postReply(unsigned char * data, unsigned sizeCompleted, bool success)
|
||||
{
|
||||
//
|
||||
// For MST: When read of edid block failed, library will attempt to read
|
||||
// same block again, but not more than EDID_POLICY_BLOCK_READ_MAX_RETRY_COUNT times
|
||||
//
|
||||
if (!success)
|
||||
{
|
||||
retriesCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Check the Checksum Error Per Block reading,
|
||||
// library will attempt to read same block again,
|
||||
// but not more than EDID_POLICY_BLOCK_READ_MAX_RETRY_COUNT times.
|
||||
//
|
||||
Buffer buffer(data, EDID_BLOCK_SIZE);
|
||||
if (buffer.isError())
|
||||
{
|
||||
retriesCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
NvU8 newBlockChecksum = getEDIDBlockChecksum(buffer);
|
||||
if (newBlockChecksum)
|
||||
{
|
||||
if (this->oldBlockChecksum != newBlockChecksum) //First failure?
|
||||
{
|
||||
this->oldBlockChecksum = newBlockChecksum;
|
||||
retriesCount++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->oldBlockChecksum = 0;
|
||||
retriesCount = 0;
|
||||
blocksRead++;
|
||||
stream.write(data, sizeCompleted);
|
||||
}
|
||||
|
||||
bool EdidAssembler::readNextRequest(NvU8 & seg, NvU8 & offset)
|
||||
{
|
||||
//
|
||||
// cache totalBlockCnt,
|
||||
// In EDID 1.3 HF-EEODB, it might changes after 1 extension block read.
|
||||
//
|
||||
if ((blocksRead == 1) || (blocksRead == 2))
|
||||
totalBlockCnt = edid->getBlockCount();
|
||||
|
||||
//
|
||||
// will return false in two scenarios
|
||||
// 1. EDID read is complete, all extension blocks were read
|
||||
// 2. First EDID block was corrupted, then totalBlockCnt = 0
|
||||
//
|
||||
if (blocksRead >= totalBlockCnt)
|
||||
return false;
|
||||
|
||||
// Retry count exceeded for particular block?
|
||||
if (retriesCount > EDID_POLICY_BLOCK_READ_MAX_RETRY_COUNT)
|
||||
return false;
|
||||
|
||||
seg = NvU8(blocksRead >> 1);
|
||||
offset = NvU8((blocksRead & 0x1) * EDID_BLOCK_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
EDID_V1_IDX_EXTENSION = 0x7E,
|
||||
EDID_V1_IDX_HEADER0 = 0x00,
|
||||
EDID_V1_HEADER0 = 0x00,
|
||||
|
||||
EDID_V1_IDX_HEADER1 = 0x01,
|
||||
EDID_V1_HEADER1 = 0xFF,
|
||||
|
||||
EDID_V1_IDX_VERSION = 0x12,
|
||||
EDID_V1_VERSION_1 = 0x01,
|
||||
EDID_V2_IDX_VERREV = 0x00,
|
||||
|
||||
//
|
||||
// from od_edid.h RM to identify VER 2, use 7:4 bits.
|
||||
// #define EDID_V2_VERREV_VERSION 7:4 /* RW--F */
|
||||
// #define EDID_V2_VERREV_VERSION_2 0x02 /* RWI-V */
|
||||
//
|
||||
// Avoiding FLD_* macros, thus shift VER2 value 4 bits to left
|
||||
//
|
||||
EDID_V2_VERREV_VERSION_2 = 0x02 << 4,
|
||||
EDID_FLAGS_CHKSUM_ATTEMPTS_DP = 0x5,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
// EDID CTA-EXT (CTA 861 Extension) block defines
|
||||
EDID_CTA_EXT_HEADER_OFFSET = 0x00,
|
||||
EDID_CTA_EXT_HEADER = 0x02,
|
||||
EDID_CTA_EXT_VERSION_OFFSET = 0x01,
|
||||
EDID_CTA_EXT_VERSION_3 = 0x03,
|
||||
EDID_CTA_EXT_DATA_BLOCK_HEADER_OFFSET = 0x04,
|
||||
EDID_CTA_EXT_DATA_BLOCK_HEADER_HF_EEODB = 0xE2,
|
||||
EDID_CTA_EXT_DATA_BLOCK_TAG_OFFSET = 0x05,
|
||||
EDID_CTA_EXT_DATA_BLOCK_TAG_HF_EEODB = 0x78,
|
||||
EDID_CTA_EXT_DATA_BLOCK_EXT_COUNT_OFFSET = 0x06,
|
||||
};
|
||||
|
||||
Edid::Edid(): buffer()
|
||||
{
|
||||
// fill EDID buffer with zeroes
|
||||
this->buffer.memZero();
|
||||
checkSumValid = false;
|
||||
forcedCheckSum = false;
|
||||
fallbackEdid = false;
|
||||
patchedChecksum = false;
|
||||
|
||||
// clear the WARFlags
|
||||
_WARFlags temp = {0};
|
||||
WARFlags = temp;
|
||||
}
|
||||
|
||||
Edid::~Edid()
|
||||
{
|
||||
}
|
||||
|
||||
bool Edid::verifyCRC()
|
||||
{
|
||||
if (getEdidSize() > 0)
|
||||
{
|
||||
this->validateCheckSum();
|
||||
return this->checkSumValid;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// this routine patches the edid crc after it has been overridden for WARs.
|
||||
void Edid::patchCrc()
|
||||
{
|
||||
// we always override some bytes within the first 128
|
||||
// recalculate and fix the checksum for the first page only.
|
||||
unsigned chksum = 0;
|
||||
for (unsigned i = 0; i < 128; i++)
|
||||
{
|
||||
chksum += buffer.data[i];
|
||||
}
|
||||
chksum = chksum & 0xFF;
|
||||
|
||||
if (chksum)
|
||||
buffer.data[127] = 0xFF & (buffer.data[127] + (0x100 - chksum));
|
||||
}
|
||||
|
||||
bool Edid::isChecksumValid() const
|
||||
{
|
||||
// return checksum valid if it is.
|
||||
// else return checksum is valid if checksum wasn't valid but we will assume it to be.
|
||||
return (checkSumValid || forcedCheckSum);
|
||||
}
|
||||
|
||||
bool Edid::isFallbackEdid() const
|
||||
{
|
||||
return fallbackEdid;
|
||||
}
|
||||
|
||||
NvU8 Edid::getFirstPageChecksum()
|
||||
{
|
||||
DP_ASSERT(buffer.getLength() >= 128);
|
||||
if (buffer.getLength() < 128)
|
||||
return 0;
|
||||
else
|
||||
return buffer.data[127];
|
||||
}
|
||||
|
||||
NvU8 Edid::getLastPageChecksum()
|
||||
{
|
||||
NvU32 bufferSize = buffer.getLength();
|
||||
NvU32 checksumLocation = this->getBlockCount() * 128 - 1;
|
||||
|
||||
if (bufferSize == 0 || bufferSize < (this->getBlockCount() * 128))
|
||||
{
|
||||
DP_LOG(("DP-EDID> Edid length is 0 or less than required"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bufferSize % 128 != 0)
|
||||
{
|
||||
DP_LOG(("DP-EDID> Edid length is not a multiple of 128"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return buffer.data[checksumLocation];
|
||||
|
||||
}
|
||||
|
||||
void Edid::validateCheckSum()
|
||||
{
|
||||
// Each page has its own checksum
|
||||
checkSumValid = false;
|
||||
for (unsigned chunk = 0; chunk < this->buffer.length; chunk += 128)
|
||||
{
|
||||
unsigned chksum = 0;
|
||||
for (unsigned i = 0; i < 128; i++)
|
||||
{
|
||||
chksum += buffer.data[i+chunk];
|
||||
}
|
||||
|
||||
if ((chksum & 0xFF) != 0)
|
||||
return;
|
||||
}
|
||||
checkSumValid = true;
|
||||
}
|
||||
|
||||
unsigned Edid::getEdidVersion()
|
||||
{
|
||||
if (buffer.isError() || buffer.length < EDID_BLOCK_SIZE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0 version is "unknown"
|
||||
unsigned version = 0;
|
||||
|
||||
// Check for Version 1 EDID
|
||||
if (this->buffer.data[EDID_V1_IDX_VERSION] == EDID_V1_VERSION_1)
|
||||
{
|
||||
version = 1;
|
||||
}
|
||||
// Check for version 2 EDID
|
||||
else if (this->buffer.data[EDID_V2_IDX_VERREV] & EDID_V2_VERREV_VERSION_2)
|
||||
{
|
||||
//
|
||||
// Version 2 has 256 bytes by default.
|
||||
// There is a note about an extra 256 byte block if byte 0x7E
|
||||
// bit 7 is set but there's no definition for it listed in
|
||||
// the EDID Version 3 (971113). So, let's just skip it for now.
|
||||
//
|
||||
version = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DP_ASSERT(version && "Unknown EDID version");
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
const char * Edid::getName() const
|
||||
{
|
||||
static char decodedName[16] = {0};
|
||||
int tail = 0;
|
||||
if (buffer.length < 128)
|
||||
return "?";
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (buffer.data[0x39 + i * 18 + 0] == 0xFC)
|
||||
{
|
||||
for (int j = 0; j < 13; j++)
|
||||
decodedName[tail++] = buffer.data[0x39 + i*18 + 2 + j];
|
||||
break;
|
||||
}
|
||||
decodedName[tail++] = 0;
|
||||
return decodedName;
|
||||
}
|
||||
|
||||
unsigned Edid::getBlockCount()
|
||||
{
|
||||
if (buffer.isError() || buffer.length < EDID_BLOCK_SIZE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned version = getEdidVersion();
|
||||
|
||||
if (version == 1)
|
||||
{
|
||||
NvU32 blockCount = (unsigned) this->buffer.data[EDID_V1_IDX_EXTENSION]+1;
|
||||
|
||||
if (blockCount > EDID_MAX_BLOCK_COUNT)
|
||||
{
|
||||
DP_LOG(("DPEDID> %s: DDC read returned questionable results: "
|
||||
"Total block Count too high: %d",
|
||||
__FUNCTION__, blockCount));
|
||||
return 1;
|
||||
}
|
||||
//
|
||||
// Check for the HF-EEODB defined in HDMI 2.1 specification.
|
||||
// 1. It is EDID version 1.3 and the extension block count is 1 (total block count = 2)
|
||||
// 2. The 1st EDID extension block is already read. (buffer.length > block size)
|
||||
// 3. The 1st EDID extension block is CTA extension block.
|
||||
// 4. It has HF-EEODB (1st extension block: byte4 == 0xE2 and byte5 == 0x78)
|
||||
//
|
||||
if ((blockCount == 2) && (buffer.length >= EDID_BLOCK_SIZE * 2))
|
||||
{
|
||||
NvU8 *pExt = &(this->buffer.data[EDID_BLOCK_SIZE]);
|
||||
|
||||
//
|
||||
// If it's a CTA-EXT block version 3 and has HF-EEODB
|
||||
// defined, update the total block count.
|
||||
//
|
||||
if ((pExt[EDID_CTA_EXT_HEADER_OFFSET] == EDID_CTA_EXT_HEADER) &&
|
||||
(pExt[EDID_CTA_EXT_VERSION_OFFSET] == EDID_CTA_EXT_VERSION_3) &&
|
||||
(pExt[EDID_CTA_EXT_DATA_BLOCK_HEADER_OFFSET] == EDID_CTA_EXT_DATA_BLOCK_HEADER_HF_EEODB) &&
|
||||
(pExt[EDID_CTA_EXT_DATA_BLOCK_TAG_OFFSET] == EDID_CTA_EXT_DATA_BLOCK_TAG_HF_EEODB))
|
||||
{
|
||||
blockCount = pExt[EDID_CTA_EXT_DATA_BLOCK_EXT_COUNT_OFFSET] + 1;
|
||||
}
|
||||
|
||||
}
|
||||
return blockCount;
|
||||
}
|
||||
else if (version == 2)
|
||||
{
|
||||
//
|
||||
// Version 2 has 256 bytes by default.
|
||||
// There is a note about an extra 256 byte block
|
||||
// if byte 0x7E bit 7 is set, but there's no
|
||||
// definition for it listed in the
|
||||
// EDID Version 3 (971113) So, let's just skip
|
||||
// it for now.
|
||||
//
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown EDID version. Skip it.
|
||||
DP_LOG(("DPEDID> %s: Unknown EDID Version!",__FUNCTION__));
|
||||
DP_ASSERT(0 && "Unknown EDID version!");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned Edid::getEdidSize() const
|
||||
{
|
||||
return this->buffer.length;
|
||||
}
|
||||
|
||||
void DisplayPort::Edid::swap(Edid & right)
|
||||
{
|
||||
swapBuffers(buffer, right.buffer);
|
||||
validateCheckSum();
|
||||
}
|
||||
|
||||
const NvU8 fallbackEdidModes[5][EDID_BLOCK_SIZE] = {
|
||||
// ID Manufacturer Name: NVD
|
||||
// VIDEO INPUT DEFINITION:
|
||||
// Digital Signal
|
||||
// VESA DFP 1.x Compatible
|
||||
|
||||
//
|
||||
// The first 4 entries are for NV_DPCD_SINK_VIDEO_FALLBACK_FORMATS (DPCD 0x20)
|
||||
// 1024x768x60Hz: defined in bit 0.
|
||||
// 1280x720x60Hz: defined in bit 1.
|
||||
// 1920x1080x60Hz: defined in bit 2. [Mandatory]
|
||||
//
|
||||
{
|
||||
// Bit 2: 1920x1080x60 only
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x04, 0xA5, 0x00, 0x00, 0x64,
|
||||
0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
|
||||
0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
|
||||
0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
|
||||
0x43, 0x00, 0xC0, 0x1C, 0x32, 0x00, 0x00, 0x1C,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB
|
||||
},
|
||||
{
|
||||
// bit 2 + bit 0: 1920x1080x60 + 1024x768x60
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x04, 0xA5, 0x00, 0x00, 0x64,
|
||||
0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
|
||||
0x50, 0x54, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
|
||||
0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
|
||||
0x43, 0x00, 0xC0, 0x1C, 0x32, 0x00, 0x00, 0x1C,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3
|
||||
},
|
||||
{
|
||||
// bit 2 + bit 1: 1920x1080x60 + 1280x720x60
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x04, 0xA5, 0x00, 0x00, 0x64,
|
||||
0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
|
||||
0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x81, 0xC0,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
|
||||
0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
|
||||
0x43, 0x00, 0xC0, 0x1C, 0x32, 0x00, 0x00, 0x1C,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C
|
||||
},
|
||||
{
|
||||
// bit2 + bit 1 + bit 0: All 3 modes.
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x04, 0xA5, 0x00, 0x00, 0x64,
|
||||
0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
|
||||
0x50, 0x54, 0x00, 0x00, 0x08, 0x00, 0x81, 0xC0,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
|
||||
0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
|
||||
0x43, 0x00, 0xC0, 0x1C, 0x32, 0x00, 0x00, 0x1C,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94
|
||||
},
|
||||
{
|
||||
// ESTABLISHED TIMING I:
|
||||
// 640 X 480 @ 60Hz (IBM,VGA)
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x04, 0x95, 0x00, 0x00, 0x78,
|
||||
0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F,
|
||||
0x50, 0x54, 0x00, 0x20, 0x00, 0x00, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Definition of DPCD 0x20:
|
||||
// 1024x768x60Hz: defined in bit 0.
|
||||
// 1280x720x60Hz: defined in bit 1.
|
||||
// 1920x1080x60Hz: defined in bit 2. [Mandatory]
|
||||
// MIN value is 4 (only 1920x1080 supported)
|
||||
// MAX value is 7 (supports all 3 modes)
|
||||
//
|
||||
#define SINK_VIDEO_FALLBACK_FORMATS_MIN_VALUE (0x00000004)
|
||||
#define SINK_VIDEO_FALLBACK_FORMATS_MAX_VALUE (0x00000007)
|
||||
|
||||
void DisplayPort::makeEdidFallback(Edid & edid, NvU32 fallbackFormatSupported)
|
||||
{
|
||||
const NvU8 *data;
|
||||
|
||||
// fallbackFormatSupported valid values = 4~7
|
||||
if (fallbackFormatSupported > SINK_VIDEO_FALLBACK_FORMATS_MAX_VALUE ||
|
||||
fallbackFormatSupported < SINK_VIDEO_FALLBACK_FORMATS_MIN_VALUE)
|
||||
{
|
||||
// 4 is default fallback mode. (only 640x480)
|
||||
data = fallbackEdidModes[4];
|
||||
}
|
||||
else
|
||||
{
|
||||
data = fallbackEdidModes[fallbackFormatSupported-4];
|
||||
}
|
||||
if (!edid.getBuffer()->resize(EDID_BLOCK_SIZE))
|
||||
return;
|
||||
|
||||
dpMemCopy(edid.getBuffer()->getData(), (const NvU8*)data, EDID_BLOCK_SIZE);
|
||||
DP_ASSERT(edid.verifyCRC());
|
||||
edid.setFallbackFlag(true);
|
||||
}
|
||||
|
||||
/*
|
||||
Fake EDID for DP2VGA dongle when the EDID of the real monitor is not available
|
||||
|
||||
Established Timings [20 CE 00]
|
||||
640 x 480 @ 60Hz
|
||||
800 x 600 @ 72Hz
|
||||
800 x 600 @ 75Hz
|
||||
1024 x 768 @ 60Hz
|
||||
1024 x 768 @ 70Hz
|
||||
1024 x 768 @ 75Hz
|
||||
|
||||
Standard Timings
|
||||
Timing [3159] : 640 x 480 @ 85Hz (4:3)
|
||||
Timing [4559] : 800 x 600 @ 85Hz (4:3)
|
||||
Timing [6159] : 1024 x 768 @ 85Hz (4:3)
|
||||
Timing [714F] : 1152 x 864 @ 75Hz (4:3)
|
||||
|
||||
Detailed Timing [DTD] 1280 x 1024 @ 60.02Hz
|
||||
Pixel Clock : 108.00Mhz
|
||||
HBlank, HBorder : 408, 0
|
||||
HSyncStart, HSyncWidth : 48, 112
|
||||
VBlank, VBorder : 42, 0
|
||||
VSyncStart, VSyncWidth : 1, 3
|
||||
Image size : 376mm x 301mm
|
||||
DigitalSeparate +/+
|
||||
*/
|
||||
|
||||
void DisplayPort::makeEdidFallbackVGA(Edid & edid)
|
||||
{
|
||||
const NvU8 data[] = {
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3A, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x13, 0x01, 0x03, 0x80, 0x26, 0x1E, 0x78, 0xEE, 0xCB, 0x05, 0xA3, 0x58, 0x4C, 0x9B, 0x25,
|
||||
0x13, 0x50, 0x54, 0x20, 0xCE, 0x00, 0x31, 0x59, 0x45, 0x59, 0x61, 0x59, 0x71, 0x4F, 0x81, 0x40,
|
||||
0x81, 0x80, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2A, 0x00, 0x98, 0x51, 0x00, 0x2A, 0x40, 0x30, 0x70,
|
||||
0x13, 0x00, 0x78, 0x2D, 0x11, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x30, 0x55, 0x1F,
|
||||
0x52, 0x0E, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x4C,
|
||||
0x43, 0x44, 0x5F, 0x56, 0x47, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8
|
||||
};
|
||||
|
||||
if (!edid.getBuffer()->resize(sizeof(data)))
|
||||
return;
|
||||
|
||||
dpMemCopy(edid.getBuffer()->getData(), (const NvU8*)data, sizeof data);
|
||||
DP_ASSERT(edid.verifyCRC());
|
||||
edid.setFallbackFlag(true);
|
||||
}
|
||||
|
||||
NvU8 DisplayPort::getEDIDBlockChecksum(const Buffer & buffer)
|
||||
{
|
||||
DP_ASSERT(buffer.getLength() == 128);
|
||||
|
||||
unsigned chksum = 0;
|
||||
for (unsigned i = 0; i < buffer.getLength(); i++)
|
||||
{
|
||||
chksum += buffer.data[i];
|
||||
}
|
||||
chksum = chksum & 0xFF;
|
||||
return (NvU8)chksum;
|
||||
}
|
||||
1843
src/common/displayport/src/dp_evoadapter.cpp
Normal file
1843
src/common/displayport/src/dp_evoadapter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
331
src/common/displayport/src/dp_groupimpl.cpp
Normal file
331
src/common/displayport/src/dp_groupimpl.cpp
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_groupimpl.cpp *
|
||||
* DP device group implementation *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_connector.h"
|
||||
#include "dp_list.h"
|
||||
#include "dp_auxdefs.h"
|
||||
#include "dp_deviceimpl.h"
|
||||
#include "dp_groupimpl.h"
|
||||
#include "dp_connectorimpl.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
void GroupImpl::update(Device * dev, bool allocationState)
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
Address devAddress = dev->getTopologyAddress();
|
||||
DP_USED(sb);
|
||||
|
||||
// Do not map a stream that is not yet turned on in the gpu. An update shall be sent later during NAE.
|
||||
if (allocationState && !this->isHeadAttached())
|
||||
return;
|
||||
|
||||
//
|
||||
// Do not enable the stream on an unplugged device but take care of
|
||||
// detached devices. We need to clear PBNs allocated by such devices
|
||||
//
|
||||
if (allocationState && !((DeviceImpl *)dev)->plugged)
|
||||
return;
|
||||
|
||||
//
|
||||
// Check if Parent's messageManager exist or not. This is required for cases
|
||||
// where parent branch itself has been unplugged. No message can be sent in this case.
|
||||
//
|
||||
if (!parent->messageManager)
|
||||
return;
|
||||
|
||||
if (timeslot.count == 0 ||
|
||||
((DeviceImpl *)dev)->payloadAllocated == allocationState)
|
||||
return;
|
||||
|
||||
if (!dev->getParent() || !((dev->getParent())->isPlugged()))
|
||||
{
|
||||
DeviceImpl * parentDev = NULL;
|
||||
|
||||
//
|
||||
// Send ALLOCATE_PAYLOAD with pbn 0 to parent port of previous branch
|
||||
// Find first plugged parent branch & send message to it
|
||||
//
|
||||
while(devAddress.size() > 2)
|
||||
{
|
||||
devAddress.pop();
|
||||
parentDev = parent->findDeviceInList(devAddress.parent());
|
||||
|
||||
if (parentDev && parentDev->isPlugged())
|
||||
break;
|
||||
}
|
||||
|
||||
// If no parent found simply return as we don't have a valid address to send message
|
||||
if (!parentDev)
|
||||
return;
|
||||
}
|
||||
|
||||
NakData nakData;
|
||||
for (int retries = 0 ; retries < 7; retries++)
|
||||
{
|
||||
AllocatePayloadMessage allocate;
|
||||
unsigned sink = 0; // hardcode the audio sink to 0th in the device.
|
||||
allocate.set(devAddress.parent(), devAddress.tail(),
|
||||
dev->isAudioSink() ? 1 : 0, streamIndex, allocationState ? timeslot.PBN : 0,
|
||||
&sink, true);
|
||||
|
||||
// Trigger a refetch of epr
|
||||
((DeviceImpl *)dev)->bandwidth.enum_path.dataValid = false;
|
||||
DeviceImpl * tail = (DeviceImpl *) dev;
|
||||
while (tail && tail->getParent())
|
||||
{
|
||||
tail->bandwidth.enum_path.dataValid = false;
|
||||
tail = (DeviceImpl *)tail->getParent();
|
||||
}
|
||||
|
||||
if (parent->messageManager->send(&allocate, nakData))
|
||||
{
|
||||
if (allocationState)
|
||||
{
|
||||
DP_LOG(("DP-TM> Attached stream:%d to %s", streamIndex, dev->getTopologyAddress().toString(sb)));
|
||||
}
|
||||
else
|
||||
{
|
||||
DP_LOG(("DP-TM> Detached stream:%d from %s", streamIndex, dev->getTopologyAddress().toString(sb)));
|
||||
}
|
||||
|
||||
((DeviceImpl *)dev)->payloadAllocated = allocationState;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we should not have ideally reached here unless allocate payload failed.
|
||||
if (allocationState)
|
||||
{
|
||||
DP_LOG(("DP-TM> Allocate_payload: Failed to ATTACH stream:%d to %s", streamIndex, dev->getTopologyAddress().toString(sb)));
|
||||
DP_ASSERT(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
DP_LOG(("DP-TM> Allocate_payload: Failed to DETACH stream:%d from %s", streamIndex, dev->getTopologyAddress().toString(sb)));
|
||||
DP_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GroupImpl::insert(Device * dev)
|
||||
{
|
||||
DP_ASSERT(!headInFirmware && "Cannot add or remove from a firmware group. You must perform a modeset away from the device");
|
||||
DeviceImpl * di = (DeviceImpl *)dev;
|
||||
|
||||
if (isHeadAttached())
|
||||
{
|
||||
if (di->activeGroup && di->activeGroup != this)
|
||||
{
|
||||
DP_ASSERT(0 && "Device already in active group, cannot add to another active group!");
|
||||
return;
|
||||
}
|
||||
di->activeGroup = this;
|
||||
}
|
||||
|
||||
members.insertFront(di);
|
||||
|
||||
update(dev, true);
|
||||
|
||||
}
|
||||
|
||||
void GroupImpl::remove(Device * dev)
|
||||
{
|
||||
DP_ASSERT(!headInFirmware && "Cannot add or remove from a firmware group. You must perform a modeset away from the device");
|
||||
|
||||
DeviceImpl * di = (DeviceImpl *)dev;
|
||||
|
||||
if (isHeadAttached())
|
||||
{
|
||||
di->activeGroup = 0;
|
||||
}
|
||||
members.remove(di);
|
||||
|
||||
update(dev, false);
|
||||
|
||||
updateVbiosScratchRegister(dev);
|
||||
}
|
||||
|
||||
void GroupImpl::destroy()
|
||||
{
|
||||
for (Device * i = enumDevices(0); i; i = enumDevices(i))
|
||||
remove(i);
|
||||
|
||||
// Cancel any queue the auth callback.
|
||||
cancelHdcpCallbacks();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void GroupImpl::cancelHdcpCallbacks()
|
||||
{
|
||||
authRetries = 0;
|
||||
|
||||
parent->timer->cancelCallback(this, &tagHDCPReauthentication);
|
||||
parent->timer->cancelCallback(this, &tagStreamValidation);
|
||||
|
||||
}
|
||||
|
||||
Device * GroupImpl::enumDevices(Device * previousDevice)
|
||||
{
|
||||
return members.next(previousDevice);
|
||||
}
|
||||
|
||||
void GroupImpl::expired(const void * tag)
|
||||
{
|
||||
if (tag == &tagHDCPReauthentication)
|
||||
{
|
||||
HDCPState hdcpState = {0};
|
||||
parent->main->configureHDCPGetHDCPState(hdcpState);
|
||||
|
||||
if (authRetries < HDCP_AUTHENTICATION_RETRIES)
|
||||
{
|
||||
this->hdcpEnabled = hdcpState.HDCP_State_Encryption;
|
||||
if (hdcpState.HDCP_State_Authenticated)
|
||||
{
|
||||
parent->isHDCPAuthOn = true;
|
||||
authRetries = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned authDelay = (hdcpState.HDCP_State_22_Capable ?
|
||||
HDCP22_AUTHENTICATION_COOLDOWN : HDCP_AUTHENTICATION_COOLDOWN);
|
||||
|
||||
authRetries++;
|
||||
parent->main->configureHDCPRenegotiate();
|
||||
parent->isHDCPAuthOn = false;
|
||||
parent->timer->queueCallback(this, &tagHDCPReauthentication,
|
||||
authDelay);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parent->isHDCPAuthOn = this->hdcpEnabled = false;
|
||||
}
|
||||
}
|
||||
else if ( tag == &tagStreamValidation)
|
||||
{
|
||||
if (!(this->streamValidationDone))
|
||||
{
|
||||
// If we are here we need to debug what has caused the problem for not getting notification from DD.
|
||||
DP_ASSERT(0 && "DP> Didn't get final notification." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GroupImpl::hdcpGetEncrypted()
|
||||
{
|
||||
//
|
||||
// Returns whether encryption is currently enabled
|
||||
//
|
||||
if (parent->isHDCPAuthOn)
|
||||
{
|
||||
return this->hdcpEnabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GroupImpl::updateVbiosScratchRegister(Device * lastDev)
|
||||
{
|
||||
if (!parent->bDisableVbiosScratchRegisterUpdate &&
|
||||
parent->lastDeviceSetForVbios == lastDev)
|
||||
{
|
||||
// Take a device which is part of a group
|
||||
for (ListElement * e = parent->deviceList.begin();
|
||||
e != parent->deviceList.end(); e = e->next)
|
||||
{
|
||||
DeviceImpl * dev = (DeviceImpl *)e;
|
||||
|
||||
if (dev->activeGroup && dev->activeGroup->isHeadAttached())
|
||||
{
|
||||
NvU32 address = 0;
|
||||
NvU32 addrSize = dev->getTopologyAddress().size();
|
||||
|
||||
// Set the MS_SCRATCH_REGISTER for lighted up display
|
||||
for (NvU32 i = addrSize; i; --i)
|
||||
{
|
||||
address |= ((dev->address[i-1] & 0xF) << ((addrSize - i)*4));
|
||||
}
|
||||
|
||||
parent->main->configureMsScratchRegisters(address, addrSize, 3);
|
||||
|
||||
parent->lastDeviceSetForVbios = (Device *)dev;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Helper function for attaching and detaching heads.
|
||||
//
|
||||
// For attach, we will assert if group already has head attached but for
|
||||
// some device in the group, active group did not point to current group.
|
||||
// For detach, we will assert if the group does not have head attached but
|
||||
// some device in group has an active group OR head is marked attached but
|
||||
// not all devies in the group have the current group as active group.
|
||||
// This also sets or clears dev->activeGroup for each contained
|
||||
// device.
|
||||
//
|
||||
void GroupImpl::setHeadAttached(bool attached)
|
||||
{
|
||||
for (Device * i = enumDevices(0); i; i = enumDevices(i))
|
||||
{
|
||||
DeviceImpl *di = (DeviceImpl *)i;
|
||||
|
||||
if (attached)
|
||||
{
|
||||
if (headAttached)
|
||||
{
|
||||
DP_ASSERT(di->activeGroup == this);
|
||||
}
|
||||
di->activeGroup = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!headAttached)
|
||||
{
|
||||
DP_ASSERT(di->activeGroup == NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
DP_ASSERT(di->activeGroup == this);
|
||||
}
|
||||
di->activeGroup = NULL;
|
||||
}
|
||||
}
|
||||
headAttached = attached;
|
||||
}
|
||||
81
src/common/displayport/src/dp_guid.cpp
Normal file
81
src/common/displayport/src/dp_guid.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_guid.cpp *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#include "dp_internal.h"
|
||||
#include "dp_guid.h"
|
||||
#include "dp_buffer.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
//
|
||||
// Linear congruential random number generator
|
||||
// Seed values chosen from numerical methods
|
||||
//
|
||||
NvU32 GUIDBuilder::random()
|
||||
{
|
||||
previousRandom = static_cast<NvU32>(( ((NvU64)1664525 * previousRandom + 1013904223) & 0xFFFFFFFF) & 0xFF);
|
||||
return previousRandom;
|
||||
}
|
||||
|
||||
|
||||
GUIDBuilder::GUIDBuilder(Timer * source, NvU32 salt)
|
||||
: salt(salt), source(source)
|
||||
{
|
||||
previousRandom = static_cast<NvU32>(( source->getTimeUs() & 0xFFFFFFFF) & 0xFF);
|
||||
}
|
||||
|
||||
void GUIDBuilder::makeGuid(GUID & guid)
|
||||
{
|
||||
NvU64 currentTimer = source->getTimeUs();
|
||||
guid.data[0] = static_cast<NvU8>(( salt >> 24) & 0xFF);
|
||||
guid.data[1] = static_cast<NvU8>(( salt >> 16) & 0xFF);
|
||||
guid.data[2] = static_cast<NvU8>(( salt >> 8) & 0xFF);
|
||||
guid.data[3] = static_cast<NvU8>(( salt) & 0xFF);
|
||||
|
||||
guid.data[4] = static_cast<NvU8>(( currentTimer >> 56) & 0xFF);
|
||||
guid.data[5] = static_cast<NvU8>(( currentTimer >> 48) & 0xFF);
|
||||
guid.data[6] = static_cast<NvU8>(( currentTimer >> 40) & 0xFF);
|
||||
guid.data[7] = static_cast<NvU8>(( currentTimer >> 32) & 0xFF);
|
||||
guid.data[8] = static_cast<NvU8>(( currentTimer >> 24) & 0xFF);
|
||||
guid.data[9] = static_cast<NvU8>(( currentTimer >> 16) & 0xFF);
|
||||
guid.data[10] = static_cast<NvU8>(( currentTimer >> 8) & 0xFF);
|
||||
guid.data[11] = static_cast<NvU8>(( currentTimer) & 0xFF);
|
||||
|
||||
unsigned rnd = random();
|
||||
guid.data[12] = static_cast<NvU8>(( rnd >> 24) & 0xFF);
|
||||
guid.data[13] = static_cast<NvU8>(( rnd >> 16) & 0xFF);
|
||||
guid.data[14] = static_cast<NvU8>(( rnd >> 8) & 0xFF);
|
||||
guid.data[15] = static_cast<NvU8>(( rnd) & 0xFF);
|
||||
|
||||
//
|
||||
// Spin until we get a new timer counter
|
||||
// This guarantees a monotonitically increased counter
|
||||
//
|
||||
while (source->getTimeUs() == currentTimer)
|
||||
;
|
||||
}
|
||||
159
src/common/displayport/src/dp_list.cpp
Normal file
159
src/common/displayport/src/dp_list.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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_list.cpp *
|
||||
* Simple doubly linked list *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_list.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
ListElement::ListElement()
|
||||
: next(0),
|
||||
prev(0)
|
||||
{
|
||||
}
|
||||
|
||||
ListElement::~ListElement()
|
||||
{
|
||||
if (this->next)
|
||||
{
|
||||
this->prev->next = this->next;
|
||||
this->next->prev = this->prev;
|
||||
this->next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
List::List()
|
||||
{
|
||||
this->next = this;
|
||||
this->prev = this;
|
||||
}
|
||||
|
||||
void List::clear()
|
||||
{
|
||||
while(!isEmpty())
|
||||
delete begin();
|
||||
}
|
||||
|
||||
List::~List()
|
||||
{
|
||||
clear();
|
||||
this->next = this;
|
||||
this->prev = this;
|
||||
}
|
||||
|
||||
bool List::isEmpty()
|
||||
{
|
||||
return this->next == this;
|
||||
}
|
||||
|
||||
void List::insertFront(ListElement * item)
|
||||
{
|
||||
DP_ASSERT(item->next == 0 && "Attempt to insert when it's already in a list");
|
||||
item->prev = this;
|
||||
item->next = this->next;
|
||||
item->prev->next = item;
|
||||
item->next->prev = item;
|
||||
}
|
||||
|
||||
void List::insertBack(ListElement * item)
|
||||
{
|
||||
DP_ASSERT(item->next == 0 && "Attempt to insert when it's already in a list");
|
||||
item->prev = this->prev;
|
||||
item->next = this;
|
||||
item->prev->next = item;
|
||||
item->next->prev = item;
|
||||
}
|
||||
|
||||
void List::insertBefore(ListElement * insertBeforeThis, ListElement * item)
|
||||
{
|
||||
DP_ASSERT(item->next == 0 && "Attempt to insert when it's already in a list");
|
||||
item->next = insertBeforeThis;
|
||||
item->prev = insertBeforeThis->prev;
|
||||
insertBeforeThis->prev->next = item;
|
||||
insertBeforeThis->prev = item;
|
||||
}
|
||||
|
||||
ListElement* List::front()
|
||||
{
|
||||
DP_ASSERT(!isEmpty());
|
||||
return this->next;
|
||||
}
|
||||
|
||||
ListElement* List::last()
|
||||
{
|
||||
DP_ASSERT(!isEmpty());
|
||||
return this->prev;
|
||||
}
|
||||
|
||||
ListElement * List::remove(ListElement * item)
|
||||
{
|
||||
// Skip if its not already in a list
|
||||
if (!item->next)
|
||||
return item;
|
||||
|
||||
item->prev->next = item->next;
|
||||
item->next->prev = item->prev;
|
||||
item->next = 0;
|
||||
item->prev = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
bool List::contains(ListElement * item)
|
||||
{
|
||||
for (ListElement * i = begin(); i!=end(); i = i->next)
|
||||
{
|
||||
if (i == item)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ListElement * List::replace(ListElement * replacement, ListElement * replacee)
|
||||
{
|
||||
if (!(replacement && replacee))
|
||||
{
|
||||
DP_ASSERT(0 && "replacement or replaces is NULL pointer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DP_ASSERT(replacement->next && replacement->prev);
|
||||
|
||||
// we are assuming replacee does exist in the list.
|
||||
replacement->next = replacee->next;
|
||||
replacement->prev = replacee->prev;
|
||||
|
||||
if (replacement->next)
|
||||
replacement->next->prev = replacement;
|
||||
|
||||
if (replacement->prev)
|
||||
replacement->prev->next = replacement;
|
||||
|
||||
return replacee;
|
||||
}
|
||||
310
src/common/displayport/src/dp_merger.cpp
Normal file
310
src/common/displayport/src/dp_merger.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_merger.cpp *
|
||||
* Asynchronous Message merger *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_bitstream.h"
|
||||
#include "dp_merger.h"
|
||||
#include "dp_auxdefs.h"
|
||||
#include "dp_crc.h"
|
||||
#include "dp_messageheader.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
|
||||
EncodedMessage * MessageTransactionMerger::pushTransaction(MessageHeader * header, Buffer * data)
|
||||
{
|
||||
if (freeOnNextCall)
|
||||
{
|
||||
delete freeOnNextCall;
|
||||
freeOnNextCall = 0;
|
||||
}
|
||||
|
||||
IncompleteMessage * imsg = getTransactionRecord(header->address, header->messageNumber);
|
||||
|
||||
if (!imsg)
|
||||
{
|
||||
DP_LOG(("DP-MM> Ignore message due to OOM"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (header->isTransactionStart)
|
||||
{
|
||||
imsg->message.isPathMessage = header->isPathMessage;
|
||||
imsg->message.isBroadcast = header->isBroadcast;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (imsg->message.buffer.length == 0)
|
||||
{
|
||||
DP_LOG(("DP-MM> Expected transaction-start, ignoring message transaction"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (imsg->message.isPathMessage != header->isPathMessage ||
|
||||
imsg->message.isBroadcast != header->isBroadcast)
|
||||
{
|
||||
DP_ASSERT(0 && "Message type changed during transmission");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Check for redundant start
|
||||
//
|
||||
if (header->isTransactionStart && imsg->message.buffer.length)
|
||||
{
|
||||
DP_LOG(("DP-MM> Unexpected repeated transaction-start, resetting message state."));
|
||||
|
||||
// We must have seen a previous incomplete transaction from this device
|
||||
// they've begun a new packet. Forget about the old thing
|
||||
imsg->message.buffer.reset();
|
||||
}
|
||||
|
||||
//
|
||||
// Kill the buffer if we've got less payload than we should
|
||||
//
|
||||
if (header->payloadBytes > data->length)
|
||||
{
|
||||
freeOnNextCall = imsg;
|
||||
imsg->message.buffer.reset();
|
||||
DP_LOG(("DP-MM> Received truncated or corrupted message transaction"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Verify transaction CRC
|
||||
//
|
||||
BitStreamReader bsr(data, header->headerSizeBits, (header->payloadBytes-1)*8);
|
||||
NvU8 dataCrc = (NvU8)dpCalculateBodyCRC(&bsr);
|
||||
|
||||
DP_ASSERT(header->headerSizeBits % 8 == 0 && "Header must be byte aligned");
|
||||
|
||||
if (dataCrc != data->data[header->headerSizeBits/8 + header->payloadBytes - 1] ||
|
||||
header->payloadBytes == 0)
|
||||
{
|
||||
DP_LOG(("DP-MM> Received corruption message transactions"));
|
||||
freeOnNextCall = imsg;
|
||||
imsg->message.buffer.reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Discount the processed CRC from the payload count
|
||||
header->payloadBytes--;
|
||||
|
||||
//
|
||||
// Append active buffer
|
||||
//
|
||||
unsigned i = imsg->message.buffer.length;
|
||||
imsg->message.buffer.resize(i + header->payloadBytes);
|
||||
dpMemCopy(&imsg->message.buffer.data[i], &data->data[header->headerSizeBits/8], header->payloadBytes);
|
||||
|
||||
//
|
||||
// Check for end of message transaction
|
||||
//
|
||||
if (header->isTransactionEnd)
|
||||
{
|
||||
freeOnNextCall = imsg;
|
||||
|
||||
return &imsg->message;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MessageTransactionMerger::IncompleteMessage * MessageTransactionMerger::getTransactionRecord(const Address & address, unsigned messageNumber)
|
||||
{
|
||||
IncompleteMessage * msg;
|
||||
NvU64 currentTime = this->timer->getTimeUs();
|
||||
|
||||
//
|
||||
// Search for existing record
|
||||
//
|
||||
for (ListElement * i = incompleteMessages.begin();i != incompleteMessages.end();)
|
||||
{
|
||||
msg = (IncompleteMessage *)i;
|
||||
i = i->next;
|
||||
if (msg->message.address == address && msg->message.messageNumber == messageNumber)
|
||||
{
|
||||
goto found;
|
||||
}
|
||||
|
||||
//
|
||||
// Found a stale message in the list
|
||||
//
|
||||
if (msg->lastUpdated + incompleteMessageTimeoutMs < currentTime)
|
||||
delete msg;
|
||||
}
|
||||
|
||||
//
|
||||
// None exists? Add a new one
|
||||
//
|
||||
msg = new IncompleteMessage();
|
||||
msg->message.address = address;
|
||||
msg->message.messageNumber = messageNumber;
|
||||
this->incompleteMessages.insertFront(msg);
|
||||
|
||||
found:
|
||||
//
|
||||
// Update the timestamp
|
||||
//
|
||||
msg->lastUpdated = currentTime;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
void IncomingTransactionManager::mailboxInterrupt()
|
||||
{
|
||||
MessageHeader msg;
|
||||
unsigned totalSize;
|
||||
AuxRetry::status result;
|
||||
unsigned txSize = (unsigned)getTransactionSize();
|
||||
|
||||
//
|
||||
// Size the static aux window
|
||||
//
|
||||
this->localWindow.resize(DP_MAX((unsigned)getTransactionSize(), (unsigned)getMessageBoxSize()));
|
||||
if (this->localWindow.isError())
|
||||
return;
|
||||
|
||||
//
|
||||
// Read one aux-transaction worth of data
|
||||
//
|
||||
result = readMessageBox(0, &this->localWindow.data[0], txSize);
|
||||
|
||||
DP_ASSERT( result != AuxRetry::defer && "Unexpected?!" );
|
||||
|
||||
if (result != AuxRetry::ack)
|
||||
return;
|
||||
|
||||
BitStreamReader reader(&this->localWindow, 0, 8*txSize);
|
||||
|
||||
|
||||
//
|
||||
// Before decoding the header, start with the downstream
|
||||
// ports address prefix
|
||||
//
|
||||
if (!decodeHeader(&reader, &msg, addressPrefix))
|
||||
{
|
||||
//
|
||||
// It's possible we should be NACKing here. Ignoring for now
|
||||
// to allow the message originator to time out (can take seconds).
|
||||
//
|
||||
DP_ASSERT(0 && "Not yet implemented");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Let's get the entire sideband message in the localWindow
|
||||
//
|
||||
|
||||
totalSize = (msg.headerSizeBits / 8) + msg.payloadBytes;
|
||||
|
||||
if (totalSize > txSize)
|
||||
{
|
||||
if (totalSize > DPCD_MESSAGEBOX_SIZE)
|
||||
{
|
||||
//
|
||||
// Corrupt packet - total packet can't be larger than the window
|
||||
//
|
||||
return;
|
||||
}
|
||||
if (AuxRetry::ack!=readMessageBox(txSize, &this->localWindow.data[txSize], totalSize - txSize))
|
||||
{
|
||||
//
|
||||
// Failed to read second half of message
|
||||
//
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
clearMessageBoxInterrupt();
|
||||
|
||||
EncodedMessage * em = incompleteMessages.pushTransaction(&msg, &this->localWindow);
|
||||
|
||||
if (em)
|
||||
{
|
||||
this->sink->messagedReceived(this, em);
|
||||
}
|
||||
}
|
||||
|
||||
IncomingTransactionManager::~IncomingTransactionManager()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
IncomingTransactionManager::IncomingTransactionManager(Timer * timer, const Address & addressPrefix, IncomingTransactionManagerEventSink * sink)
|
||||
: incompleteMessages(timer, DP_INCOMPLETE_MESSAGE_TIMEOUT_USEC), addressPrefix(addressPrefix)
|
||||
{
|
||||
this->sink = sink;
|
||||
this->timer = timer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AuxRetry::status DownReplyManager::readMessageBox(NvU32 offset, NvU8 * data, size_t length)
|
||||
{
|
||||
return hal->readDownReplyMessageBox(offset, data, length);
|
||||
}
|
||||
|
||||
size_t DownReplyManager::getMessageBoxSize()
|
||||
{
|
||||
return hal->getDownReplyMessageBoxSize();
|
||||
}
|
||||
|
||||
size_t DownReplyManager::getTransactionSize()
|
||||
{
|
||||
return hal->getTransactionSize();
|
||||
}
|
||||
|
||||
void DownReplyManager::clearMessageBoxInterrupt()
|
||||
{
|
||||
hal->clearInterruptDownReplyReady();
|
||||
}
|
||||
|
||||
AuxRetry::status UpRequestManager::readMessageBox(NvU32 offset, NvU8 * data, size_t length)
|
||||
{
|
||||
return hal->readUpRequestMessageBox(offset, data, length);
|
||||
}
|
||||
|
||||
size_t UpRequestManager::getMessageBoxSize()
|
||||
{
|
||||
return hal->getUpRequestMessageBoxSize();
|
||||
}
|
||||
|
||||
size_t UpRequestManager::getTransactionSize()
|
||||
{
|
||||
return hal->getTransactionSize();
|
||||
}
|
||||
|
||||
void UpRequestManager::clearMessageBoxInterrupt()
|
||||
{
|
||||
hal->clearInterruptUpRequestReady();
|
||||
}
|
||||
|
||||
692
src/common/displayport/src/dp_messagecodings.cpp
Normal file
692
src/common/displayport/src/dp_messagecodings.cpp
Normal file
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_messagecodings.cpp *
|
||||
* Encoding routines for various messages *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#include "dp_internal.h"
|
||||
#include "dp_messagecodings.h"
|
||||
#include "dp_auxdefs.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
//
|
||||
// LINK_ADDRESS 0x1
|
||||
//
|
||||
void LinkAddressMessage::set(const Address & target)
|
||||
{
|
||||
clear();
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
//
|
||||
// Write request identifier
|
||||
//
|
||||
writer.write(0 /*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
encodedMessage.isPathMessage = false;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
}
|
||||
|
||||
ParseResponseStatus LinkAddressMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
DisplayPort::extractGUID(reader, &reply.guid);
|
||||
reader->readOrDefault(4 /*zeroes*/, 0);
|
||||
reply.numberOfPorts = reader->readOrDefault(4 /*Number_Of_Ports*/, 0xF);
|
||||
|
||||
for (unsigned i = 0; i < reply.numberOfPorts; i++)
|
||||
{
|
||||
reply.res[i].isInputPort = !!reader->readOrDefault(1 /*Input_Port*/, 1);
|
||||
reply.res[i].peerDeviceType = (PeerDevice) reader->readOrDefault(3 /*Peer_Device_Type*/, 0x0);
|
||||
reply.res[i].portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
reply.res[i].hasMessaging = !!reader->readOrDefault(1 /*Messaging_Capability_Status*/, 0x1);
|
||||
reply.res[i].dpPlugged = !!reader->readOrDefault(1 /*DisplayPort_Device_Plug_Status*/, 0x1);
|
||||
|
||||
if (reply.res[i].isInputPort == false)
|
||||
{
|
||||
reply.res[i].legacyPlugged = !!reader->readOrDefault(1 /*Legacy_Device_Plug_Status*/, 0x1);
|
||||
|
||||
reader->readOrDefault(5 /*zeroes*/, 0x0);
|
||||
|
||||
unsigned ver = reader->readOrDefault(8/*DPCD_Revision*/, 0);
|
||||
reply.res[i].dpcdRevisionMajor = ver >> 4;
|
||||
reply.res[i].dpcdRevisionMinor = ver & 0xF;
|
||||
DisplayPort::extractGUID(reader, &reply.res[i].peerGUID);
|
||||
reply.res[i].SDPStreams = reader->readOrDefault(4 /*Number_SDP_Streams*/, 0xF);
|
||||
reply.res[i].SDPStreamSinks = reader->readOrDefault(4 /*Number_SDP_Stream_Sinks*/, 0xF);
|
||||
}
|
||||
else
|
||||
{
|
||||
reader->readOrDefault(6 /*zeroes*/, 0x0);
|
||||
reply.res[i].dpcdRevisionMajor = 1;
|
||||
reply.res[i].dpcdRevisionMinor = 2;
|
||||
}
|
||||
}
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
//
|
||||
// CONNECTION_STATUS_NOTIFY 0x2
|
||||
//
|
||||
ConnStatusNotifyMessage::ConnStatusNotifyMessage(MessageReceiverEventSink * sink)
|
||||
: MessageReceiver(sink, NV_DP_SBMSG_REQUEST_ID_CONNECTION_STATUS_NOTIFY /*request id*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool ConnStatusNotifyMessage::processByType(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
// read the request body
|
||||
request.port = reader->readOrDefault(4/*Port_Number*/, 0xF);
|
||||
reader->readOrDefault(4/*zeroes*/, 0);
|
||||
bool status = DisplayPort::extractGUID(reader/*GUID of the originating branch device*/, &request.guid);
|
||||
reader->readOrDefault(1/*zero*/, 0);
|
||||
request.legacyPlugged = !!reader->readOrDefault(1/*Legacy_Device_Plug_Status*/, 0);
|
||||
request.devicePlugged = !!reader->readOrDefault(1/*DisplayPort_Device_Plug_Status*/, 0);
|
||||
request.messagingCapability = !!reader->readOrDefault(1/*Messaging_Capability_Status*/, 0);
|
||||
request.isInputPort = !!reader->readOrDefault(1/*Input_Port*/, 0);
|
||||
request.peerDeviceType = (PeerDevice) reader->readOrDefault(3/*Peer_Device_Type*/, 0);
|
||||
|
||||
// action will be implemented by evensink
|
||||
this->sink->messageProcessed(this);
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// GENERIC_UP_REPLY 0xnn
|
||||
//
|
||||
void GenericUpReplyMessage::set(const Address & target,
|
||||
bool bReplyIsNack,
|
||||
bool bBroadcast,
|
||||
bool bPath)
|
||||
{
|
||||
clear();
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
writer.write(bReplyIsNack?1:0, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
encodedMessage.isPathMessage = bPath;
|
||||
encodedMessage.isBroadcast = bBroadcast;
|
||||
encodedMessage.address = target;
|
||||
}
|
||||
|
||||
GenericUpReplyMessage::GenericUpReplyMessage(unsigned requestId, bool bReplyIsNack, bool bBroadcast, bool bPath)
|
||||
: Message(requestId, NV_DP_SBMSG_PRIORITY_LEVEL_DEFAULT)
|
||||
{
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
//
|
||||
// Write request identifier
|
||||
//
|
||||
writer.write(bReplyIsNack?1:0, 1);
|
||||
writer.write(requestId, 7);
|
||||
|
||||
encodedMessage.isPathMessage = bPath;
|
||||
encodedMessage.isBroadcast = bBroadcast;
|
||||
}
|
||||
|
||||
GenericUpReplyMessage::GenericUpReplyMessage(const Address & target, unsigned requestId, bool bReplyIsNack, bool bBroadcast, bool bPath)
|
||||
: Message(requestId, NV_DP_SBMSG_PRIORITY_LEVEL_DEFAULT)
|
||||
{
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
//
|
||||
// Write request identifier
|
||||
//
|
||||
writer.write(bReplyIsNack?1:0, 1);
|
||||
writer.write(requestId, 7);
|
||||
|
||||
encodedMessage.isPathMessage = bPath;
|
||||
encodedMessage.isBroadcast = bBroadcast;
|
||||
encodedMessage.address = target;
|
||||
}
|
||||
|
||||
ParseResponseStatus GenericUpReplyMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
//
|
||||
// we are not expecting any replies here
|
||||
// Since the corresponding post for this kind of message is of reply type;
|
||||
// message manager won't queue an awaiting down reply for the same.
|
||||
//
|
||||
DP_ASSERT(0 && "We shouldn't be here!!");
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
//
|
||||
// CLEAR_PAYLOAD_ID_TABLE 0x14
|
||||
//
|
||||
ClearPayloadIdTableMessage::ClearPayloadIdTableMessage()
|
||||
: Message(NV_DP_SBMSG_REQUEST_ID_CLEAR_PAYLOAD_ID_TABLE /* request id */, NV_DP_SBMSG_PRIORITY_LEVEL_1)
|
||||
{
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0/*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
encodedMessage.isPathMessage = true;
|
||||
encodedMessage.isBroadcast = true;
|
||||
encodedMessage.address = Address();
|
||||
}
|
||||
|
||||
ParseResponseStatus ClearPayloadIdTableMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
ParseResponseStatus ClearPayloadIdTableMessage::parseResponse(EncodedMessage * message)
|
||||
{
|
||||
sink->messageCompleted(this);
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
//
|
||||
// ENUM_PATH_RESOURCES 0x10
|
||||
//
|
||||
EnumPathResMessage::EnumPathResMessage(const Address & target, unsigned port, bool point)
|
||||
: Message(NV_DP_SBMSG_REQUEST_ID_ENUM_PATH_RESOURCES /* request identifier */,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_4)
|
||||
{
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0/*zereo*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
writer.write(port, 4);
|
||||
writer.write(0/*zeroes*/, 4);
|
||||
|
||||
encodedMessage.isPathMessage = !point;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
sinkPort = port;
|
||||
dpMemZero(&reply, sizeof(reply));
|
||||
}
|
||||
|
||||
ParseResponseStatus EnumPathResMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
reply.portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
reader->readOrDefault(3 /*zeroes*/, 0);
|
||||
reply.bFECCapability = (reader->readOrDefault(1 /*FEC*/, 0x0) == 1) ? true : false;
|
||||
reply.TotalPBN = reader->readOrDefault(16 /*PBN*/, 0xFFFF);
|
||||
reply.FreePBN = reader->readOrDefault(16 /*PBN*/, 0xFFFF);
|
||||
|
||||
if (this->getSinkPort() != reply.portNumber)
|
||||
return ParseResponseWrong;
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
//
|
||||
// ALLOCATE_PAYLOAD 0x11
|
||||
//
|
||||
void AllocatePayloadMessage::set
|
||||
(
|
||||
const Address & target,
|
||||
unsigned port,
|
||||
unsigned nSDPStreams,
|
||||
unsigned vcPayloadId,
|
||||
unsigned PBN,
|
||||
unsigned* SDPStreamSink,
|
||||
bool entirePath
|
||||
)
|
||||
{
|
||||
clear();
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0/*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
DP_ASSERT(SDPStreamSink || (!nSDPStreams));
|
||||
|
||||
// Write message request body
|
||||
writer.write(port, 4);
|
||||
writer.write(nSDPStreams, 4);
|
||||
writer.write(0/*zero*/, 1);
|
||||
writer.write(vcPayloadId, 7);
|
||||
writer.write(PBN, 16);
|
||||
for (unsigned i=0; i<nSDPStreams; i++)
|
||||
{
|
||||
writer.write(SDPStreamSink[i], 4);
|
||||
}
|
||||
|
||||
// emit 0s until byte aligned.
|
||||
writer.align(8);
|
||||
|
||||
encodedMessage.isPathMessage = entirePath;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
sinkPort = port;
|
||||
}
|
||||
|
||||
ParseResponseStatus AllocatePayloadMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
reply.portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
reader->readOrDefault(5 /*zeroes*/, 0);
|
||||
reply.virtualChannelPayloadId = reader->readOrDefault(7 /*Virtual_Channel_Payload_Identifier*/, 0x0);
|
||||
reply.PBN = reader->readOrDefault(16 /*PBN*/, 0xFFFF);
|
||||
|
||||
if (this->getSinkPort() != reply.portNumber)
|
||||
return ParseResponseWrong;
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
//
|
||||
// QUERY_PAYLOAD 0x12
|
||||
//
|
||||
QueryPayloadMessage::QueryPayloadMessage
|
||||
(
|
||||
const Address & target,
|
||||
unsigned port,
|
||||
unsigned vcPayloadId
|
||||
)
|
||||
: Message(NV_DP_SBMSG_REQUEST_ID_QUERY_PAYLOAD /* request identifier*/,
|
||||
NV_DP_SBMSG_PRIORITY_LEVEL_DEFAULT)
|
||||
{
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0 /*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
// Write message request
|
||||
writer.write(port, 4);
|
||||
writer.write(0 /*zeroes*/, 5);
|
||||
writer.write(vcPayloadId, 7);
|
||||
|
||||
encodedMessage.isPathMessage = false;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
sinkPort = port;
|
||||
dpMemZero(&reply, sizeof(reply));
|
||||
}
|
||||
|
||||
ParseResponseStatus QueryPayloadMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
reply.portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
reader->readOrDefault(4 /*zeroes*/, 0);
|
||||
reply.allocatedPBN = reader->readOrDefault(16 /*Allocated_PBN*/, 0xFFFF);
|
||||
|
||||
if (this->getSinkPort() != reply.portNumber)
|
||||
return ParseResponseWrong;
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// RESOURCE_STATUS_NOTIFY 0x13
|
||||
//
|
||||
|
||||
ResStatusNotifyMessage::ResStatusNotifyMessage(MessageReceiverEventSink * sink)
|
||||
: MessageReceiver(sink, NV_DP_SBMSG_REQUEST_ID_RESOURCE_STATUS_NOTIFY /*request id*/)
|
||||
{
|
||||
dpMemZero(&request, sizeof(request));
|
||||
}
|
||||
|
||||
bool ResStatusNotifyMessage::processByType(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
bool status;
|
||||
|
||||
// read the request body
|
||||
request.port = reader->readOrDefault(4/*Port_Number*/, 0xF);
|
||||
reader->readOrDefault(4/*zeroes*/, 0);
|
||||
status = DisplayPort::extractGUID(reader, &request.guid);
|
||||
request.PBN = reader->readOrDefault(16/*Available_PBN*/, 0);
|
||||
|
||||
// action will be implemented by evensink
|
||||
this->sink->messageProcessed(this);
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// REMOTE_DPCD_READ 0x20
|
||||
//
|
||||
void RemoteDpcdReadMessage::set
|
||||
(
|
||||
const Address & target,
|
||||
unsigned port,
|
||||
unsigned dpcdAddress,
|
||||
unsigned nBytesToRead
|
||||
)
|
||||
{
|
||||
clear();
|
||||
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0/*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
// write request data
|
||||
writer.write(port, 4);
|
||||
writer.write(dpcdAddress, 20);
|
||||
writer.write(nBytesToRead, 8);
|
||||
|
||||
encodedMessage.isPathMessage = false;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
sinkPort = port;
|
||||
}
|
||||
|
||||
ParseResponseStatus RemoteDpcdReadMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
reader->readOrDefault(4 /*zeroes*/, 0);
|
||||
reply.portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
reply.numBytesReadDPCD = reader->readOrDefault(8 /*Num_Of_Bytes_Read*/, 0x0);
|
||||
for (unsigned i=0; i<reply.numBytesReadDPCD; i++)
|
||||
{
|
||||
reply.readData[i] = (NvU8)reader->readOrDefault(8 /*data*/, 0x0);
|
||||
}
|
||||
|
||||
if (this->getSinkPort() != reply.portNumber)
|
||||
return ParseResponseWrong;
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
//
|
||||
// REMOTE_DPCD_WRITE 0x21
|
||||
//
|
||||
void RemoteDpcdWriteMessage::set
|
||||
(
|
||||
const Address & target,
|
||||
unsigned port,
|
||||
unsigned dpcdAddress,
|
||||
unsigned nBytesToWrite,
|
||||
const NvU8 * writeData
|
||||
)
|
||||
{
|
||||
clear();
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
DP_ASSERT(writeData || (!nBytesToWrite));
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0/*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
// write request data
|
||||
writer.write(port, 4);
|
||||
writer.write(dpcdAddress, 20);
|
||||
writer.write(nBytesToWrite, 8);
|
||||
|
||||
for (unsigned i=0; i<nBytesToWrite; i++)
|
||||
{
|
||||
writer.write(writeData[i], 8);
|
||||
}
|
||||
|
||||
encodedMessage.isPathMessage = false;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
sinkPort = port;
|
||||
}
|
||||
|
||||
ParseResponseStatus RemoteDpcdWriteMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
reader->readOrDefault(4 /*zeroes*/, 0);
|
||||
unsigned portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
|
||||
DP_ASSERT(portNumber == this->sinkPort);
|
||||
DP_USED(portNumber);
|
||||
|
||||
if (this->getSinkPort() != portNumber)
|
||||
return ParseResponseWrong;
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
//
|
||||
// REMOTE_I2C_READ 0x22
|
||||
//
|
||||
void RemoteI2cReadMessage::set
|
||||
(
|
||||
const Address & target,
|
||||
unsigned nWriteTransactions,
|
||||
unsigned port,
|
||||
I2cWriteTransaction* transactions,
|
||||
unsigned readI2cDeviceId,
|
||||
unsigned nBytesToRead
|
||||
)
|
||||
{
|
||||
clear();
|
||||
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
DP_ASSERT(transactions || (!nWriteTransactions));
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0 /*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
// write request specific data
|
||||
writer.write(port, 4);
|
||||
writer.write(0/*zeroes*/, 2);
|
||||
writer.write(nWriteTransactions, 2);
|
||||
|
||||
for (unsigned i=0; i<nWriteTransactions; i++)
|
||||
{
|
||||
writer.write(0/*zero*/, 1);
|
||||
writer.write(transactions[i].WriteI2cDeviceId, 7);
|
||||
writer.write(transactions[i].NumBytes, 8);
|
||||
for(unsigned j=0; j<transactions[i].NumBytes; j++)
|
||||
{
|
||||
writer.write(transactions[i].I2cData[j], 8);
|
||||
}
|
||||
writer.write(0/*zeroes*/, 3);
|
||||
writer.write(transactions[i].NoStopBit ? 1 : 0, 1);
|
||||
writer.write(transactions[i].I2cTransactionDelay, 4);
|
||||
}
|
||||
writer.write(0/*zero*/, 1);
|
||||
writer.write(readI2cDeviceId, 7);
|
||||
writer.write(nBytesToRead, 8);
|
||||
|
||||
encodedMessage.isPathMessage = false;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
sinkPort = port;
|
||||
}
|
||||
|
||||
ParseResponseStatus RemoteI2cReadMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
reader->readOrDefault(4 /*zeroes*/, 0);
|
||||
reply.portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
reply.numBytesReadI2C = reader->readOrDefault(8 /*Num_Of_Bytes_Read*/, 0x0);
|
||||
for (unsigned i=0; i<reply.numBytesReadI2C; i++)
|
||||
{
|
||||
reply.readData[i] = (NvU8)reader->readOrDefault(8 /*data*/, 0x0);
|
||||
}
|
||||
|
||||
if (this->getSinkPort() != reply.portNumber)
|
||||
return ParseResponseWrong;
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
//
|
||||
// REMOTE_I2C_WRITE 0x23
|
||||
//
|
||||
void RemoteI2cWriteMessage::set
|
||||
(
|
||||
const Address & target,
|
||||
unsigned port,
|
||||
unsigned writeI2cDeviceId,
|
||||
unsigned nBytesToWrite,
|
||||
unsigned char* writeData
|
||||
)
|
||||
{
|
||||
clear();
|
||||
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
DP_ASSERT(writeData || (!nBytesToWrite));
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0 /*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
// write request data
|
||||
writer.write(port, 4);
|
||||
writer.write(0/*zero*/, 5);
|
||||
writer.write(writeI2cDeviceId, 7);
|
||||
writer.write(nBytesToWrite, 8);
|
||||
|
||||
for (unsigned i=0; i<nBytesToWrite; i++)
|
||||
{
|
||||
writer.write(writeData[i], 8);
|
||||
}
|
||||
|
||||
encodedMessage.isPathMessage = false;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
sinkPort = port;
|
||||
}
|
||||
|
||||
ParseResponseStatus RemoteI2cWriteMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
reader->readOrDefault(4 /*zeroes*/, 0);
|
||||
reply.portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
|
||||
if (this->getSinkPort() != reply.portNumber)
|
||||
return ParseResponseWrong;
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
//
|
||||
// POWER_UP_PHY 0x24
|
||||
//
|
||||
void PowerUpPhyMessage::set
|
||||
(
|
||||
const Address & target,
|
||||
unsigned port,
|
||||
bool entirePath
|
||||
)
|
||||
{
|
||||
clear();
|
||||
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0 /*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
// write request specific data
|
||||
writer.write(port, 4);
|
||||
writer.write(0 /*zero*/, 4);
|
||||
|
||||
encodedMessage.isPathMessage = entirePath;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
sinkPort = port;
|
||||
}
|
||||
|
||||
//
|
||||
// POWER_DOWN_PHY 0x25
|
||||
//
|
||||
ParseResponseStatus PowerUpPhyMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
reply.portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
reader->readOrDefault(4 /*zeroes*/, 0);
|
||||
|
||||
if (this->getSinkPort() != reply.portNumber)
|
||||
return ParseResponseWrong;
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
void PowerDownPhyMessage::set
|
||||
(
|
||||
const Address & target,
|
||||
unsigned port,
|
||||
bool entirePath
|
||||
)
|
||||
{
|
||||
BitStreamWriter writer(&encodedMessage.buffer, 0);
|
||||
|
||||
// Write request identifier
|
||||
writer.write(0 /*zero*/, 1);
|
||||
writer.write(requestIdentifier, 7);
|
||||
|
||||
// write request specific data
|
||||
writer.write(port, 4);
|
||||
writer.write(0/*zeros*/, 4);
|
||||
|
||||
encodedMessage.isPathMessage = entirePath;
|
||||
encodedMessage.isBroadcast = false;
|
||||
encodedMessage.address = target;
|
||||
sinkPort = port;
|
||||
}
|
||||
|
||||
ParseResponseStatus PowerDownPhyMessage::parseResponseAck(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
reply.portNumber = reader->readOrDefault(4 /*Port_Number*/, 0xF);
|
||||
reader->readOrDefault(4 /*zeroes*/, 0);
|
||||
|
||||
if (this->getSinkPort() != reply.portNumber)
|
||||
return ParseResponseWrong;
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
//
|
||||
// SINK_EVENT_NOTIFY 0x30
|
||||
//
|
||||
|
||||
SinkEventNotifyMessage::SinkEventNotifyMessage(MessageReceiverEventSink * sink, unsigned requestId)
|
||||
: MessageReceiver(sink, 0x30 /*request id*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool SinkEventNotifyMessage::processByType(EncodedMessage * message, BitStreamReader * reader)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
I2cWriteTransaction::I2cWriteTransaction
|
||||
(
|
||||
unsigned WriteI2cDeviceId,
|
||||
unsigned NumBytes,
|
||||
unsigned char * buffer,
|
||||
bool NoStopBit,
|
||||
unsigned I2cTransactionDelay
|
||||
)
|
||||
{
|
||||
this->WriteI2cDeviceId = WriteI2cDeviceId;
|
||||
this->NumBytes = NumBytes;
|
||||
this->NoStopBit = NoStopBit;
|
||||
this->I2cTransactionDelay = I2cTransactionDelay;
|
||||
this->I2cData = buffer;
|
||||
}
|
||||
|
||||
I2cWriteTransaction::I2cWriteTransaction():
|
||||
WriteI2cDeviceId(0), NumBytes(0), I2cData(0), NoStopBit(0), I2cTransactionDelay(0)
|
||||
{
|
||||
}
|
||||
|
||||
85
src/common/displayport/src/dp_messageheader.cpp
Normal file
85
src/common/displayport/src/dp_messageheader.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_messageheader.cpp *
|
||||
* DP message header parser *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_bitstream.h"
|
||||
#include "dp_crc.h"
|
||||
#include "dp_messageheader.h"
|
||||
|
||||
|
||||
bool DisplayPort::decodeHeader(BitStreamReader * reader, MessageHeader * header, const Address & address)
|
||||
{
|
||||
unsigned startOffset = reader->offset();
|
||||
int LCT, i;
|
||||
|
||||
//
|
||||
// Read the RAD
|
||||
//
|
||||
LCT = reader->readOrDefault( 4, 0);
|
||||
reader->readOrDefault( 4, 0);
|
||||
|
||||
header->address = address;
|
||||
|
||||
for (i = 0; i < LCT - 1; i++)
|
||||
{
|
||||
header->address.append(reader->readOrDefault( 4, 0));
|
||||
}
|
||||
|
||||
reader->align( 8);
|
||||
|
||||
//
|
||||
// Read flags
|
||||
//
|
||||
header->isBroadcast = !!reader->readOrDefault( 1, 0);
|
||||
header->isPathMessage = !!reader->readOrDefault( 1, 0);
|
||||
header->payloadBytes = reader->readOrDefault( 6, 0) ;
|
||||
|
||||
header->isTransactionStart = !!reader->readOrDefault( 1, 0);
|
||||
header->isTransactionEnd = !!reader->readOrDefault( 1, 0);
|
||||
reader->readOrDefault( 1, 0);
|
||||
header->messageNumber = reader->readOrDefault( 1, 0);
|
||||
|
||||
|
||||
// Build a bit reader for the slice of header we just processed
|
||||
BitStreamReader crcReader(reader->buffer(), startOffset, reader->offset());
|
||||
|
||||
if (reader->readOrDefault( 4, (NvU32)~0) != dpCalculateHeaderCRC(&crcReader))
|
||||
{
|
||||
// Corrupt packet received
|
||||
char buffer[48*3+1];
|
||||
dpHexDump(&buffer[0], sizeof(buffer), (NvU8*)reader->buffer() + startOffset, reader->offset() - startOffset);
|
||||
DP_LOG(("DP-MM> Corrupt message transaction. Expected CRC %d. Message = {%s}", dpCalculateHeaderCRC(&crcReader), buffer));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
header->headerSizeBits = reader->offset() - startOffset;
|
||||
return true;
|
||||
}
|
||||
606
src/common/displayport/src/dp_messages.cpp
Normal file
606
src/common/displayport/src/dp_messages.cpp
Normal file
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_messages.cpp *
|
||||
* Encoding for aux common messages. *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#include "dp_internal.h"
|
||||
#include "dp_bitstream.h"
|
||||
#include "dp_splitter.h"
|
||||
#include "dp_messages.h"
|
||||
#include "dp_merger.h"
|
||||
#include "dp_list.h"
|
||||
#include "dp_tracing.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
namespace DisplayPort
|
||||
{
|
||||
GenericMessageCompletion::GenericMessageCompletion() :
|
||||
failed(false), completed(false) {}
|
||||
void GenericMessageCompletion::messageFailed(MessageManager::Message * from, NakData * data)
|
||||
{
|
||||
nakData = *data;
|
||||
failed = true;
|
||||
completed = true;
|
||||
}
|
||||
void GenericMessageCompletion::messageCompleted(MessageManager::Message * from)
|
||||
{
|
||||
failed = false;
|
||||
completed = true;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Transmit a message and wait for the response in place.
|
||||
//
|
||||
bool MessageManager::send(MessageManager::Message * message, NakData & nakData)
|
||||
{
|
||||
GenericMessageCompletion completion;
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
|
||||
NvU64 startTime, elapsedTime;
|
||||
post(message, &completion);
|
||||
startTime = timer->getTimeUs();
|
||||
do
|
||||
{
|
||||
hal->notifyIRQ();
|
||||
if (hal->interruptDownReplyReady())
|
||||
IRQDownReply();
|
||||
|
||||
if (completion.completed)
|
||||
{
|
||||
nakData = completion.nakData;
|
||||
break;
|
||||
}
|
||||
elapsedTime = timer->getTimeUs() - startTime;
|
||||
|
||||
if (elapsedTime > (DPCD_MESSAGE_REPLY_TIMEOUT * 1000))
|
||||
{
|
||||
message->expired(NULL);
|
||||
nakData.reason = NakTimeout;
|
||||
break;
|
||||
}
|
||||
|
||||
// Sleep while processing timer callbacks
|
||||
timer->sleep(1);
|
||||
} while(true);
|
||||
|
||||
return !completion.failed;
|
||||
}
|
||||
|
||||
bool DisplayPort::extractGUID(BitStreamReader * reader, GUID * guid)
|
||||
{
|
||||
for (unsigned i=0; i < 128; i += 8)
|
||||
{
|
||||
unsigned data;
|
||||
if (!reader->read(&data, 8))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
guid->data[i/8] = (NvU8)data;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageManager::messagedReceived(IncomingTransactionManager * from, EncodedMessage * message)
|
||||
{
|
||||
if (from == &mergerUpRequest)
|
||||
{
|
||||
onUpRequestReceived(true, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
onDownReplyReceived(true, message);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::Message::splitterFailed(OutgoingTransactionManager * from)
|
||||
{
|
||||
//
|
||||
// Message failed
|
||||
//
|
||||
NakData nakData;
|
||||
nakData.reason = NakTimeout;
|
||||
MessageManager * parent = this->parent;
|
||||
|
||||
if (sink)
|
||||
sink->messageFailed(this, &nakData);
|
||||
|
||||
if (from == &parent->splitterDownRequest)
|
||||
{
|
||||
//
|
||||
// Tell the message manager he may begin sending the next message
|
||||
//
|
||||
parent->transmitAwaitingDownRequests();
|
||||
}
|
||||
else
|
||||
{
|
||||
parent->transmitAwaitingUpReplies();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::Message::splitterTransmitted(OutgoingTransactionManager * from)
|
||||
{
|
||||
bTransmitted = true;
|
||||
MessageManager * parent = this->parent;
|
||||
|
||||
if (from == &parent->splitterDownRequest)
|
||||
{
|
||||
//
|
||||
// Start the countdown timer for the reply
|
||||
//
|
||||
parent->timer->queueCallback(this, "SPLI", DPCD_MESSAGE_REPLY_TIMEOUT);
|
||||
|
||||
//
|
||||
// Tell the message manager he may begin sending the next message
|
||||
//
|
||||
parent->transmitAwaitingDownRequests();
|
||||
}
|
||||
else // UpReply
|
||||
{
|
||||
if (sink)
|
||||
sink->messageCompleted(this); // This is the end for an up reply
|
||||
|
||||
parent->transmitAwaitingUpReplies();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Since transmit DPCD_MESSAGE_REPLY_TIMEOUT time has elapsed.
|
||||
// - Let's assume the message was not replied to
|
||||
void MessageManager::Message::expired(const void * tag)
|
||||
{
|
||||
Address::StringBuffer sb;
|
||||
DP_USED(sb);
|
||||
|
||||
DP_LOG(("DP-MM> Message transmit time expired on message %p (ID = %02X, target = %s)",
|
||||
(Message*)this, ((Message*)this)->requestIdentifier, (((Message*)this)->state.target).toString(sb)));
|
||||
|
||||
Address::NvU32Buffer addrBuffer;
|
||||
dpMemZero(addrBuffer, sizeof(addrBuffer));
|
||||
(((Message*)this)->state.target).toNvU32Buffer(addrBuffer);
|
||||
NV_DPTRACE_WARNING(MESSAGE_EXPIRED, ((Message*)this)->requestIdentifier, (((Message*)this)->state.target).size(),
|
||||
addrBuffer[0], addrBuffer[1], addrBuffer[2], addrBuffer[3]);
|
||||
|
||||
NakData nakData;
|
||||
nakData.reason = NakTimeout;
|
||||
|
||||
MessageManager * parent = this->parent;
|
||||
|
||||
DP_ASSERT(parent);
|
||||
if (parent && !parent->isBeingDestroyed)
|
||||
{
|
||||
parent->awaitingReplyDownRequest.remove(this);
|
||||
parent->clearPendingMsg();
|
||||
parent->transmitAwaitingDownRequests();
|
||||
parent->transmitAwaitingUpReplies();
|
||||
}
|
||||
|
||||
if (sink)
|
||||
sink->messageFailed(this, &nakData);
|
||||
}
|
||||
|
||||
//
|
||||
// Enqueue the next message to the splitterDownRequest
|
||||
//
|
||||
void MessageManager::transmitAwaitingDownRequests()
|
||||
{
|
||||
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
||||
{
|
||||
Message * m = (Message *)i;
|
||||
i = i->next; // Do this first since we may unlink the current node
|
||||
|
||||
if (awaitingReplyDownRequest.isEmpty())
|
||||
{
|
||||
//
|
||||
// Set the message number, and unlink from the outgoing queue
|
||||
//
|
||||
m->encodedMessage.messageNumber = 0;
|
||||
m->state.messageNumber = 0;
|
||||
|
||||
notYetSentDownRequest.remove(m);
|
||||
awaitingReplyDownRequest.insertBack(m);
|
||||
|
||||
//
|
||||
// This call can cause transmitAwaitingDownRequests to be called again
|
||||
//
|
||||
bool sent = splitterDownRequest.send(m->encodedMessage, m);
|
||||
DP_ASSERT(sent);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Enqueue the next message to the splitterUpReply
|
||||
//
|
||||
void MessageManager::transmitAwaitingUpReplies()
|
||||
{
|
||||
for (ListElement * i = notYetSentUpReply.begin(); i!=notYetSentUpReply.end(); )
|
||||
{
|
||||
Message * m = (Message *)i;
|
||||
i = i->next; // Do this first since we may unlink the current node
|
||||
|
||||
notYetSentUpReply.remove(m);
|
||||
|
||||
//
|
||||
// This call can cause transmitAwaitingUpReplies to be called again
|
||||
//
|
||||
bool sent = splitterUpReply.send(m->encodedMessage, m);
|
||||
DP_ASSERT(sent);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::postReply(Message * message, Message::MessageEventSink * sink)
|
||||
{
|
||||
post(message, sink, true);
|
||||
}
|
||||
|
||||
void MessageManager::cancelAllByType(unsigned type)
|
||||
{
|
||||
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
||||
{
|
||||
Message * m = (Message *)i;
|
||||
i = i->next;
|
||||
|
||||
if (m->requestIdentifier == type)
|
||||
notYetSentDownRequest.remove(m);
|
||||
}
|
||||
|
||||
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); )
|
||||
{
|
||||
Message * m = (Message *)i;
|
||||
i = i->next;
|
||||
|
||||
if (m->requestIdentifier == type)
|
||||
awaitingReplyDownRequest.remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::cancelAll(Message * message)
|
||||
{
|
||||
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
||||
{
|
||||
Message * m = (Message *)i;
|
||||
i = i->next;
|
||||
|
||||
if (m == message && m->requestIdentifier == message->requestIdentifier)
|
||||
notYetSentDownRequest.remove(m);
|
||||
}
|
||||
|
||||
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); )
|
||||
{
|
||||
Message * m = (Message *)i;
|
||||
i = i->next;
|
||||
|
||||
if (m == message && m->requestIdentifier == message->requestIdentifier)
|
||||
awaitingReplyDownRequest.remove(m);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::post(Message * message, Message::MessageEventSink * sink, bool transmitReply)
|
||||
{
|
||||
DP_ASSERT(!isBeingDestroyed && "You may not post messages in response to a shutdown");
|
||||
|
||||
if (isPaused)
|
||||
return;
|
||||
|
||||
//
|
||||
// Initialize the fields
|
||||
//
|
||||
message->sink = sink;
|
||||
message->bTransmitted = false;
|
||||
|
||||
//
|
||||
// Queue the message for the outgoing queue.
|
||||
// Later on we'll walk to the queue and make sure
|
||||
// we have at most two outstanding messages PER
|
||||
// target address. This is how the message
|
||||
// number is decided.
|
||||
//
|
||||
|
||||
message->parent = this;
|
||||
message->transmitReply = transmitReply;
|
||||
if (message->encodedMessage.isBroadcast)
|
||||
{
|
||||
// if its a broadcast message; the target would be the immediate branch.
|
||||
Address addr;
|
||||
addr.clear();
|
||||
addr.append(0);
|
||||
message->state.target = addr;
|
||||
}
|
||||
else
|
||||
message->state.target = message->encodedMessage.address;
|
||||
|
||||
if ( transmitReply )
|
||||
{
|
||||
notYetSentUpReply.insertBack(message);
|
||||
transmitAwaitingUpReplies();
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// If the list is empty or the incoming message has the least priority possible (DEFAULT priority),
|
||||
// then just add the incoming message to the back of the list.
|
||||
// Otherwise, find the right location by traversing the list.
|
||||
//
|
||||
if(message->messagePriority == NV_DP_SBMSG_PRIORITY_LEVEL_DEFAULT || notYetSentDownRequest.isEmpty())
|
||||
{
|
||||
notYetSentDownRequest.insertBack(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
ListElement *tmp = notYetSentDownRequest.last();
|
||||
Message *msg = (Message*) notYetSentDownRequest.last();
|
||||
while((msg->prev != tmp) && (msg->messagePriority < message->messagePriority))
|
||||
{
|
||||
msg = (Message*)msg->prev;
|
||||
}
|
||||
notYetSentDownRequest.insertBefore(msg->next, message);
|
||||
}
|
||||
transmitAwaitingDownRequests();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageManager::onUpRequestReceived(bool status, EncodedMessage * message)
|
||||
{
|
||||
if (!status)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Broadcast the up-request message to all
|
||||
// the receivers on messageReceivers
|
||||
//
|
||||
for (ListElement * i = messageReceivers.begin(); i!=messageReceivers.end(); i=i->next)
|
||||
{
|
||||
MessageReceiver * rcr = (MessageReceiver *)i;
|
||||
if (rcr->process((EncodedMessage *)message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DP_ASSERT(0 && "Warning: Unknown upstream UP_REQ message");
|
||||
}
|
||||
|
||||
|
||||
void MessageManager::onDownReplyReceived(bool status, EncodedMessage * message)
|
||||
{
|
||||
if (!status)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Broadcast the down-request message to all
|
||||
// the receivers on awaitingReplyDownRequest
|
||||
//
|
||||
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); i=i->next)
|
||||
{
|
||||
Message * messageAwaitingReply = (Message *)i;
|
||||
|
||||
if( messageAwaitingReply->state.target == message->address &&
|
||||
messageAwaitingReply->state.messageNumber == message->messageNumber)
|
||||
{
|
||||
awaitingReplyDownRequest.remove(messageAwaitingReply);
|
||||
if (messageAwaitingReply->parseResponse(message) == ParseResponseWrong)
|
||||
{
|
||||
//
|
||||
// parseResponse() returns ParseResposeWrong when 'Request_Identifier' of down request
|
||||
// message and down reply message are mis-matched. So insert message in waiting queue
|
||||
// and wait for correct down reply message.
|
||||
//
|
||||
awaitingReplyDownRequest.insertBack(messageAwaitingReply);
|
||||
}
|
||||
|
||||
goto nextMessage;
|
||||
}
|
||||
}
|
||||
|
||||
DP_LOG(("DPMM> Warning: Unmatched reply message"));
|
||||
nextMessage:
|
||||
transmitAwaitingUpReplies();
|
||||
transmitAwaitingDownRequests();
|
||||
}
|
||||
|
||||
MessageManager::~MessageManager()
|
||||
{
|
||||
// This causes any posts they may attempt to do to fail
|
||||
isBeingDestroyed = true;
|
||||
|
||||
//
|
||||
// The message manager should not be shut down until
|
||||
// all outgoing messages are in the cancelled state
|
||||
//
|
||||
NakData nakUndef;
|
||||
nakUndef.reason = NakUndefined;
|
||||
|
||||
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
||||
{
|
||||
ListElement * next = i->next;
|
||||
if (((Message *)i)->sink)
|
||||
((Message *)i)->sink->messageFailed(((Message *)i), &nakUndef);
|
||||
i = next;
|
||||
}
|
||||
if (!notYetSentDownRequest.isEmpty())
|
||||
{
|
||||
|
||||
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
||||
{
|
||||
ListElement * next = i->next;
|
||||
DP_LOG(("Down request message type 0x%x client is not cleaning up.", ((Message *)i)->requestIdentifier));
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
for (ListElement * i = notYetSentUpReply.begin(); i!=notYetSentUpReply.end();)
|
||||
{
|
||||
ListElement * next = i->next;
|
||||
if (((Message *)i)->sink)
|
||||
((Message *)i)->sink->messageFailed(((Message *)i), &nakUndef);
|
||||
i = next;
|
||||
}
|
||||
if (!notYetSentUpReply.isEmpty())
|
||||
{
|
||||
|
||||
for (ListElement * i = notYetSentUpReply.begin(); i!=notYetSentUpReply.end(); )
|
||||
{
|
||||
ListElement * next = i->next;
|
||||
DP_LOG(("Up reply message type 0x%x client is not cleaning up.", ((Message *)i)->requestIdentifier));
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); )
|
||||
{
|
||||
ListElement * next = i->next;
|
||||
if (((Message *)i)->sink)
|
||||
((Message *)i)->sink->messageFailed(((Message *)i), &nakUndef);
|
||||
i = next;
|
||||
}
|
||||
if (!awaitingReplyDownRequest.isEmpty())
|
||||
{
|
||||
|
||||
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); )
|
||||
{
|
||||
ListElement * next = i->next;
|
||||
DP_LOG(("Down request message type 0x%x client is not cleaning up.", ((Message *)i)->requestIdentifier));
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not reclaim the memory of our registered receivers
|
||||
while (!messageReceivers.isEmpty())
|
||||
messageReceivers.remove(messageReceivers.front());
|
||||
}
|
||||
|
||||
ParseResponseStatus MessageManager::Message::parseResponse(EncodedMessage * message)
|
||||
{
|
||||
BitStreamReader reader(&message->buffer, 0, message->buffer.length*8);
|
||||
|
||||
// Read ReplyType
|
||||
bool replyNacked = !!reader.readOrDefault(1, true);
|
||||
|
||||
// Read RequestIdentifier
|
||||
unsigned requestId = reader.readOrDefault(7, 0);
|
||||
if (requestId != requestIdentifier)
|
||||
{
|
||||
DP_LOG(("DP-MM> Requested = %x Received = %x", requestId, requestIdentifier));
|
||||
DP_ASSERT(0 && "Reply type doesn't match");
|
||||
return ParseResponseWrong;
|
||||
}
|
||||
|
||||
if (replyNacked)
|
||||
{
|
||||
NakData nakData;
|
||||
|
||||
// failure handler will parse the NAK response and do the required action
|
||||
if (DisplayPort::extractGUID(&reader, &nakData.guid) == false)
|
||||
{
|
||||
DP_ASSERT(0 && "Invalid GUID in NAK");
|
||||
}
|
||||
|
||||
nakData.reason = (NakReason)reader.readOrDefault(8, 0);
|
||||
nakData.nak_data = reader.readOrDefault(8, 0);
|
||||
|
||||
// call specific handler after parsing.
|
||||
parent->timer->cancelCallbacks(this);
|
||||
|
||||
MessageManager * parent = this->parent;
|
||||
|
||||
if (sink)
|
||||
sink->messageFailed(this, &nakData);
|
||||
|
||||
parent->transmitAwaitingDownRequests();
|
||||
|
||||
return ParseResponseSuccess;
|
||||
}
|
||||
|
||||
ParseResponseStatus parseResult = parseResponseAck(message, &reader);
|
||||
|
||||
if (parseResult == ParseResponseSuccess)
|
||||
{
|
||||
parent->timer->cancelCallbacks(this);
|
||||
|
||||
if (this->sink)
|
||||
{
|
||||
MessageEventSink * msgSink = this->sink;
|
||||
msgSink->messageCompleted(this);
|
||||
}
|
||||
}
|
||||
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
void MessageManager::Message::MessageEventSink::messageFailed(Message * from, NakData * nakData)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MessageManager::registerReceiver(MessageReceiver * receiver)
|
||||
{
|
||||
messageReceivers.insertBack(receiver);
|
||||
}
|
||||
|
||||
|
||||
bool MessageManager::MessageReceiver::process(EncodedMessage * message)
|
||||
{
|
||||
BitStreamReader reader(&message->buffer, 0, message->buffer.length*8);
|
||||
|
||||
// Read RequestIdentifier
|
||||
reader.readOrDefault(1, 0);
|
||||
unsigned reqId = reader.readOrDefault(7, 0);
|
||||
|
||||
if (reqId != this->getRequestId())
|
||||
{
|
||||
//
|
||||
// This receiver is not meant for this message;
|
||||
// let the next in the queue handle it.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
|
||||
this->address = message->address;
|
||||
|
||||
// processByType should parse the request, create a response and queue it if needed
|
||||
bool status = processByType(message, &reader);
|
||||
if (!status)
|
||||
{
|
||||
//
|
||||
// if we are here; we could get a receiver to handle the request
|
||||
// but something else went wrong.
|
||||
//
|
||||
DP_ASSERT(0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
188
src/common/displayport/src/dp_mst_edid.cpp
Normal file
188
src/common/displayport/src/dp_mst_edid.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_mst_edid.c *
|
||||
* Implementation Multi Stream EDID reads *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_edid.h"
|
||||
#include "dp_address.h"
|
||||
#include "dp_messagecodings.h"
|
||||
#include "dp_messages.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
EdidReadMultistream::~EdidReadMultistream()
|
||||
{
|
||||
timer->cancelCallbacks(this);
|
||||
}
|
||||
|
||||
void EdidReadMultistream::startReadingEdid()
|
||||
{
|
||||
NvU8 offset = 0;
|
||||
I2cWriteTransaction i2cWriteTransactions[1];
|
||||
Address::StringBuffer buffer;
|
||||
DP_USED(buffer);
|
||||
DP_LOG(("%s(): start for %s", __FUNCTION__,
|
||||
topologyAddress.toString(buffer)));
|
||||
|
||||
edidReaderManager.reset();
|
||||
edid.resetData();
|
||||
|
||||
DDCAddress = ddcAddrList[ddcIndex];
|
||||
|
||||
// set offset within segment 0, no need to set segment, because we're starting reading EDID
|
||||
i2cWriteTransactions[0] = I2cWriteTransaction(DDCAddress >> 1,
|
||||
sizeof(offset),
|
||||
&offset,
|
||||
true);
|
||||
NvU8 nWriteTransactions = 1;
|
||||
|
||||
remoteI2cRead.set(topologyAddress.parent(), // topology Address
|
||||
nWriteTransactions, // number of write transactions
|
||||
topologyAddress.tail(), // port of Device
|
||||
i2cWriteTransactions, // list of write transactions
|
||||
DDCAddress >> 1, // right shifted DDC Address (request identifier in spec)
|
||||
EDID_BLOCK_SIZE); // requested size
|
||||
|
||||
manager->post(&remoteI2cRead, this);
|
||||
}
|
||||
|
||||
void EdidReadMultistream::messageCompleted(MessageManager::Message * from)
|
||||
{
|
||||
RemoteI2cReadMessage* I2CReadMessage = (RemoteI2cReadMessage*)from;
|
||||
unsigned char * data = 0;
|
||||
unsigned numBytesRead;
|
||||
Address::StringBuffer buffer;
|
||||
DP_USED(buffer);
|
||||
|
||||
NvU8 seg;
|
||||
NvU8 offset;
|
||||
DP_LOG(("%s for %s", __FUNCTION__, topologyAddress.toString(buffer)));
|
||||
|
||||
DP_ASSERT(DDCAddress && "DDCAddress is 0, it is wrong");
|
||||
|
||||
data = I2CReadMessage->replyGetI2CData(&numBytesRead);
|
||||
DP_ASSERT(data);
|
||||
|
||||
// this is not required, but I'd like to keep things simple at first submission
|
||||
DP_ASSERT(numBytesRead == EDID_BLOCK_SIZE);
|
||||
edidReaderManager.postReply(data, numBytesRead, true);
|
||||
|
||||
if (edidReaderManager.readNextRequest(seg, offset))
|
||||
{
|
||||
readNextBlock(seg, offset);
|
||||
}
|
||||
else // EDID read is finished or failed.
|
||||
{
|
||||
edidAttemptDone(edidReaderManager.readIsComplete() && edid.verifyCRC());
|
||||
}
|
||||
}
|
||||
|
||||
void EdidReadMultistream::edidAttemptDone(bool succeeded)
|
||||
{
|
||||
if (succeeded)
|
||||
sink->mstEdidCompleted(this);
|
||||
else if (ddcIndex + 1 < ddcAddrListSize)
|
||||
{
|
||||
ddcIndex++;
|
||||
startReadingEdid();
|
||||
}
|
||||
else
|
||||
sink->mstEdidReadFailed(this);
|
||||
}
|
||||
|
||||
void EdidReadMultistream::readNextBlock(NvU8 seg, NvU8 offset)
|
||||
{
|
||||
I2cWriteTransaction i2cWriteTransactions[2];
|
||||
Address::StringBuffer buffer;
|
||||
DP_USED(buffer);
|
||||
|
||||
// ensure that init function for i2cWriteTranscation for segment and offset won't break
|
||||
DP_ASSERT(sizeof(seg) == 1);
|
||||
DP_ASSERT(sizeof(offset) == 1);
|
||||
|
||||
DP_LOG(("%s(): for %s (seg/offset) = %d/%d", __FUNCTION__,
|
||||
topologyAddress.toString(buffer),
|
||||
seg, offset));
|
||||
|
||||
unsigned nWriteTransactions = 2;
|
||||
if (seg)
|
||||
{
|
||||
// select segment
|
||||
i2cWriteTransactions[0] = I2cWriteTransaction(EDID_SEG_SELECTOR_OFFSET >> 1,
|
||||
1, &seg, true);
|
||||
// set offset within segment
|
||||
i2cWriteTransactions[1] = I2cWriteTransaction(DDCAddress >> 1,
|
||||
1, &offset, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// set offset within segment 0
|
||||
i2cWriteTransactions[0] = I2cWriteTransaction(DDCAddress >> 1, 1, &offset, true);
|
||||
nWriteTransactions = 1;
|
||||
}
|
||||
|
||||
remoteI2cRead.set(topologyAddress.parent(), // topology Address
|
||||
nWriteTransactions, // number of write transactions
|
||||
topologyAddress.tail(), // port of Device
|
||||
i2cWriteTransactions, // list of write transactions
|
||||
DDCAddress >> 1, // right shifted DDC Address (request identifier in spec)
|
||||
EDID_BLOCK_SIZE); // requested size
|
||||
|
||||
manager->post(&remoteI2cRead, this, false);
|
||||
}
|
||||
|
||||
void EdidReadMultistream::expired(const void * tag)
|
||||
{
|
||||
Address::StringBuffer buffer;
|
||||
DP_USED(buffer);
|
||||
DP_LOG(("%s on %s", __FUNCTION__, topologyAddress.toString(buffer)));
|
||||
startReadingEdid();
|
||||
}
|
||||
|
||||
void EdidReadMultistream::messageFailed(MessageManager::Message * from, NakData * nakData)
|
||||
{
|
||||
Address::StringBuffer buffer;
|
||||
DP_USED(buffer);
|
||||
DP_LOG(("%s on %s", __FUNCTION__, topologyAddress.toString(buffer)));
|
||||
|
||||
if (nakData->reason == NakDefer || nakData->reason == NakTimeout)
|
||||
{
|
||||
if (retries < MST_EDID_RETRIES)
|
||||
{
|
||||
++retries;
|
||||
timer->queueCallback(this, "EDID", MST_EDID_COOLDOWN);
|
||||
}
|
||||
else
|
||||
edidAttemptDone(false /* failed */);
|
||||
}
|
||||
else
|
||||
{
|
||||
edidAttemptDone(false /* failed */);
|
||||
}
|
||||
}
|
||||
314
src/common/displayport/src/dp_splitter.cpp
Normal file
314
src/common/displayport/src/dp_splitter.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort********************************\
|
||||
* *
|
||||
* Module: dp_splitter.cpp *
|
||||
* Asynchronous Message Splitter *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_internal.h"
|
||||
#include "dp_bitstream.h"
|
||||
#include "dp_splitter.h"
|
||||
#include "dp_auxdefs.h"
|
||||
#include "dp_crc.h"
|
||||
#include "dp_configcaps.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
#define DP_MAX_HEADER_SIZE 16
|
||||
// timeout after 110ms with a retry recurring every 5ms for 10 times
|
||||
#define DOWNSTREAM_RETRY_ON_DEFER_TIMEOUT 110
|
||||
#define DOWNSTREAM_RETRY_ON_DEFER_PERIOD 5
|
||||
#define DOWNSTREAM_RETRY_ON_DEFER_COUNT 10
|
||||
|
||||
bool MessageTransactionSplitter::get(Buffer & assemblyBuffer)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned payloadSize;
|
||||
bool isTransactionStart, isTransactionEnd;
|
||||
Address address;
|
||||
unsigned LCT;
|
||||
unsigned LCR;
|
||||
unsigned headerSizeBits;
|
||||
|
||||
assemblyBuffer.reset();
|
||||
|
||||
//
|
||||
// Done?
|
||||
//
|
||||
if (this->messageOutstanding->buffer.length == this->assemblyTransmitted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
address = this->messageOutstanding->address;
|
||||
if (this->messageOutstanding->isBroadcast)
|
||||
{
|
||||
// no RAD
|
||||
address.clear();
|
||||
LCT = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
LCT = address.size();
|
||||
}
|
||||
|
||||
// Calculate header size
|
||||
headerSizeBits = 8 + // LCT/LCR
|
||||
(((4 * (LCT -1)) + 4) &~ 7) + // byte aligned RAD
|
||||
16;
|
||||
|
||||
//
|
||||
// Pick how much data to send. Header+payloadSize <= 48 bytes.
|
||||
//
|
||||
payloadSize = DP_MIN(DPCD_MESSAGEBOX_SIZE - (headerSizeBits+7)/8, /*crc*/1 + this->messageOutstanding->buffer.length - this->assemblyTransmitted);
|
||||
|
||||
//
|
||||
// Is the first or last transaction in the sequence?
|
||||
//
|
||||
isTransactionStart = assemblyTransmitted == 0;
|
||||
isTransactionEnd = (assemblyTransmitted + payloadSize - 1) == messageOutstanding->buffer.length;
|
||||
|
||||
BitStreamWriter writer(&assemblyBuffer, 0);
|
||||
|
||||
//
|
||||
// Write the header
|
||||
//
|
||||
writer.write(LCT, 4);
|
||||
|
||||
LCR = this->messageOutstanding->isBroadcast ? 6 : LCT > 1 ? LCT - 1 : 0;
|
||||
|
||||
writer.write(LCR, 4);
|
||||
|
||||
// port at i=0 is the outport of source/gpu which should not be included in the RAD in outgoing message header
|
||||
// if this is a broadcast message; LCT would be 1; hence no RAD.
|
||||
for (i = 1; i < LCT; i++)
|
||||
writer.write(address[i], 4);
|
||||
writer.align(8);
|
||||
|
||||
writer.write(this->messageOutstanding->isBroadcast, 1);
|
||||
writer.write(this->messageOutstanding->isPathMessage, 1);
|
||||
writer.write(payloadSize, 6);
|
||||
|
||||
writer.write(isTransactionStart, 1);
|
||||
writer.write(isTransactionEnd, 1);
|
||||
writer.write(0, 1);
|
||||
|
||||
DP_ASSERT(messageOutstanding->messageNumber == 0 || messageOutstanding->messageNumber == 1);
|
||||
writer.write(messageOutstanding->messageNumber, 1);
|
||||
|
||||
//
|
||||
// Generate 4 bit CRC. (Nibble-wise CRC of previous values)
|
||||
//
|
||||
BitStreamReader reader(&assemblyBuffer, 0, writer.offset());
|
||||
writer.write(dpCalculateHeaderCRC(&reader), 4);
|
||||
|
||||
DP_ASSERT(writer.offset() == headerSizeBits && "Header size mismatch");
|
||||
DP_ASSERT((writer.offset() & 7) == 0 && "Packet header must end byte aligned");
|
||||
|
||||
//
|
||||
// Generate body CRC
|
||||
//
|
||||
BitStreamReader bodyReader(&this->messageOutstanding->buffer, this->assemblyTransmitted * 8, (payloadSize - 1) * 8);
|
||||
NvU8 bodyCrc = (NvU8)dpCalculateBodyCRC(&bodyReader);
|
||||
|
||||
// Copy in remaining buffer (leaving room for the CRC)
|
||||
for (i = 0; i < payloadSize - 1; ++i)
|
||||
writer.write(this->messageOutstanding->buffer.data[i + this->assemblyTransmitted], 8);
|
||||
writer.write(bodyCrc, 8);
|
||||
|
||||
this->assemblyTransmitted += payloadSize - 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OutgoingTransactionManager::expired(const void * tag)
|
||||
{
|
||||
writeToWindow(false);
|
||||
}
|
||||
|
||||
void OutgoingTransactionManager::cancel(OutgoingTransactionManagerEventSink * sink)
|
||||
{
|
||||
if (activeMessage && activeMessage->eventSink == sink)
|
||||
activeMessage->eventSink = 0;
|
||||
|
||||
for (ListElement * el = queuedMessages.begin(); el && el!=queuedMessages.end(); el = el->next)
|
||||
if (((OutgoingMessage *)el)->eventSink == sink)
|
||||
((OutgoingMessage *)el)->eventSink = 0;
|
||||
}
|
||||
|
||||
bool OutgoingTransactionManager::send( EncodedMessage & payload, OutgoingTransactionManagerEventSink * sink)
|
||||
{
|
||||
OutgoingMessage * om = new OutgoingMessage();
|
||||
|
||||
if (!om)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
om->eventSink = sink;
|
||||
om->message.swap(payload);
|
||||
|
||||
if (!activeMessage)
|
||||
{
|
||||
activeMessage = om;
|
||||
transactionSplitter.set(&om->message);
|
||||
transactionSplitter.get(this->assemblyBuffer);
|
||||
writeToWindow(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
queuedMessages.insertBack(om);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OutgoingTransactionManager::writeToWindow( bool firstAttempt)
|
||||
{
|
||||
AuxRetry::status result;
|
||||
|
||||
if (!activeMessage || !activeMessage->eventSink)
|
||||
goto findNextMessage;
|
||||
|
||||
result = this->writeMessageBox(assemblyBuffer.data, assemblyBuffer.length);
|
||||
|
||||
if (result == AuxRetry::defer)
|
||||
{
|
||||
|
||||
//
|
||||
// if retries left; queue one.
|
||||
//
|
||||
if (firstAttempt || retriesLeft )
|
||||
{
|
||||
if (firstAttempt)
|
||||
{
|
||||
// initialize retriesLeft
|
||||
retriesLeft = DOWNSTREAM_RETRY_ON_DEFER_COUNT;
|
||||
}
|
||||
|
||||
retriesLeft--;
|
||||
DP_LOG(("DP-MM> Messagebox write defer-ed. Q-ing retry."));
|
||||
this->timer->queueCallback(this, "SPDE", DOWNSTREAM_RETRY_ON_DEFER_PERIOD);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Notify message sender of failure. Keep in mind sender
|
||||
// might turn around immediately with a queue'd send.
|
||||
//
|
||||
if (activeMessage)
|
||||
{
|
||||
activeMessage->eventSink->splitterFailed(this);
|
||||
}
|
||||
|
||||
goto findNextMessage;
|
||||
}
|
||||
else if (result == AuxRetry::ack)
|
||||
{
|
||||
//
|
||||
// Split off another chunk and transmit
|
||||
//
|
||||
if (transactionSplitter.get(assemblyBuffer))
|
||||
{
|
||||
writeToWindow(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Notify message sender of success. Keep in mind sender
|
||||
// might turn around immediately with a queue'd send.
|
||||
//
|
||||
if (activeMessage)
|
||||
{
|
||||
activeMessage->eventSink->splitterTransmitted(this);
|
||||
}
|
||||
|
||||
goto findNextMessage;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Notify message sender of failure. Keep in mind sender
|
||||
// might turn around immediately with a queued send.
|
||||
//
|
||||
if (activeMessage)
|
||||
{
|
||||
activeMessage->eventSink->splitterFailed(this);
|
||||
}
|
||||
|
||||
findNextMessage:
|
||||
//
|
||||
// The old transaction is complete. Free the memory
|
||||
//
|
||||
delete activeMessage;
|
||||
activeMessage = 0;
|
||||
|
||||
//
|
||||
// Look for the next transaction
|
||||
//
|
||||
if (queuedMessages.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
activeMessage = (OutgoingMessage *)queuedMessages.begin();
|
||||
queuedMessages.remove(activeMessage);
|
||||
|
||||
transactionSplitter.set(&activeMessage->message);
|
||||
transactionSplitter.get(this->assemblyBuffer);
|
||||
writeToWindow(true);
|
||||
}
|
||||
}
|
||||
|
||||
OutgoingTransactionManager::OutgoingTransactionManager(Timer * timer)
|
||||
: timer(timer)
|
||||
{
|
||||
this->activeMessage = 0;
|
||||
}
|
||||
|
||||
AuxRetry::status DownRequestManager::writeMessageBox(NvU8 * data, size_t length)
|
||||
{
|
||||
return hal->writeDownRequestMessageBox(data, length);
|
||||
}
|
||||
|
||||
size_t DownRequestManager::getMessageBoxSize()
|
||||
{
|
||||
return hal->getDownRequestMessageBoxSize();
|
||||
}
|
||||
|
||||
AuxRetry::status UpReplyManager::writeMessageBox(NvU8 * data, size_t length)
|
||||
{
|
||||
return hal->writeUpReplyMessageBox(data, length);
|
||||
}
|
||||
|
||||
size_t UpReplyManager::getMessageBoxSize()
|
||||
{
|
||||
return hal->getUpReplyMessageBoxSize();
|
||||
}
|
||||
336
src/common/displayport/src/dp_sst_edid.cpp
Normal file
336
src/common/displayport/src/dp_sst_edid.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_sst_edid.c *
|
||||
* Implementation Single Stream EDID reads *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dp_buffer.h"
|
||||
#include "dp_auxbus.h"
|
||||
#include "dp_internal.h"
|
||||
#include "dp_edid.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
/*
|
||||
* seg -> 256 segment of EDID
|
||||
* offset -> offset within segment
|
||||
*/
|
||||
static bool readNextBlock(AuxBus * auxBus, NvU8 seg, NvU8 offset, Buffer & buffer, unsigned & totalRead, unsigned DDCAddress, Timer * timer)
|
||||
{
|
||||
AuxBus::Type type = AuxBus::i2cMot;
|
||||
AuxBus::status auxStatus;
|
||||
|
||||
unsigned retries = 0;
|
||||
unsigned sizeRequested;
|
||||
unsigned sizeCompleted;
|
||||
unsigned transactionSize = auxBus->transactionSize();
|
||||
totalRead = 0;
|
||||
|
||||
DP_ASSERT(auxBus);
|
||||
DP_ASSERT(transactionSize > 0);
|
||||
|
||||
// ASSERT if edidOffset offset wasn't increased in block len sizes
|
||||
DP_ASSERT(offset == 0 || offset == EDID_BLOCK_SIZE);
|
||||
|
||||
sizeRequested = transactionSize;
|
||||
if (!buffer.resize(EDID_BLOCK_SIZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DP_ASSERT(sizeof(seg) == 1);
|
||||
DP_ASSERT(sizeof(offset) == 1);
|
||||
|
||||
// only set segment if it is required
|
||||
if (seg)
|
||||
{
|
||||
// start EDID read by specifying appropriate Edid segment id
|
||||
for (unsigned retry = 0; retry < EDID_MAX_AUX_RETRIES; retry++)
|
||||
{
|
||||
auxStatus = auxBus->transaction(AuxBus::write, AuxBus::i2cMot, EDID_SEG_SELECTOR_OFFSET >> 1,
|
||||
&seg, sizeof(seg), &sizeCompleted);
|
||||
if (auxStatus == AuxBus::success)
|
||||
break;
|
||||
|
||||
// If deferred due to timeout
|
||||
if (auxStatus == AuxBus::defer)
|
||||
{
|
||||
// Wait for sometime between retries
|
||||
timer->sleep(EDID_AUX_WAIT_TIME);
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auxStatus = AuxBus::nack;
|
||||
|
||||
for (retries = 0; totalRead < EDID_BLOCK_SIZE;)
|
||||
{
|
||||
//
|
||||
// For retry, rewrite the Offset for the internal read pointer
|
||||
// except when the previous Read auxstatus was an Aux::defer
|
||||
// since in that case, the offset was never incremented by sink
|
||||
//
|
||||
if ((auxStatus != AuxBus::success) && (auxStatus != AuxBus::defer))
|
||||
{
|
||||
// start from this offset, need to verify with display with multiple edid blocks
|
||||
for (unsigned retry = 0; retry < EDID_MAX_AUX_RETRIES; retry++)
|
||||
{
|
||||
auxStatus = auxBus->transaction(AuxBus::write, AuxBus::i2cMot, DDCAddress >> 1,
|
||||
(NvU8*)(&offset), sizeof(offset), &sizeCompleted);
|
||||
if (auxStatus == AuxBus::success)
|
||||
break;
|
||||
// If deferred due to timeout
|
||||
if (auxStatus == AuxBus::defer)
|
||||
{
|
||||
// Wait for sometime between retries
|
||||
timer->sleep(EDID_AUX_WAIT_TIME);
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// if retries exceed EDID_MAX_AUX_RETRIES, give up
|
||||
if (auxStatus != AuxBus::success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// need to change to I2C (not MOT) to read just one last part of EDID block
|
||||
if (totalRead + transactionSize >= EDID_BLOCK_SIZE)
|
||||
type = AuxBus::i2c;
|
||||
|
||||
sizeRequested = DP_MIN(transactionSize, EDID_BLOCK_SIZE - totalRead);
|
||||
auxStatus = auxBus->transaction(AuxBus::read, type, DDCAddress >> 1,
|
||||
&(buffer.data[totalRead]), sizeRequested, &sizeCompleted);
|
||||
|
||||
if (AuxBus::success != auxStatus || (sizeRequested && (sizeCompleted == 0)))
|
||||
{
|
||||
if (retries >= EDID_MAX_AUX_RETRIES)
|
||||
return false;
|
||||
|
||||
DP_LOG(("DisplayPort: %s: Retrying at totalRead 0x%08x (replyType %x, size %x)",
|
||||
__FUNCTION__, totalRead, auxStatus, sizeRequested));
|
||||
|
||||
// Wait for sometime between retries
|
||||
timer->sleep(EDID_AUX_WAIT_TIME);
|
||||
retries++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Assert when size mismatches and it is not last block
|
||||
if ((sizeRequested != sizeCompleted) &&
|
||||
(totalRead + transactionSize < EDID_BLOCK_SIZE))
|
||||
{
|
||||
DP_LOG(("DisplayPort: %s: dpAux returned edid block smaller than expected. Read from totalRead 0x%08x (replyType %x, size %x)",
|
||||
__FUNCTION__, totalRead, auxStatus, sizeRequested));
|
||||
DP_ASSERT(0);
|
||||
}
|
||||
|
||||
retries = 0; // reset the number of retries
|
||||
totalRead += sizeCompleted;
|
||||
offset += (NvU8)sizeCompleted;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @return: true => EDID read is success, false => read is failure
|
||||
*/
|
||||
static bool sstReadEdid(AuxBus * auxBus, Edid & edid, unsigned DDCAddr, Timer * timer, bool pendingTestRequestEdidRead)
|
||||
{
|
||||
//
|
||||
// If there is pending test request for edid read,
|
||||
// ask edidReaderManager to take whatever posted,
|
||||
// instead of discarding bytes read by a failed read.
|
||||
// Because cert devices may need to see the checksum of these bytes,
|
||||
// even if they seem corrupted.
|
||||
//
|
||||
EdidAssembler edidReaderManager(&edid, pendingTestRequestEdidRead);
|
||||
NvU32 retryCount = 0;
|
||||
Buffer buffer;
|
||||
if (!buffer.resize(EDID_BLOCK_SIZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DP_ASSERT(auxBus);
|
||||
|
||||
do
|
||||
{
|
||||
NvU8 seg = 0;
|
||||
NvU8 offset = 0;
|
||||
unsigned totalRead = 0;
|
||||
edidReaderManager.reset();
|
||||
|
||||
// start by reading first EDID block, posting it and analyzing for next request
|
||||
do
|
||||
{
|
||||
bool success = readNextBlock(auxBus, seg, offset, buffer, totalRead, DDCAddr, timer);
|
||||
edidReaderManager.postReply(buffer, totalRead, success);
|
||||
}
|
||||
while (edidReaderManager.readNextRequest(seg, offset));
|
||||
if (!edid.isPatchedChecksum())
|
||||
break;
|
||||
} while (retryCount++ < EDID_POLICY_BLOCK_READ_MAX_RETRY_COUNT);
|
||||
|
||||
//
|
||||
// EDID read is successful when
|
||||
// 1. read was done to the end (i.e. no corruption, no blocks exceeding retry count)
|
||||
// 2. EDID CRC is correct
|
||||
//
|
||||
return edidReaderManager.readIsComplete();
|
||||
}
|
||||
|
||||
EDID_DDC DisplayPort::sstDDCPing(AuxBus & dpAux)
|
||||
{
|
||||
unsigned sizeRequested = 0, sizeCompleted;
|
||||
AuxBus::status auxStatus = AuxBus::nack;
|
||||
NvU8 offset = 0;
|
||||
unsigned ddcAddrIdx;
|
||||
|
||||
for (ddcAddrIdx = 0; ddcAddrIdx < ddcAddrListSize; ddcAddrIdx++)
|
||||
{
|
||||
//
|
||||
// Don't use an I2C write. Some devices erroneously ACK on the write
|
||||
//
|
||||
auxStatus = dpAux.transaction(AuxBus::read, AuxBus::i2c, ddcAddrList[ddcAddrIdx] >> 1,
|
||||
&offset, sizeRequested, &sizeCompleted);
|
||||
|
||||
if (AuxBus::success == auxStatus)
|
||||
return (EDID_DDC)ddcAddrList[ddcAddrIdx];
|
||||
}
|
||||
|
||||
return EDID_DDC_NONE;
|
||||
|
||||
}
|
||||
|
||||
bool DisplayPort::EdidReadSST(Edid & edid, AuxBus * auxBus, Timer* timer,
|
||||
bool pendingTestRequestEdidRead, bool bBypassAssembler,
|
||||
MainLink * main)
|
||||
{
|
||||
Edid previousEdid;
|
||||
Buffer *buffer;
|
||||
bool status;
|
||||
|
||||
for (unsigned i = 0; i < ddcAddrListSize; i++)
|
||||
{
|
||||
for (unsigned j = 0; j < EDID_READ_MAX_RETRY_COUNT; j++)
|
||||
{
|
||||
//
|
||||
// Client asks to use RM control code to fetch EDID.
|
||||
//
|
||||
if (bBypassAssembler && main)
|
||||
{
|
||||
unsigned blockCnt;
|
||||
buffer = edid.getBuffer();
|
||||
if (!buffer->resize(EDID_BLOCK_SIZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
status = main->fetchEdidByRmCtrl(buffer->getData(), buffer->getLength());
|
||||
|
||||
if (status)
|
||||
{
|
||||
blockCnt = edid.getBlockCount();
|
||||
|
||||
// If read successfully, check if there are two or more blocks.
|
||||
if (blockCnt != 1)
|
||||
{
|
||||
if (!buffer->resize(EDID_BLOCK_SIZE * blockCnt))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
status = main->fetchEdidByRmCtrl(buffer->getData(), buffer->getLength());
|
||||
}
|
||||
}
|
||||
if (!status)
|
||||
{
|
||||
//
|
||||
// If fetchEdidByRmCtrl fails for some reasons:
|
||||
// Try to read again using DPLib read function.
|
||||
// One reason client to request read from RM is to making sure
|
||||
// the EDID is overridden (regkey or others). So call the RM
|
||||
// control call to apply the EDID overrides.
|
||||
//
|
||||
status = sstReadEdid(auxBus, edid, ddcAddrList[i], timer,
|
||||
pendingTestRequestEdidRead);
|
||||
if (status)
|
||||
{
|
||||
main->applyEdidOverrideByRmCtrl(buffer->getData(),
|
||||
buffer->getLength());
|
||||
}
|
||||
else
|
||||
{
|
||||
DP_LOG(("EDID> Failed to read EDID from RM and DPLib"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// If there is pending test request for edid read, make sure we get the raw bytes without check.
|
||||
// Because cert devices may need to see the checksum of whatever is read for edid, even if they seem corrupted.
|
||||
//
|
||||
status = sstReadEdid(auxBus, edid, ddcAddrList[i], timer, pendingTestRequestEdidRead);
|
||||
|
||||
}
|
||||
|
||||
if (status)
|
||||
{
|
||||
if (edid.verifyCRC())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j == 0) // first failure?
|
||||
{
|
||||
previousEdid.swap(edid);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (previousEdid == edid)
|
||||
{
|
||||
// we got the same invalid checksum again; we will assume it is valid.
|
||||
edid.setForcedEdidChecksum(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DP_LOG(("EDID> Failed to ping sst DDC addresses"));
|
||||
|
||||
return false;
|
||||
}
|
||||
199
src/common/displayport/src/dp_timer.cpp
Normal file
199
src/common/displayport/src/dp_timer.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_timer.cpp *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#include "dp_internal.h"
|
||||
#include "dp_timer.h"
|
||||
using namespace DisplayPort;
|
||||
|
||||
void Timer::expired()
|
||||
{
|
||||
fire(false);
|
||||
}
|
||||
|
||||
// Take care, this function is re-entrant.
|
||||
// Consider that sleep() is effectively a call to fire().
|
||||
// Clients may sleep in response to a timer callback.
|
||||
unsigned Timer::fire(bool fromSleep) // returns min time to next item to be fired
|
||||
{
|
||||
restart:
|
||||
|
||||
NvU64 now = getTimeUs();
|
||||
NvU64 nearest = (NvU64)-1;
|
||||
for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); )
|
||||
{
|
||||
if (fromSleep && !i->executeInSleep) {
|
||||
i = (PendingCallback*)i->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (now >= i->timestamp)
|
||||
{
|
||||
const void * context = i->context;
|
||||
TimerCallback * target = i->target;
|
||||
delete i;
|
||||
if (target)
|
||||
target->expired(context); // Take care, the client may have made
|
||||
// a recursive call to fire in here.
|
||||
// Easy solution: Restart at front of list.
|
||||
// current time may have also changed
|
||||
// drastically from a nested sleep
|
||||
goto restart;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i->timestamp < nearest)
|
||||
nearest = i->timestamp;
|
||||
i = (PendingCallback*)i->next;
|
||||
}
|
||||
}
|
||||
unsigned minleft = (unsigned)((nearest - now + 999)/ 1000);
|
||||
return minleft;
|
||||
}
|
||||
|
||||
void Timer::_pump(unsigned milliseconds, bool fromSleep)
|
||||
{
|
||||
do
|
||||
{
|
||||
unsigned amt = fire(fromSleep);
|
||||
if (amt >= milliseconds) {
|
||||
raw->sleep(milliseconds);
|
||||
return;
|
||||
}
|
||||
raw->sleep(amt);
|
||||
milliseconds-=amt;
|
||||
} while(milliseconds);
|
||||
}
|
||||
|
||||
//
|
||||
// Queue a timer callback.
|
||||
// Unless the dont-execute-in-sleep flag is set
|
||||
//
|
||||
void Timer::queueCallback(Timer::TimerCallback * target, const void * context, unsigned milliseconds, bool executeInSleep)
|
||||
{
|
||||
NvU64 now = getTimeUs();
|
||||
PendingCallback * callback = new PendingCallback();
|
||||
if (callback == NULL)
|
||||
{
|
||||
DP_LOG(("DP> %s: Failed to allocate callback",
|
||||
__FUNCTION__));
|
||||
return;
|
||||
}
|
||||
callback->target = target;
|
||||
callback->context = context;
|
||||
callback->timestamp = now + milliseconds * 1000;
|
||||
callback->executeInSleep = executeInSleep;
|
||||
pending.insertBack(callback);
|
||||
raw->queueCallback(this, milliseconds);
|
||||
}
|
||||
|
||||
NvU64 Timer::getTimeUs()
|
||||
{
|
||||
return raw->getTimeUs();
|
||||
}
|
||||
|
||||
// Sleep a number of milliseconds.
|
||||
// timer callbacks will be serviced!
|
||||
void Timer::sleep(unsigned milliseconds)
|
||||
{
|
||||
_pump(milliseconds, true);
|
||||
}
|
||||
|
||||
void Timer::cancelCallbacks(Timer::TimerCallback * to)
|
||||
{
|
||||
if (!to)
|
||||
return;
|
||||
for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); i = (PendingCallback *)i->next)
|
||||
if (i->target == to)
|
||||
i->target = 0;
|
||||
}
|
||||
|
||||
void Timer::cancelCallback(Timer::TimerCallback * to, const void * context)
|
||||
{
|
||||
if (!to)
|
||||
return;
|
||||
for (PendingCallback * i = (PendingCallback *)pending.begin(); i!=pending.end(); i = (PendingCallback*)i->next)
|
||||
if (i->target == to && i->context == context)
|
||||
i->target = 0;
|
||||
}
|
||||
|
||||
// Queue callbacks in order.
|
||||
void Timer::queueCallbackInOrder(Timer::TimerCallback * target, const void * context, unsigned milliseconds, bool executeInSleep)
|
||||
{
|
||||
NvU64 now = getTimeUs();
|
||||
PendingCallback * callback = new PendingCallback();
|
||||
callback->target = target;
|
||||
callback->context = context;
|
||||
callback->timestamp = now + milliseconds * 1000;
|
||||
callback->executeInSleep = executeInSleep;
|
||||
|
||||
//Figure out where to insert the current callback
|
||||
Timer::PendingCallback* i;
|
||||
|
||||
for (i = (PendingCallback*)pending.begin(); i != pending.end();)
|
||||
{
|
||||
// only for the given context.
|
||||
if(i->context == context)
|
||||
{
|
||||
if(i->timestamp > callback->timestamp)
|
||||
break;
|
||||
}
|
||||
i = (PendingCallback*) i->next;
|
||||
}
|
||||
if (i == pending.end())
|
||||
{
|
||||
pending.insertBack(callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
pending.insertBefore(i, callback);
|
||||
}
|
||||
|
||||
raw->queueCallback(this, milliseconds);
|
||||
}
|
||||
|
||||
void Timer::cancelAllCallbacks()
|
||||
{
|
||||
for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); i = (PendingCallback *)i->next)
|
||||
i->target = 0;
|
||||
}
|
||||
|
||||
void Timer::cancelCallbacksWithoutContext(const void * context)
|
||||
{
|
||||
for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); i = (PendingCallback *)i->next)
|
||||
if(i->context != context)
|
||||
i->target = 0;
|
||||
}
|
||||
|
||||
bool Timer::checkCallbacksOfSameContext(const void * context)
|
||||
{
|
||||
for (PendingCallback * i = (PendingCallback*)pending.begin(); i!=pending.end(); i = (PendingCallback *)i->next)
|
||||
if(i->context == context)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
247
src/common/displayport/src/dp_vrr.cpp
Normal file
247
src/common/displayport/src/dp_vrr.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_vrr.cpp *
|
||||
* Implementation of VRR enablement *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#include "dp_connectorimpl.h"
|
||||
#include "dp_vrr.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
bool VrrEnablement::start()
|
||||
{
|
||||
bool rc;
|
||||
|
||||
DP_LOG(("DPHAL_VRR_ENABLE> **** VRR Enablement Started ****"));
|
||||
rc = vrrGetPublicInfo();
|
||||
if(rc)
|
||||
{
|
||||
rc = vrrEnableMonitor();
|
||||
if(rc != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
rc = vrrEnableDriver();
|
||||
if(rc != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DP_LOG(("DPHAL_VRR_ENABLE> **** VRR Enablement Ends ****"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VrrEnablement::vrrGetPublicInfo()
|
||||
{
|
||||
MainLink *main = this->parent->connector->main;
|
||||
if (main->vrrRunEnablementStage(VRR_ENABLE_STAGE_INIT_PUBLIC_INFO, NULL) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (main->vrrRunEnablementStage(VRR_ENABLE_STAGE_RESET_MONITOR, NULL) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vrrWaitOnEnableStatus() != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (main->vrrRunEnablementStage(VRR_ENABLE_STAGE_GET_PUBLIC_INFO, NULL) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return vrrWaitOnEnableStatus();
|
||||
}
|
||||
|
||||
bool VrrEnablement::vrrEnableMonitor()
|
||||
{
|
||||
MainLink *main = this->parent->connector->main;
|
||||
|
||||
DP_LOG(("DPHAL_VRR_ENABLE> ** VRR_MON_ENABLE starts **"));
|
||||
|
||||
// Always set the enable F/W state m/c to a known state.
|
||||
if(main->vrrRunEnablementStage(VRR_ENABLE_STAGE_RESET_MONITOR, NULL) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for VRR to be 'ready'.
|
||||
if (vrrWaitOnEnableStatus() != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(main->vrrRunEnablementStage(VRR_ENABLE_STAGE_MONITOR_ENABLE_BEGIN, NULL) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for VRR to be 'ready'.
|
||||
if (vrrWaitOnEnableStatus() != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
main->vrrRunEnablementStage(VRR_ENABLE_STAGE_MONITOR_ENABLE_CHALLENGE, NULL);
|
||||
|
||||
// Wait for VRR to be ready.
|
||||
if (vrrWaitOnEnableStatus() != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Compare and enable on successful comparison.
|
||||
if(main->vrrRunEnablementStage(VRR_ENABLE_STAGE_MONITOR_ENABLE_CHECK, NULL) == true)
|
||||
{
|
||||
this->bMonitorEnabled = true;
|
||||
}
|
||||
|
||||
DP_LOG(("DPHAL_VRR_ENABLE> ** VRR_MON_ENABLE ends **"));
|
||||
|
||||
return this->bMonitorEnabled;
|
||||
}
|
||||
|
||||
bool VrrEnablement::vrrEnableDriver()
|
||||
{
|
||||
NvU32 enableResult;
|
||||
|
||||
MainLink *main = this->parent->connector->main;
|
||||
|
||||
DP_LOG(("DPHAL_VRR_ENABLE> ** VRR_DRV_ENABLE starts **"));
|
||||
|
||||
// Always set the enable F/W state m/c to a known state.
|
||||
if(main->vrrRunEnablementStage(VRR_ENABLE_STAGE_RESET_MONITOR, NULL) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for VRR to be 'ready'.
|
||||
if (vrrWaitOnEnableStatus() != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (main->vrrRunEnablementStage(VRR_ENABLE_STAGE_DRIVER_ENABLE_BEGIN, &enableResult) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enableResult == NV0073_CTRL_DP_CMD_ENABLE_VRR_STATUS_PENDING)
|
||||
{
|
||||
// Wait for VRR to be ready.
|
||||
if (vrrWaitOnEnableStatus() != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (enableResult == NV0073_CTRL_DP_CMD_ENABLE_VRR_STATUS_OK)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (main->vrrRunEnablementStage(VRR_ENABLE_STAGE_DRIVER_ENABLE_CHALLENGE, NULL) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for VRR to be 'ready'.
|
||||
if (vrrWaitOnEnableStatus() != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (main->vrrRunEnablementStage(VRR_ENABLE_STAGE_DRIVER_ENABLE_CHECK, NULL) != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DP_LOG(("DPHAL_VRR_ENABLE> ** VRR_DRV_ENABLE ends **"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VrrEnablement::vrrWaitOnEnableStatus(void)
|
||||
{
|
||||
NvU32 timeout = VRR_ENABLE_STATUS_TIMEOUT_THRESHOLD;
|
||||
NvU32 enableResult;
|
||||
|
||||
MainLink *main = this->parent->connector->main;
|
||||
ConnectorImpl *connector = this->parent->connector;
|
||||
do
|
||||
{
|
||||
if (main->vrrRunEnablementStage(VRR_ENABLE_STAGE_STATUS_CHECK, &enableResult) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enableResult == NV0073_CTRL_DP_CMD_ENABLE_VRR_STATUS_READ_ERROR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (enableResult == NV0073_CTRL_DP_CMD_ENABLE_VRR_STATUS_PENDING)
|
||||
{
|
||||
Timeout timeout(connector->timer, VRR_ENABLE_STATUS_TIMEOUT_INTERVAL_MS);
|
||||
while(timeout.valid());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}while(--timeout);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VrrEnablement::isMonitorEnabled(void)
|
||||
{
|
||||
return (this->bMonitorEnabled);
|
||||
}
|
||||
|
||||
bool VrrEnablement::isDriverEnabled(void)
|
||||
{
|
||||
NvU32 enableResult;
|
||||
MainLink *main = this->parent->connector->main;
|
||||
if (main->vrrRunEnablementStage(VRR_ENABLE_STAGE_DRIVER_ENABLE_CHECK,
|
||||
&enableResult) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
645
src/common/displayport/src/dp_wardatabase.cpp
Normal file
645
src/common/displayport/src/dp_wardatabase.cpp
Normal file
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 1993-2022 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_wardatabase.cpp *
|
||||
* EDID and OUI based workarounds for panel/TCON issues *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#include "dp_wardatabase.h"
|
||||
#include "dp_edid.h"
|
||||
#include "dp_connectorimpl.h"
|
||||
|
||||
using namespace DisplayPort;
|
||||
|
||||
void ConnectorImpl::applyOuiWARs()
|
||||
{
|
||||
switch (ouiId)
|
||||
{
|
||||
// Megachips Mystique
|
||||
case 0xE18000:
|
||||
if (((modelName[0] == 'D') && (modelName[1] == 'p') && (modelName[2] == '1') &&
|
||||
(modelName[3] == '.') && (modelName[4] == '1')))
|
||||
{
|
||||
//
|
||||
// Mystique based link box for HTC Vive has a peculiar behaviour
|
||||
// of sending a link retraining pulse if the link is powered down in the absence
|
||||
// of an active stream. Bug# 1793084. Set the flag so that link is not powered down.
|
||||
//
|
||||
bKeepOptLinkAlive = true;
|
||||
}
|
||||
|
||||
if (((modelName[0] == 'D') && (modelName[1] == 'p') && (modelName[2] == '1') &&
|
||||
(modelName[3] == '.') && (modelName[4] == '2')))
|
||||
{
|
||||
//
|
||||
// ASUS monitor loses link sometimes during assessing link or link training.
|
||||
// So if we retrain link by lowering config from HBR2 to HBR we see black screen
|
||||
// Set the flag so that we first retry link training with same link config
|
||||
// before following link training fallback. Bug #1846925
|
||||
//
|
||||
bNoFallbackInPostLQA = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// Synaptics
|
||||
case 0x24CC90:
|
||||
if ((modelName[0] == 'S') && (modelName[1] == 'Y') && (modelName[2] == 'N') &&
|
||||
(modelName[3] == 'A') && (modelName[4] == 'S') &&
|
||||
((modelName[5] == '1') || (modelName[5] == '2') ||
|
||||
(modelName[5] == '3') || (modelName[5] == '#') ||
|
||||
(modelName[5] == '\"')))
|
||||
{
|
||||
//
|
||||
// Extended latency from link-train end to FEC enable pattern
|
||||
// to avoid link lost or blank screen with Synaptics branch.
|
||||
// (Bug 2561206)
|
||||
//
|
||||
// Dock SKU ID:
|
||||
// Dell Salomon-WD19TB SYNAS1
|
||||
// HP Hook SYNAS3
|
||||
// HP Adira-A SYNAS#
|
||||
// Lenovo SYNAS" / SYNAS2
|
||||
//
|
||||
LT2FecLatencyMs = 57;
|
||||
|
||||
if (bDscMstCapBug3143315)
|
||||
{
|
||||
//
|
||||
// Synaptics branch device doesn't support Virtual Peer Devices so DSC
|
||||
// capability of downstream device should be decided based on device's own
|
||||
// and its parent's DSC capability
|
||||
//
|
||||
bDscCapBasedOnParent = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Edid::applyEdidWorkArounds(NvU32 warFlag, const DpMonitorDenylistData *pDenylistData)
|
||||
{
|
||||
|
||||
unsigned ManufacturerID = this->getManufId();
|
||||
unsigned ProductID = this->getProductId();
|
||||
unsigned YearWeek = this->getYearWeek();
|
||||
|
||||
//
|
||||
// Work around EDID problems, using manufacturer, product ID, and date of manufacture,
|
||||
// to identify each case.
|
||||
//
|
||||
switch (ManufacturerID)
|
||||
{
|
||||
// Apple
|
||||
case 0x1006:
|
||||
if (0x9227 == ProductID)
|
||||
{
|
||||
this->WARFlags.powerOnBeforeLt = true;
|
||||
DP_LOG(("DP-WAR> WAR for Apple thunderbolt J29 panel"));
|
||||
DP_LOG(("DP-WAR> - Monitor needs to be powered up before LT. Bug 933051"));
|
||||
}
|
||||
break;
|
||||
|
||||
// Acer
|
||||
case 0x7204:
|
||||
// Bug 451868: Acer AL1512 monitor has a wrong extension count:
|
||||
if(0xad15 == ProductID && YearWeek <= 0x0d01)
|
||||
{
|
||||
// clear the extension count
|
||||
buffer.data[0x7E] = 0;
|
||||
this->WARFlags.extensionCountDisabled = true;
|
||||
this->WARFlags.dataForced = true;
|
||||
DP_LOG(("DP-WAR> Edid override on Acer AL1512"));
|
||||
DP_LOG(("DP-WAR> - Disabling extension count.Bug 451868"));
|
||||
}
|
||||
break;
|
||||
|
||||
// Westinghouse
|
||||
case 0x855C:
|
||||
|
||||
// Westinghouse 37" 1080p TV. LVM-37w3 (Port DVI1 EDID).
|
||||
// Westinghouse 42" 1080p TV. LVM-42w2 (Port DVI1 EDID).
|
||||
if (ProductID == 0x3703 || ProductID == 0x4202)
|
||||
{
|
||||
// Claims HDMI support, but audio causes picture corruption.
|
||||
// Removing HDMI extension block
|
||||
|
||||
if (buffer.getLength() > 0x80 &&
|
||||
buffer.data[0x7E] == 1 && // extension block present
|
||||
buffer.data[0x80] == 0x02 && // CEA block
|
||||
buffer.data[0x81] == 0x03 && // revision 3
|
||||
!(buffer.data[0x83] & 0x40)) // No basic audio, must not be the HDMI port
|
||||
{
|
||||
// clear the extension count
|
||||
buffer.data[0x7E] = 0;
|
||||
this->WARFlags.extensionCountDisabled = true;
|
||||
this->WARFlags.dataForced = true;
|
||||
DP_LOG(("DP-WAR> Edid overrid on Westinghouse AL1512 LVM- <37/42> w <2/3>"));
|
||||
DP_LOG(("DP-WAR> - Disabling extension count."));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// IBM
|
||||
case 0x4D24:
|
||||
if(ProductID == 0x1A03)
|
||||
{
|
||||
// 2001 Week 50
|
||||
if (YearWeek == 0x0B32)
|
||||
{
|
||||
// Override IBM T210. IBM T210 reports 2048x1536x60Hz in the edid but it's
|
||||
// actually 2048x1536x40Hz. See bug 76347. This hack was, earlier, in disp driver
|
||||
// Now it's being moved down to keep all overrides in same place.
|
||||
// This hack was also preventing disp driver from comparing entire edid when
|
||||
// trying to figure out whether or not the edid for some device has changed.
|
||||
buffer.data[0x36] = 0x32;
|
||||
buffer.data[0x37] = 0x3E;
|
||||
this->WARFlags.dataForced = true;
|
||||
DP_LOG(("DP-WAR> Edid overrid on IBM T210"));
|
||||
DP_LOG(("DP-WAR> 2048x1536x60Hz(misreported) -> 2048x1536x40Hz. Bug 76347"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
// GWY (Gateway) or EMA (eMachines)
|
||||
case 0xF91E: // GWY
|
||||
case 0xA115: // EMA
|
||||
// Some Gateway monitors present the eMachines mfg code, so these two cases are combined.
|
||||
// Future fixes may require the two cases to be separated.
|
||||
// Fix for Bug 343870. NOTE: Problem found on G80; fix applied to all GPUs.
|
||||
if ((ProductID >= 0x0776 ) && (ProductID <= 0x0779)) // Product id's range from decimal 1910 to 1913
|
||||
{
|
||||
// if detailed pixel clock frequency = 106.50MHz
|
||||
if ( (buffer.data[0x36] == 0x9A) &&
|
||||
(buffer.data[0x37] == 0x29) )
|
||||
{
|
||||
// then change detailed pixel clock frequency to 106.54MHz to fix bug 343870
|
||||
buffer.data[0x36] = 0x9E;
|
||||
buffer.data[0x37] = 0x29;
|
||||
this->WARFlags.dataForced = true;
|
||||
DP_LOG(("DP-WAR> Edid overrid on GWY/EMA"));
|
||||
DP_LOG(("DP-WAR> 106.50MHz(misreported) -> 106.50MHz.Bug 343870"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// INX
|
||||
case 0x2C0C:
|
||||
// INX L15CX monitor has an invalid detailed timing 10x311 @ 78Hz.
|
||||
if( ProductID == 0x1502)
|
||||
{
|
||||
// remove detailed timing #4: zero out the first 3 bytes of DTD#4 block
|
||||
buffer.data[0x6c] = 0x0;
|
||||
buffer.data[0x6d] = 0x0;
|
||||
buffer.data[0x6e] = 0x0;
|
||||
this->WARFlags.dataForced = true;
|
||||
DP_LOG(("DP-WAR> Edid overrid on INX L15CX"));
|
||||
DP_LOG(("DP-WAR> Removing invalid detailed timing 10x311 @ 78Hz"));
|
||||
}
|
||||
break;
|
||||
|
||||
// AUO
|
||||
case 0xAF06:
|
||||
if ((ProductID == 0x103C) || (ProductID == 0x113C))
|
||||
{
|
||||
//
|
||||
// Acer have faulty AUO eDP panels which have
|
||||
// wrong HBlank in the EDID. Correcting it here.
|
||||
//
|
||||
buffer.data[0x39] = 0x4B; // new hblank width: 75
|
||||
buffer.data[0x3F] = 0x1B; // new hsync pulse width: 27
|
||||
this->WARFlags.dataForced = true;
|
||||
DP_LOG(("DP-WAR> Edid overrid on AUO eDP panel"));
|
||||
DP_LOG(("DP-WAR> Modifying HBlank and HSync pulse width."));
|
||||
DP_LOG(("DP-WAR> Bugs 907998, 1001160"));
|
||||
}
|
||||
else if (ProductID == 0x109B || ProductID == 0x119B)
|
||||
{
|
||||
this->WARFlags.useLegacyAddress = true;
|
||||
DP_LOG(("DP-WAR> AUO eDP"));
|
||||
DP_LOG(("implements only Legacy interrupt address range"));
|
||||
|
||||
// Bug 1792962 - Panel got glitch on D3 write, apply this WAR.
|
||||
this->WARFlags.disableDpcdPowerOff = true;
|
||||
DP_LOG(("DP-WAR> Disable DPCD Power Off"));
|
||||
}
|
||||
break;
|
||||
|
||||
// LPL
|
||||
case 0x0C32:
|
||||
if (ProductID == 0x0000)
|
||||
{
|
||||
//
|
||||
// Patch EDID for Quanta - Toshiba LG 1440x900 panel. See Bug 201428
|
||||
// Must 1st verify that we have that panel. It has MFG id 32, 0C
|
||||
// BUT product ID for this (and other different LG panels) are 0000.
|
||||
// So verify that the last "Custom Timing" area of the EDID has
|
||||
// a "Monitor Description" of type FE = "ASCII Data String" which
|
||||
// has this panel's name = "LP171WX2-A4K5".
|
||||
//
|
||||
if ( (buffer.data[0x71] == 0x4C) &&
|
||||
(buffer.data[0x72] == 0x50) &&
|
||||
(buffer.data[0x73] == 0x31) &&
|
||||
(buffer.data[0x74] == 0x37) &&
|
||||
(buffer.data[0x75] == 0x31) &&
|
||||
(buffer.data[0x76] == 0x57) &&
|
||||
(buffer.data[0x77] == 0x58) &&
|
||||
(buffer.data[0x78] == 0x32) &&
|
||||
(buffer.data[0x79] == 0x2D) &&
|
||||
(buffer.data[0x7A] == 0x41) &&
|
||||
(buffer.data[0x7B] == 0x34) &&
|
||||
(buffer.data[0x7C] == 0x4B) &&
|
||||
(buffer.data[0x7D] == 0x35) )
|
||||
{
|
||||
//
|
||||
// Was 0x95, 0x25 = -> 0x2595 = 9621 or 96.21 Mhz.
|
||||
// 96,210,000 / 1760 / 912 = 59.939 Hz
|
||||
// Want 60 * 1760 * 912 ~= 9631 or 96.31 MHz
|
||||
// 9631 = 0x259F -> 0x9F 0x25.
|
||||
// So, change byte 36 from 0x95 to 0x9F.
|
||||
//
|
||||
buffer.data[0x36] = 0x9F;
|
||||
this->WARFlags.dataForced = true;
|
||||
DP_LOG(("DP-WAR> Edid overrid on Quanta - Toshiba LG 1440x900"));
|
||||
DP_LOG(("DP-WAR> Correcting pclk. Bug 201428"));
|
||||
}
|
||||
}
|
||||
else
|
||||
if (ProductID == 0xE300)
|
||||
{
|
||||
//
|
||||
// Patch EDID for MSI - LG LPL 1280x800 panel. See Bug 359313
|
||||
// Must 1st verify that we have that panel. It has MFG id 32, 0C
|
||||
// BUT product ID for this (and other different LG panels) are E300.
|
||||
// So verify that the last "Custom Timing" area of the EDID has
|
||||
// a "Monitor Description" of type FE = "ASCII Data String" which
|
||||
// has this panel's name = "LP154WX4-TLC3".
|
||||
//
|
||||
if ( (buffer.data[0x71] == 0x4C) &&
|
||||
(buffer.data[0x72] == 0x50) &&
|
||||
(buffer.data[0x73] == 0x31) &&
|
||||
(buffer.data[0x74] == 0x35) &&
|
||||
(buffer.data[0x75] == 0x34) &&
|
||||
(buffer.data[0x76] == 0x57) &&
|
||||
(buffer.data[0x77] == 0x58) &&
|
||||
(buffer.data[0x78] == 0x34) &&
|
||||
(buffer.data[0x79] == 0x2D) &&
|
||||
(buffer.data[0x7A] == 0x54) &&
|
||||
(buffer.data[0x7B] == 0x4C) &&
|
||||
(buffer.data[0x7C] == 0x43) &&
|
||||
(buffer.data[0x7D] == 0x33) )
|
||||
{
|
||||
//
|
||||
// Was 0xBC, 0x1B = -> 0x1BBC = 7100 or 71.00 Mhz.
|
||||
// 71,000,000 / 1488 / 826 = 59.939 Hz
|
||||
// Want 60 * 1488 * 826 ~= 7111 or 71.11 MHz
|
||||
// 7111 = 0x1BC7 -> 0xC7 0x1B.
|
||||
// So, change byte 36 from 0xBC to 0xC7.
|
||||
//
|
||||
buffer.data[0x36] = 0xC7;
|
||||
this->WARFlags.dataForced = true;
|
||||
DP_LOG(("DP-WAR> Edid overrid on MSI - LG LPL 1280x800"));
|
||||
DP_LOG(("DP-WAR> Correcting pclk. Bug 359313"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// SKY
|
||||
case 0x794D:
|
||||
if (ProductID == 0x9880)
|
||||
{
|
||||
//
|
||||
// Override for Haier TV to remove resolution
|
||||
// 1366x768 from EDID data. Refer bug 351680 & 327891
|
||||
// Overriding 18 bytes from offset 0x36.
|
||||
//
|
||||
buffer.data[0x36] = 0x01;
|
||||
buffer.data[0x37] = 0x1D;
|
||||
buffer.data[0x38] = 0x00;
|
||||
buffer.data[0x39] = 0x72;
|
||||
buffer.data[0x3A] = 0x51;
|
||||
buffer.data[0x3B] = 0xD0;
|
||||
buffer.data[0x3C] = 0x1E;
|
||||
buffer.data[0x3D] = 0x20;
|
||||
buffer.data[0x3E] = 0x6E;
|
||||
buffer.data[0x3F] = 0x28;
|
||||
buffer.data[0x40] = 0x55;
|
||||
buffer.data[0x41] = 0x00;
|
||||
buffer.data[0x42] = 0xC4;
|
||||
buffer.data[0x43] = 0x8E;
|
||||
buffer.data[0x44] = 0x21;
|
||||
buffer.data[0x45] = 0x00;
|
||||
buffer.data[0x46] = 0x00;
|
||||
buffer.data[0x47] = 0x1E;
|
||||
|
||||
this->WARFlags.dataForced = true;
|
||||
DP_LOG(("DP-WAR> Edid overrid on Haier TV."));
|
||||
DP_LOG(("DP-WAR> Removing 1366x768. bug 351680 & 327891"));
|
||||
|
||||
}
|
||||
break;
|
||||
// HP
|
||||
case 0xF022:
|
||||
switch (ProductID)
|
||||
{
|
||||
case 0x192F:
|
||||
//
|
||||
// WAR for bug 1643712 - Issue specific to HP Z1 G2 (Zeus) All-In-One
|
||||
// Putting the Rx in power save mode before BL_EN is deasserted, makes this specific sink unhappy
|
||||
// Bug 1559465 will address the right power down sequence. We need to revisit this WAR once Bug 1559465 is fixed.
|
||||
//
|
||||
this->WARFlags.disableDpcdPowerOff = true;
|
||||
DP_LOG(("DP-WAR> Disable DPCD Power Off"));
|
||||
DP_LOG(("DP-WAR> HP Z1 G2 (Zeus) AIO Bug 1643712"));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// Sharp
|
||||
case 0x104d:
|
||||
switch (ProductID)
|
||||
{
|
||||
case 0x141c: // HP Valor QHD+ N15P-Q3 Sharp EDP
|
||||
//
|
||||
// HP Valor QHD+ N15P-Q3 EDP needs 50 ms delay
|
||||
// after D3 to avoid black screen issues.
|
||||
//
|
||||
this->WARFlags.delayAfterD3 = true;
|
||||
DP_LOG(("DP-WAR> HP Valor QHD+ N15P-Q3 Sharp EDP needs 50 ms after D3"));
|
||||
DP_LOG(("DP-WAR> bug 1520011"));
|
||||
break;
|
||||
|
||||
//Sharp EDPs that declares DP1.2 but doesn't implement ESI address space
|
||||
case 0x1414:
|
||||
case 0x1430:
|
||||
case 0x1445:
|
||||
case 0x1446:
|
||||
case 0x144C:
|
||||
case 0x1450:
|
||||
case 0x1467:
|
||||
case 0x145e:
|
||||
//
|
||||
// Use Legacy address space for DP1.2 panel
|
||||
//
|
||||
this->WARFlags.useLegacyAddress = true;
|
||||
DP_LOG(("DP-WAR> Sharp EDP implements only Legacy interrupt address range"));
|
||||
break;
|
||||
|
||||
case 0x143B:
|
||||
//
|
||||
// Bug 200113041
|
||||
// Need to be unique to identify this Sharp panel. Besides
|
||||
// manufacturer ID and ProductID, we have to add the mode
|
||||
// name to make this happen as LQ156D1JW05 in ASCII.
|
||||
//
|
||||
if ((buffer.data[0x71] == 0x4C) &&
|
||||
(buffer.data[0x72] == 0x51) &&
|
||||
(buffer.data[0x73] == 0x31) &&
|
||||
(buffer.data[0x74] == 0x35) &&
|
||||
(buffer.data[0x75] == 0x36) &&
|
||||
(buffer.data[0x76] == 0x44) &&
|
||||
(buffer.data[0x77] == 0x31) &&
|
||||
(buffer.data[0x78] == 0x4A) &&
|
||||
(buffer.data[0x79] == 0x57) &&
|
||||
(buffer.data[0x7A] == 0x30) &&
|
||||
(buffer.data[0x7B] == 0x35) &&
|
||||
(buffer.data[0x7C] == 0x0A) &&
|
||||
(buffer.data[0x7D] == 0x20))
|
||||
{
|
||||
this->WARFlags.useLegacyAddress = true;
|
||||
DP_LOG(("DP-WAR> Sharp EDP implements only Legacy interrupt address range"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// EIZO
|
||||
case 0xc315:
|
||||
if (ProductID == 0x2227)
|
||||
{
|
||||
//
|
||||
// The EIZO FlexScan SX2762W generates a redundant long HPD
|
||||
// pulse after a modeset, which triggers another modeset on GPUs
|
||||
// without flush mode, triggering an infinite link training
|
||||
// loop.
|
||||
//
|
||||
this->WARFlags.ignoreRedundantHotplug = true;
|
||||
DP_LOG(("DP-WAR> EIZO FlexScan SX2762W generates redundant"));
|
||||
DP_LOG(("DP-WAR> hotplugs (bug 1048796)"));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// MEI-Panasonic
|
||||
case 0xa934:
|
||||
if (ProductID == 0x96a2)
|
||||
{
|
||||
//
|
||||
// Bug 200113041
|
||||
// Need to be unique to identify this MEI-Panasonic panel.
|
||||
// Besides manufacturer ID and ProductID, we have to add the
|
||||
// model name to make this happen as VVX17P051J00^ in ASCII.
|
||||
//
|
||||
if ((buffer.data[0x71] == 0x56) &&
|
||||
(buffer.data[0x72] == 0x56) &&
|
||||
(buffer.data[0x73] == 0x58) &&
|
||||
(buffer.data[0x74] == 0x31) &&
|
||||
(buffer.data[0x75] == 0x37) &&
|
||||
(buffer.data[0x76] == 0x50) &&
|
||||
(buffer.data[0x77] == 0x30) &&
|
||||
(buffer.data[0x78] == 0x35) &&
|
||||
(buffer.data[0x79] == 0x31) &&
|
||||
(buffer.data[0x7A] == 0x4A) &&
|
||||
(buffer.data[0x7B] == 0x30) &&
|
||||
(buffer.data[0x7C] == 0x30) &&
|
||||
(buffer.data[0x7D] == 0x0A))
|
||||
{
|
||||
this->WARFlags.useLegacyAddress = true;
|
||||
DP_LOG(("DP-WAR> MEI-Panasonic EDP"));
|
||||
DP_LOG(("implements only Legacy interrupt address range"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// LG
|
||||
case 0xE430:
|
||||
if (ProductID == 0x0469)
|
||||
{
|
||||
//
|
||||
// The LG display can't be driven at FHD with 2*RBR.
|
||||
// Force max link config
|
||||
//
|
||||
this->WARFlags.forceMaxLinkConfig = true;
|
||||
DP_LOG(("DP-WAR> Force maximum link config WAR required on LG panel."));
|
||||
DP_LOG(("DP-WAR> bug 1649626"));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x8F34:
|
||||
if (ProductID == 0xAA55)
|
||||
{
|
||||
this->WARFlags.forceMaxLinkConfig = true;
|
||||
DP_LOG(("DP-WAR> Force maximum link config WAR required on Sharp-CerebrEx panel."));
|
||||
}
|
||||
break;
|
||||
|
||||
// Dell
|
||||
case 0xAC10:
|
||||
// Dell U2713H has problem with LQA. Disable it.
|
||||
if ((ProductID == 0xA092) || (ProductID == 0xF046))
|
||||
{
|
||||
this->WARFlags.reassessMaxLink = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// CMN
|
||||
case 0xAE0D:
|
||||
if (ProductID == 0x1747)
|
||||
{
|
||||
this->WARFlags.useLegacyAddress = true;
|
||||
DP_LOG(("DP-WAR> CMN eDP"));
|
||||
DP_LOG(("implements only Legacy interrupt address range"));
|
||||
}
|
||||
break;
|
||||
|
||||
// BenQ
|
||||
case 0xD109:
|
||||
if ((ProductID == 0x7F2B) || (ProductID == 0x7F2F))
|
||||
{
|
||||
this->WARFlags.ignoreRedundantHotplug = true;
|
||||
DP_LOG(("DP-WAR> BenQ GSync power on/off redundant hotplug"));
|
||||
}
|
||||
break;
|
||||
|
||||
// MSI
|
||||
case 0x834C:
|
||||
if (ProductID == 0x4C48)
|
||||
{
|
||||
this->WARFlags.useLegacyAddress = true;
|
||||
DP_LOG(("DP-WAR> MSI eDP\n"));
|
||||
DP_LOG(("implements only Legacy interrupt address range\n"));
|
||||
}
|
||||
break;
|
||||
|
||||
// Unigraf
|
||||
case 0xC754:
|
||||
case 0x1863:
|
||||
{
|
||||
DP_LOG(("DP-WAR> Unigraf device, keep link alive during detection\n"));
|
||||
this->WARFlags.keepLinkAlive = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// BOE
|
||||
case 0xE509:
|
||||
if ((ProductID == 0x977) || (ProductID == 0x974) || (ProductID == 0x9D9))
|
||||
{
|
||||
this->WARFlags.bIgnoreDscCap = true;
|
||||
DP_LOG(("DP-WAR> BOE panels incorrectly exposing DSC capability. Ignoring it."));
|
||||
}
|
||||
break;
|
||||
|
||||
// NCP
|
||||
case 0x7038:
|
||||
if ((ProductID == 0x005F))
|
||||
{
|
||||
this->WARFlags.bIgnoreDscCap = true;
|
||||
DP_LOG(("DP-WAR> NCP panels incorrectly exposing DSC capability. Ignoring it."));
|
||||
}
|
||||
break;
|
||||
|
||||
//
|
||||
// This panel advertise DSC capabilities, but panel doesn't support DSC
|
||||
// So ignoring DSC capability on this panel
|
||||
//
|
||||
case 0x6F0E:
|
||||
if (ProductID == 0x1609)
|
||||
{
|
||||
this->WARFlags.bIgnoreDscCap = true;
|
||||
DP_LOG(("DP-WAR> Ignoring DSC capability on Lenovo CSOT 1609 Panel."));
|
||||
DP_LOG(("DP-WAR> Bug 3444252"));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Find out if the monitor needs a WAR to applied.
|
||||
if (warFlag)
|
||||
{
|
||||
if (warFlag & DP_MONITOR_CAPABILITY_DP_SKIP_REDUNDANT_LT)
|
||||
{
|
||||
this->WARFlags.skipRedundantLt = true;
|
||||
}
|
||||
|
||||
if (warFlag & DP_MONITOR_CAPABILITY_DP_SKIP_CABLE_BW_CHECK)
|
||||
{
|
||||
this->WARFlags.skipCableBWCheck = true;
|
||||
this->WARData.maxLaneAtHighRate = pDenylistData->dpSkipCheckLink.maxLaneAtHighRate;
|
||||
this->WARData.maxLaneAtLowRate = pDenylistData->dpSkipCheckLink.maxLaneAtLowRate;
|
||||
}
|
||||
|
||||
if (warFlag & DP_MONITOR_CAPABILITY_DP_WRITE_0x600_BEFORE_LT)
|
||||
{
|
||||
// all HP monitors need to be powered up before link training
|
||||
this->WARFlags.powerOnBeforeLt = true;
|
||||
DP_LOG(("DP-WAR> HP monitors need to be powered up before LT"));
|
||||
}
|
||||
|
||||
if (warFlag & DP_MONITOR_CAPABILITY_DP_OVERRIDE_OPTIMAL_LINK_CONFIG)
|
||||
{
|
||||
//
|
||||
// Instead of calculating the optimum link config
|
||||
// based on timing, bpc etc. just used a default
|
||||
// fixed link config for the monitor for all modes
|
||||
//
|
||||
this->WARFlags.overrideOptimalLinkCfg = true;
|
||||
// Force the fix max LT
|
||||
this->WARFlags.forceMaxLinkConfig = true;
|
||||
this->WARData.optimalLinkRate = pDenylistData->dpOverrideOptimalLinkConfig.linkRate;
|
||||
this->WARData.optimalLaneCount = pDenylistData->dpOverrideOptimalLinkConfig.laneCount;
|
||||
DP_LOG(("DP-WAR> Overriding optimal link config on Dell U2410."));
|
||||
DP_LOG(("DP-WAR> bug 632801"));
|
||||
}
|
||||
|
||||
if (warFlag & DP_MONITOR_CAPABILITY_DP_OVERRIDE_MAX_LANE_COUNT)
|
||||
{
|
||||
//
|
||||
// Some monitors claim more lanes than they actually support.
|
||||
// This particular Lenovo monitos has just 2 lanes, but its DPCD says 4.
|
||||
// This WAR is to override the max lane count read from DPCD.
|
||||
//
|
||||
this->WARFlags.overrideMaxLaneCount = true;
|
||||
this->WARData.maxLaneCount = pDenylistData->dpMaxLaneCountOverride;
|
||||
DP_LOG(("DP-WAR> Overriding max lane count on Lenovo L2440x."));
|
||||
DP_LOG(("DP-WAR> bug 687952"));
|
||||
}
|
||||
}
|
||||
|
||||
if (this->WARFlags.dataForced)
|
||||
{
|
||||
DP_LOG(("DP-WAR> EDID was overridden for some data. Patching CRC."));
|
||||
this->patchCrc();
|
||||
}
|
||||
}
|
||||
872
src/common/displayport/src/dp_watermark.cpp
Normal file
872
src/common/displayport/src/dp_watermark.cpp
Normal file
@@ -0,0 +1,872 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_watermark.cpp *
|
||||
* DP watermark IsModePossible calculations *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#include "dp_internal.h"
|
||||
#include "dp_watermark.h"
|
||||
#include "dp_linkconfig.h"
|
||||
#include "displayport.h"
|
||||
|
||||
#define FEC_TOTAL_SYMBOLS_PER_BLK(lanes) ((NvU32)((lanes == 1) ? 512U : 256U))
|
||||
#define FEC_PARITY_SYMBOLS_PER_BLK(lanes) ((NvU32)((lanes == 1) ? 12U : 6U))
|
||||
//return max number of FEC parity symbols in x link clock cycles
|
||||
#define FEC_PARITY_SYM_SST(lanes, x) (DP_MIN((NvU32)(x) % FEC_TOTAL_SYMBOLS_PER_BLK(lanes), FEC_PARITY_SYMBOLS_PER_BLK(lanes)) + (NvU32)(x) / FEC_TOTAL_SYMBOLS_PER_BLK(lanes) * FEC_PARITY_SYMBOLS_PER_BLK(lanes) + FEC_PARITY_SYMBOLS_PER_BLK(lanes) + 1U)
|
||||
#define FEC_PARITY_SYM_MST(lanes, x) (DP_MIN((NvU32)(x) % FEC_TOTAL_SYMBOLS_PER_BLK(lanes), FEC_PARITY_SYMBOLS_PER_BLK(lanes)) + (NvU32)(x) / FEC_TOTAL_SYMBOLS_PER_BLK(lanes) * FEC_PARITY_SYMBOLS_PER_BLK(lanes) + 1U)
|
||||
|
||||
|
||||
bool DisplayPort::isModePossibleMST
|
||||
(
|
||||
const LinkConfiguration & linkConfig,
|
||||
const ModesetInfo & modesetInfo,
|
||||
Watermark * dpInfo
|
||||
)
|
||||
{
|
||||
//
|
||||
// For MST, use downspread 0.6%
|
||||
//
|
||||
NvU64 linkFreq = linkConfig.peakRate * 994 / 1000;
|
||||
|
||||
//
|
||||
// This function is for multistream only!
|
||||
//
|
||||
DP_ASSERT( linkConfig.multistream );
|
||||
|
||||
if(!modesetInfo.pixelClockHz || !modesetInfo.depth)
|
||||
{
|
||||
DP_ASSERT(0 && "INVALID PIXEL CLOCK and DEPTH sent by the client ");
|
||||
return false;
|
||||
}
|
||||
|
||||
// depth is multiplied by 16 in case of DSC enable
|
||||
unsigned DSC_FACTOR = modesetInfo.bEnableDsc ? 16 : 1;
|
||||
|
||||
// Extra bits that we need to send
|
||||
//(hActiveDiv4Remainder > 0 ? (4- hActiveDiv4Remainder) : 0) -->
|
||||
// Number of extra pixels that we need to insert due to mapping pixels
|
||||
// to the DP lanes. (4 lanes for MS)
|
||||
//
|
||||
// 160 --> Extra bits that we need to send during horizontal blanking
|
||||
// (BS+VBID+MVID+MAUD+BE) => 5*8*num_lanes
|
||||
//
|
||||
// 6 * 4 --> Pixel padding worst case
|
||||
//
|
||||
NvU32 minHBlank = ( ((modesetInfo.surfaceWidth % 4) > 0) ? ((4-(modesetInfo.surfaceWidth % 4)) * modesetInfo.depth)/ DSC_FACTOR : 0 ) + (160 + 6 * 4);
|
||||
|
||||
// Rounding to nearest multiple of 32 since we always send 32 bits in one time slice
|
||||
minHBlank = minHBlank + (32 - minHBlank % 32);
|
||||
|
||||
// bpp - 1 --> Rounding
|
||||
minHBlank = ((minHBlank * DSC_FACTOR) + modesetInfo.depth - (1 * DSC_FACTOR))/modesetInfo.depth;
|
||||
|
||||
if (minHBlank > modesetInfo.rasterWidth - modesetInfo.surfaceWidth)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: Blanking Width is smaller than minimum permissible value."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bug 702290 - Active Width should be greater than 60
|
||||
if (modesetInfo.surfaceWidth <= 60)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: Minimum Horizontal Active Width <= 60 not supported."));
|
||||
return false;
|
||||
}
|
||||
|
||||
NvS32 vblank_symbols;
|
||||
NvS32 hblank_symbols = (NvS32)(((NvU64)(modesetInfo.rasterWidth - modesetInfo.surfaceWidth - minHBlank) * linkFreq) / modesetInfo.pixelClockHz);
|
||||
|
||||
//reduce HBlank Symbols to account for secondary data packet
|
||||
hblank_symbols -= 1; //Stuffer latency to send BS
|
||||
hblank_symbols -= 3; //SPKT latency to send data to stuffer
|
||||
|
||||
hblank_symbols -= linkConfig.lanes == 1 ? 9 : linkConfig.lanes == 2 ? 6 : 3;
|
||||
|
||||
dpInfo->hBlankSym = (hblank_symbols < 0) ? 0 : hblank_symbols;
|
||||
|
||||
|
||||
//
|
||||
// Audio IMP calculations
|
||||
// Perform the related audio calculation to determine the number of extra symbols needed.
|
||||
//
|
||||
NvU32 twoChannelAudio_symbols = 0;
|
||||
|
||||
if (modesetInfo.twoChannelAudioHz != 0)
|
||||
{
|
||||
// 1-2 channel case
|
||||
NvU32 samples = (NvU32)divide_ceil(modesetInfo.twoChannelAudioHz * modesetInfo.rasterWidth, modesetInfo.pixelClockHz);
|
||||
|
||||
// Round to the next even sample to account for stuffing (2 ch, 4 lanes)
|
||||
samples = samples + (2 - samples % 2);
|
||||
|
||||
// Convert sample count to symbols
|
||||
twoChannelAudio_symbols = 10 * samples + 16;
|
||||
}
|
||||
|
||||
NvU32 eightChannelAudio_symbols = 0;
|
||||
if (modesetInfo.eightChannelAudioHz != 0)
|
||||
{
|
||||
// 3-8 channel case
|
||||
NvU32 samples = (NvU32)divide_ceil(modesetInfo.eightChannelAudioHz * modesetInfo.rasterWidth, modesetInfo.pixelClockHz);
|
||||
|
||||
// Convert sample count to symbols
|
||||
eightChannelAudio_symbols = 40 * samples + 16;
|
||||
}
|
||||
|
||||
if (dpInfo->hBlankSym < DP_MAX(twoChannelAudio_symbols, eightChannelAudio_symbols))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Refer to dev_disp.ref for more information.
|
||||
// # symbols/vblank = ((SetRasterBlankEnd.X + SetRasterSize.Width - SetRasterBlankStart.X - 40) * link_clk / pclk) - Y - 1;
|
||||
// where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39
|
||||
if (modesetInfo.surfaceWidth < 40)
|
||||
{
|
||||
vblank_symbols = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
vblank_symbols = (NvS32)(((NvU64)(modesetInfo.surfaceWidth - 40) * linkFreq) / modesetInfo.pixelClockHz) - 1;
|
||||
|
||||
vblank_symbols -= linkConfig.lanes == 1 ? 39 : linkConfig.lanes == 2 ? 21 : 12;
|
||||
}
|
||||
|
||||
dpInfo->vBlankSym = (vblank_symbols < 0) ? 0 : vblank_symbols;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DisplayPort::isModePossibleSST
|
||||
(
|
||||
const LinkConfiguration & linkConfig,
|
||||
const ModesetInfo & modesetInfo,
|
||||
Watermark * dpInfo,
|
||||
bool bUseIncreasedWatermarkLimits
|
||||
)
|
||||
{
|
||||
//
|
||||
// This function is for single stream only!
|
||||
//
|
||||
DP_ASSERT( !linkConfig.multistream );
|
||||
|
||||
unsigned watermarkAdjust = DP_CONFIG_WATERMARK_ADJUST;
|
||||
unsigned watermarkMinimum = DP_CONFIG_WATERMARK_LIMIT;
|
||||
// depth is multiplied by 16 in case of DSC enable
|
||||
unsigned DSC_FACTOR = modesetInfo.bEnableDsc ? 16 : 1;
|
||||
|
||||
if(bUseIncreasedWatermarkLimits)
|
||||
{
|
||||
watermarkAdjust = DP_CONFIG_INCREASED_WATERMARK_ADJUST;
|
||||
watermarkMinimum = DP_CONFIG_INCREASED_WATERMARK_LIMIT;
|
||||
}
|
||||
|
||||
if(!modesetInfo.pixelClockHz || !modesetInfo.depth)
|
||||
{
|
||||
DP_ASSERT(0 && "INVALID PIXEL CLOCK or DEPTH sent by the client ");
|
||||
return false;
|
||||
}
|
||||
// number of link clocks per line.
|
||||
int vblank_symbols = 0;
|
||||
NvU64 PrecisionFactor, ratioF, watermarkF;
|
||||
|
||||
NvU32 numLanesPerLink = linkConfig.lanes;
|
||||
|
||||
DP_ASSERT(!linkConfig.multistream && "MST!");
|
||||
|
||||
// Check if we have a valid laneCount as currently we support only up to 4-lanes
|
||||
if (!IS_VALID_LANECOUNT(linkConfig.lanes))
|
||||
{
|
||||
//
|
||||
// Print debug message and Assert. All calculations assume a max of 8 lanes
|
||||
// & any increase in lanes should cause these calculation to be updated
|
||||
//
|
||||
DP_LOG(("NVRM: %s: ERROR: LaneCount - %d is not supported for waterMark calculations.",
|
||||
__FUNCTION__, linkConfig.lanes));
|
||||
DP_LOG(("Current support is only up to 4-Lanes & any change/increase in supported lanes "
|
||||
"should be reflected in waterMark calculations algorithm. "
|
||||
"Ex: See calc for minHBlank variable below"));
|
||||
|
||||
DP_ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((modesetInfo.pixelClockHz * modesetInfo.depth) >= (8 * linkConfig.minRate * linkConfig.lanes * DSC_FACTOR))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// For DSC, if (pclk * bpp) < (1/64 * orclk * 8 * lanes) then some TU may end up with
|
||||
// 0 active symbols. This may cause HW hang. Bug 200379426
|
||||
//
|
||||
if ((modesetInfo.bEnableDsc) &&
|
||||
((modesetInfo.pixelClockHz * modesetInfo.depth) < ((8 * linkConfig.minRate * linkConfig.lanes * DSC_FACTOR) / 64)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform the SST calculation.
|
||||
// For auto mode the watermark calculation does not need to track accumulated error the
|
||||
// formulas for manual mode will not work. So below calculation was extracted from the DTB.
|
||||
//
|
||||
dpInfo->tuSize = 64;
|
||||
PrecisionFactor = 100000;
|
||||
ratioF = ((NvU64)modesetInfo.pixelClockHz * modesetInfo.depth * PrecisionFactor) / DSC_FACTOR;
|
||||
|
||||
ratioF /= 8 * (NvU64) linkConfig.minRate * linkConfig.lanes;
|
||||
|
||||
if (PrecisionFactor < ratioF) // Assert if we will end up with a negative number in below
|
||||
return false;
|
||||
|
||||
watermarkF = ratioF * dpInfo->tuSize * (PrecisionFactor - ratioF) / PrecisionFactor;
|
||||
dpInfo->waterMark = (unsigned)(watermarkAdjust + ((2 * (modesetInfo.depth * PrecisionFactor / (8 * numLanesPerLink * DSC_FACTOR)) + watermarkF) / PrecisionFactor));
|
||||
|
||||
//
|
||||
// Bounds check the watermark
|
||||
//
|
||||
NvU32 numSymbolsPerLine = (modesetInfo.surfaceWidth * modesetInfo.depth) / (8 * linkConfig.lanes * DSC_FACTOR);
|
||||
|
||||
if (dpInfo->waterMark > 39 || dpInfo->waterMark > numSymbolsPerLine)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: watermark should not be greater than 39."));
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Clamp the low side
|
||||
//
|
||||
if (dpInfo->waterMark < watermarkMinimum)
|
||||
dpInfo->waterMark = watermarkMinimum;
|
||||
|
||||
//Bits to send BS/BE/Extra symbols due to pixel padding
|
||||
//Also accounts for enhanced framing.
|
||||
NvU32 BlankingBits = 3*8*numLanesPerLink + (linkConfig.enhancedFraming ? 3*8*numLanesPerLink : 0);
|
||||
|
||||
//VBID/MVID/MAUD sent 4 times all the time
|
||||
BlankingBits += 3*8*4;
|
||||
|
||||
NvU32 surfaceWidthPerLink = modesetInfo.surfaceWidth;
|
||||
|
||||
//Extra bits sent due to pixel steering
|
||||
NvU32 PixelSteeringBits = (surfaceWidthPerLink % numLanesPerLink) ? (((numLanesPerLink - surfaceWidthPerLink % numLanesPerLink) * modesetInfo.depth) / DSC_FACTOR) : 0;
|
||||
|
||||
BlankingBits += PixelSteeringBits;
|
||||
NvU64 NumBlankingLinkClocks = (NvU64)BlankingBits * PrecisionFactor / (8 * numLanesPerLink);
|
||||
NvU32 MinHBlank = (NvU32)(NumBlankingLinkClocks * modesetInfo.pixelClockHz/ linkConfig.minRate / PrecisionFactor);
|
||||
MinHBlank += 12;
|
||||
|
||||
if (MinHBlank > modesetInfo.rasterWidth - modesetInfo.surfaceWidth)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: Blanking Width is smaller than minimum permissible value."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bug 702290 - Active Width should be greater than 60
|
||||
if (modesetInfo.surfaceWidth <= 60)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: Minimum Horizontal Active Width <= 60 not supported."));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
NvS32 hblank_symbols = (NvS32)(((NvU64)(modesetInfo.rasterWidth - modesetInfo.surfaceWidth - MinHBlank) * linkConfig.minRate) / modesetInfo.pixelClockHz);
|
||||
|
||||
//reduce HBlank Symbols to account for secondary data packet
|
||||
hblank_symbols -= 1; //Stuffer latency to send BS
|
||||
hblank_symbols -= 3; //SPKT latency to send data to stuffer
|
||||
|
||||
hblank_symbols -= numLanesPerLink == 1 ? 9 : numLanesPerLink == 2 ? 6 : 3;
|
||||
|
||||
dpInfo->hBlankSym = (hblank_symbols < 0) ? 0 : hblank_symbols;
|
||||
|
||||
//
|
||||
// Audio IMP calculations
|
||||
//
|
||||
|
||||
// From dev_disp.ref:
|
||||
|
||||
// The packet generation logic needs to know the length of the hblank period. If there is no room
|
||||
// in the current hblank for a new packet, it will be delayed until the next blanking period. This
|
||||
// field should be programmed during the second Supervisor interrupt based on the new raster
|
||||
// dimensions.
|
||||
|
||||
// ...
|
||||
|
||||
// --------------------------------------
|
||||
// The following formulas can be used to calculate the maximum audio sampling rate that can
|
||||
// be supported by DisplayPort given the current raster dimensions. DisplayPort has much more
|
||||
// bandwidth during blanking periods than HDMI has, so hblank size is less of an issue.
|
||||
|
||||
// ...
|
||||
|
||||
// Size of a packet for 2ch audio = 20 symbols (up to 2 samples)
|
||||
// Size of a packet for 8ch audio = 40 symbols
|
||||
// Size of an audio packet header plus control symbols = 2*#lanes + 8 symbols (assuming < 32 samples per line)
|
||||
// number of packets/hblank for 2ch audio = Floor ((number of free symbols/hblank - (2*#lanes + 8) / 20)
|
||||
// number of packets/hblank for 8ch audio = Floor ((number of free symbols/hblank - (2*#lanes + 8) / 40)
|
||||
|
||||
// Maximum audio sample rate possible:
|
||||
// number of audio samples/line = SetRasterSize.Width * audio_fs / pclk
|
||||
// number of audio packets needed for 2ch audio = Ceiling(SetRasterSize.Width * audio_fs / (pclk*2))
|
||||
// number of audio packets needed for 3-8ch audio = SetRasterSize.Width * audio_fs / pclk
|
||||
|
||||
// If number of audio packets needed > number of packets/hblank, then you cannot support that audio frequency
|
||||
|
||||
// Note that the hBlankSym calculated is per lane. So the number of symbols available for audio is
|
||||
// (number of lanes * hBlankSym).
|
||||
// The calculation of audio packets per Hblank needs to account for the following -
|
||||
// 2 symbols for SS and SE; 8 symbols for header; and additional 2 symbols to account for actual values used by HW.
|
||||
// --------------------------------------
|
||||
|
||||
if (modesetInfo.twoChannelAudioHz != 0)
|
||||
{
|
||||
if ((dpInfo->hBlankSym * numLanesPerLink) < (2 * numLanesPerLink + 8))
|
||||
{
|
||||
// There aren't enough symbols/hblank available.
|
||||
return false;
|
||||
}
|
||||
|
||||
NvU32 twoChannelAudioPacketsPerHBlank = (NvU32)divide_floor(((dpInfo->hBlankSym * numLanesPerLink) - (2 * numLanesPerLink) - 8 - (2 * numLanesPerLink)), 20);
|
||||
|
||||
NvU32 twoChannelAudioPackets = (NvU32)divide_ceil(modesetInfo.twoChannelAudioHz * modesetInfo.rasterWidth, modesetInfo.pixelClockHz * 2);
|
||||
|
||||
if (twoChannelAudioPackets > twoChannelAudioPacketsPerHBlank)
|
||||
{
|
||||
// There aren't enough packets/hblank available.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (modesetInfo.eightChannelAudioHz != 0)
|
||||
{
|
||||
if ((dpInfo->hBlankSym * numLanesPerLink) < (2 * numLanesPerLink + 8))
|
||||
{
|
||||
// There aren't enough symbols/hblank available.
|
||||
return false;
|
||||
}
|
||||
|
||||
NvU32 eightChannelAudioPacketsPerHBlank = (NvU32)divide_floor(((dpInfo->hBlankSym * numLanesPerLink) - (2 * numLanesPerLink) - 8 - (2 * numLanesPerLink)), 40);
|
||||
|
||||
NvU32 eightChannelAudioPackets = (NvU32)divide_ceil(modesetInfo.eightChannelAudioHz * modesetInfo.rasterWidth, modesetInfo.pixelClockHz);
|
||||
|
||||
if (eightChannelAudioPackets > eightChannelAudioPacketsPerHBlank)
|
||||
{
|
||||
// There aren't enough packets/hblank available.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Refer to dev_disp.ref for more information.
|
||||
// # symbols/vblank = ((SetRasterBlankEnd.X + SetRasterSize.Width - SetRasterBlankStart.X - 40) * link_clk / pclk) - Y - 1;
|
||||
// where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39
|
||||
if (modesetInfo.surfaceWidth < 40)
|
||||
{
|
||||
vblank_symbols = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
vblank_symbols = (NvS32)(((NvU64)(modesetInfo.surfaceWidth - 40) * linkConfig.minRate) / modesetInfo.pixelClockHz) - 1;
|
||||
|
||||
vblank_symbols -= numLanesPerLink == 1 ? 39 : numLanesPerLink == 2 ? 21 : 12;
|
||||
}
|
||||
|
||||
dpInfo->vBlankSym = (vblank_symbols < 0) ? 0 : vblank_symbols;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayPort::isModePossibleSSTWithFEC
|
||||
(
|
||||
const LinkConfiguration & linkConfig,
|
||||
const ModesetInfo & modesetInfo,
|
||||
Watermark * dpInfo,
|
||||
bool bUseIncreasedWatermarkLimits
|
||||
)
|
||||
{
|
||||
//
|
||||
// This function is for single stream only!
|
||||
// Refer to Bug 200406501 and 200401850 for algorithm
|
||||
//
|
||||
DP_ASSERT( !linkConfig.multistream );
|
||||
|
||||
unsigned watermarkAdjust = DP_CONFIG_WATERMARK_ADJUST;
|
||||
unsigned watermarkMinimum = DP_CONFIG_WATERMARK_LIMIT;
|
||||
// depth is multiplied by 16 in case of DSC enable
|
||||
unsigned DSC_FACTOR = modesetInfo.bEnableDsc ? 16 : 1;
|
||||
|
||||
if(bUseIncreasedWatermarkLimits)
|
||||
{
|
||||
watermarkAdjust = DP_CONFIG_INCREASED_WATERMARK_ADJUST;
|
||||
watermarkMinimum = DP_CONFIG_INCREASED_WATERMARK_LIMIT;
|
||||
}
|
||||
|
||||
if(!modesetInfo.pixelClockHz || !modesetInfo.depth)
|
||||
{
|
||||
DP_ASSERT(0 && "INVALID PIXEL CLOCK or DEPTH sent by the client ");
|
||||
return false;
|
||||
}
|
||||
// number of link clocks per line.
|
||||
int vblank_symbols = 0;
|
||||
NvU64 PrecisionFactor, ratioF, watermarkF;
|
||||
NvS32 w0, s;
|
||||
|
||||
NvU32 numLanesPerLink = linkConfig.lanes;
|
||||
|
||||
DP_ASSERT(!linkConfig.multistream && "MST!");
|
||||
|
||||
// Check if we have a valid laneCount as currently we support only up to 4-lanes
|
||||
if (!IS_VALID_LANECOUNT(linkConfig.lanes))
|
||||
{
|
||||
//
|
||||
// Print debug message and Assert. All calculations assume a max of 8 lanes
|
||||
// & any increase in lanes should cause these calculation to be updated
|
||||
//
|
||||
DP_LOG(("NVRM: %s: ERROR: LaneCount - %d is not supported for waterMark calculations.",
|
||||
__FUNCTION__, linkConfig.lanes));
|
||||
DP_LOG(("Current support is only up to 4-Lanes & any change/increase in supported lanes "
|
||||
"should be reflected in waterMark calculations algorithm. "
|
||||
"Ex: See calc for minHBlank variable below"));
|
||||
|
||||
DP_ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((modesetInfo.pixelClockHz * modesetInfo.depth) >= (8 * linkConfig.minRate * linkConfig.lanes * DSC_FACTOR))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// For DSC, if (pclk * bpp) < (1/64 * orclk * 8 * lanes) then some TU may end up with
|
||||
// 0 active symbols. This may cause HW hang. Bug 200379426
|
||||
//
|
||||
if ((modesetInfo.bEnableDsc) &&
|
||||
((modesetInfo.pixelClockHz * modesetInfo.depth) < ((8 * linkConfig.minRate * linkConfig.lanes * DSC_FACTOR) / 64)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Perform the SST calculation.
|
||||
// For auto mode the watermark calculation does not need to track accumulated error the
|
||||
// formulas for manual mode will not work. So below calculation was extracted from the DTB.
|
||||
//
|
||||
dpInfo->tuSize = 64;
|
||||
PrecisionFactor = 100000;
|
||||
ratioF = ((NvU64)modesetInfo.pixelClockHz * modesetInfo.depth * PrecisionFactor) / DSC_FACTOR;
|
||||
|
||||
ratioF /= 8 * (NvU64)linkConfig.minRate * linkConfig.lanes;
|
||||
|
||||
if (PrecisionFactor < ratioF) // Assert if we will end up with a negative number in below
|
||||
return false;
|
||||
|
||||
watermarkF = (ratioF * dpInfo->tuSize * (PrecisionFactor - ratioF)) / PrecisionFactor;
|
||||
|
||||
w0 = (8 / linkConfig.lanes);
|
||||
if (linkConfig.bEnableFEC)
|
||||
{
|
||||
s = (linkConfig.lanes == 1) ? 15 : 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = 3 - w0;
|
||||
}
|
||||
|
||||
dpInfo->waterMark = (unsigned)(watermarkAdjust + ((3 * (modesetInfo.depth * PrecisionFactor / (8 * numLanesPerLink * DSC_FACTOR)) + watermarkF) / PrecisionFactor) + w0 + 3);
|
||||
|
||||
s = ((NvS32)ratioF * s);
|
||||
|
||||
dpInfo->waterMark = (unsigned)((NvS32)dpInfo->waterMark + (s / (NvS32)PrecisionFactor));
|
||||
|
||||
//
|
||||
// Bounds check the watermark
|
||||
//
|
||||
NvU32 numSymbolsPerLine = (modesetInfo.surfaceWidth * modesetInfo.depth) / (8 * linkConfig.lanes * DSC_FACTOR);
|
||||
|
||||
if (dpInfo->waterMark > numSymbolsPerLine)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: watermark = %d should not be greater than numSymbolsPerLine = %d.", dpInfo->waterMark, numSymbolsPerLine));
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Clamp the low side
|
||||
//
|
||||
if (dpInfo->waterMark < watermarkMinimum)
|
||||
dpInfo->waterMark = watermarkMinimum;
|
||||
|
||||
unsigned MinHBlank = 0;
|
||||
unsigned MinHBlankFEC = 0;
|
||||
NvU32 BlankingBits = 0;
|
||||
NvU32 BlankingSymbolsPerLane = 0;
|
||||
|
||||
BlankingBits = (3U * 8U * 4U) + (2U * 8U * numLanesPerLink);
|
||||
|
||||
if (modesetInfo.bEnableDsc)
|
||||
{
|
||||
NvU32 sliceCount, sliceWidth, chunkSize;
|
||||
|
||||
sliceCount = (modesetInfo.mode == DSC_DUAL) ? 8U : 4U;
|
||||
sliceWidth = (NvU32)divide_ceil(modesetInfo.surfaceWidth, sliceCount);
|
||||
chunkSize = (NvU32)divide_ceil(modesetInfo.depth * sliceWidth, 8U * DSC_FACTOR);
|
||||
|
||||
if(((NvU64)(chunkSize + 1U) * sliceCount * modesetInfo.pixelClockHz) < (NvU64)(linkConfig.minRate * numLanesPerLink * modesetInfo.surfaceWidth))
|
||||
{
|
||||
// BW is plenty, this is common case.
|
||||
//EOC symbols, when BW enough, only last EOC needs to be considered.
|
||||
BlankingBits += 8U * numLanesPerLink; //+BlankingBits_DSC_EOC
|
||||
BlankingBits += (chunkSize * 8U) - (sliceWidth * modesetInfo.depth / DSC_FACTOR); //+BlankingBits_DSC_bytePadding, only need to consider last slice
|
||||
BlankingBits += (NvU32)(sliceCount * 8U * (divide_ceil(chunkSize, numLanesPerLink) * numLanesPerLink - chunkSize)); //+BlankingBits_DSC_lane_padding
|
||||
}
|
||||
else
|
||||
{ // no extra room in link BW
|
||||
//EOC symbols, EOC will be accumulated until hblank period.
|
||||
BlankingBits += (sliceCount * 8U * numLanesPerLink); //+BlankingBits_EOC
|
||||
//padding, can also use simplified but pessimistic version : BlankingBits += SliceNum * (logic_lanes *8-1);
|
||||
BlankingBits += (NvU32)(sliceCount * ((divide_ceil(chunkSize, numLanesPerLink) * numLanesPerLink * 8U) - (NvU32)(sliceWidth * modesetInfo.depth / DSC_FACTOR))); //+BlankingBits_DSC_padding
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NvU32 surfaceWidthPerLink = modesetInfo.surfaceWidth;
|
||||
NvU32 surfaceWidthPerLane = (NvU32)divide_ceil(surfaceWidthPerLink, numLanesPerLink);
|
||||
|
||||
// Padding
|
||||
BlankingBits += (NvU32)divide_ceil(surfaceWidthPerLane * modesetInfo.depth, 8U) * 8U * numLanesPerLink - (NvU32)(surfaceWidthPerLink * modesetInfo.depth); //+BlankingBits_nonDSC_padding
|
||||
}
|
||||
|
||||
BlankingSymbolsPerLane = (NvU32)divide_ceil(BlankingBits , (8U * numLanesPerLink)); //in symbols per lane
|
||||
BlankingSymbolsPerLane += (linkConfig.enhancedFraming ? 3U : 0U);
|
||||
|
||||
if (linkConfig.bEnableFEC)
|
||||
{
|
||||
//
|
||||
// In worst case, FEC symbols fall into a narrow Hblank period,
|
||||
// we have to consider this in HBlank checker, see bug 200496977
|
||||
// but we don't have to consider this in the calculation of hblank_symbols
|
||||
//
|
||||
|
||||
MinHBlankFEC = FEC_PARITY_SYM_SST(numLanesPerLink, BlankingSymbolsPerLane); //in symbols
|
||||
BlankingSymbolsPerLane += MinHBlankFEC;
|
||||
}
|
||||
|
||||
// BlankingSymbolsPerLane is the MinHBlank in link clock cycles,
|
||||
MinHBlank = (unsigned)(divide_ceil(BlankingSymbolsPerLane * modesetInfo.pixelClockHz,
|
||||
linkConfig.peakRate)); //in pclk cycles
|
||||
MinHBlank += 3U; //add some margin
|
||||
|
||||
NvU32 HBlank = (modesetInfo.rasterWidth - modesetInfo.surfaceWidth);
|
||||
|
||||
if (MinHBlank > HBlank)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: Blanking Width is smaller than minimum permissible value."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bug 702290 - Active Width should be greater than 60
|
||||
if (modesetInfo.surfaceWidth <= 60)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: Minimum Horizontal Active Width <= 60 not supported."));
|
||||
return false;
|
||||
}
|
||||
|
||||
NvU32 total_hblank_symbols = (NvS32)divide_ceil((HBlank * linkConfig.peakRate), modesetInfo.pixelClockHz);
|
||||
NvS32 hblank_symbols = (NvS32)(((NvU64)(HBlank - MinHBlank) * linkConfig.peakRate) / modesetInfo.pixelClockHz);
|
||||
|
||||
if (linkConfig.bEnableFEC)
|
||||
{
|
||||
hblank_symbols -= (FEC_PARITY_SYM_SST(numLanesPerLink, total_hblank_symbols));
|
||||
hblank_symbols += MinHBlankFEC;
|
||||
}
|
||||
|
||||
//reduce HBlank Symbols to account for secondary data packet
|
||||
hblank_symbols -= 1; //Stuffer latency to send BS
|
||||
hblank_symbols -= 3; //SPKT latency to send data to stuffer
|
||||
hblank_symbols -= 3; //add some margin
|
||||
|
||||
dpInfo->hBlankSym = (hblank_symbols < 0) ? 0 : hblank_symbols;
|
||||
|
||||
//
|
||||
// Audio IMP calculations
|
||||
//
|
||||
|
||||
// From dev_disp.ref:
|
||||
|
||||
// The packet generation logic needs to know the length of the hblank period. If there is no room
|
||||
// in the current hblank for a new packet, it will be delayed until the next blanking period. This
|
||||
// field should be programmed during the second Supervisor interrupt based on the new raster
|
||||
// dimensions.
|
||||
|
||||
// ...
|
||||
|
||||
// --------------------------------------
|
||||
// The following formulas can be used to calculate the maximum audio sampling rate that can
|
||||
// be supported by DisplayPort given the current raster dimensions. DisplayPort has much more
|
||||
// bandwidth during blanking periods than HDMI has, so hblank size is less of an issue.
|
||||
|
||||
// ...
|
||||
|
||||
// Size of a packet for 2ch audio = 20 symbols (up to 2 samples)
|
||||
// Size of a packet for 8ch audio = 40 symbols
|
||||
// Size of an audio packet header plus control symbols = 2*#lanes + 8 symbols (assuming < 32 samples per line)
|
||||
// number of packets/hblank for 2ch audio = Floor ((number of free symbols/hblank - (2*#lanes + 8) / 20)
|
||||
// number of packets/hblank for 8ch audio = Floor ((number of free symbols/hblank - (2*#lanes + 8) / 40)
|
||||
|
||||
// Maximum audio sample rate possible:
|
||||
// number of audio samples/line = SetRasterSize.Width * audio_fs / pclk
|
||||
// number of audio packets needed for 2ch audio = Ceiling(SetRasterSize.Width * audio_fs / (pclk*2))
|
||||
// number of audio packets needed for 3-8ch audio = SetRasterSize.Width * audio_fs / pclk
|
||||
|
||||
// If number of audio packets needed > number of packets/hblank, then you cannot support that audio frequency
|
||||
|
||||
// Note that the hBlankSym calculated is per lane. So the number of symbols available for audio is
|
||||
// (number of lanes * hBlankSym).
|
||||
// The calculation of audio packets per Hblank needs to account for the following -
|
||||
// 2 symbols for SS and SE; 8 symbols for header; and additional 2 symbols to account for actual values used by HW.
|
||||
// --------------------------------------
|
||||
|
||||
if (modesetInfo.twoChannelAudioHz != 0)
|
||||
{
|
||||
if ((dpInfo->hBlankSym * numLanesPerLink) < ((2 * numLanesPerLink) + 8))
|
||||
{
|
||||
// There aren't enough symbols/hblank available.
|
||||
return false;
|
||||
}
|
||||
|
||||
NvU32 twoChannelAudioPacketsPerHBlank = (NvU32)divide_floor(((dpInfo->hBlankSym * numLanesPerLink) - (2 * numLanesPerLink) - 8 - (2 * numLanesPerLink)), 20);
|
||||
|
||||
NvU32 twoChannelAudioPackets = (NvU32)divide_ceil(modesetInfo.twoChannelAudioHz * modesetInfo.rasterWidth, modesetInfo.pixelClockHz * 2);
|
||||
|
||||
if (twoChannelAudioPackets > twoChannelAudioPacketsPerHBlank)
|
||||
{
|
||||
// There aren't enough packets/hblank available.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (modesetInfo.eightChannelAudioHz != 0)
|
||||
{
|
||||
if ((dpInfo->hBlankSym * numLanesPerLink) < (2 * numLanesPerLink + 8))
|
||||
{
|
||||
// There aren't enough symbols/hblank available.
|
||||
return false;
|
||||
}
|
||||
|
||||
NvU32 eightChannelAudioPacketsPerHBlank = (NvU32)divide_floor(((dpInfo->hBlankSym * numLanesPerLink) - (2 * numLanesPerLink) - 8 - (2 * numLanesPerLink)), 40);
|
||||
|
||||
NvU32 eightChannelAudioPackets = (NvU32)divide_ceil(modesetInfo.eightChannelAudioHz * modesetInfo.rasterWidth, modesetInfo.pixelClockHz);
|
||||
|
||||
if (eightChannelAudioPackets > eightChannelAudioPacketsPerHBlank)
|
||||
{
|
||||
// There aren't enough packets/hblank available.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Refer to dev_disp.ref for more information.
|
||||
// # symbols/vblank = ((SetRasterBlankEnd.X + SetRasterSize.Width - SetRasterBlankStart.X - 40) * link_clk / pclk) - Y - 1;
|
||||
// where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39
|
||||
if (modesetInfo.surfaceWidth < 40)
|
||||
{
|
||||
vblank_symbols = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
vblank_symbols = (NvS32)(((NvU64)(modesetInfo.surfaceWidth - 3) * linkConfig.peakRate) / modesetInfo.pixelClockHz);
|
||||
|
||||
//
|
||||
// The active region transmission is delayed because of lane fifo storage.
|
||||
// compare to the negedge of hblank, all the BE will be delayed by watermark/ratio cycles.
|
||||
// compare to the posedge of hblank(i.e. the time of sending out BS symbols in vblank period),
|
||||
// all the BS after active pixels will be delayed by maximum 1.5 TU cycles,
|
||||
// the delay of the BS will cause the 1st vblank line shorter than expected,
|
||||
// but it will squeeze hblank period first,
|
||||
// if hblank is short, the BS will be in hactive period and impact vblank_symbols.
|
||||
//
|
||||
|
||||
NvS32 squeezed_symbols = (dpInfo->tuSize * 3 / 2) - hblank_symbols;
|
||||
squeezed_symbols = DP_MAX(squeezed_symbols, 0);
|
||||
NvS32 msa_symbols = (36 / numLanesPerLink) + 3;
|
||||
|
||||
//
|
||||
// MSA can't be in the 1st vblank line, except v_front_porch=0
|
||||
// if we know v_front_porch != 0,
|
||||
// we can use MAX(squeezed_symbols, msa_symbols) instead of squeezed_symbols+msa_symbols
|
||||
//
|
||||
vblank_symbols -= (squeezed_symbols + msa_symbols);
|
||||
|
||||
if (linkConfig.bEnableFEC)
|
||||
{
|
||||
vblank_symbols -= FEC_PARITY_SYM_SST(numLanesPerLink, vblank_symbols);
|
||||
}
|
||||
vblank_symbols -= 3U; //add some margin
|
||||
}
|
||||
|
||||
dpInfo->vBlankSym = (vblank_symbols < 0) ? 0 : vblank_symbols;
|
||||
|
||||
if (modesetInfo.bEnableDsc)
|
||||
{
|
||||
//
|
||||
// For DSC enabled case, the vblank_symbols must be large enough to accommodate DSC PPS SDP, see bug 2760673
|
||||
// For 1 lane, it requires at least 170+13 symbols
|
||||
// For 2 lane, it requires at least 86+3 symbols
|
||||
// For 4 lane, it requires at least 44+3 symbols
|
||||
// normally, no need to check this, except in some small resolution test case.
|
||||
//
|
||||
if ((numLanesPerLink == 1U) && (dpInfo->vBlankSym < 183U))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ((numLanesPerLink == 2U) && (dpInfo->vBlankSym < 89U))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((numLanesPerLink == 4U) && (dpInfo->vBlankSym <47U))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayPort::isModePossibleMSTWithFEC
|
||||
(
|
||||
const LinkConfiguration & linkConfig,
|
||||
const ModesetInfo & modesetInfo,
|
||||
Watermark * dpInfo
|
||||
)
|
||||
{
|
||||
//
|
||||
// This function is for multistream only!
|
||||
// Refer to Bug 200406501 and 200401850 for algorithm
|
||||
//
|
||||
DP_ASSERT(linkConfig.multistream);
|
||||
|
||||
if (!modesetInfo.pixelClockHz || !modesetInfo.depth)
|
||||
{
|
||||
DP_ASSERT(0 && "INVALID PIXEL CLOCK and DEPTH sent by the client ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (linkConfig.lanes == 0)
|
||||
{
|
||||
DP_ASSERT(0 && "No Active link / link train failed ");
|
||||
return false;
|
||||
}
|
||||
|
||||
// depth is multiplied by 16 in case of DSC enable
|
||||
unsigned DSC_FACTOR = modesetInfo.bEnableDsc ? 16 : 1;
|
||||
dpInfo->tuSize = 64;
|
||||
|
||||
NvU32 BlankingBits, BlankingSymbolsPerLane;
|
||||
NvU32 numLanesPerLink = 4U;
|
||||
NvU32 MinHBlank;
|
||||
|
||||
BlankingBits = (3U * 8U * 4U) + (2U * 8U * numLanesPerLink);
|
||||
|
||||
if(modesetInfo.bEnableDsc)
|
||||
{
|
||||
NvU32 sliceCount, sliceWidth, chunkSize;
|
||||
|
||||
sliceCount = (modesetInfo.mode == DSC_DUAL) ? 8U : 4U;
|
||||
sliceWidth = (NvU32)divide_ceil(modesetInfo.surfaceWidth, sliceCount);
|
||||
chunkSize = (NvU32)divide_ceil(modesetInfo.depth * sliceWidth, 8U * DSC_FACTOR);
|
||||
|
||||
//EOC symbols, EOC will be accumulated until hblank period.
|
||||
BlankingBits += (sliceCount * 8U * numLanesPerLink); //+BlankingBits_EOC
|
||||
//+BlankingBits_DSC_padding
|
||||
BlankingBits += (NvU32)(sliceCount * ((divide_ceil(chunkSize, numLanesPerLink) * numLanesPerLink * 8U) - (NvU32)(sliceWidth * modesetInfo.depth / DSC_FACTOR)));
|
||||
}
|
||||
else
|
||||
{
|
||||
NvU32 surfaceWidthPerLane = (NvU32)divide_ceil(modesetInfo.surfaceWidth, numLanesPerLink);
|
||||
|
||||
//Extra bits sent due to pixel steering
|
||||
BlankingBits = (NvU32)divide_ceil(surfaceWidthPerLane * modesetInfo.depth, 8U) * 8U * numLanesPerLink - (NvU32)(modesetInfo.surfaceWidth * modesetInfo.depth); //+BlankingBits_nonDSC_padding
|
||||
}
|
||||
|
||||
BlankingSymbolsPerLane = (NvU32)divide_ceil(BlankingBits, (8U * numLanesPerLink)); //in symbols per lane
|
||||
|
||||
MinHBlank = (NvU32)divide_ceil(BlankingSymbolsPerLane * 8U * numLanesPerLink * DSC_FACTOR, modesetInfo.depth);
|
||||
MinHBlank += 3U; //add some margin
|
||||
|
||||
NvU32 HBlank = (modesetInfo.rasterWidth - modesetInfo.surfaceWidth);
|
||||
|
||||
if (MinHBlank > HBlank)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: Blanking Width is smaller than minimum permissible value."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bug 702290 - Active Width should be greater than 60
|
||||
if (modesetInfo.surfaceWidth <= 60)
|
||||
{
|
||||
DP_LOG(("NVRM: %s:", __FUNCTION__));
|
||||
DP_LOG(("\t\tERROR: Minimum Horizontal Active Width <= 60 not supported."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// MST can do SDP splitting so all audio configuration are possible.
|
||||
dpInfo->hBlankSym = 0U;
|
||||
dpInfo->vBlankSym = 0U;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned DisplayPort::pbnForMode(const ModesetInfo & modesetInfo)
|
||||
{
|
||||
//
|
||||
// Calculate PBN in terms of 54/64 mbyte/sec
|
||||
// round up by .6% for spread de-rate. Note: if we're not spreading our link
|
||||
// this MUST still be counted. It's also to allow downstream links to be spread.
|
||||
//
|
||||
unsigned pbnForMode = (NvU32)(divide_ceil(modesetInfo.pixelClockHz * modesetInfo.depth * 1006 * 64 / 8,
|
||||
(NvU64)54000000 *1000));
|
||||
|
||||
if(modesetInfo.bEnableDsc)
|
||||
{
|
||||
//
|
||||
// When DSC is enabled consider depth will multiplied by 16 and also 3% FEC Overhead
|
||||
// as per DP1.4 spec
|
||||
pbnForMode = (NvU32)(divide_ceil(pbnForMode * 100, 97 * DSC_DEPTH_FACTOR));
|
||||
}
|
||||
|
||||
return pbnForMode;
|
||||
}
|
||||
94
src/common/displayport/src/dptestutil/dp_testmessage.cpp
Normal file
94
src/common/displayport/src/dptestutil/dp_testmessage.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2010-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.
|
||||
*/
|
||||
|
||||
/******************************* DisplayPort *******************************\
|
||||
* *
|
||||
* Module: dp_testmessage.cpp *
|
||||
* Used for DP Test Utility *
|
||||
* *
|
||||
\***************************************************************************/
|
||||
#include "dp_internal.h"
|
||||
#include "dp_auxdefs.h"
|
||||
#include "dp_messages.h"
|
||||
#include "dp_testmessage.h"
|
||||
#include "dp_connectorimpl.h"
|
||||
using namespace DisplayPort;
|
||||
|
||||
// the dp lib status must be set to DONE indicating there is no pending message
|
||||
void DPTestMessageCompletion::messageFailed(MessageManager::Message * from, NakData * data)
|
||||
{
|
||||
parent->testMessageStatus = DP_TESTMESSAGE_REQUEST_STATUS_DONE;
|
||||
|
||||
{
|
||||
{
|
||||
DP_ASSERT(0 && "unknown msg type when msg failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DPTestMessageCompletion::messageCompleted(MessageManager::Message * from)
|
||||
{
|
||||
parent->testMessageStatus = DP_TESTMESSAGE_REQUEST_STATUS_DONE;
|
||||
|
||||
{
|
||||
{
|
||||
DP_ASSERT(0 && "unknown msg type when msg complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageManager * TestMessage::getMessageManager()
|
||||
{
|
||||
return pMsgManager;
|
||||
}
|
||||
|
||||
//
|
||||
// The function request that the request struct size should be check first to ensure the right structure is used and
|
||||
// no BSOD will happen.
|
||||
//
|
||||
// For each request type, the DP lib status for that type should be check in case of request conflict. At one time,
|
||||
// for each request type, only ONE instance could be processed
|
||||
//
|
||||
DP_TESTMESSAGE_STATUS TestMessage::sendDPTestMessage
|
||||
(
|
||||
void *pBuffer,
|
||||
NvU32 requestSize,
|
||||
NvU32 *pDpStatus
|
||||
)
|
||||
{
|
||||
DP_ASSERT(pBuffer);
|
||||
DP_TESTMESSAGE_REQUEST_TYPE type;
|
||||
|
||||
// the buffer must contain a requestType field at least
|
||||
if (requestSize < sizeof(DP_TESTMESSAGE_REQUEST_TYPE))
|
||||
return DP_TESTMESSAGE_STATUS_ERROR_INVALID_PARAM;
|
||||
|
||||
type = *(DP_TESTMESSAGE_REQUEST_TYPE *)pBuffer;
|
||||
|
||||
if (!isValidStruct(type, requestSize))
|
||||
return DP_TESTMESSAGE_STATUS_ERROR_INVALID_PARAM;
|
||||
|
||||
*pDpStatus = DP_TESTMESSAGE_REQUEST_STATUS_ERROR;
|
||||
return DP_TESTMESSAGE_STATUS_ERROR;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user