515.43.04

This commit is contained in:
Andy Ritger
2022-05-09 13:18:59 -07:00
commit 1739a20efc
2519 changed files with 1060036 additions and 0 deletions

View 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

View 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

View 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__

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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() {}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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);
}

View 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;
}

View 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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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;
}

File diff suppressed because it is too large Load Diff

View 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 &currentDevices[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 &currentDevices[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-&currentDevices[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(&currentDevices[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;
}
}
}

View 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;
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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)
;
}

View 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;
}

View 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();
}

View 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)
{
}

View 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;
}

View 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;
}

View 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 */);
}
}

View 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();
}

View 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;
}

View 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;
}

View 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;
}

View 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();
}
}

View 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;
}

View 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;
}