radeon_hd: Improve displayport support

* Modified patch submitted by Bill Randle.
* DisplayPort aux communications now working.
* DP Link Training still not functioning properly.
* The DP edid data isn't used yet as we still use
  the vesa edid during the mode setting.
This commit is contained in:
Alexander von Gluck IV
2013-01-06 20:25:41 -06:00
parent 45f77dcd70
commit 042344329e
3 changed files with 131 additions and 26 deletions
+32 -14
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2006-2011, Haiku, Inc. All Rights Reserved. * Copyright 2006-2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -21,6 +21,7 @@
#include "accelerant_protos.h" #include "accelerant_protos.h"
#include "bios.h" #include "bios.h"
#include "connector.h" #include "connector.h"
#include "displayport.h"
#include "encoder.h" #include "encoder.h"
@@ -225,8 +226,7 @@ detect_crt_ranges(uint32 crtid)
edid1_detailed_monitor* monitor edid1_detailed_monitor* monitor
= &edid->detailed_monitor[index]; = &edid->detailed_monitor[index];
if (monitor->monitor_desc_type if (monitor->monitor_desc_type == EDID1_MONITOR_RANGES) {
== EDID1_MONITOR_RANGES) {
edid1_monitor_range range = monitor->data.monitor_range; edid1_monitor_range range = monitor->data.monitor_range;
gDisplay[crtid]->vfreqMin = range.min_v; /* in Hz */ gDisplay[crtid]->vfreqMin = range.min_v; /* in Hz */
gDisplay[crtid]->vfreqMax = range.max_v; gDisplay[crtid]->vfreqMax = range.max_v;
@@ -258,11 +258,24 @@ detect_displays()
continue; continue;
if (gConnector[id]->type == VIDEO_CONNECTOR_9DIN) { if (gConnector[id]->type == VIDEO_CONNECTOR_9DIN) {
TRACE("%s: Skipping 9DIN connector (not yet supported)\n", TRACE("%s: connector(%" B_PRIu32 "): Skipping 9DIN connector "
__func__); "(not yet supported)\n", __func__, id);
continue; continue;
} }
if (gConnector[id]->type == VIDEO_CONNECTOR_DP) {
edid1_info* edid = &gDisplay[displayIndex]->edidData;
TRACE("%s: connector(%" B_PRIu32 "): Checking DP.\n", __func__, id);
status_t dpHasEDID = ddc2_dp_read_edid1(id, edid);
gDisplay[displayIndex]->attached
= (dpHasEDID == B_OK ? true : false);
if (gDisplay[displayIndex]->attached) {
TRACE("%s: connector(%" B_PRIu32 "): Found DisplayPort EDID!\n",
__func__);
}
}
// TODO: As DP aux transactions don't work yet, just use LVDS as a hack // TODO: As DP aux transactions don't work yet, just use LVDS as a hack
#if 0 #if 0
if (gConnector[id]->encoderExternal.isDPBridge == true) { if (gConnector[id]->encoderExternal.isDPBridge == true) {
@@ -276,17 +289,22 @@ detect_displays()
// TODO: DDC Router switching for DisplayPort (and others?) // TODO: DDC Router switching for DisplayPort (and others?)
} else if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) { } else if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) {
#endif #endif
if (gConnector[id]->type == VIDEO_CONNECTOR_LVDS) {
if (gDisplay[displayIndex]->attached == false
&& gConnector[id]->type == VIDEO_CONNECTOR_LVDS) {
// If plain (non-DP) laptop LVDS, read mode info from AtomBIOS // If plain (non-DP) laptop LVDS, read mode info from AtomBIOS
//TRACE("%s: non-DP laptop LVDS detected\n", __func__); //TRACE("%s: non-DP laptop LVDS detected\n", __func__);
gDisplay[displayIndex]->attached gDisplay[displayIndex]->attached = connector_read_mode_lvds(id,
= connector_read_mode_lvds(id,
&gDisplay[displayIndex]->preferredMode); &gDisplay[displayIndex]->preferredMode);
if (gDisplay[displayIndex]->attached) {
TRACE("%s: connector(%" B_PRIu32 "): found LVDS preferred "
"mode\n", __func__, id);
}
} }
// If no display found yet, try more standard detection methods // If no display found yet, try more standard detection methods
if (gDisplay[displayIndex]->attached == false) { if (gDisplay[displayIndex]->attached == false) {
TRACE("%s: bit-banging ddc for EDID on connector %" B_PRIu32 "\n", TRACE("%s: connector(%" B_PRIu32 "): bit-banging ddc for EDID.\n",
__func__, id); __func__, id);
// Lets try bit-banging edid from connector // Lets try bit-banging edid from connector
@@ -299,7 +317,7 @@ detect_displays()
// Found EDID data? // Found EDID data?
if (gDisplay[displayIndex]->attached) { if (gDisplay[displayIndex]->attached) {
TRACE("%s: found EDID data on connector %" B_PRIu32 "\n", TRACE("%s: connector(%" B_PRIu32 "): found EDID data.\n",
__func__, id); __func__, id);
bool analogEncoder bool analogEncoder
@@ -309,20 +327,20 @@ detect_displays()
edid1_info* edid = &gDisplay[displayIndex]->edidData; edid1_info* edid = &gDisplay[displayIndex]->edidData;
if (!edid->display.input_type && analogEncoder) { if (!edid->display.input_type && analogEncoder) {
// If non-digital EDID + the encoder is analog... // If non-digital EDID + the encoder is analog...
TRACE("%s: connector %" B_PRIu32 " has non-digital EDID " TRACE("%s: connector(%" B_PRIu32 "): has non-digital EDID "
"and a analog encoder.\n", __func__, id); "and a analog encoder.\n", __func__, id);
gDisplay[displayIndex]->attached gDisplay[displayIndex]->attached
= encoder_analog_load_detect(id); = encoder_analog_load_detect(id);
} else if (edid->display.input_type && !analogEncoder) { } else if (edid->display.input_type && !analogEncoder) {
// If EDID is digital, we make an assumption here. // If EDID is digital, we make an assumption here.
TRACE("%s: connector %" B_PRIu32 " has digital EDID " TRACE("%s: connector(%" B_PRIu32 "): has digital EDID "
"and is not a analog encoder.\n", __func__, id); "and is not a analog encoder.\n", __func__, id);
} else { } else {
// This generally means the monitor is of poor design // This generally means the monitor is of poor design
// Since we *know* there is no load on the analog encoder // Since we *know* there is no load on the analog encoder
// we assume that it is a digital display. // we assume that it is a digital display.
TRACE("%s: Warning: monitor on connector %" B_PRIu32 " has " TRACE("%s: connector(%" B_PRIu32 "): Warning: monitor has "
"false digital EDID flag and unloaded analog encoder!\n", "false digital EDID flag + unloaded analog encoder!\n",
__func__, id); __func__, id);
} }
} }
@@ -1,9 +1,10 @@
/* /*
* Copyright 2011, Haiku, Inc. All Rights Reserved. * Copyright 2011-2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
* Alexander von Gluck IV, [email protected] * Alexander von Gluck IV, [email protected]
* Bill Randle, [email protected]
*/ */
@@ -178,14 +179,14 @@ dpcd_reg_read(uint32 hwPin, uint16 address)
status_t status_t
dp_aux_get_i2c_byte(uint32 hwPin, uint16 address, uint8* data, bool end) dp_aux_get_i2c_byte(uint32 hwPin, uint16 address, uint8* data, bool start, bool stop)
{ {
uint8 auxMessage[5]; uint8 auxMessage[5];
int auxMessageBytes = 4; // 4 for read int auxMessageBytes = 4; // 4 for read
/* Set up the command byte */ /* Set up the command byte */
auxMessage[2] = AUX_I2C_READ << 4; auxMessage[2] = AUX_I2C_READ << 4;
if (end == false) if (stop == false)
auxMessage[2] |= AUX_I2C_MOT << 4; auxMessage[2] |= AUX_I2C_MOT << 4;
auxMessage[0] = address; auxMessage[0] = address;
@@ -193,6 +194,12 @@ dp_aux_get_i2c_byte(uint32 hwPin, uint16 address, uint8* data, bool end)
auxMessage[3] = auxMessageBytes << 4; auxMessage[3] = auxMessageBytes << 4;
/* special case for sending the START or STOP */
if (start || stop) {
auxMessage[3] = 3 << 4;
auxMessageBytes = 4;
}
int retry; int retry;
for (retry = 0; retry < 4; retry++) { for (retry = 0; retry < 4; retry++) {
uint8 ack; uint8 ack;
@@ -249,14 +256,14 @@ dp_aux_get_i2c_byte(uint32 hwPin, uint16 address, uint8* data, bool end)
status_t status_t
dp_aux_set_i2c_byte(uint32 hwPin, uint16 address, uint8* data, bool end) dp_aux_set_i2c_byte(uint32 hwPin, uint16 address, uint8* data, bool start, bool stop)
{ {
uint8 auxMessage[5]; uint8 auxMessage[5];
int auxMessageBytes = 5; // 5 for write int auxMessageBytes = 5; // 5 for write
/* Set up the command byte */ /* Set up the command byte */
auxMessage[2] = AUX_I2C_WRITE << 4; auxMessage[2] = AUX_I2C_WRITE << 4;
if (end == false) if (stop == false)
auxMessage[2] |= AUX_I2C_MOT << 4; auxMessage[2] |= AUX_I2C_MOT << 4;
auxMessage[0] = address; auxMessage[0] = address;
@@ -265,6 +272,12 @@ dp_aux_set_i2c_byte(uint32 hwPin, uint16 address, uint8* data, bool end)
auxMessage[3] = auxMessageBytes << 4; auxMessage[3] = auxMessageBytes << 4;
auxMessage[4] = *data; auxMessage[4] = *data;
/* special case for sending the START or STOP */
if (start || stop) {
auxMessage[3] = 3 << 4;
auxMessageBytes = 4;
}
int retry; int retry;
for (retry = 0; retry < 4; retry++) { for (retry = 0; retry < 4; retry++) {
uint8 ack; uint8 ack;
@@ -328,7 +341,7 @@ dp_get_lane_count(uint32 connectorIndex, display_mode* mode)
size_t pixelChunk; size_t pixelChunk;
size_t pixelsPerChunk; size_t pixelsPerChunk;
status_t result = get_pixel_size_for((color_space)mode->space, &pixelChunk, status_t result = dp_get_pixel_size_for((color_space)mode->space, &pixelChunk,
NULL, &pixelsPerChunk); NULL, &pixelsPerChunk);
if (result != B_OK) { if (result != B_OK) {
@@ -367,7 +380,7 @@ dp_get_link_rate(uint32 connectorIndex, display_mode* mode)
size_t pixelChunk; size_t pixelChunk;
size_t pixelsPerChunk; size_t pixelsPerChunk;
status_t result = get_pixel_size_for((color_space)mode->space, &pixelChunk, status_t result = dp_get_pixel_size_for((color_space)mode->space, &pixelChunk,
NULL, &pixelsPerChunk); NULL, &pixelsPerChunk);
if (result != B_OK) { if (result != B_OK) {
@@ -859,6 +872,74 @@ dp_link_train(uint32 connectorIndex, display_mode* mode)
} }
status_t
ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info* edid)
{
TRACE("%s\n", __func__);
dp_info* dpInfo = &gConnector[connectorIndex]->dpInfo;
if (!dpInfo->valid)
return B_ERROR;
edid1_raw raw;
uint8* rdata = (uint8_t*)&raw;
uint8 sdata = 0;
// The following sequence is from a trace of the Linux kernel
// radeon code; not sure if the initial writes to address 0 are
// requried.
dp_aux_set_i2c_byte(dpInfo->auxPin, 0x00, &sdata, true, false);
dp_aux_set_i2c_byte(dpInfo->auxPin, 0x00, &sdata, false, true);
dp_aux_set_i2c_byte(dpInfo->auxPin, 0x50, &sdata, true, false);
dp_aux_set_i2c_byte(dpInfo->auxPin, 0x50, &sdata, false, false);
dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, rdata, true, false);
dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, rdata, false, false);
dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, rdata, false, true);
dp_aux_set_i2c_byte(dpInfo->auxPin, 0x50, &sdata, true, false);
dp_aux_set_i2c_byte(dpInfo->auxPin, 0x50, &sdata, false, false);
dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, rdata, true, false);
for (uint32 i = 0; i < sizeof(raw); i++) {
status_t result = dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, rdata++,
false, false);
if (result) {
ERROR("%s: error reading EDID data at index %" B_PRIu32 "\n",
__func__, i);
dp_aux_get_i2c_byte(dpInfo->auxPin, 0x50, &sdata, false, true);
return B_ERROR;
}
}
dp_aux_get_i2c_byte(dpInfo->auxPin, 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 B_ERROR;
}
edid_decode(edid, &raw);
return B_OK;
}
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;
}
void void
debug_dp_info() debug_dp_info()
{ {
@@ -1,9 +1,10 @@
/* /*
* Copyright 2011, Haiku, Inc. All Rights Reserved. * Copyright 2011-2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
* Alexander von Gluck IV, kallisti5@unixzen.com * Alexander von Gluck IV, kallisti5@unixzen.com
* Bill Randle, billr@neocat.org
*/ */
#ifndef RADEON_HD_DISPLAYPORT_H #ifndef RADEON_HD_DISPLAYPORT_H
#define RADEON_HD_DISPLAYPORT_H #define RADEON_HD_DISPLAYPORT_H
@@ -30,9 +31,9 @@ int dp_aux_write(uint32 hwPin, uint16 address, uint8* send,
int dp_aux_read(uint32 hwPin, uint16 address, uint8* recv, int dp_aux_read(uint32 hwPin, uint16 address, uint8* recv,
int recvBytes, uint8 delay); int recvBytes, uint8 delay);
status_t dp_aux_set_i2c_byte(uint32 hwPin, uint16 address, status_t dp_aux_set_i2c_byte(uint32 hwPin, uint16 address,
uint8* data, bool end); uint8* data, bool start, bool stop);
status_t dp_aux_get_i2c_byte(uint32 hwPin, uint16 address, status_t dp_aux_get_i2c_byte(uint32 hwPin, uint16 address,
uint8* data, bool end); uint8* data, bool start, bool stop);
uint32 dp_get_link_rate(uint32 connectorIndex, display_mode* mode); uint32 dp_get_link_rate(uint32 connectorIndex, display_mode* mode);
uint32 dp_get_lane_count(uint32 connectorIndex, display_mode* mode); uint32 dp_get_lane_count(uint32 connectorIndex, display_mode* mode);
@@ -45,4 +46,9 @@ status_t dp_link_train_ce(uint32 connectorIndex);
void debug_dp_info(); void debug_dp_info();
status_t dp_get_pixel_size_for(color_space space, size_t *pixelChunk,
size_t *rowAlignment, size_t *pixelsPerChunk);
status_t ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info *edid);
#endif /* RADEON_HD_DISPLAYPORT_H */ #endif /* RADEON_HD_DISPLAYPORT_H */