radeon_hd: Move i2c to dp aux transaction function.

* WIP: EDID version 255.255 found
This commit is contained in:
Alexander von Gluck IV 2015-07-13 00:43:38 -05:00
parent 8a5884f561
commit 8611df9d0b

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2011-2013, Haiku, Inc. All Rights Reserved. * Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@ -31,7 +31,7 @@
#define ERROR(x...) _sPrintf("radeon_hd: " x) #define ERROR(x...) _sPrintf("radeon_hd: " x)
static status_t static ssize_t
dp_aux_speak(uint32 connectorIndex, uint8* send, int sendBytes, dp_aux_speak(uint32 connectorIndex, uint8* send, int sendBytes,
uint8* recv, int recvBytes, uint8 delay, uint8* ack) uint8* recv, int recvBytes, uint8 delay, uint8* ack)
{ {
@ -53,11 +53,11 @@ dp_aux_speak(uint32 connectorIndex, uint8* send, int sendBytes,
union auxChannelTransaction args; union auxChannelTransaction args;
memset(&args, 0, sizeof(args)); memset(&args, 0, sizeof(args));
args.v1.lpAuxRequest = B_HOST_TO_LENDIAN_INT16(0 + 4); args.v2.lpAuxRequest = B_HOST_TO_LENDIAN_INT16(0 + 4);
args.v1.lpDataOut = B_HOST_TO_LENDIAN_INT16(16 + 4); args.v2.lpDataOut = B_HOST_TO_LENDIAN_INT16(16 + 4);
args.v1.ucDataOutLen = 0; args.v2.ucDataOutLen = 0;
args.v1.ucChannelID = dpInfo->auxPin; args.v2.ucChannelID = dpInfo->auxPin;
args.v1.ucDelay = delay / 10; args.v2.ucDelay = delay / 10;
uint16 hpdPinIndex = gConnector[connectorIndex]->hpdPinIndex; uint16 hpdPinIndex = gConnector[connectorIndex]->hpdPinIndex;
if (info.dceMajor >= 4 if (info.dceMajor >= 4
@ -105,17 +105,17 @@ dp_aux_speak(uint32 connectorIndex, uint8* send, int sendBytes,
atom_execute_table(gAtomContext, index, (uint32*)&args); atom_execute_table(gAtomContext, index, (uint32*)&args);
*ack = args.v1.ucReplyStatus; *ack = args.v2.ucReplyStatus;
switch (args.v1.ucReplyStatus) { switch (args.v2.ucReplyStatus) {
case 1: case 1:
ERROR("%s: dp_aux_ch timeout!\n", __func__); ERROR("%s: dp_aux channel timeout!\n", __func__);
return B_TIMED_OUT; return B_TIMED_OUT;
case 2: case 2:
ERROR("%s: dp_aux_ch flags not zero!\n", __func__); ERROR("%s: dp_aux channel flags not zero!\n", __func__);
return B_BUSY; return B_BUSY;
case 3: case 3:
ERROR("%s: dp_aux_ch error!\n", __func__); ERROR("%s: dp_aux channel error!\n", __func__);
return B_IO_ERROR; return B_IO_ERROR;
} }
@ -128,7 +128,7 @@ dp_aux_speak(uint32 connectorIndex, uint8* send, int sendBytes,
if (recv && recvBytes) if (recv && recvBytes)
memcpy(recv, base + 16, recvLength); memcpy(recv, base + 16, recvLength);
return B_OK; return recvLength;
} }
@ -141,8 +141,9 @@ dp_aux_transaction(uint32 connectorIndex, dp_aux_msg* message)
return B_ERROR; return B_ERROR;
} }
if (message->buffer == NULL) { if (message->size > 16) {
ERROR("%s: DP message uninitalized buffer!\n", __func__); ERROR("%s: Too many bytes! (%" B_PRIuSIZE ")\n", __func__,
message->size);
return B_ERROR; return B_ERROR;
} }
@ -151,16 +152,14 @@ dp_aux_transaction(uint32 connectorIndex, dp_aux_msg* message)
switch(message->request) { switch(message->request) {
case AUX_NATIVE_WRITE: case AUX_NATIVE_WRITE:
case AUX_I2C_WRITE: case AUX_I2C_WRITE:
if (message->buffer == NULL) {
ERROR("%s: DP message uninitalized buffer!\n", __func__);
return B_ERROR;
}
transactionSize += message->size; transactionSize += message->size;
break; break;
} }
if (transactionSize > 20) {
ERROR("%s: Too many bytes! (%" B_PRIu8 ")\n", __func__,
transactionSize);
return B_ERROR;
}
uint8 auxMessage[20]; uint8 auxMessage[20];
auxMessage[0] = message->address & 0xff; auxMessage[0] = message->address & 0xff;
auxMessage[1] = message->address >> 8; auxMessage[1] = message->address >> 8;
@ -175,11 +174,11 @@ dp_aux_transaction(uint32 connectorIndex, dp_aux_msg* message)
uint8 retry; uint8 retry;
for (retry = 0; retry < 7; retry++) { for (retry = 0; retry < 7; retry++) {
uint8 ack; uint8 ack;
status_t result = B_ERROR; ssize_t result = B_ERROR;
switch(message->request) { switch(message->request & ~AUX_I2C_MOT) {
case AUX_NATIVE_WRITE: case AUX_NATIVE_WRITE:
case AUX_I2C_WRITE: case AUX_I2C_WRITE:
memcpy(&auxMessage[4], message->buffer, message->size); memcpy(auxMessage + 4, message->buffer, message->size);
result = dp_aux_speak(connectorIndex, auxMessage, result = dp_aux_speak(connectorIndex, auxMessage,
transactionSize, NULL, 0, delay, &ack); transactionSize, NULL, 0, delay, &ack);
break; break;
@ -189,22 +188,32 @@ dp_aux_transaction(uint32 connectorIndex, dp_aux_msg* message)
transactionSize, (uint8*)message->buffer, message->size, transactionSize, (uint8*)message->buffer, message->size,
delay, &ack); delay, &ack);
break; break;
default:
ERROR("%s: Unknown dp_aux_msg request!\n", __func__);
return B_ERROR;
} }
TRACE("%s: dp_aux_speak result: %" B_PRIdSSIZE "\n", __func__, result);
if (result == B_BUSY) if (result == B_BUSY)
continue; continue;
else if (result != B_OK) else if (result < B_OK)
return result; return result;
ack >>= 4; ack >>= 4;
message->reply = ack; message->reply = ack;
if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) switch(message->reply & AUX_NATIVE_REPLY_MASK) {
return B_OK; case AUX_NATIVE_REPLY_ACK:
else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) { return B_OK;
TRACE("%s: aux reply defer received. Snoozing.\n", __func__); case AUX_NATIVE_REPLY_DEFER:
snooze(400); TRACE("%s: aux reply defer received. Snoozing.\n", __func__);
} else snooze(400);
return B_IO_ERROR; 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); ERROR("%s: IO Error. %" B_PRIu8 " attempts\n", __func__, retry);
@ -215,6 +224,9 @@ dp_aux_transaction(uint32 connectorIndex, dp_aux_msg* message)
void void
dpcd_reg_write(uint32 connectorIndex, uint16 address, uint8 value) dpcd_reg_write(uint32 connectorIndex, uint16 address, uint8 value)
{ {
TRACE("%s: connector(%" B_PRId32 "): 0x%" B_PRIx16 " -> 0x%" B_PRIx8 "\n",
__func__, connectorIndex, address, value);
dp_aux_msg message; dp_aux_msg message;
message.address = address; message.address = address;
message.buffer = &value; message.buffer = &value;
@ -233,13 +245,16 @@ dpcd_reg_write(uint32 connectorIndex, uint16 address, uint8 value)
uint8 uint8
dpcd_reg_read(uint32 connectorIndex, uint16 address) dpcd_reg_read(uint32 connectorIndex, uint16 address)
{ {
// TODO: Review dpcd response size response[3]? TRACE("%s: connector(%" B_PRId32 "): read 0x%" B_PRIx16 ".\n",
uint8 response; __func__, connectorIndex, address);
uint8 response[3];
dp_aux_msg message; dp_aux_msg message;
message.address = address; message.address = address;
message.request = AUX_NATIVE_READ;
message.buffer = &response; message.buffer = &response;
message.request = AUX_NATIVE_READ;
message.size = 1;
status_t result = dp_aux_transaction(connectorIndex, &message); status_t result = dp_aux_transaction(connectorIndex, &message);
if (result != B_OK) { if (result != B_OK) {
@ -249,7 +264,7 @@ dpcd_reg_read(uint32 connectorIndex, uint16 address)
TRACE("%s: aux message reply: 0x%" B_PRIx8 "\n", __func__, message.reply); TRACE("%s: aux message reply: 0x%" B_PRIx8 "\n", __func__, message.reply);
return response; return response[0];
} }
@ -257,77 +272,46 @@ status_t
dp_aux_get_i2c_byte(uint32 connectorIndex, uint16 address, uint8* data, dp_aux_get_i2c_byte(uint32 connectorIndex, uint16 address, uint8* data,
bool start, bool stop) bool start, bool stop)
{ {
// TODO: Leverage dp_aux_transaction uint8 reply[3];
uint8 auxMessage[5]; dp_aux_msg message;
int auxMessageBytes = 4; // 4 for read message.address = address;
message.buffer = reply;
/* Set up the command byte */ message.request = AUX_I2C_READ;
auxMessage[2] = AUX_I2C_READ << 4; message.size = 1;
if (stop == false) if (stop == false) {
auxMessage[2] |= AUX_I2C_MOT << 4; // Remove Middle-Of-Transmission on final transaction
message.request |= AUX_I2C_MOT;
auxMessage[0] = address; }
auxMessage[1] = address >> 8; if (stop || start) {
// Bare address packet
auxMessage[3] = auxMessageBytes << 4; message.buffer = NULL;
message.size = 0;
/* special case for sending the START or STOP */
if (start || stop) {
auxMessage[3] = 3 << 4;
auxMessageBytes = 4;
} }
int retry; for (int attempt = 0; attempt < 7; attempt++) {
for (retry = 0; retry < 4; retry++) { status_t result = dp_aux_transaction(connectorIndex, &message);
uint8 ack; if (result != B_OK) {
uint8 reply[2]; ERROR("%s: aux_ch transaction failed!\n", __func__);
int replyBytes = 1; return result;
status_t result = dp_aux_speak(connectorIndex, auxMessage,
auxMessageBytes, reply, replyBytes, 0, &ack);
if (result == B_BUSY)
continue;
else if (result != B_OK) {
ERROR("%s: aux_ch speak failed 0x%" B_PRIx32 "\n", __func__, result);
return B_ERROR;
} }
switch (ack & AUX_NATIVE_REPLY_MASK) { switch (message.reply & AUX_I2C_REPLY_MASK) {
case AUX_NATIVE_REPLY_ACK:
// I2C-over-AUX Reply field is only valid for AUX_ACK
break;
case AUX_NATIVE_REPLY_NACK:
TRACE("%s: aux_ch native nack\n", __func__);
return B_IO_ERROR;
case AUX_NATIVE_REPLY_DEFER:
TRACE("%s: aux_ch native defer\n", __func__);
snooze(400);
continue;
default:
TRACE("%s: aux_ch invalid native reply: 0x%02x\n",
__func__, ack);
return B_ERROR;
}
switch (ack & AUX_I2C_REPLY_MASK) {
case AUX_I2C_REPLY_ACK: case AUX_I2C_REPLY_ACK:
*data = reply[0]; *data = reply[0];
return B_OK; return B_OK;
case AUX_I2C_REPLY_NACK: case AUX_I2C_REPLY_NACK:
TRACE("%s: aux_i2c nack\n", __func__); TRACE("%s: aux i2c nack\n", __func__);
return B_IO_ERROR; return B_IO_ERROR;
case AUX_I2C_REPLY_DEFER: case AUX_I2C_REPLY_DEFER:
TRACE("%s: aux_i2c defer\n", __func__); TRACE("%s: aux i2c defer\n", __func__);
snooze(400); snooze(400);
break; break;
default: default:
TRACE("%s: aux_i2c invalid native reply: 0x%02x\n", TRACE("%s: aux invalid I2C reply: 0x%02x\n",
__func__, ack); __func__, message.reply);
return B_ERROR; return B_ERROR;
} }
} }
TRACE("%s: aux i2c too many retries, giving up.\n", __func__);
return B_ERROR; return B_ERROR;
} }
@ -336,79 +320,40 @@ status_t
dp_aux_set_i2c_byte(uint32 connectorIndex, uint16 address, uint8* data, dp_aux_set_i2c_byte(uint32 connectorIndex, uint16 address, uint8* data,
bool start, bool stop) bool start, bool stop)
{ {
// TODO: Leverage dp_aux_transaction dp_aux_msg message;
uint8 auxMessage[5]; message.address = address;
int auxMessageBytes = 5; // 5 for write message.buffer = data;
message.request = AUX_I2C_WRITE;
/* Set up the command byte */
auxMessage[2] = AUX_I2C_WRITE << 4;
if (stop == false) if (stop == false)
auxMessage[2] |= AUX_I2C_MOT << 4; message.request |= AUX_I2C_MOT;
message.size = 1;
if (stop || start)
message.size = 0;
auxMessage[0] = address; for (int attempt = 0; attempt < 7; attempt++) {
auxMessage[1] = address >> 8; status_t result = dp_aux_transaction(connectorIndex, &message);
if (result != B_OK) {
auxMessage[3] = auxMessageBytes << 4; ERROR("%s: aux_ch transaction failed!\n", __func__);
auxMessage[4] = *data; return result;
/* special case for sending the START or STOP */
if (start || stop) {
auxMessage[3] = 3 << 4;
auxMessageBytes = 4;
}
int retry;
for (retry = 0; retry < 4; retry++) {
uint8 ack;
uint8 reply[2];
int replyBytes = 1;
status_t result = dp_aux_speak(connectorIndex, auxMessage,
auxMessageBytes, reply, replyBytes, 0, &ack);
if (result == B_BUSY)
continue;
else if (result != B_OK) {
ERROR("%s: aux_ch speak failed 0x%" B_PRIx32 "\n", __func__, result);
return B_ERROR;
} }
switch (message.reply & AUX_I2C_REPLY_MASK) {
switch (ack & AUX_NATIVE_REPLY_MASK) {
case AUX_NATIVE_REPLY_ACK:
// I2C-over-AUX Reply field is only valid for AUX_ACK
break;
case AUX_NATIVE_REPLY_NACK:
TRACE("%s: aux_ch native nack\n", __func__);
return B_IO_ERROR;
case AUX_NATIVE_REPLY_DEFER:
TRACE("%s: aux_ch native defer\n", __func__);
snooze(400);
continue;
default:
TRACE("%s: aux_ch invalid native reply: 0x%02x\n",
__func__, ack);
return B_ERROR;
}
switch (ack & AUX_I2C_REPLY_MASK) {
case AUX_I2C_REPLY_ACK: case AUX_I2C_REPLY_ACK:
// Success!
return B_OK; return B_OK;
case AUX_I2C_REPLY_NACK: case AUX_I2C_REPLY_NACK:
TRACE("%s: aux_i2c nack\n", __func__); ERROR("%s: aux i2c nack\n", __func__);
return B_IO_ERROR; return B_IO_ERROR;
case AUX_I2C_REPLY_DEFER: case AUX_I2C_REPLY_DEFER:
TRACE("%s: aux_i2c defer\n", __func__); TRACE("%s: aux i2c defer\n", __func__);
snooze(400); snooze(400);
break; break;
default: default:
TRACE("%s: aux_i2c invalid native reply: 0x%02x\n", ERROR("%s: aux invalid I2C reply: 0x%02x\n", __func__,
__func__, ack); message.reply);
return B_ERROR; return B_IO_ERROR;
} }
} }
TRACE("%s: aux i2c too many retries, giving up.\n", __func__); return B_ERROR;
return B_OK;
} }
@ -1046,10 +991,10 @@ ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info* edid)
dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, true, 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, false);
dp_aux_get_i2c_byte(connectorIndex, 0x50, rdata, false, true); 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, true, false);
dp_aux_set_i2c_byte(connectorIndex, 0x50, &sdata, false, 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, true, false);
for (uint32 i = 0; i < sizeof(raw); i++) { for (uint32 i = 0; i < sizeof(raw); i++) {
status_t result = dp_aux_get_i2c_byte(connectorIndex, 0x50, status_t result = dp_aux_get_i2c_byte(connectorIndex, 0x50,
rdata++, false, false); rdata++, false, false);
@ -1062,6 +1007,9 @@ ddc2_dp_read_edid1(uint32 connectorIndex, edid1_info* edid)
} }
dp_aux_get_i2c_byte(connectorIndex, 0x50, &sdata, false, true); dp_aux_get_i2c_byte(connectorIndex, 0x50, &sdata, false, true);
TRACE("%s: EDID version %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
raw.version.version, raw.version.revision);
if (raw.version.version != 1 || raw.version.revision > 4) { if (raw.version.version != 1 || raw.version.revision > 4) {
ERROR("%s: EDID version or revision out of range\n", __func__); ERROR("%s: EDID version or revision out of range\n", __func__);
return false; return false;