* Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexander von Gluck IV, kallisti5@unixzen.com
* Bill Randle, billr@neocat.org
*/
#include "displayport.h"
#include <Debug.h>
#include "accelerant_protos.h"
#include "atombios-obsolete.h"
#include "connector.h"
#include "mode.h"
#include "edid.h"
#include "encoder.h"
#undef TRACE
#define TRACE_DP
#ifdef TRACE_DP
# define TRACE(x...) _sPrintf("radeon_hd: " x)
#else
# define TRACE(x...) ;
#endif
#define ERROR(x...) _sPrintf("radeon_hd: " x)
static ssize_t
dp_aux_speak(uint32 connectorIndex, uint8* send, int sendBytes,
uint8* recv, int recvBytes, uint8 delay, uint8* ack)
{
dp_info* dpInfo = &gConnector[connectorIndex]->dpInfo;
if (!dpInfo->valid) {
ERROR("%s: cannot speak on invalid dpInfo!\n", __func__);
return B_IO_ERROR;
}
int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
union auxChannelTransaction {
PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1;
PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2;
};
union auxChannelTransaction args;
memset(&args, 0, sizeof(args));
args.v2.lpAuxRequest = B_HOST_TO_LENDIAN_INT16(0 + 4);
args.v2.lpDataOut = B_HOST_TO_LENDIAN_INT16(16 + 4);
args.v2.ucDataOutLen = 0;
args.v2.ucChannelID = dpInfo->auxPin;
args.v2.ucDelay = delay / 10;
args.v2.ucHPD_ID = connector_pick_atom_hpdid(connectorIndex);
unsigned char* base = (unsigned char*)(gAtomContext->scratch + 1);
memcpy(base, send, sendBytes);
atom_execute_table(gAtomContext, index, (uint32*)&args);
*ack = args.v2.ucReplyStatus;
switch (args.v2.ucReplyStatus) {
case 1:
ERROR("%s: dp_aux channel timeout!\n", __func__);
return B_TIMED_OUT;
case 2:
ERROR("%s: dp_aux channel flags not zero!\n", __func__);
return B_BUSY;
case 3:
ERROR("%s: dp_aux channel error!\n", __func__);
return B_IO_ERROR;
}
int recvLength = args.v1.ucDataOutLen;
if (recvLength > recvBytes)
recvLength = recvBytes;
if (recv && recvBytes)
memcpy(recv, base + 16, recvLength);
return recvLength;
}
status_t
dp_aux_transaction(uint32 connectorIndex, dp_aux_msg* message)
{
uint8 delay = 0;
if (message == NULL) {
ERROR("%s: DP message is invalid!\n", __func__);
return B_ERROR;
}
if (message->size > 16) {
ERROR("%s: Too many bytes! (%" B_PRIuSIZE ")\n", __func__,
message->size);
return B_ERROR;
}
uint8 transactionSize = 4;
switch(message->request & ~DP_AUX_I2C_MOT) {
case DP_AUX_NATIVE_WRITE:
case DP_AUX_I2C_WRITE:
case DP_AUX_I2C_WRITE_STATUS_UPDATE:
transactionSize += message->size;
break;
}
if (message->size > 0 && message->buffer == NULL) {
ERROR("%s: DP message uninitalized buffer!\n", __func__);
return B_ERROR;
}
uint8 auxMessage[20];
auxMessage[0] = message->address & 0xff;
auxMessage[1] = (message->address >> 8) & 0xff;
auxMessage[2] = (message->request << 4) | ((message->address >> 16) & 0xf);
auxMessage[3] = message->size ? (message->size - 1) : 0;
if (message->size == 0)
auxMessage[3] |= 3 << 4;
else
auxMessage[3] |= transactionSize << 4;
uint8 retry;
for (retry = 0; retry < 7; retry++) {
uint8 ack;
ssize_t result = B_ERROR;
switch(message->request & ~DP_AUX_I2C_MOT) {
case DP_AUX_NATIVE_WRITE:
case DP_AUX_I2C_WRITE:
case DP_AUX_I2C_WRITE_STATUS_UPDATE:
memcpy(auxMessage + 4, message->buffer, message->size);
result = dp_aux_speak(connectorIndex, auxMessage,
transactionSize, NULL, 0, delay, &ack);
break;
case DP_AUX_NATIVE_READ:
case DP_AUX_I2C_READ:
result = dp_aux_speak(connectorIndex, auxMessage,
transactionSize, (uint8*)message->buffer, message->size,
delay, &ack);
break;
default:
ERROR("%s: Unknown dp_aux_msg request!\n", __func__);
return B_ERROR;
}
if (result == B_BUSY)
continue;
else if (result < B_OK)
return result;
ack >>= 4;
message->reply = ack;
switch(message->reply & DP_AUX_NATIVE_REPLY_MASK) {
case DP_AUX_NATIVE_REPLY_ACK:
return B_OK;
case DP_AUX_NATIVE_REPLY_DEFER:
TRACE("%s: aux reply defer received. Snoozing.\n", __func__);
snooze(400);
break;
default:
TRACE("%s: aux invalid native reply: 0x%02x\n", __func__,
message->reply);
return B_IO_ERROR;
}
}
ERROR("%s: IO Error. %" B_PRIu8 " attempts\n", __func__, retry);
return B_IO_ERROR;
}
void
dpcd_reg_write(uint32 connectorIndex, uint32 address, uint8 value)
{
dp_aux_msg message;
memset(&message, 0, sizeof(message));
message.address = address;
message.buffer = &value;
message.request = DP_AUX_NATIVE_WRITE;
message.size = 1;
status_t result = dp_aux_transaction(connectorIndex, &message);
if (result != B_OK) {
ERROR("%s: error on DisplayPort aux write (0x%" B_PRIx32 ")\n",
__func__, result);
}
}
uint8
dpcd_reg_read(uint32 connectorIndex, uint32 address)
{
uint8 response[3];
dp_aux_msg message;
memset(&message, 0, sizeof(message));
message.address = address;
message.buffer = &response;
message.request = DP_AUX_NATIVE_READ;
message.size = 1;
status_t result = dp_aux_transaction(connectorIndex, &message);
if (result != B_OK) {
ERROR("%s: error on DisplayPort aux read (0x%" B_PRIx32 ")\n",
__func__, result);
}
return response[0];
}
status_t
dp_aux_get_i2c_byte(uint32 connectorIndex, uint32 address, uint8* data,
bool start, bool stop)
{
uint8 reply[3];
dp_aux_msg message;
memset(&message, 0, sizeof(message));
message.address = address;
message.buffer = reply;
message.request = DP_AUX_I2C_READ;
message.size = 1;
if (stop == false) {
message.request |= DP_AUX_I2C_MOT;
}
if (stop || start) {
message.buffer = NULL;
message.size = 0;
}
for (int attempt = 0; attempt < 7; attempt++) {
status_t result = dp_aux_transaction(connectorIndex, &message);
if (result != B_OK) {
ERROR("%s: aux_ch transaction failed!\n", __func__);
return result;
}
switch (message.reply & DP_AUX_I2C_REPLY_MASK) {
case DP_AUX_I2C_REPLY_ACK:
*data = reply[0];
return B_OK;
case DP_AUX_I2C_REPLY_NACK:
TRACE("%s: aux i2c nack\n", __func__);
return B_IO_ERROR;
case DP_AUX_I2C_REPLY_DEFER:
TRACE("%s: aux i2c defer\n", __func__);
snooze(400);
break;
default:
TRACE("%s: aux invalid I2C reply: 0x%02x\n",
__func__, message.reply);
return B_ERROR;
}
}
return B_ERROR;
}
status_t
dp_aux_set_i2c_byte(uint32 connectorIndex, uint32 address, uint8* data,
bool start, bool stop)
{
dp_aux_msg message;
memset(&message, 0, sizeof(message));
message.address = address;
message.buffer = data;
message.request = DP_AUX_I2C_WRITE;
message.size = 1;
if (stop == false)
message.request |= DP_AUX_I2C_MOT;
if (stop || start) {
message.buffer = NULL;
message.size = 0;
}
for (int attempt = 0; attempt < 7; attempt++) {
status_t result = dp_aux_transaction(connectorIndex, &message);
if (result != B_OK) {
ERROR("%s: aux_ch transaction failed!\n", __func__);
return result;
}
switch (message.reply & DP_AUX_I2C_REPLY_MASK) {
case DP_AUX_I2C_REPLY_ACK:
return B_OK;
case DP_AUX_I2C_REPLY_NACK:
ERROR("%s: aux i2c nack\n", __func__);
return B_IO_ERROR;
case DP_AUX_I2C_REPLY_DEFER:
TRACE("%s: aux i2c defer\n", __func__);
snooze(400);
break;
default:
ERROR("%s: aux invalid I2C reply: 0x%02x\n", __func__,
message.reply);
return B_IO_ERROR;
}
}
return B_ERROR;
}
uint32
dp_get_encoder_config(uint32 connectorIndex)
{
uint32 result = 0;
uint32 digEncoderID = encoder_pick_dig(connectorIndex);
if (digEncoderID)
result |= ATOM_DP_CONFIG_DIG2_ENCODER;
else
result |= ATOM_DP_CONFIG_DIG1_ENCODER;
bool linkB = gConnector[connectorIndex]->encoder.linkEnumeration
== GRAPH_OBJECT_ENUM_ID2 ? true : false;
if (linkB)
result |= ATOM_DP_CONFIG_LINK_B;
else
result |= ATOM_DP_CONFIG_LINK_A;
return result;
}
uint32
dp_get_lane_count(uint32 connectorIndex, display_mode* mode)
{
size_t pixelChunk;
size_t pixelsPerChunk;
status_t result = dp_get_pixel_size_for((color_space)mode->space,
&pixelChunk, NULL, &pixelsPerChunk);
if (result != B_OK) {
TRACE("%s: Invalid color space!\n", __func__);
return 0;
}
uint32 bitsPerPixel = (pixelChunk / pixelsPerChunk) * 8;
uint32 dpMaxLinkRate
= dp_decode_link_rate(dpcd_reg_read(connectorIndex, DP_MAX_LINK_RATE));
uint32 dpMaxLaneCount = dpcd_reg_read(connectorIndex,
DP_MAX_LANE_COUNT) & DP_MAX_LANE_COUNT_MASK;
uint32 lane;
for (lane = 2; lane < dpMaxLaneCount; lane <<= 1) {
uint32 maxPixelClock = dp_get_pixel_clock_max(dpMaxLinkRate, lane,
bitsPerPixel);
if (mode->timing.pixel_clock <= maxPixelClock)
break;
}
TRACE("%s: Lanes: %" B_PRIu32 "\n", __func__, lane);
return lane;
}
uint32
dp_get_link_rate(uint32 connectorIndex, display_mode* mode)
{
uint16 encoderID = gConnector[connectorIndex]->encoderExternal.objectID;
if (encoderID == ENCODER_OBJECT_ID_NUTMEG)
return 270000;
size_t pixelChunk;
size_t pixelsPerChunk;
status_t result = dp_get_pixel_size_for((color_space)mode->space,
&pixelChunk, NULL, &pixelsPerChunk);
if (result != B_OK) {
TRACE("%s: Invalid color space!\n", __func__);
return 0;
}
uint32 bitsPerPixel = (pixelChunk / pixelsPerChunk) * 8;
uint32 laneCount = dp_get_lane_count(connectorIndex, mode);
uint32 maxPixelClock
= dp_get_pixel_clock_max(162000, laneCount, bitsPerPixel);
if (mode->timing.pixel_clock <= maxPixelClock)
return 162000;
maxPixelClock = dp_get_pixel_clock_max(270000, laneCount, bitsPerPixel);
if (mode->timing.pixel_clock <= maxPixelClock)
return 270000;
#if 0
if (dp_is_dp12_capable(connectorIndex)) {
maxPixelClock = dp_get_pixel_clock_max(540000, laneCount, bitsPerPixel);
if (mode->timing.pixel_clock <= maxPixelClock)
return 540000;
}
#endif
return dp_decode_link_rate(dpcd_reg_read(connectorIndex, DP_MAX_LINK_RATE));
}
static uint8
dp_encoder_service(int action, int linkRate, uint8 lane, uint8 config)
{
DP_ENCODER_SERVICE_PARAMETERS args;
int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
memset(&args, 0, sizeof(args));
args.ucLinkClock = linkRate / 10;
args.ucConfig = config;
args.ucAction = action;
args.ucLaneNum = lane;
args.ucStatus = 0;
atom_execute_table(gAtomContext, index, (uint32*)&args);
return args.ucStatus;
}
uint8
dp_get_sink_type(uint32 connectorIndex)
{
dp_info* dpInfo = &gConnector[connectorIndex]->dpInfo;
return dp_encoder_service(ATOM_DP_ACTION_GET_SINK_TYPE, 0, 0,
dpInfo->auxPin);
}
void
dp_setup_connectors()
{
TRACE("%s\n", __func__);
for (uint32 index = 0; index < ATOM_MAX_SUPPORTED_DEVICE; index++) {
dp_info* dpInfo = &gConnector[index]->dpInfo;
if (gConnector[index]->valid == false
|| connector_is_dp(index) == false) {
dpInfo->valid = false;
continue;
}
uint32 i2cPinIndex = gConnector[index]->i2cPinIndex;
uint32 auxPin = gGPIOInfo[i2cPinIndex]->hwPin;
dpInfo->auxPin = auxPin;
dpInfo->valid = true;
}
}
bool
dp_get_link_status(uint32 connectorIndex)
{
dp_info* dp = &gConnector[connectorIndex]->dpInfo;
dp_aux_msg message;
memset(&message, 0, sizeof(message));
message.request = DP_AUX_NATIVE_READ;
message.address = DP_LANE_STATUS_0_1;
message.size = DP_LINK_STATUS_SIZE;
message.buffer = dp->linkStatus;
status_t result = dp_aux_transaction(connectorIndex, &message);
if (result != B_OK) {
ERROR("%s: DisplayPort link status failed\n", __func__);
return false;
}
return true;
}
static uint8
dp_get_lane_status(dp_info* dp, int lane)
{
int i = DP_LANE_STATUS_0_1 + (lane >> 1);
int s = (lane & 1) * 4;
uint8 l = dp->linkStatus[i - DP_LANE_STATUS_0_1];
return (l >> s) & 0xf;
}
static bool
dp_clock_recovery_ok(dp_info* dp)
{
int lane;
uint8 laneStatus;
for (lane = 0; lane < dp->laneCount; lane++) {
laneStatus = dp_get_lane_status(dp, lane);
if ((laneStatus & DP_LANE_STATUS_CR_DONE_A) == 0)
return false;
}
return true;
}
static bool
dp_clock_equalization_ok(dp_info* dp)
{
uint8 laneAlignment
= dp->linkStatus[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE_STATUS_0_1];
if ((laneAlignment & DP_INTERLANE_ALIGN_DONE) == 0) {
TRACE("%s: false. Lane alignment incomplete.\n", __func__);
return false;
}
int lane;
for (lane = 0; lane < dp->laneCount; lane++) {
uint8 laneStatus = dp_get_lane_status(dp, lane);
if ((laneStatus & DP_LANE_STATUS_EQUALIZED_A)
!= DP_LANE_STATUS_EQUALIZED_A) {
TRACE("%s: false. Lanes not yet equalized.\n", __func__);
return false;
}
}
TRACE("%s: true. Lanes equalized.\n", __func__);
return true;
}
static void
dp_update_vs_emph(uint32 connectorIndex)
{
dp_info* dp = &gConnector[connectorIndex]->dpInfo;
transmitter_dig_setup(connectorIndex, dp->linkRate, 0,
dp->trainingSet[0], ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH);
dp_aux_msg message;
memset(&message, 0, sizeof(message));
message.request = DP_AUX_NATIVE_WRITE;
message.address = DP_TRAINING_LANE0_SET;
message.buffer = dp->trainingSet;
message.size = dp->laneCount;
dp_aux_transaction(connectorIndex, &message);
}
static uint8
dp_get_adjust_request_voltage(dp_info* dp, int lane)
{
int i = DP_ADJ_REQUEST_0_1 + (lane >> 1);
int s = (((lane & 1) != 0) ? DP_ADJ_VCC_SWING_LANEB_SHIFT
: DP_ADJ_VCC_SWING_LANEA_SHIFT);
uint8 l = dp->linkStatus[i - DP_LANE_STATUS_0_1];
return ((l >> s) & 0x3) << DP_TRAIN_VCC_SWING_SHIFT;
}
static uint8
dp_get_adjust_request_pre_emphasis(dp_info* dp, int lane)
{
int i = DP_ADJ_REQUEST_0_1 + (lane >> 1);
int s = (((lane & 1) != 0) ? DP_ADJ_PRE_EMPHASIS_LANEB_SHIFT
: DP_ADJ_PRE_EMPHASIS_LANEA_SHIFT);
uint8 l = dp->linkStatus[i - DP_LANE_STATUS_0_1];
return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
}
static void
dp_get_adjust_train(dp_info* dp)
{
TRACE("%s\n", __func__);
const char* voltageNames[] = {
"0.4V", "0.6V", "0.8V", "1.2V"
};
const char* preEmphasisNames[] = {
"0dB", "3.5dB", "6dB", "9.5dB"
};
uint8 voltage = 0;
uint8 preEmphasis = 0;
int lane;
for (lane = 0; lane < dp->laneCount; lane++) {
uint8 laneVoltage = dp_get_adjust_request_voltage(dp, lane);
uint8 lanePreEmphasis = dp_get_adjust_request_pre_emphasis(dp, lane);
TRACE("%s: Requested %s at %s for lane %d\n", __func__,
preEmphasisNames[lanePreEmphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT],
voltageNames[laneVoltage >> DP_TRAIN_VCC_SWING_SHIFT],
lane);
if (laneVoltage > voltage)
voltage = laneVoltage;
if (lanePreEmphasis > preEmphasis)
preEmphasis = lanePreEmphasis;
}
if (voltage >= DP_TRAIN_VCC_SWING_1200)
voltage |= DP_TRAIN_MAX_SWING_EN;
if (preEmphasis >= DP_TRAIN_PRE_EMPHASIS_9_5)
preEmphasis |= DP_TRAIN_MAX_EMPHASIS_EN;
for (lane = 0; lane < 4; lane++)
dp->trainingSet[lane] = voltage | preEmphasis;
}
static void
dp_set_tp(uint32 connectorIndex, int trainingPattern)
{
TRACE("%s\n", __func__);
radeon_shared_info &info = *gInfo->shared_info;
pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
int rawTrainingPattern = 0;
if (info.dceMajor >= 4) {
TRACE("%s: Training with encoder...\n", __func__);
switch (trainingPattern) {
case DP_TRAINING_PATTERN_1:
rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1;
break;
case DP_TRAINING_PATTERN_2:
rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2;
break;
case DP_TRAINING_PATTERN_3:
rawTrainingPattern = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3;
break;
}
encoder_dig_setup(connectorIndex, pll->pixelClock, rawTrainingPattern);
} else {
switch (trainingPattern) {
case DP_TRAINING_PATTERN_1:
rawTrainingPattern = 0;
break;
case DP_TRAINING_PATTERN_2:
rawTrainingPattern = 1;
break;
}
uint32 encoderConfig = dp_get_encoder_config(connectorIndex);
dp_encoder_service(ATOM_DP_ACTION_TRAINING_PATTERN_SEL, pll->pixelClock,
rawTrainingPattern, encoderConfig);
}
dpcd_reg_write(connectorIndex, DP_TRAINING_PATTERN_SET, trainingPattern);
}
status_t
dp_link_train_cr(uint32 connectorIndex)
{
TRACE("%s: connector %" B_PRIu32 "\n", __func__, connectorIndex);
dp_info* dp = &gConnector[connectorIndex]->dpInfo;
bool clockRecovery = false;
uint8 voltage = 0xff;
int lane;
dp_set_tp(connectorIndex, DP_TRAINING_PATTERN_1);
memset(dp->trainingSet, 0, 4);
dp_update_vs_emph(connectorIndex);
snooze(400);
while (1) {
if (dp->trainingReadInterval == 0)
snooze(100);
else
snooze(1000 * 4 * dp->trainingReadInterval);
if (!dp_get_link_status(connectorIndex))
break;
if (dp_clock_recovery_ok(dp)) {
clockRecovery = true;
break;
}
for (lane = 0; lane < dp->laneCount; lane++) {
if ((dp->trainingSet[lane] & DP_TRAIN_MAX_SWING_EN) == 0)
break;
}
if (lane == dp->laneCount) {
ERROR("%s: clock recovery reached max voltage\n", __func__);
break;
}
if ((dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK) == voltage) {
dp->trainingAttempts++;
if (dp->trainingAttempts >= 5) {
ERROR("%s: clock recovery tried 5 times\n", __func__);
break;
}
} else
dp->trainingAttempts = 0;
voltage = dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK;
dp_get_adjust_train(dp);
dp_update_vs_emph(connectorIndex);
}
if (!clockRecovery) {
ERROR("%s: clock recovery failed\n", __func__);
return B_ERROR;
}
TRACE("%s: clock recovery at voltage %d pre-emphasis %d\n",
__func__, dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK,
(dp->trainingSet[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
>> DP_TRAIN_PRE_EMPHASIS_SHIFT);
return B_OK;
}
status_t
dp_link_train_ce(uint32 connectorIndex, bool tp3Support)
{
TRACE("%s: connector %" B_PRIu32 "\n", __func__, connectorIndex);
dp_info* dp = &gConnector[connectorIndex]->dpInfo;
if (tp3Support)
dp_set_tp(connectorIndex, DP_TRAINING_PATTERN_3);
else
dp_set_tp(connectorIndex, DP_TRAINING_PATTERN_2);
dp->trainingAttempts = 0;
bool channelEqual = false;
while (1) {
if (dp->trainingReadInterval == 0)
snooze(400);
else
snooze(1000 * 4 * dp->trainingReadInterval);
if (!dp_get_link_status(connectorIndex)) {
ERROR("%s: ERROR: Unable to get link status!\n", __func__);
break;
}
if (dp_clock_equalization_ok(dp)) {
channelEqual = true;
break;
}
if (dp->trainingAttempts > 5) {
ERROR("%s: ERROR: failed > 5 times!\n", __func__);
break;
}
dp_get_adjust_train(dp);
dp_update_vs_emph(connectorIndex);
dp->trainingAttempts++;
}
if (!channelEqual) {
ERROR("%s: ERROR: failed\n", __func__);
return B_ERROR;
}
TRACE("%s: channels equalized at voltage %d pre-emphasis %d\n",
__func__, dp->trainingSet[0] & DP_ADJ_VCC_SWING_LANEA_MASK,
(dp->trainingSet[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
>> DP_TRAIN_PRE_EMPHASIS_SHIFT);
return B_OK;
}
status_t
dp_link_train(uint8 crtcID)
{
TRACE("%s\n", __func__);
uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
dp_info* dp = &gConnector[connectorIndex]->dpInfo;
display_mode* mode = &gDisplay[crtcID]->currentMode;
if (dp->valid != true) {
ERROR("%s: started on invalid DisplayPort connector #%" B_PRIu32 "\n",
__func__, connectorIndex);
return B_ERROR;
}
dp->revision = dpcd_reg_read(connectorIndex, DP_DPCD_REV);
dp->trainingReadInterval
= dpcd_reg_read(connectorIndex, DP_TRAINING_AUX_RD_INTERVAL);
uint8 sandbox = dpcd_reg_read(connectorIndex, DP_MAX_LANE_COUNT);
radeon_shared_info &info = *gInfo->shared_info;
bool dpTPS3Supported = false;
if (info.dceMajor >= 5 && (sandbox & DP_TPS3_SUPPORTED) != 0)
dpTPS3Supported = true;
if (dp->revision >= DP_DPCD_REV_11)
dpcd_reg_write(connectorIndex, DP_SET_POWER, DP_SET_POWER_D0);
if ((dpcd_reg_read(connectorIndex, DP_MAX_DOWNSPREAD) & 0x1) != 0) {
dpcd_reg_write(connectorIndex, DP_DOWNSPREAD_CTRL,
DP_DOWNSPREAD_CTRL_AMP_EN);
} else
dpcd_reg_write(connectorIndex, DP_DOWNSPREAD_CTRL, 0);
encoder_dig_setup(connectorIndex, mode->timing.pixel_clock,
ATOM_ENCODER_CMD_SETUP_PANEL_MODE);
sandbox = dpcd_reg_read(connectorIndex, DP_LANE_COUNT);
if (dp->revision >= DP_DPCD_REV_11
&& (dpcd_reg_read(connectorIndex, DP_MAX_LANE_COUNT)
& DP_ENHANCED_FRAME_CAP)) {
sandbox |= DP_ENHANCED_FRAME_EN;
}
dpcd_reg_write(connectorIndex, DP_LANE_COUNT, sandbox);
sandbox = dp_encode_link_rate(dp->linkRate);
dpcd_reg_write(connectorIndex, DP_LINK_RATE, sandbox);
uint32 encoderConfig = dp_get_encoder_config(connectorIndex);
if (info.dceMajor >= 4) {
encoder_dig_setup(connectorIndex, mode->timing.pixel_clock,
ATOM_ENCODER_CMD_DP_LINK_TRAINING_START);
} else {
dp_encoder_service(ATOM_DP_ACTION_TRAINING_START,
mode->timing.pixel_clock, 0, encoderConfig);
}
dpcd_reg_write(connectorIndex, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE);
dp_link_train_cr(connectorIndex);
dp_link_train_ce(connectorIndex, dpTPS3Supported);
snooze(400);
dpcd_reg_write(connectorIndex, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE);
if (info.dceMajor >= 4) {
encoder_dig_setup(connectorIndex, mode->timing.pixel_clock,
ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE);
} else {
dp_encoder_service(ATOM_DP_ACTION_TRAINING_COMPLETE,
mode->timing.pixel_clock, 0, encoderConfig);
}
return B_OK;
}
bool
ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info* edid)
{
TRACE("%s\n", __func__);
dp_info* dpInfo = &gConnector[connectorIndex]->dpInfo;
if (!dpInfo->valid) {
ERROR("%s: connector(%" B_PRIu32 ") missing valid DisplayPort data!\n",
__func__, connectorIndex);
return false;
}
edid1_raw raw;
uint8* rdata = (uint8*)&raw;
uint8 sdata = 0;
dp_aux_set_i2c_byte(connectorIndex, 0x00, &sdata, true, false);
dp_aux_set_i2c_byte(connectorIndex, 0x00, &sdata, false, true);
dp_aux_set_i2c_byte(connectorIndex, 0x50, &sdata, true, false);
dp_aux_set_i2c_byte(connectorIndex, 0x50, &sdata, false, false);
dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, true, false);
dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, false, false);
dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, false, true);
dp_aux_set_i2c_byte(connectorIndex, 0x50, &sdata, true, false);
dp_aux_set_i2c_byte(connectorIndex, 0x50, &sdata, false, false);
dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, true, false);
for (uint32 i = 0; i < sizeof(raw); i++) {
status_t result = dp_aux_get_i2c_byte(connectorIndex, 0x50,
rdata++, false, false);
if (result != B_OK) {
TRACE("%s: error reading EDID data at index %" B_PRIu32 ", "
"result = 0x%" B_PRIx32 "\n", __func__, i, result);
dp_aux_get_i2c_byte(connectorIndex, 0x50, &sdata, false, true);
return false;
}
}
dp_aux_get_i2c_byte(connectorIndex, 0x50, &sdata, false, true);
if (raw.version.version != 1 || raw.version.revision > 4) {
ERROR("%s: EDID version or revision out of range\n", __func__);
return false;
}
edid_decode(edid, &raw);
return true;
}
status_t
dp_get_pixel_size_for(color_space space, size_t *pixelChunk,
size_t *rowAlignment, size_t *pixelsPerChunk)
{
status_t result = get_pixel_size_for(space, pixelChunk, NULL,
pixelsPerChunk);
if ((space == B_RGB32) || (space == B_RGBA32) || (space == B_RGB32_BIG)
|| (space == B_RGBA32_BIG)) {
*pixelChunk = 3;
}
return result;
}
bool
dp_is_dp12_capable(uint32 connectorIndex)
{
TRACE("%s\n", __func__);
radeon_shared_info &info = *gInfo->shared_info;
uint32 capabilities = gConnector[connectorIndex]->encoder.capabilities;
if (info.dceMajor >= 5
&& gInfo->dpExternalClock >= 539000
&& (capabilities & ATOM_ENCODER_CAP_RECORD_HBR2) != 0) {
return true;
}
return false;
}
void
debug_dp_info()
{
ERROR("Current DisplayPort Info =================\n");
for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
if (gConnector[id]->valid == true) {
dp_info* dp = &gConnector[id]->dpInfo;
ERROR("Connector #%" B_PRIu32 ") DP: %s\n", id,
dp->valid ? "true" : "false");
if (!dp->valid)
continue;
ERROR(" + DP Config Data\n");
ERROR(" - max lane count: %d\n",
dpcd_reg_read(id, DP_MAX_LANE_COUNT) & DP_MAX_LANE_COUNT_MASK);
ERROR(" - max link rate: %d\n",
dpcd_reg_read(id, DP_MAX_LINK_RATE));
ERROR(" - receiver port count: %d\n",
dpcd_reg_read(id, DP_NORP) & DP_NORP_MASK);
ERROR(" - downstream port present: %s\n",
dpcd_reg_read(id, DP_DOWNSTREAMPORT_PRESENT) & DP_DWN_STRM_PORT_PRESENT
? "yes" : "no");
ERROR(" - downstream port count: %d\n",
dpcd_reg_read(id, DP_DOWN_STREAM_PORT_COUNT)
& DP_PORT_COUNT_MASK);
ERROR(" + Training\n");
ERROR(" - attempts: %" B_PRIu8 "\n",
dp->trainingAttempts);
ERROR(" - delay: %d\n",
dp->trainingReadInterval);
ERROR(" + Data\n");
ERROR(" - auxPin: 0x%" B_PRIX32"\n", dp->auxPin);
ERROR(" + Video\n");
ERROR(" - laneCount: %d\n", dp->laneCount);
ERROR(" - linkRate: %" B_PRIu32 "\n",
dp->linkRate);
}
}
ERROR("==========================================\n");
}