root/src/add-ons/accelerants/intel_extreme/FlexibleDisplayInterface.cpp
/*
 * 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,
};


// #pragma mark - FDITransmitter


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) {
                // already enabled, possibly IronLake where it always is
                TRACE("%s: Already enabled.\n", __func__);
                return;
        }

        value &= ~FDI_DP_PORT_WIDTH_MASK;
        value |= FDI_DP_PORT_WIDTH(lanes);

        //first update config, -then- enable PLL to be sure config is indeed updated
        write32(targetRegister, value);
        read32(targetRegister);

        write32(targetRegister, value | FDI_TX_PLL_ENABLED);
        read32(targetRegister);
        spin(100); // warmup 10us + dmi delay 20us, be generous
}


void
FDITransmitter::DisablePLL()
{
        CALLED();
        if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_ILK)) {
                // on IronLake the FDI PLL is always enabled, so no point in trying...
                return;
        }

        uint32 targetRegister = FDI_TX_CTL(fPipeIndex);
        write32(targetRegister, read32(targetRegister) & ~FDI_TX_PLL_ENABLED);
        read32(targetRegister);
        spin(100);
}


// #pragma mark - FDIReceiver


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) {
                // already enabled, possibly IronLake where it always is
                TRACE("%s: Already enabled.\n", __func__);
                return;
        }

        //Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8)
        //currently using BIOS preconfigured setup
        //value &= ~FDI_DP_PORT_WIDTH_MASK;
        //value |= FDI_RX_LINK_BPC(INTEL_PIPE_8BPC);

        value &= ~FDI_DP_PORT_WIDTH_MASK;
        value |= FDI_DP_PORT_WIDTH(lanes);

        //first update config, -then- enable PLL to be sure config is indeed updated
        write32(targetRegister, value);
        read32(targetRegister);

        write32(targetRegister, value | FDI_RX_PLL_ENABLED);
        read32(targetRegister);
        spin(200); // warmup 10us + dmi delay 20us, be generous
}


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);
}


// #pragma mark - FDILink


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);

        //Link bit depth: this should be globally known per FDI link (i.e. laptop panel 3x6, rest 3x8)
        *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);

        // Khz / 10. ( each output octet encoded as 10 bits.
        *linkBandwidth = gInfo->shared_info->fdi_link_frequency * 1000 / 10;
        //Reserving 5% bandwidth for possible spread spectrum clock use
        uint32 bps = target->pixel_clock * *bitsPerPixel * 21 / 20;

        //use DIV_ROUND_UP:
        *lanes = (bps + (*linkBandwidth * 8) - 1) / (*linkBandwidth * 8);
        //remove below line when link training is to be done
        *lanes = ((read32(txControl) & FDI_DP_PORT_WIDTH_MASK) >> FDI_DP_PORT_WIDTH_SHIFT) + 1;

        TRACE("%s: FDI Link Lanes: %" B_PRIu32 "\n", __func__, *lanes);
        //assuming we'll only use link A and B (not C)
        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
        //when link training is to be done re-enable this code

        //The order of handling things is important here..
        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);

        // Disable FDI clocks
        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);

        //Set receiving end TU size bits to match sending end's setting
        write32(FDI_RX_TUSIZE1(fPipeIndex), FDI_RX_TRANS_UNIT_MASK);
        write32(FDI_RX_TUSIZE2(fPipeIndex), FDI_RX_TRANS_UNIT_MASK);

#if 0
        //when link training is to be done re-enable this code
        // Enable FDI clocks
        Receiver().EnablePLL(lanes);
        Receiver().SwitchClock(true);
        Transmitter().EnablePLL(lanes);

        // TODO: Only _AutoTrain on IVYB Stepping B or later
        // otherwise, _ManualTrain
        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);

        // Enable normal link training
        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);

        // Wait 1x idle pattern
        read32(rxControl);
        spin(1000);

        // Enable ecc on IVB
        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);

        // Train 1: unmask FDI RX Interrupt symbol_lock and bit_lock
        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);

        // Enable CPU FDI TX and RX
        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();

        // ILK Workaround, enable clk after FDI 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;
        }

        // Train 2
        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);

        // Train 1
        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;
        }

        // Train 2
        tmp = read32(txControl);
        tmp &= ~FDI_LINK_TRAIN_NONE;
        tmp |= FDI_LINK_TRAIN_PATTERN_2;

        // if gen6? It's always gen6
        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();
        //uint32 txControl = Transmitter().Base() + PCH_FDI_TX_CONTROL;
        //uint32 rxControl = Receiver().Base() + PCH_FDI_RX_CONTROL;

        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);

        // Clear port width selection and set number of lanes
        // fixme: does not belong in the train routines (?), (now) sits in FDI EnablePLL() routines
        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);//looks like datasheet specified 5uS is not enough..?

                        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, we fall out of autotraining
                if (trained)
                        break;
        }

        if (!trained) {
                ERROR("%s: FDI auto train failed!\n", __func__);
                return B_ERROR;
        }

        // Enable ecc on IVB (and disable test pattern at sending and receiving end)
        if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_IVB)) {
                write32(rxControl, read32(rxControl)
                        | FDI_FS_ERRC_ENABLE | FDI_FE_ERRC_ENABLE);
                read32(rxControl);
                //enable normal pixels (kill testpattern)
                write32(txControl, read32(txControl) | (0x3 << 8));
                read32(txControl);
        }

        return B_OK;
}


FDILink::~FDILink()
{
}