* Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz, mmlr@mlotz.ch
* Alexander von Gluck IV, kallisti5@unixzen.com
*/
#include "FlexibleDisplayInterface.h"
#include <stdlib.h>
#include <string.h>
#include <Debug.h>
#include <KernelExport.h>
#include "accelerant.h"
#include "intel_extreme.h"
#undef TRACE
#define TRACE_FDI
#ifdef TRACE_FDI
# define TRACE(x...) _sPrintf("intel_extreme: " x)
#else
# define TRACE(x...)
#endif
#define ERROR(x...) _sPrintf("intel_extreme: " x)
#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
static const int gSnbBFDITrainParam[] = {
FDI_LINK_TRAIN_400MV_0DB_SNB_B,
FDI_LINK_TRAIN_400MV_6DB_SNB_B,
FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
FDI_LINK_TRAIN_800MV_0DB_SNB_B,
};
FDITransmitter::FDITransmitter(pipe_index pipeIndex)
:
fPipeIndex(pipeIndex)
{
}
FDITransmitter::~FDITransmitter()
{
}
void
FDITransmitter::Enable()
{
CALLED();
uint32 targetRegister = FDI_TX_CTL(fPipeIndex);
uint32 value = read32(targetRegister);
write32(targetRegister, value | FDI_TX_ENABLE);
read32(targetRegister);
spin(150);
}
void
FDITransmitter::Disable()
{
CALLED();
uint32 targetRegister = FDI_TX_CTL(fPipeIndex);
uint32 value = read32(targetRegister);
write32(targetRegister, value & ~FDI_TX_ENABLE);
read32(targetRegister);
spin(150);
}
bool
FDITransmitter::IsPLLEnabled()
{
CALLED();
return (read32(FDI_TX_CTL(fPipeIndex)) & FDI_TX_PLL_ENABLED) != 0;
}
void
FDITransmitter::EnablePLL(uint32 lanes)
{
CALLED();
uint32 targetRegister = FDI_TX_CTL(fPipeIndex);
uint32 value = read32(targetRegister);
if ((value & FDI_TX_PLL_ENABLED) != 0) {
TRACE("%s: Already enabled.\n", __func__);
return;
}
value &= ~FDI_DP_PORT_WIDTH_MASK;
value |= FDI_DP_PORT_WIDTH(lanes);
write32(targetRegister, value);
read32(targetRegister);
write32(targetRegister, value | FDI_TX_PLL_ENABLED);
read32(targetRegister);
spin(100);
}
void
FDITransmitter::DisablePLL()
{
CALLED();
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_ILK)) {
return;
}
uint32 targetRegister = FDI_TX_CTL(fPipeIndex);
write32(targetRegister, read32(targetRegister) & ~FDI_TX_PLL_ENABLED);
read32(targetRegister);
spin(100);
}
FDIReceiver::FDIReceiver(pipe_index pipeIndex)
:
fPipeIndex(pipeIndex)
{
}
FDIReceiver::~FDIReceiver()
{
}
void
FDIReceiver::Enable()
{
CALLED();
uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
uint32 value = read32(targetRegister);
write32(targetRegister, value | FDI_RX_ENABLE);
read32(targetRegister);
spin(150);
}
void
FDIReceiver::Disable()
{
CALLED();
uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
uint32 value = read32(targetRegister);
write32(targetRegister, value & ~FDI_RX_ENABLE);
read32(targetRegister);
spin(150);
}
bool
FDIReceiver::IsPLLEnabled()
{
CALLED();
return (read32(FDI_RX_CTL(fPipeIndex)) & FDI_RX_PLL_ENABLED) != 0;
}
void
FDIReceiver::EnablePLL(uint32 lanes)
{
CALLED();
uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
uint32 value = read32(targetRegister);
if ((value & FDI_RX_PLL_ENABLED) != 0) {
TRACE("%s: Already enabled.\n", __func__);
return;
}
value &= ~FDI_DP_PORT_WIDTH_MASK;
value |= FDI_DP_PORT_WIDTH(lanes);
write32(targetRegister, value);
read32(targetRegister);
write32(targetRegister, value | FDI_RX_PLL_ENABLED);
read32(targetRegister);
spin(200);
}
void
FDIReceiver::DisablePLL()
{
CALLED();
uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
write32(targetRegister, read32(targetRegister) & ~FDI_RX_PLL_ENABLED);
read32(targetRegister);
spin(100);
}
void
FDIReceiver::SwitchClock(bool toPCDClock)
{
CALLED();
uint32 targetRegister = FDI_RX_CTL(fPipeIndex);
write32(targetRegister, (read32(targetRegister) & ~FDI_RX_CLOCK_MASK)
| (toPCDClock ? FDI_RX_CLOCK_PCD : FDI_RX_CLOCK_RAW));
read32(targetRegister);
spin(200);
}
FDILink::FDILink(pipe_index pipeIndex)
:
fTransmitter(pipeIndex),
fReceiver(pipeIndex),
fPipeIndex(pipeIndex)
{
}
status_t
FDILink::PreTrain(display_timing* target, uint32* linkBandwidth, uint32* lanes, uint32* bitsPerPixel)
{
CALLED();
uint32 txControl = FDI_TX_CTL(fPipeIndex);
uint32 rxControl = FDI_RX_CTL(fPipeIndex);
*bitsPerPixel = ((read32(rxControl) & FDI_RX_LINK_BPC_MASK) >> FDI_RX_LINK_COLOR_SHIFT);
switch (*bitsPerPixel) {
case INTEL_PIPE_8BPC:
*bitsPerPixel = 24;
break;
case INTEL_PIPE_10BPC:
*bitsPerPixel = 30;
break;
case INTEL_PIPE_6BPC:
*bitsPerPixel = 18;
break;
case INTEL_PIPE_12BPC:
*bitsPerPixel = 36;
break;
default:
*bitsPerPixel = 0;
ERROR("%s: FDI illegal link colordepth set.\n", __func__);
return B_ERROR;
}
TRACE("%s: FDI Link %s:\n", __func__, (fPipeIndex == INTEL_PIPE_A) ? "A" : "B");
TRACE("%s: FDI Link Colordepth: %" B_PRIu32 "\n", __func__, *bitsPerPixel);
*linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
uint32 bps = target->pixel_clock * *bitsPerPixel * 21 / 20;
*lanes = (bps + (*linkBandwidth * 8) - 1) / (*linkBandwidth * 8);
*lanes = ((read32(txControl) & FDI_DP_PORT_WIDTH_MASK) >> FDI_DP_PORT_WIDTH_SHIFT) + 1;
TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, *lanes);
if (*lanes > 4) {
ERROR("%s: FDI not enough lanes in hardware.\n", __func__);
return B_ERROR;
}
TRACE("%s: FDI TX ctrl before: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
TRACE("%s: FDI RX ctrl before: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
#if 0
write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
read32(txControl);
write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
read32(rxControl);
write32(txControl, (read32(txControl) & ~FDI_LINK_TRAIN_NONE) | FDI_LINK_TRAIN_PATTERN_1);
read32(txControl);
if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
write32(rxControl, (read32(rxControl) & ~FDI_LINK_TRAIN_PATTERN_MASK_CPT) | FDI_LINK_TRAIN_PATTERN_1_CPT);
} else {
write32(rxControl, (read32(rxControl) & ~FDI_LINK_TRAIN_NONE) | FDI_LINK_TRAIN_PATTERN_1);
}
read32(rxControl);
spin(100);
Receiver().SwitchClock(false);
Transmitter().DisablePLL();
Receiver().DisablePLL();
#endif
return B_OK;
}
status_t
FDILink::Train(display_timing* target, uint32 lanes)
{
CALLED();
status_t result = B_OK;
uint32 txControl = FDI_TX_CTL(fPipeIndex);
uint32 rxControl = FDI_RX_CTL(fPipeIndex);
write32(FDI_RX_TUSIZE1(fPipeIndex), FDI_RX_TRANS_UNIT_MASK);
write32(FDI_RX_TUSIZE2(fPipeIndex), FDI_RX_TRANS_UNIT_MASK);
#if 0
Receiver().EnablePLL(lanes);
Receiver().SwitchClock(true);
Transmitter().EnablePLL(lanes);
if (gInfo->shared_info->device_type.Generation() >= 7)
result = _AutoTrain(lanes);
else if (gInfo->shared_info->device_type.Generation() == 6)
result = _SnbTrain(lanes);
else if (gInfo->shared_info->device_type.Generation() == 5)
result = _IlkTrain(lanes);
else
result = _NormalTrain(lanes);
#endif
TRACE("%s: FDI TX ctrl after: 0x%" B_PRIx32 "\n", __func__, read32(txControl));
TRACE("%s: FDI RX ctrl after: 0x%" B_PRIx32 "\n", __func__, read32(rxControl));
if (result != B_OK)
ERROR("%s: FDI training fault.\n", __func__);
return result;
}
status_t
FDILink::_NormalTrain(uint32 lanes)
{
CALLED();
uint32 txControl = FDI_TX_CTL(fPipeIndex);
uint32 rxControl = FDI_RX_CTL(fPipeIndex);
uint32 tmp = read32(txControl);
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
tmp &= ~FDI_LINK_TRAIN_NONE_IVB;
tmp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE;
} else {
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
}
write32(txControl, tmp);
tmp = read32(rxControl);
if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
tmp |= FDI_LINK_TRAIN_NORMAL_CPT;
} else {
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_NONE;
}
write32(rxControl, tmp | FDI_RX_ENHANCE_FRAME_ENABLE);
read32(rxControl);
spin(1000);
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
write32(rxControl, read32(rxControl)
| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
read32(rxControl);
}
return B_OK;
}
status_t
FDILink::_IlkTrain(uint32 lanes)
{
CALLED();
uint32 txControl = FDI_TX_CTL(fPipeIndex);
uint32 rxControl = FDI_RX_CTL(fPipeIndex);
uint32 tmp = read32(FDI_RX_IMR(fPipeIndex));
tmp &= ~FDI_RX_SYMBOL_LOCK;
tmp &= ~FDI_RX_BIT_LOCK;
write32(FDI_RX_IMR(fPipeIndex), tmp);
spin(150);
tmp = read32(txControl);
tmp &= ~FDI_DP_PORT_WIDTH_MASK;
tmp |= FDI_DP_PORT_WIDTH(lanes);
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_PATTERN_1;
write32(txControl, tmp);
Transmitter().Enable();
tmp = read32(rxControl);
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_PATTERN_1;
write32(rxControl, tmp);
Receiver().Enable();
if (fPipeIndex == INTEL_PIPE_B) {
write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR);
write32(PCH_FDI_RXB_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR
| FDI_RX_PHASE_SYNC_POINTER_EN);
} else {
write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR);
write32(PCH_FDI_RXA_CHICKEN, FDI_RX_PHASE_SYNC_POINTER_OVR
| FDI_RX_PHASE_SYNC_POINTER_EN);
}
uint32 iirControl = FDI_RX_IIR(fPipeIndex);
TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl);
int tries = 0;
for (tries = 0; tries < 5; tries++) {
tmp = read32(iirControl);
TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
if ((tmp & FDI_RX_BIT_LOCK)) {
TRACE("%s: FDI train 1 done\n", __func__);
write32(iirControl, tmp | FDI_RX_BIT_LOCK);
break;
}
}
if (tries == 5) {
ERROR("%s: FDI train 1 failure!\n", __func__);
return B_ERROR;
}
tmp = read32(txControl);
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_PATTERN_2;
write32(txControl, tmp);
tmp = read32(rxControl);
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_PATTERN_2;
write32(rxControl, tmp);
read32(rxControl);
spin(150);
for (tries = 0; tries < 5; tries++) {
tmp = read32(iirControl);
TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
if (tmp & FDI_RX_SYMBOL_LOCK) {
TRACE("%s: FDI train 2 done\n", __func__);
write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK);
break;
}
}
if (tries == 5) {
ERROR("%s: FDI train 2 failure!\n", __func__);
return B_ERROR;
}
return B_OK;
}
status_t
FDILink::_SnbTrain(uint32 lanes)
{
CALLED();
uint32 txControl = FDI_TX_CTL(fPipeIndex);
uint32 rxControl = FDI_RX_CTL(fPipeIndex);
uint32 imrControl = FDI_RX_IMR(fPipeIndex);
uint32 tmp = read32(imrControl);
tmp &= ~FDI_RX_SYMBOL_LOCK;
tmp &= ~FDI_RX_BIT_LOCK;
write32(imrControl, tmp);
read32(imrControl);
spin(150);
tmp = read32(txControl);
tmp &= ~FDI_DP_PORT_WIDTH_MASK;
tmp |= FDI_DP_PORT_WIDTH(lanes);
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_PATTERN_1;
tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
write32(txControl, tmp);
write32(FDI_RX_MISC(fPipeIndex),
FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
tmp = read32(rxControl);
if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
tmp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
} else {
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_PATTERN_1;
}
write32(rxControl, tmp);
Receiver().Enable();
uint32 iirControl = FDI_RX_IIR(fPipeIndex);
TRACE("%s: FDI RX IIR Control @ 0x%" B_PRIx32 "\n", __func__, iirControl);
int i = 0;
for (i = 0; i < 4; i++) {
tmp = read32(txControl);
tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
tmp |= gSnbBFDITrainParam[i];
write32(txControl, tmp);
read32(txControl);
spin(500);
int retry = 0;
for (retry = 0; retry < 5; retry++) {
tmp = read32(iirControl);
TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
if (tmp & FDI_RX_BIT_LOCK) {
TRACE("%s: FDI train 1 done\n", __func__);
write32(iirControl, tmp | FDI_RX_BIT_LOCK);
break;
}
spin(50);
}
if (retry < 5)
break;
}
if (i == 4) {
ERROR("%s: FDI train 1 failure!\n", __func__);
return B_ERROR;
}
tmp = read32(txControl);
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_PATTERN_2;
tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
tmp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
write32(txControl, tmp);
tmp = read32(rxControl);
if (gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
tmp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
tmp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
} else {
tmp &= ~FDI_LINK_TRAIN_NONE;
tmp |= FDI_LINK_TRAIN_PATTERN_2;
}
write32(rxControl, tmp);
read32(rxControl);
spin(150);
for (i = 0; i < 4; i++) {
tmp = read32(txControl);
tmp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
tmp |= gSnbBFDITrainParam[i];
write32(txControl, tmp);
read32(txControl);
spin(500);
int retry = 0;
for (retry = 0; retry < 5; retry++) {
tmp = read32(iirControl);
TRACE("%s: FDI RX IIR 0x%" B_PRIx32 "\n", __func__, tmp);
if (tmp & FDI_RX_SYMBOL_LOCK) {
TRACE("%s: FDI train 2 done\n", __func__);
write32(iirControl, tmp | FDI_RX_SYMBOL_LOCK);
break;
}
spin(50);
}
if (retry < 5)
break;
}
if (i == 4) {
ERROR("%s: FDI train 1 failure!\n", __func__);
return B_ERROR;
}
return B_OK;
}
status_t
FDILink::_ManualTrain(uint32 lanes)
{
CALLED();
ERROR("%s: TODO\n", __func__);
return B_ERROR;
}
status_t
FDILink::_AutoTrain(uint32 lanes)
{
CALLED();
uint32 txControl = FDI_TX_CTL(fPipeIndex);
uint32 rxControl = FDI_RX_CTL(fPipeIndex);
uint32 buffer = read32(txControl);
buffer &= ~(7 << 19);
buffer |= (lanes - 1) << 19;
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB))
buffer &= ~FDI_LINK_TRAIN_NONE_IVB;
else
buffer &= ~FDI_LINK_TRAIN_NONE;
write32(txControl, buffer);
write32(FDI_RX_MISC(fPipeIndex), FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
bool trained = false;
for (uint32 i = 0; i < (sizeof(gSnbBFDITrainParam)
/ sizeof(gSnbBFDITrainParam[0])); i++) {
for (int j = 0; j < 2; j++) {
buffer = read32(txControl);
buffer |= FDI_AUTO_TRAINING;
buffer &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
buffer |= gSnbBFDITrainParam[i];
write32(txControl, buffer | FDI_TX_ENABLE);
read32(txControl);
write32(rxControl, read32(rxControl) | FDI_RX_ENABLE);
read32(rxControl);
spin(50);
buffer = read32(txControl);
if ((buffer & FDI_AUTO_TRAIN_DONE) != 0) {
TRACE("%s: FDI auto train complete!\n", __func__);
trained = true;
break;
}
write32(txControl, read32(txControl) & ~FDI_TX_ENABLE);
read32(txControl);
write32(rxControl, read32(rxControl) & ~FDI_RX_ENABLE);
read32(rxControl);
spin(31);
}
if (trained)
break;
}
if (!trained) {
ERROR("%s: FDI auto train failed!\n", __func__);
return B_ERROR;
}
if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
write32(rxControl, read32(rxControl)
| FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
read32(rxControl);
write32(txControl, read32(txControl) | (0x3 << 8));
read32(txControl);
}
return B_OK;
}
FDILink::~FDILink()
{
}