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:
@@ -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, [email protected]
|
* Alexander von Gluck IV, [email protected]
|
||||||
|
* Bill Randle, [email protected]
|
||||||
*/
|
*/
|
||||||
#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();
|
||||||
|
|
||||||
#endif /* RADEON_HD_DISPLAYPORT_H */
|
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 */
|
||||||
Reference in New Issue
Block a user