Logo Search packages:      
Sourcecode: xserver-xorg-video-ati version File versions  Download package

atombios_output.c

/*
 * Copyright © 2007 Red Hat, Inc.
 * Copyright 2007  Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Authors:
 *    Dave Airlie <airlied@redhat.com>
 *    Alex Deucher <alexdeucher@gmail.com>
 *
 */

/*
 * avivo output handling functions.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* DPMS */
#ifdef HAVE_XEXTPROTO_71
#include <X11/extensions/dpmsconst.h>
#else
#define DPMS_SERVER
#include <X11/extensions/dpms.h>
#endif

#include <unistd.h>

#include "radeon.h"
#include "radeon_reg.h"
#include "radeon_macros.h"
#include "radeon_atombios.h"

#include "ati_pciids_gen.h"

const char *device_name[12] = {
    "CRT1",
    "LCD1",
    "TV1",
    "DFP1",
    "CRT2",
    "LCD2",
    "TV2",
    "DFP2",
    "CV",
    "DFP3",
    "DFP4",
    "DFP5",
};

static void do_displayport_link_train(xf86OutputPtr output);

static void atombios_set_output_crtc_source(xf86OutputPtr output);

static int
atombios_output_dac_setup(xf86OutputPtr output, int action)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
    radeon_tvout_ptr tvout = &radeon_output->tvout;
    DAC_ENCODER_CONTROL_PS_ALLOCATION disp_data;
    AtomBiosArgRec data;
    unsigned char *space;
    int index = 0, num = 0;
    int clock = radeon_output->pixel_clock;

    if (radeon_encoder == NULL)
      return ATOM_NOT_IMPLEMENTED;

    memset(&disp_data,0, sizeof(disp_data));

    switch (radeon_encoder->encoder_id) {
    case ENCODER_OBJECT_ID_INTERNAL_DAC1:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
      index = GetIndexIntoMasterTable(COMMAND, DAC1EncoderControl);
      num = 1;
      break;
    case ENCODER_OBJECT_ID_INTERNAL_DAC2:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
      index = GetIndexIntoMasterTable(COMMAND, DAC2EncoderControl);
      num = 2;
      break;
    }

    disp_data.ucAction =action;

    if (radeon_output->active_device & (ATOM_DEVICE_CRT_SUPPORT))
      disp_data.ucDacStandard = ATOM_DAC1_PS2;
    else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
      disp_data.ucDacStandard = ATOM_DAC1_CV;
    else {
      switch (tvout->tvStd) {
      case TV_STD_PAL:
      case TV_STD_PAL_M:
      case TV_STD_SCART_PAL:
      case TV_STD_SECAM:
      case TV_STD_PAL_CN:
          disp_data.ucDacStandard = ATOM_DAC1_PAL;
          break;
      case TV_STD_NTSC:
      case TV_STD_NTSC_J:
      case TV_STD_PAL_60:
      default:
          disp_data.ucDacStandard = ATOM_DAC1_NTSC;
          break;
      }
    }
    disp_data.usPixelClock = cpu_to_le16(clock / 10);

    data.exec.index = index;
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &disp_data;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("Output DAC%d setup success\n", num);
      return ATOM_SUCCESS;
    }

    ErrorF("Output DAC%d setup failed\n", num);
    return ATOM_NOT_IMPLEMENTED;

}

static int
atombios_output_tv_setup(xf86OutputPtr output, int action)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    radeon_tvout_ptr tvout = &radeon_output->tvout;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    TV_ENCODER_CONTROL_PS_ALLOCATION disp_data;
    AtomBiosArgRec data;
    unsigned char *space;
    int clock = radeon_output->pixel_clock;

    memset(&disp_data,0, sizeof(disp_data));

    disp_data.sTVEncoder.ucAction = action;

    if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
      disp_data.sTVEncoder.ucTvStandard = ATOM_TV_CV;
    else {
      switch (tvout->tvStd) {
      case TV_STD_NTSC:
          disp_data.sTVEncoder.ucTvStandard = ATOM_TV_NTSC;
          break;
      case TV_STD_PAL:
          disp_data.sTVEncoder.ucTvStandard = ATOM_TV_PAL;
          break;
      case TV_STD_PAL_M:
          disp_data.sTVEncoder.ucTvStandard = ATOM_TV_PALM;
          break;
      case TV_STD_PAL_60:
          disp_data.sTVEncoder.ucTvStandard = ATOM_TV_PAL60;
          break;
      case TV_STD_NTSC_J:
          disp_data.sTVEncoder.ucTvStandard = ATOM_TV_NTSCJ;
          break;
      case TV_STD_SCART_PAL:
          disp_data.sTVEncoder.ucTvStandard = ATOM_TV_PAL; /* ??? */
          break;
      case TV_STD_SECAM:
          disp_data.sTVEncoder.ucTvStandard = ATOM_TV_SECAM;
          break;
      case TV_STD_PAL_CN:
          disp_data.sTVEncoder.ucTvStandard = ATOM_TV_PALCN;
          break;
      default:
          disp_data.sTVEncoder.ucTvStandard = ATOM_TV_NTSC;
          break;
      }
    }

    disp_data.sTVEncoder.usPixelClock = cpu_to_le16(clock / 10);
    data.exec.index = GetIndexIntoMasterTable(COMMAND, TVEncoderControl);
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &disp_data;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("Output TV setup success\n");
      return ATOM_SUCCESS;
    }

    ErrorF("Output TV setup failed\n");
    return ATOM_NOT_IMPLEMENTED;

}

int
atombios_external_tmds_setup(xf86OutputPtr output, int action)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    ScrnInfoPtr pScrn = output->scrn;
    RADEONInfoPtr info       = RADEONPTR(pScrn);
    ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION disp_data;
    AtomBiosArgRec data;
    unsigned char *space;
    int clock = radeon_output->pixel_clock;

    memset(&disp_data,0, sizeof(disp_data));

    disp_data.sXTmdsEncoder.ucEnable = action;

    if (clock > 165000)
      disp_data.sXTmdsEncoder.ucMisc = PANEL_ENCODER_MISC_DUAL;

    if (pScrn->rgbBits == 8)
      disp_data.sXTmdsEncoder.ucMisc |= (1 << 1);

    data.exec.index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl);
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &disp_data;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("External TMDS setup success\n");
      return ATOM_SUCCESS;
    }

    ErrorF("External TMDS setup failed\n");
    return ATOM_NOT_IMPLEMENTED;
}

static int
atombios_output_ddia_setup(xf86OutputPtr output, int action)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    DVO_ENCODER_CONTROL_PS_ALLOCATION disp_data;
    AtomBiosArgRec data;
    unsigned char *space;
    int clock = radeon_output->pixel_clock;

    memset(&disp_data,0, sizeof(disp_data));

    disp_data.sDVOEncoder.ucAction = action;
    disp_data.sDVOEncoder.usPixelClock = cpu_to_le16(clock / 10);

    if (clock > 165000)
      disp_data.sDVOEncoder.usDevAttr.sDigAttrib.ucAttribute = PANEL_ENCODER_MISC_DUAL;

    data.exec.index = GetIndexIntoMasterTable(COMMAND, DVOEncoderControl);
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &disp_data;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("DDIA setup success\n");
      return ATOM_SUCCESS;
    }

    ErrorF("DDIA setup failed\n");
    return ATOM_NOT_IMPLEMENTED;
}

static int
atombios_output_digital_setup(xf86OutputPtr output, int action)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    ScrnInfoPtr pScrn = output->scrn;
    RADEONInfoPtr info       = RADEONPTR(pScrn);
    radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
    LVDS_ENCODER_CONTROL_PS_ALLOCATION disp_data;
    LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 disp_data2;
    AtomBiosArgRec data;
    unsigned char *space;
    int index = 0;
    int major, minor;
    int lvds_misc = 0;
    int clock = radeon_output->pixel_clock;

    if (radeon_encoder == NULL)
      return ATOM_NOT_IMPLEMENTED;

    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
      radeon_lvds_ptr lvds = (radeon_lvds_ptr)radeon_encoder->dev_priv;
      if (lvds == NULL)
          return ATOM_NOT_IMPLEMENTED;
      lvds_misc = lvds->lvds_misc;
    }

    memset(&disp_data,0, sizeof(disp_data));
    memset(&disp_data2,0, sizeof(disp_data2));

    switch (radeon_encoder->encoder_id) {
    case ENCODER_OBJECT_ID_INTERNAL_LVDS:
      index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
      index = GetIndexIntoMasterTable(COMMAND, TMDS1EncoderControl);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
      if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
          index = GetIndexIntoMasterTable(COMMAND, LVDSEncoderControl);
      else
          index = GetIndexIntoMasterTable(COMMAND, TMDS2EncoderControl);
      break;
    }

    atombios_get_command_table_version(info->atomBIOS, index, &major, &minor);

    /*ErrorF("table is %d %d\n", major, minor);*/
    switch (major) {
    case 0:
    case 1:
    case 2:
      switch (minor) {
      case 1:
          disp_data.ucMisc = 0;
          disp_data.ucAction = action;
          if ((radeon_output->ConnectorType == CONNECTOR_HDMI_TYPE_A) ||
            (radeon_output->ConnectorType == CONNECTOR_HDMI_TYPE_B))
            disp_data.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
          disp_data.usPixelClock = cpu_to_le16(clock / 10);
          if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
            if (lvds_misc & (1 << 0))
                disp_data.ucMisc |= PANEL_ENCODER_MISC_DUAL;
            if (lvds_misc & (1 << 1))
                disp_data.ucMisc |= (1 << 1);
          } else {
            if (radeon_output->linkb)
                disp_data.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB;
            if (clock > 165000)
                disp_data.ucMisc |= PANEL_ENCODER_MISC_DUAL;
            if (pScrn->rgbBits == 8)
                disp_data.ucMisc |= (1 << 1);
          }
          data.exec.pspace = &disp_data;
          break;
      case 2:
      case 3:
          disp_data2.ucMisc = 0;
          disp_data2.ucAction = action;
          if (minor == 3) {
            if (radeon_output->coherent_mode) {
                disp_data2.ucMisc |= PANEL_ENCODER_MISC_COHERENT;
                xf86DrvMsg(output->scrn->scrnIndex, X_INFO, "Coherent Mode enabled\n");
            }
          }
          if ((radeon_output->ConnectorType == CONNECTOR_HDMI_TYPE_A) ||
            (radeon_output->ConnectorType == CONNECTOR_HDMI_TYPE_B))
            disp_data2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
          disp_data2.usPixelClock = cpu_to_le16(clock / 10);
          disp_data2.ucTruncate = 0;
          disp_data2.ucSpatial = 0;
          disp_data2.ucTemporal = 0;
          disp_data2.ucFRC = 0;
          if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
            if (lvds_misc & (1 << 0))
                disp_data2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
            if (lvds_misc & (1 << 5)) {
                disp_data2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN;
                if (lvds_misc & (1 << 1))
                  disp_data2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH;
            }
            if (lvds_misc & (1 << 6)) {
                disp_data2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN;
                if (lvds_misc & (1 << 1))
                  disp_data2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH;
                if (((lvds_misc >> 2) & 0x3) == 2)
                  disp_data2.ucTemporal |= PANEL_ENCODER_TEMPORAL_LEVEL_4;
            }
          } else {
            if (radeon_output->linkb)
                disp_data2.ucMisc |= PANEL_ENCODER_MISC_TMDS_LINKB;
            if (clock > 165000)
                disp_data2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
          }
          data.exec.pspace = &disp_data2;
          break;
      default:
          ErrorF("Unknown table version\n");
          exit(-1);
      }
      break;
    default:
      ErrorF("Unknown table version\n");
      exit(-1);
    }

    data.exec.index = index;
    data.exec.dataSpace = (void *)&space;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("Output digital setup success\n");
      return ATOM_SUCCESS;
    }

    ErrorF("Output digital setup failed\n");
    return ATOM_NOT_IMPLEMENTED;
}

static int
atombios_maybe_hdmi_mode(xf86OutputPtr output)
{
#ifndef EDID_COMPLETE_RAWDATA
    /* there's no getting this right unless we have complete EDID */
    return ATOM_ENCODER_MODE_HDMI;
#else
    if (output && xf86MonitorIsHDMI(output->MonInfo))
      return ATOM_ENCODER_MODE_HDMI;

    return ATOM_ENCODER_MODE_DVI;
#endif
}

int
atombios_get_encoder_mode(xf86OutputPtr output)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;

    /* DVI should really be atombios_maybe_hdmi_mode() as well */
    switch (radeon_output->ConnectorType) {
    case CONNECTOR_DVI_I:
      if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT))
          return ATOM_ENCODER_MODE_DVI;
      else
          return ATOM_ENCODER_MODE_CRT;
      break;
    case CONNECTOR_DVI_D:
    default:
      return ATOM_ENCODER_MODE_DVI;
      break;
    case CONNECTOR_HDMI_TYPE_A:
    case CONNECTOR_HDMI_TYPE_B:
      return atombios_maybe_hdmi_mode(output);
      break;
    case CONNECTOR_LVDS:
      return ATOM_ENCODER_MODE_LVDS;
      break;
    case CONNECTOR_DISPLAY_PORT:
    case CONNECTOR_EDP:
      if (radeon_output->MonType == MT_DP)
          return ATOM_ENCODER_MODE_DP;
      else
          return atombios_maybe_hdmi_mode(output);
      break;
    case CONNECTOR_DVI_A:
    case CONNECTOR_VGA:
    case CONNECTOR_STV:
    case CONNECTOR_CTV:
    case CONNECTOR_DIN:
      if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT))
          return ATOM_ENCODER_MODE_TV;
      else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
          return ATOM_ENCODER_MODE_CV;
      else
          return ATOM_ENCODER_MODE_CRT;
      break;
    }

}

static const int dp_clocks[] = {
    5400,  // 1 lane, 1.62 Ghz
    9000,  // 1 lane, 2.70 Ghz
    10800, // 2 lane, 1.62 Ghz
    18000, // 2 lane, 2.70 Ghz
    21600, // 4 lane, 1.62 Ghz
    36000, // 4 lane, 2.70 Ghz
};
static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int);

# define DP_LINK_BW_1_62                    0x06
# define DP_LINK_BW_2_7                     0x0a

static int
dp_lanes_for_mode_clock(RADEONOutputPrivatePtr radeon_output,
                  int mode_clock)
{
    int i;
    int max_link_bw = radeon_output->dpcd[1];

    switch (max_link_bw) {
    case DP_LINK_BW_1_62:
    default:
      for (i = 0; i < num_dp_clocks; i++) {
          if (i % 2)
            continue;
          if (dp_clocks[i] > (mode_clock / 10)) {
            if (i < 2)
                return 1;
            else if (i < 4)
                return 2;
            else
                return 4;
          }
      }
      break;
    case DP_LINK_BW_2_7:
      for (i = 0; i < num_dp_clocks; i++) {
          if (dp_clocks[i] > (mode_clock / 10)) {
            if (i < 2)
                return 1;
            else if (i < 4)
                return 2;
            else
                return 4;
          }
      }
        break;
    }

    return 0;
}

static int
dp_link_clock_for_mode_clock(RADEONOutputPrivatePtr radeon_output,
                       int mode_clock)
{
    int i;
    int max_link_bw = radeon_output->dpcd[1];

    switch (max_link_bw) {
    case DP_LINK_BW_1_62:
    default:
      return 16200;
      break;
    case DP_LINK_BW_2_7:
      for (i = 0; i < num_dp_clocks; i++)
          if (dp_clocks[i] > (mode_clock / 10))
            return (i % 2) ? 27000 : 16200;
        break;
    }

    return 0;
}

/*
 * DIG Encoder/Transmitter Setup
 *
 * DCE 3.0/3.1
 * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA.
 * Supports up to 3 digital outputs
 * - 2 DIG encoder blocks.
 * DIG1 can drive UNIPHY link A or link B
 * DIG2 can drive UNIPHY link B or LVTMA
 *
 * DCE 3.2
 * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B).
 * Supports up to 5 digital outputs
 * - 2 DIG encoder blocks.
 * DIG1/2 can drive UNIPHY0/1/2 link A or link B
 *
 * Routing
 * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links)
 * Examples:
 * crtc0 -> dig2 -> LVTMA links A+B
 * crtc1 -> dig1 -> UNIPHY0 link B
 */
static int
atombios_output_dig_encoder_setup(xf86OutputPtr output, int action)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
    DIG_ENCODER_CONTROL_PS_ALLOCATION disp_data;
    AtomBiosArgRec data;
    unsigned char *space;
    int index = 0, major, minor;
    int clock = radeon_output->pixel_clock;

    if (radeon_encoder == NULL)
      return ATOM_NOT_IMPLEMENTED;

    memset(&disp_data,0, sizeof(disp_data));

    if (radeon_output->dig_encoder)
        index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
    else
        index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);

    atombios_get_command_table_version(info->atomBIOS, index, &major, &minor);

    disp_data.ucAction = action;
    disp_data.usPixelClock = cpu_to_le16(clock / 10);

    if (IS_DCE32_VARIANT) {
      switch (radeon_encoder->encoder_id) {
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
          disp_data.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1;
          break;
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
          disp_data.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2;
          break;
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
          disp_data.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3;
          break;
      }
    } else {
      switch (radeon_encoder->encoder_id) {
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
          disp_data.ucConfig = ATOM_ENCODER_CONFIG_UNIPHY;
          break;
      case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
          disp_data.ucConfig = ATOM_ENCODER_CONFIG_LVTMA;
          break;
      }
    }

    disp_data.ucEncoderMode = atombios_get_encoder_mode(output);

    if (disp_data.ucEncoderMode == ATOM_ENCODER_MODE_DP) {
      if (dp_link_clock_for_mode_clock(radeon_output, clock) == 27000)
          disp_data.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
      disp_data.ucLaneNum = dp_lanes_for_mode_clock(radeon_output, clock);
    } else if (clock > 165000)
      disp_data.ucLaneNum = 8;
    else
      disp_data.ucLaneNum = 4;

    if (radeon_output->linkb)
      disp_data.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
    else
      disp_data.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;

    data.exec.index = index;
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &disp_data;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("Output DIG%d encoder setup success\n", radeon_output->dig_encoder);
      return ATOM_SUCCESS;
    }

    ErrorF("Output DIG%d setup failed\n", radeon_output->dig_encoder);
    return ATOM_NOT_IMPLEMENTED;

}

static int
atombios_dce4_output_dig_encoder_setup(xf86OutputPtr output, int action)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
    DIG_ENCODER_CONTROL_PARAMETERS_V3 disp_data;
    AtomBiosArgRec data;
    unsigned char *space;
    int index;
    int clock = radeon_output->pixel_clock;

    if (radeon_encoder == NULL)
      return ATOM_NOT_IMPLEMENTED;

    memset(&disp_data,0, sizeof(disp_data));

    index = GetIndexIntoMasterTable(COMMAND, DIGxEncoderControl);

    disp_data.ucAction = action;
    disp_data.usPixelClock = cpu_to_le16(clock / 10);
    disp_data.ucEncoderMode = atombios_get_encoder_mode(output);
    disp_data.acConfig.ucDigSel = radeon_output->dig_encoder;

    if (disp_data.ucEncoderMode == ATOM_ENCODER_MODE_DP) {
      if (dp_link_clock_for_mode_clock(radeon_output, clock) == 27000)
          disp_data.acConfig.ucDPLinkRate = 1;
      disp_data.ucLaneNum = dp_lanes_for_mode_clock(radeon_output, clock);
    } else if (clock > 165000)
      disp_data.ucLaneNum = 8;
    else
      disp_data.ucLaneNum = 4;

    disp_data.ucBitPerColor = PANEL_8BIT_PER_COLOR;

    data.exec.index = index;
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &disp_data;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("Output DIG%d encoder setup success\n", radeon_output->dig_encoder);
      return ATOM_SUCCESS;
    }

    ErrorF("Output DIG%d setup failed\n", radeon_output->dig_encoder);
    return ATOM_NOT_IMPLEMENTED;
}

union dig_transmitter_control {
    DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1;
    DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2;
    DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 v3;
};

static int
atombios_output_dig_transmitter_setup(xf86OutputPtr output, int action, uint8_t lane_num, uint8_t lane_set)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
    union dig_transmitter_control disp_data;
    AtomBiosArgRec data;
    unsigned char *space;
    int index = 0, num = 0;
    int major, minor;
    int clock = radeon_output->pixel_clock;

    if (radeon_encoder == NULL)
        return ATOM_NOT_IMPLEMENTED;

    memset(&disp_data,0, sizeof(disp_data));

    if (IS_DCE32_VARIANT || IS_DCE4_VARIANT)
      index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl);
    else {
      switch (radeon_encoder->encoder_id) {
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
          index = GetIndexIntoMasterTable(COMMAND, DIG1TransmitterControl);
          break;
      case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
          index = GetIndexIntoMasterTable(COMMAND, DIG2TransmitterControl);
          break;
      }
    }

    atombios_get_command_table_version(info->atomBIOS, index, &major, &minor);

    disp_data.v1.ucAction = action;

    if (IS_DCE4_VARIANT) {
      if (action == ATOM_TRANSMITTER_ACTION_INIT) {
          disp_data.v3.usInitInfo = radeon_output->connector_object_id;
      } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
          disp_data.v3.asMode.ucLaneSel = lane_num;
          disp_data.v3.asMode.ucLaneSet = lane_set;
      } else {
          if (radeon_output->MonType == MT_DP) {
            disp_data.v3.usPixelClock =
                cpu_to_le16(dp_link_clock_for_mode_clock(radeon_output, clock));
          } else if (clock > 165000) {
            disp_data.v3.usPixelClock = cpu_to_le16((clock / 2) / 10);
            disp_data.v3.acConfig.fDualLinkConnector = 1;
          } else {
            disp_data.v3.usPixelClock = cpu_to_le16(clock / 10);
          }
      }

      if (radeon_output->MonType == MT_DP)
          disp_data.v3.ucLaneNum = dp_lanes_for_mode_clock(radeon_output, clock);
      else if (clock > 165000)
          disp_data.v3.ucLaneNum = 8;
      else
          disp_data.v3.ucLaneNum = 4;

      if (radeon_output->linkb)
          disp_data.v3.acConfig.ucLinkSel = 1;

      //if (radeon_output->dig_encoder)
      // disp_data.v2.acConfig.ucEncoderSel = 1;

      // select the PLL
      disp_data.v3.acConfig.ucRefClkSource = radeon_output->pll_id;

      switch (radeon_encoder->encoder_id) {
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
          disp_data.v3.acConfig.ucTransmitterSel = 0;
          num = 0;
          break;
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
          disp_data.v3.acConfig.ucTransmitterSel = 1;
          num = 1;
          break;
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
          disp_data.v3.acConfig.ucTransmitterSel = 2;
          num = 2;
          break;
      }

      if (radeon_output->MonType == MT_DP)
          disp_data.v3.acConfig.fCoherentMode = 1; /* DP requires coherent */
      else if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT)) {
          if (radeon_output->coherent_mode)
            disp_data.v3.acConfig.fCoherentMode = 1;
      }
    } else if (IS_DCE32_VARIANT) {
      if (action == ATOM_TRANSMITTER_ACTION_INIT) {
          disp_data.v2.usInitInfo = radeon_output->connector_object_id;
      } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
          disp_data.v2.asMode.ucLaneSel = lane_num;
          disp_data.v2.asMode.ucLaneSet = lane_set;
      } else {
          if (radeon_output->MonType == MT_DP) {
            disp_data.v2.usPixelClock =
                cpu_to_le16(dp_link_clock_for_mode_clock(radeon_output, clock));
            disp_data.v2.acConfig.fDPConnector = 1;
          } else if (clock > 165000) {
            disp_data.v2.usPixelClock = cpu_to_le16((clock / 2) / 10);
            disp_data.v2.acConfig.fDualLinkConnector = 1;
          } else {
            disp_data.v2.usPixelClock = cpu_to_le16(clock / 10);
          }
      }
      if (radeon_output->dig_encoder)
          disp_data.v2.acConfig.ucEncoderSel = 1;

      if (radeon_output->linkb)
          disp_data.v2.acConfig.ucLinkSel = 1;

      switch (radeon_encoder->encoder_id) {
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
          disp_data.v2.acConfig.ucTransmitterSel = 0;
          num = 0;
          break;
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
          disp_data.v2.acConfig.ucTransmitterSel = 1;
          num = 1;
          break;
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
          disp_data.v2.acConfig.ucTransmitterSel = 2;
          num = 2;
          break;
      }

      if (radeon_output->MonType == MT_DP)
          disp_data.v2.acConfig.fCoherentMode = 1; /* DP requires coherent */
      else if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT)) {
          if (radeon_output->coherent_mode)
            disp_data.v2.acConfig.fCoherentMode = 1;
      }
    } else {
      disp_data.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL;

      if (action == ATOM_TRANSMITTER_ACTION_INIT) {
          disp_data.v1.usInitInfo = radeon_output->connector_object_id;
      } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
          disp_data.v1.asMode.ucLaneSel = lane_num;
          disp_data.v1.asMode.ucLaneSet = lane_set;
      } else {
          if (radeon_output->MonType == MT_DP)
            disp_data.v1.usPixelClock =
                cpu_to_le16(dp_link_clock_for_mode_clock(radeon_output, clock));
          else if (clock > 165000)
            disp_data.v1.usPixelClock = cpu_to_le16((clock / 2) / 10);
          else
            disp_data.v1.usPixelClock = cpu_to_le16(clock / 10);
      }

      if (radeon_output->dig_encoder)
          disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
      else
          disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;

      switch (radeon_encoder->encoder_id) {
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
          if (info->IsIGP) {
            if (clock > 165000) {
                if (radeon_output->igp_lane_info & 0x3)
                  disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7;
                else if (radeon_output->igp_lane_info & 0xc)
                  disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15;
            } else {
                if (radeon_output->igp_lane_info & 0x1)
                  disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3;
                else if (radeon_output->igp_lane_info & 0x2)
                  disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_4_7;
                else if (radeon_output->igp_lane_info & 0x4)
                  disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_11;
                else if (radeon_output->igp_lane_info & 0x8)
                  disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15;
            }
          }
          break;
      }
      if (clock > 165000)
          disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK;
      if (radeon_output->linkb)
          disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB;
      else
          disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;

      if (radeon_output->MonType == MT_DP)
          disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;  /* DP requires coherent */
      else if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT)) {
          if (radeon_output->coherent_mode)
            disp_data.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
      }
    }

    data.exec.index = index;
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &disp_data;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      if (IS_DCE32_VARIANT)
          ErrorF("Output UNIPHY%d transmitter setup success\n", num);
      else
         ErrorF("Output DIG%d transmitter setup success\n", num);
      return ATOM_SUCCESS;
    }

    ErrorF("Output DIG%d transmitter setup failed\n", num);
    return ATOM_NOT_IMPLEMENTED;

}

static void atom_rv515_force_tv_scaler(ScrnInfoPtr pScrn, RADEONCrtcPrivatePtr radeon_crtc)
{
    RADEONInfoPtr info       = RADEONPTR(pScrn);
    unsigned char *RADEONMMIO = info->MMIO;
    int index_reg = 0x6578, data_reg = 0x657c;

    index_reg += radeon_crtc->crtc_offset;
    data_reg += radeon_crtc->crtc_offset;

    OUTREG(0x659C + radeon_crtc->crtc_offset, 0x0);
    OUTREG(0x6594 + radeon_crtc->crtc_offset, 0x705);
    OUTREG(0x65A4 + radeon_crtc->crtc_offset, 0x10001);
    OUTREG(0x65D8 + radeon_crtc->crtc_offset, 0x0);
    OUTREG(0x65B0 + radeon_crtc->crtc_offset, 0x0);
    OUTREG(0x65C0 + radeon_crtc->crtc_offset, 0x0);
    OUTREG(0x65D4 + radeon_crtc->crtc_offset, 0x0);
    OUTREG(index_reg,0x0);
    OUTREG(data_reg,0x841880A8);
    OUTREG(index_reg,0x1);
    OUTREG(data_reg,0x84208680);
    OUTREG(index_reg,0x2);
    OUTREG(data_reg,0xBFF880B0);
    OUTREG(index_reg,0x100);
    OUTREG(data_reg,0x83D88088);
    OUTREG(index_reg,0x101);
    OUTREG(data_reg,0x84608680);
    OUTREG(index_reg,0x102);
    OUTREG(data_reg,0xBFF080D0);
    OUTREG(index_reg,0x200);
    OUTREG(data_reg,0x83988068);
    OUTREG(index_reg,0x201);
    OUTREG(data_reg,0x84A08680);
    OUTREG(index_reg,0x202);
    OUTREG(data_reg,0xBFF080F8);
    OUTREG(index_reg,0x300);
    OUTREG(data_reg,0x83588058);
    OUTREG(index_reg,0x301);
    OUTREG(data_reg,0x84E08660);
    OUTREG(index_reg,0x302);
    OUTREG(data_reg,0xBFF88120);
    OUTREG(index_reg,0x400);
    OUTREG(data_reg,0x83188040);
    OUTREG(index_reg,0x401);
    OUTREG(data_reg,0x85008660);
    OUTREG(index_reg,0x402);
    OUTREG(data_reg,0xBFF88150);
    OUTREG(index_reg,0x500);
    OUTREG(data_reg,0x82D88030);
    OUTREG(index_reg,0x501);
    OUTREG(data_reg,0x85408640);
    OUTREG(index_reg,0x502);
    OUTREG(data_reg,0xBFF88180);
    OUTREG(index_reg,0x600);
    OUTREG(data_reg,0x82A08018);
    OUTREG(index_reg,0x601);
    OUTREG(data_reg,0x85808620);
    OUTREG(index_reg,0x602);
    OUTREG(data_reg,0xBFF081B8);
    OUTREG(index_reg,0x700);
    OUTREG(data_reg,0x82608010);
    OUTREG(index_reg,0x701);
    OUTREG(data_reg,0x85A08600);
    OUTREG(index_reg,0x702);
    OUTREG(data_reg,0x800081F0);
    OUTREG(index_reg,0x800);
    OUTREG(data_reg,0x8228BFF8);
    OUTREG(index_reg,0x801);
    OUTREG(data_reg,0x85E085E0);
    OUTREG(index_reg,0x802);
    OUTREG(data_reg,0xBFF88228);
    OUTREG(index_reg,0x10000);
    OUTREG(data_reg,0x82A8BF00);
    OUTREG(index_reg,0x10001);
    OUTREG(data_reg,0x82A08CC0);
    OUTREG(index_reg,0x10002);
    OUTREG(data_reg,0x8008BEF8);
    OUTREG(index_reg,0x10100);
    OUTREG(data_reg,0x81F0BF28);
    OUTREG(index_reg,0x10101);
    OUTREG(data_reg,0x83608CA0);
    OUTREG(index_reg,0x10102);
    OUTREG(data_reg,0x8018BED0);
    OUTREG(index_reg,0x10200);
    OUTREG(data_reg,0x8148BF38);
    OUTREG(index_reg,0x10201);
    OUTREG(data_reg,0x84408C80);
    OUTREG(index_reg,0x10202);
    OUTREG(data_reg,0x8008BEB8);
    OUTREG(index_reg,0x10300);
    OUTREG(data_reg,0x80B0BF78);
    OUTREG(index_reg,0x10301);
    OUTREG(data_reg,0x85008C20);
    OUTREG(index_reg,0x10302);
    OUTREG(data_reg,0x8020BEA0);
    OUTREG(index_reg,0x10400);
    OUTREG(data_reg,0x8028BF90);
    OUTREG(index_reg,0x10401);
    OUTREG(data_reg,0x85E08BC0);
    OUTREG(index_reg,0x10402);
    OUTREG(data_reg,0x8018BE90);
    OUTREG(index_reg,0x10500);
    OUTREG(data_reg,0xBFB8BFB0);
    OUTREG(index_reg,0x10501);
    OUTREG(data_reg,0x86C08B40);
    OUTREG(index_reg,0x10502);
    OUTREG(data_reg,0x8010BE90);
    OUTREG(index_reg,0x10600);
    OUTREG(data_reg,0xBF58BFC8);
    OUTREG(index_reg,0x10601);
    OUTREG(data_reg,0x87A08AA0);
    OUTREG(index_reg,0x10602);
    OUTREG(data_reg,0x8010BE98);
    OUTREG(index_reg,0x10700);
    OUTREG(data_reg,0xBF10BFF0);
    OUTREG(index_reg,0x10701);
    OUTREG(data_reg,0x886089E0);
    OUTREG(index_reg,0x10702);
    OUTREG(data_reg,0x8018BEB0);
    OUTREG(index_reg,0x10800);
    OUTREG(data_reg,0xBED8BFE8);
    OUTREG(index_reg,0x10801);
    OUTREG(data_reg,0x89408940);
    OUTREG(index_reg,0x10802);
    OUTREG(data_reg,0xBFE8BED8);
    OUTREG(index_reg,0x20000);
    OUTREG(data_reg,0x80008000);
    OUTREG(index_reg,0x20001);
    OUTREG(data_reg,0x90008000);
    OUTREG(index_reg,0x20002);
    OUTREG(data_reg,0x80008000);
    OUTREG(index_reg,0x20003);
    OUTREG(data_reg,0x80008000);
    OUTREG(index_reg,0x20100);
    OUTREG(data_reg,0x80108000);
    OUTREG(index_reg,0x20101);
    OUTREG(data_reg,0x8FE0BF70);
    OUTREG(index_reg,0x20102);
    OUTREG(data_reg,0xBFE880C0);
    OUTREG(index_reg,0x20103);
    OUTREG(data_reg,0x80008000);
    OUTREG(index_reg,0x20200);
    OUTREG(data_reg,0x8018BFF8);
    OUTREG(index_reg,0x20201);
    OUTREG(data_reg,0x8F80BF08);
    OUTREG(index_reg,0x20202);
    OUTREG(data_reg,0xBFD081A0);
    OUTREG(index_reg,0x20203);
    OUTREG(data_reg,0xBFF88000);
    OUTREG(index_reg,0x20300);
    OUTREG(data_reg,0x80188000);
    OUTREG(index_reg,0x20301);
    OUTREG(data_reg,0x8EE0BEC0);
    OUTREG(index_reg,0x20302);
    OUTREG(data_reg,0xBFB082A0);
    OUTREG(index_reg,0x20303);
    OUTREG(data_reg,0x80008000);
    OUTREG(index_reg,0x20400);
    OUTREG(data_reg,0x80188000);
    OUTREG(index_reg,0x20401);
    OUTREG(data_reg,0x8E00BEA0);
    OUTREG(index_reg,0x20402);
    OUTREG(data_reg,0xBF8883C0);
    OUTREG(index_reg,0x20403);
    OUTREG(data_reg,0x80008000);
    OUTREG(index_reg,0x20500);
    OUTREG(data_reg,0x80188000);
    OUTREG(index_reg,0x20501);
    OUTREG(data_reg,0x8D00BE90);
    OUTREG(index_reg,0x20502);
    OUTREG(data_reg,0xBF588500);
    OUTREG(index_reg,0x20503);
    OUTREG(data_reg,0x80008008);
    OUTREG(index_reg,0x20600);
    OUTREG(data_reg,0x80188000);
    OUTREG(index_reg,0x20601);
    OUTREG(data_reg,0x8BC0BE98);
    OUTREG(index_reg,0x20602);
    OUTREG(data_reg,0xBF308660);
    OUTREG(index_reg,0x20603);
    OUTREG(data_reg,0x80008008);
    OUTREG(index_reg,0x20700);
    OUTREG(data_reg,0x80108000);
    OUTREG(index_reg,0x20701);
    OUTREG(data_reg,0x8A80BEB0);
    OUTREG(index_reg,0x20702);
    OUTREG(data_reg,0xBF0087C0);
    OUTREG(index_reg,0x20703);
    OUTREG(data_reg,0x80008008);
    OUTREG(index_reg,0x20800);
    OUTREG(data_reg,0x80108000);
    OUTREG(index_reg,0x20801);
    OUTREG(data_reg,0x8920BED0);
    OUTREG(index_reg,0x20802);
    OUTREG(data_reg,0xBED08920);
    OUTREG(index_reg,0x20803);
    OUTREG(data_reg,0x80008010);
    OUTREG(index_reg,0x30000);
    OUTREG(data_reg,0x90008000);
    OUTREG(index_reg,0x30001);
    OUTREG(data_reg,0x80008000);
    OUTREG(index_reg,0x30100);
    OUTREG(data_reg,0x8FE0BF90);
    OUTREG(index_reg,0x30101);
    OUTREG(data_reg,0xBFF880A0);
    OUTREG(index_reg,0x30200);
    OUTREG(data_reg,0x8F60BF40);
    OUTREG(index_reg,0x30201);
    OUTREG(data_reg,0xBFE88180);
    OUTREG(index_reg,0x30300);
    OUTREG(data_reg,0x8EC0BF00);
    OUTREG(index_reg,0x30301);
    OUTREG(data_reg,0xBFC88280);
    OUTREG(index_reg,0x30400);
    OUTREG(data_reg,0x8DE0BEE0);
    OUTREG(index_reg,0x30401);
    OUTREG(data_reg,0xBFA083A0);
    OUTREG(index_reg,0x30500);
    OUTREG(data_reg,0x8CE0BED0);
    OUTREG(index_reg,0x30501);
    OUTREG(data_reg,0xBF7884E0);
    OUTREG(index_reg,0x30600);
    OUTREG(data_reg,0x8BA0BED8);
    OUTREG(index_reg,0x30601);
    OUTREG(data_reg,0xBF508640);
    OUTREG(index_reg,0x30700);
    OUTREG(data_reg,0x8A60BEE8);
    OUTREG(index_reg,0x30701);
    OUTREG(data_reg,0xBF2087A0);
    OUTREG(index_reg,0x30800);
    OUTREG(data_reg,0x8900BF00);
    OUTREG(index_reg,0x30801);
    OUTREG(data_reg,0xBF008900);
}

static int
atombios_output_yuv_setup(xf86OutputPtr output, Bool enable)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    RADEONCrtcPrivatePtr radeon_crtc = output->crtc->driver_private;
    ENABLE_YUV_PS_ALLOCATION disp_data;
    AtomBiosArgRec data;
    unsigned char *space;
    unsigned char *RADEONMMIO = info->MMIO;
    uint32_t temp, reg;

    if (info->ChipFamily >= CHIP_FAMILY_R600)
      reg = R600_BIOS_3_SCRATCH;
    else
      reg = RADEON_BIOS_3_SCRATCH;

    //fix up scratch reg handling
    temp = INREG(reg);
    if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT))
      OUTREG(reg, (ATOM_S3_TV1_ACTIVE |
                 (radeon_crtc->crtc_id << 18)));
    else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
      OUTREG(reg, (ATOM_S3_CV_ACTIVE |
                 (radeon_crtc->crtc_id << 24)));
    else
      OUTREG(reg, 0);

    memset(&disp_data, 0, sizeof(disp_data));

    if (enable)
      disp_data.ucEnable = ATOM_ENABLE;
    disp_data.ucCRTC = radeon_crtc->crtc_id;

    data.exec.index = GetIndexIntoMasterTable(COMMAND, EnableYUV);
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &disp_data;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {

      OUTREG(reg, temp);

      ErrorF("crtc %d YUV %s setup success\n", radeon_crtc->crtc_id, enable ? "enable" : "disable");
      return ATOM_SUCCESS;
    }

    OUTREG(reg, temp);

    ErrorF("crtc %d YUV %s setup failed\n", radeon_crtc->crtc_id, enable ? "enable" : "disable");
    return ATOM_NOT_IMPLEMENTED;

}

static int
atombios_output_overscan_setup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONCrtcPrivatePtr radeon_crtc = output->crtc->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    SET_CRTC_OVERSCAN_PS_ALLOCATION overscan_param;
    AtomBiosArgRec data;
    unsigned char *space;
    memset(&overscan_param, 0, sizeof(overscan_param));

    overscan_param.usOverscanRight = 0;
    overscan_param.usOverscanLeft = 0;
    overscan_param.usOverscanBottom = 0;
    overscan_param.usOverscanTop = 0;
    overscan_param.ucCRTC = radeon_crtc->crtc_id;

    if (radeon_output->Flags & RADEON_USE_RMX) {
      if (radeon_output->rmx_type == RMX_FULL) {
          overscan_param.usOverscanRight = 0;
          overscan_param.usOverscanLeft = 0;
          overscan_param.usOverscanBottom = 0;
          overscan_param.usOverscanTop = 0;
      } else if (radeon_output->rmx_type == RMX_CENTER) {
          overscan_param.usOverscanTop = (adjusted_mode->CrtcVDisplay - mode->CrtcVDisplay) / 2;
          overscan_param.usOverscanBottom = (adjusted_mode->CrtcVDisplay - mode->CrtcVDisplay) / 2;
          overscan_param.usOverscanLeft = (adjusted_mode->CrtcHDisplay - mode->CrtcHDisplay) / 2;
          overscan_param.usOverscanRight = (adjusted_mode->CrtcHDisplay - mode->CrtcHDisplay) / 2;
      } else if (radeon_output->rmx_type == RMX_ASPECT) {
          int a1 = mode->CrtcVDisplay * adjusted_mode->CrtcHDisplay;
          int a2 = adjusted_mode->CrtcVDisplay * mode->CrtcHDisplay;

          if (a1 > a2) {
            overscan_param.usOverscanLeft = (adjusted_mode->CrtcHDisplay - (a2 / mode->CrtcVDisplay)) / 2;
            overscan_param.usOverscanRight = (adjusted_mode->CrtcHDisplay - (a2 / mode->CrtcVDisplay)) / 2;
          } else if (a2 > a1) {
            overscan_param.usOverscanLeft = (adjusted_mode->CrtcVDisplay - (a1 / mode->CrtcHDisplay)) / 2;
            overscan_param.usOverscanRight = (adjusted_mode->CrtcVDisplay - (a1 / mode->CrtcHDisplay)) / 2;
          }
      }
    }

    data.exec.index = GetIndexIntoMasterTable(COMMAND, SetCRTC_OverScan);
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &overscan_param;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("Set CRTC %d Overscan success\n", radeon_crtc->crtc_id);
      return ATOM_SUCCESS ;
    }

    ErrorF("Set CRTC %d Overscan failed\n", radeon_crtc->crtc_id);
    return ATOM_NOT_IMPLEMENTED;
}

static int
atombios_output_scaler_setup(xf86OutputPtr output)
{
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    radeon_tvout_ptr tvout = &radeon_output->tvout;
    RADEONCrtcPrivatePtr radeon_crtc = output->crtc->driver_private;
    ENABLE_SCALER_PS_ALLOCATION disp_data;
    AtomBiosArgRec data;
    unsigned char *space;

    if (!IS_AVIVO_VARIANT && radeon_crtc->crtc_id)
      return ATOM_SUCCESS;

    memset(&disp_data, 0, sizeof(disp_data));

    disp_data.ucScaler = radeon_crtc->crtc_id;

    if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
      switch (tvout->tvStd) {
      case TV_STD_NTSC:
          disp_data.ucTVStandard = ATOM_TV_NTSC;
          break;
      case TV_STD_PAL:
          disp_data.ucTVStandard = ATOM_TV_PAL;
          break;
      case TV_STD_PAL_M:
          disp_data.ucTVStandard = ATOM_TV_PALM;
          break;
      case TV_STD_PAL_60:
          disp_data.ucTVStandard = ATOM_TV_PAL60;
          break;
      case TV_STD_NTSC_J:
          disp_data.ucTVStandard = ATOM_TV_NTSCJ;
          break;
      case TV_STD_SCART_PAL:
          disp_data.ucTVStandard = ATOM_TV_PAL; /* ??? */
          break;
      case TV_STD_SECAM:
          disp_data.ucTVStandard = ATOM_TV_SECAM;
          break;
      case TV_STD_PAL_CN:
          disp_data.ucTVStandard = ATOM_TV_PALCN;
          break;
      default:
          disp_data.ucTVStandard = ATOM_TV_NTSC;
          break;
      }
      disp_data.ucEnable = SCALER_ENABLE_MULTITAP_MODE;
        ErrorF("Using TV scaler %x %x\n", disp_data.ucTVStandard, disp_data.ucEnable);
    } else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) {
      disp_data.ucTVStandard = ATOM_TV_CV;
      disp_data.ucEnable = SCALER_ENABLE_MULTITAP_MODE;
        ErrorF("Using CV scaler %x %x\n", disp_data.ucTVStandard, disp_data.ucEnable);
    } else if (radeon_output->Flags & RADEON_USE_RMX) {
      ErrorF("Using RMX\n");
      if (radeon_output->rmx_type == RMX_FULL)
          disp_data.ucEnable = ATOM_SCALER_EXPANSION;
      else if (radeon_output->rmx_type == RMX_CENTER)
          disp_data.ucEnable = ATOM_SCALER_CENTER;
      else if (radeon_output->rmx_type == RMX_ASPECT)
          disp_data.ucEnable = ATOM_SCALER_EXPANSION;
    } else {
      ErrorF("Not using RMX\n");
      if (IS_AVIVO_VARIANT)
          disp_data.ucEnable = ATOM_SCALER_DISABLE;
      else
          disp_data.ucEnable = ATOM_SCALER_CENTER;
    }

    data.exec.index = GetIndexIntoMasterTable(COMMAND, EnableScaler);
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &disp_data;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT)
          && info->ChipFamily >= CHIP_FAMILY_RV515 && info->ChipFamily <= CHIP_FAMILY_RV570) {
          ErrorF("forcing TV scaler\n");
          atom_rv515_force_tv_scaler(output->scrn, radeon_crtc);
      }
      ErrorF("scaler %d setup success\n", radeon_crtc->crtc_id);
      return ATOM_SUCCESS;
    }

    ErrorF("scaler %d setup failed\n", radeon_crtc->crtc_id);
    return ATOM_NOT_IMPLEMENTED;

}

void
atombios_output_dpms(xf86OutputPtr output, int mode)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION disp_data;
    AtomBiosArgRec data;
    unsigned char *space;
    int index = 0;
    Bool is_dig = FALSE;

    if (radeon_encoder == NULL)
        return;

    switch (radeon_encoder->encoder_id) {
    case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
      index = GetIndexIntoMasterTable(COMMAND, TMDSAOutputControl);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
    case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
    case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
      is_dig = TRUE;
      break;
    case ENCODER_OBJECT_ID_INTERNAL_DVO1:
    case ENCODER_OBJECT_ID_INTERNAL_DDI:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
      index = GetIndexIntoMasterTable(COMMAND, DVOOutputControl);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_LVDS:
      index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
      if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
          index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl);
      else
          index = GetIndexIntoMasterTable(COMMAND, LVTMAOutputControl);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_DAC1:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
      if (IS_DCE32_VARIANT)
          index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl);
      else {
          if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT))
            index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl);
          else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
            index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl);
          else
            index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl);
      }
      break;
    case ENCODER_OBJECT_ID_INTERNAL_DAC2:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
      if (IS_DCE32_VARIANT)
          index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl);
      else {
          if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT))
            index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl);
          else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
            index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl);
          else
            index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl);
      }
      break;
    }

    switch (mode) {
    case DPMSModeOn:
      radeon_encoder->devices |= radeon_output->active_device;
      if (is_dig) {
          atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
          if (((radeon_output->ConnectorType == CONNECTOR_DISPLAY_PORT) ||
             (radeon_output->ConnectorType == CONNECTOR_EDP)) &&
            (radeon_output->MonType == MT_DP)) {
            do_displayport_link_train(output);
          }
      }
      else {
          disp_data.ucAction = ATOM_ENABLE;
          data.exec.index = index;
          data.exec.dataSpace = (void *)&space;
          data.exec.pspace = &disp_data;

          if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS)
            ErrorF("Output %s enable success\n",
                   device_name[radeon_get_device_index(radeon_output->active_device)]);
          else
            ErrorF("Output %s enable failed\n",
                   device_name[radeon_get_device_index(radeon_output->active_device)]);
      }
      /* at least for TV atom fails to reassociate the correct crtc source at dpms on */
      if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT))
            atombios_set_output_crtc_source(output);
      break;
    case DPMSModeStandby:
    case DPMSModeSuspend:
    case DPMSModeOff:
      radeon_encoder->devices &= ~(radeon_output->active_device);
      if (!radeon_encoder->devices) {
          if (is_dig)
            atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
          else {
            disp_data.ucAction = ATOM_DISABLE;
            data.exec.index = index;
            data.exec.dataSpace = (void *)&space;
            data.exec.pspace = &disp_data;

            if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data)
                == ATOM_SUCCESS)
                ErrorF("Output %s disable success\n",
                     device_name[radeon_get_device_index(radeon_output->active_device)]);
            else
                ErrorF("Output %s disable failed\n",
                     device_name[radeon_get_device_index(radeon_output->active_device)]);
          }
      }
      break;
    }
}

static void
atombios_set_output_crtc_source(xf86OutputPtr output)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONCrtcPrivatePtr radeon_crtc = output->crtc->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
    AtomBiosArgRec data;
    unsigned char *space;
    SELECT_CRTC_SOURCE_PS_ALLOCATION crtc_src_param;
    SELECT_CRTC_SOURCE_PARAMETERS_V2 crtc_src_param2;
    int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source);
    int major, minor;

    if (radeon_encoder == NULL)
      return;

    memset(&crtc_src_param, 0, sizeof(crtc_src_param));
    memset(&crtc_src_param2, 0, sizeof(crtc_src_param2));
    atombios_get_command_table_version(info->atomBIOS, index, &major, &minor);

    /*ErrorF("select crtc source table is %d %d\n", major, minor);*/

    switch(major) {
    case 1:
      switch(minor) {
      case 0:
      case 1:
      default:
          if (IS_AVIVO_VARIANT)
            crtc_src_param.ucCRTC = radeon_crtc->crtc_id;
          else {
            if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1)
                crtc_src_param.ucCRTC = radeon_crtc->crtc_id;
            else
                crtc_src_param.ucCRTC = radeon_crtc->crtc_id << 2;
          }
          switch (radeon_encoder->encoder_id) {
          case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
          case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
            crtc_src_param.ucDevice = ATOM_DEVICE_DFP1_INDEX;
            break;
          case ENCODER_OBJECT_ID_INTERNAL_LVDS:
          case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
            if (radeon_output->active_device & ATOM_DEVICE_LCD1_SUPPORT)
                crtc_src_param.ucDevice = ATOM_DEVICE_LCD1_INDEX;
            else
                crtc_src_param.ucDevice = ATOM_DEVICE_DFP3_INDEX;
            break;
          case ENCODER_OBJECT_ID_INTERNAL_DVO1:
          case ENCODER_OBJECT_ID_INTERNAL_DDI:
          case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
            crtc_src_param.ucDevice = ATOM_DEVICE_DFP2_INDEX;
            break;
          case ENCODER_OBJECT_ID_INTERNAL_DAC1:
          case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
            if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT))
                crtc_src_param.ucDevice = ATOM_DEVICE_TV1_INDEX;
            else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
                crtc_src_param.ucDevice = ATOM_DEVICE_CV_INDEX;
            else
                crtc_src_param.ucDevice = ATOM_DEVICE_CRT1_INDEX;
            break;
          case ENCODER_OBJECT_ID_INTERNAL_DAC2:
          case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
            if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT))
                crtc_src_param.ucDevice = ATOM_DEVICE_TV1_INDEX;
            else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
                crtc_src_param.ucDevice = ATOM_DEVICE_CV_INDEX;
            else
                crtc_src_param.ucDevice = ATOM_DEVICE_CRT2_INDEX;
            break;
          }
          data.exec.pspace = &crtc_src_param;
          /*ErrorF("device sourced: 0x%x\n", crtc_src_param.ucDevice);*/
          break;
      case 2:
          crtc_src_param2.ucCRTC = radeon_crtc->crtc_id;
          crtc_src_param2.ucEncodeMode = atombios_get_encoder_mode(output);
          switch (radeon_encoder->encoder_id) {
          case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
          case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
          case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
          case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
            switch (radeon_output->dig_encoder) {
            case 0:
                crtc_src_param2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
                break;
            case 1:
                crtc_src_param2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
                break;
            case 2:
                crtc_src_param2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID;
                break;
            case 3:
                crtc_src_param2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID;
                break;
            case 4:
                crtc_src_param2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID;
                break;
            case 5:
                crtc_src_param2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID;
                break;
            }
            break;
          case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
            if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT))
                crtc_src_param2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
            else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
                crtc_src_param2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
            else
                crtc_src_param2.ucEncoderID = ASIC_INT_DAC1_ENCODER_ID;
            break;
          case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
            if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT))
                crtc_src_param2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
            else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT))
                crtc_src_param2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
            else
                crtc_src_param2.ucEncoderID = ASIC_INT_DAC2_ENCODER_ID;
            break;
          }
          data.exec.pspace = &crtc_src_param2;
          /*ErrorF("device sourced: 0x%x\n", crtc_src_param2.ucEncoderID);*/
          break;
      }
      break;
    default:
      ErrorF("Unknown table version\n");
      exit(-1);
    }

    data.exec.index = index;
    data.exec.dataSpace = (void *)&space;

    if (RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("Set CRTC %d Source success\n", radeon_crtc->crtc_id);
      return;
    }

    ErrorF("Set CRTC Source failed\n");
    return;
}

static void
atombios_apply_output_quirks(xf86OutputPtr output, DisplayModePtr mode)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONCrtcPrivatePtr radeon_crtc = output->crtc->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    unsigned char *RADEONMMIO = info->MMIO;

    /* Funky macbooks */
    if ((info->Chipset == PCI_CHIP_RV530_71C5) &&
      (PCI_SUB_VENDOR_ID(info->PciInfo) == 0x106b) &&
      (PCI_SUB_DEVICE_ID(info->PciInfo) == 0x0080)) {
      if (radeon_output->MonType == MT_LCD) {
          if (radeon_output->devices & ATOM_DEVICE_LCD1_SUPPORT) {
            uint32_t lvtma_bit_depth_control = INREG(AVIVO_LVTMA_BIT_DEPTH_CONTROL);

            lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_TRUNCATE_EN;
            lvtma_bit_depth_control &= ~AVIVO_LVTMA_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN;

            OUTREG(AVIVO_LVTMA_BIT_DEPTH_CONTROL, lvtma_bit_depth_control);
          }
      }
    }

    /* set scaler clears this on some chips */
    if (!(radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT))) {
      if (IS_AVIVO_VARIANT && (mode->Flags & V_INTERLACE))
          OUTREG(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, AVIVO_D1MODE_INTERLEAVE_EN);
    }

    if (IS_DCE32_VARIANT &&
      (!IS_DCE4_VARIANT) &&
      (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT))) {
      radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
      if (radeon_encoder == NULL)
          return;
      /* XXX: need to sort out why transmitter control table sometimes sets this to a
       * different golden value.
       */
      if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY2) {
          OUTREG(0x7ec4, 0x00824002);
      }
    }
}

static void
atombios_pick_dig_encoder(xf86OutputPtr output)
{
    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(output->scrn);
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    radeon_encoder_ptr radeon_encoder = NULL;
    Bool is_lvtma = FALSE;
    int i, mode;
    uint32_t dig_enc_use_mask = 0;

    /* non digital encoders don't need a dig block */
    mode = atombios_get_encoder_mode(output);
    if (mode == ATOM_ENCODER_MODE_CRT ||
        mode == ATOM_ENCODER_MODE_TV ||
        mode == ATOM_ENCODER_MODE_CV)
        return;

    if (IS_DCE4_VARIANT) {
        radeon_encoder = radeon_get_encoder(output);

      switch (radeon_encoder->encoder_id) {
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
          if (radeon_output->linkb)
            radeon_output->dig_encoder = 1;
          else
            radeon_output->dig_encoder = 0;
          break;
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
          if (radeon_output->linkb)
            radeon_output->dig_encoder = 3;
          else
            radeon_output->dig_encoder = 2;
          break;
      case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
          if (radeon_output->linkb)
            radeon_output->dig_encoder = 5;
          else
            radeon_output->dig_encoder = 4;
          break;
      default:
          ErrorF("Unknown encoder\n");
          break;
      }
      return;
    }

    if (IS_DCE32_VARIANT) {
        RADEONCrtcPrivatePtr radeon_crtc = output->crtc->driver_private;
        radeon_output->dig_encoder = radeon_crtc->crtc_id;
        return;
    }

    for (i = 0; i < xf86_config->num_output; i++) {
        xf86OutputPtr test = xf86_config->output[i];
        RADEONOutputPrivatePtr radeon_test = test->driver_private;
        radeon_encoder = radeon_get_encoder(test);

        if (!radeon_encoder || !test->crtc)
            continue;

        if (output == test && radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA)
            is_lvtma = TRUE;
        if (output != test && (radeon_test->dig_encoder >= 0))
            dig_enc_use_mask |= (1 << radeon_test->dig_encoder);

    }
    if (is_lvtma) {
        if (dig_enc_use_mask & 0x2)
            ErrorF("Need digital encoder 2 for LVTMA and it isn't free - stealing\n");
        radeon_output->dig_encoder = 1;
        return;
    }
    if (!(dig_enc_use_mask & 1))
        radeon_output->dig_encoder = 0;
    else
        radeon_output->dig_encoder = 1;
}
void
atombios_output_mode_set(xf86OutputPtr output,
                   DisplayModePtr mode,
                   DisplayModePtr adjusted_mode)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    if (radeon_encoder == NULL)
      return;

    radeon_output->pixel_clock = adjusted_mode->Clock;
    atombios_pick_dig_encoder(output);
    atombios_output_overscan_setup(output, mode, adjusted_mode);
    atombios_output_scaler_setup(output);
    atombios_set_output_crtc_source(output);

    if (IS_AVIVO_VARIANT) {
      if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT))
          atombios_output_yuv_setup(output, TRUE);
      else
          atombios_output_yuv_setup(output, FALSE);
    }

    switch (radeon_encoder->encoder_id) {
    case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
    case ENCODER_OBJECT_ID_INTERNAL_LVDS:
    case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
      atombios_output_digital_setup(output, PANEL_ENCODER_ACTION_ENABLE);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
    case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
    case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
      /* disable encoder and transmitter */
      /* setup and enable the encoder and transmitter */
      atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
      if (IS_DCE4_VARIANT)
          atombios_dce4_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_SETUP);
      else {
          atombios_output_dig_encoder_setup(output, ATOM_DISABLE);
          atombios_output_dig_encoder_setup(output, ATOM_ENABLE);
      }
      atombios_output_dig_encoder_setup(output, ATOM_ENABLE);
      atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
      atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
      atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_DDI:
      atombios_output_ddia_setup(output, ATOM_ENABLE);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_DVO1:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
      atombios_external_tmds_setup(output, ATOM_ENABLE);
      break;
    case ENCODER_OBJECT_ID_INTERNAL_DAC1:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
    case ENCODER_OBJECT_ID_INTERNAL_DAC2:
    case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
      atombios_output_dac_setup(output, ATOM_ENABLE);
      if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
          atombios_output_tv_setup(output, ATOM_ENABLE);
      break;
    }
    atombios_apply_output_quirks(output, adjusted_mode);
}

static AtomBiosResult
atom_bios_dac_load_detect(atomBiosHandlePtr atomBIOS, xf86OutputPtr output)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    DAC_LOAD_DETECTION_PS_ALLOCATION dac_data;
    AtomBiosArgRec data;
    unsigned char *space;
    int major, minor;
    int index = GetIndexIntoMasterTable(COMMAND, DAC_LoadDetection);

    atombios_get_command_table_version(info->atomBIOS, index, &major, &minor);

    dac_data.sDacload.ucMisc = 0;

    if (radeon_output->devices & ATOM_DEVICE_CRT1_SUPPORT) {
      dac_data.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT1_SUPPORT);
      if (info->encoders[ATOM_DEVICE_CRT1_INDEX] &&
          ((info->encoders[ATOM_DEVICE_CRT1_INDEX]->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) ||
           (info->encoders[ATOM_DEVICE_CRT1_INDEX]->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1)))
          dac_data.sDacload.ucDacType = ATOM_DAC_A;
      else
          dac_data.sDacload.ucDacType = ATOM_DAC_B;
    } else if (radeon_output->devices & ATOM_DEVICE_CRT2_SUPPORT) {
      dac_data.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CRT2_SUPPORT);
      if (info->encoders[ATOM_DEVICE_CRT2_INDEX] &&
          ((info->encoders[ATOM_DEVICE_CRT2_INDEX]->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) ||
           (info->encoders[ATOM_DEVICE_CRT2_INDEX]->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1)))
          dac_data.sDacload.ucDacType = ATOM_DAC_A;
      else
          dac_data.sDacload.ucDacType = ATOM_DAC_B;
    } else if (radeon_output->devices & ATOM_DEVICE_CV_SUPPORT) {
      dac_data.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_CV_SUPPORT);
      if (info->encoders[ATOM_DEVICE_CV_INDEX] &&
          ((info->encoders[ATOM_DEVICE_CV_INDEX]->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) ||
           (info->encoders[ATOM_DEVICE_CV_INDEX]->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1)))
          dac_data.sDacload.ucDacType = ATOM_DAC_A;
      else
          dac_data.sDacload.ucDacType = ATOM_DAC_B;
      if (minor >= 3)
          dac_data.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb;
    } else if (radeon_output->devices & ATOM_DEVICE_TV1_SUPPORT) {
      dac_data.sDacload.usDeviceID = cpu_to_le16(ATOM_DEVICE_TV1_SUPPORT);
      if (info->encoders[ATOM_DEVICE_TV1_INDEX] &&
          ((info->encoders[ATOM_DEVICE_TV1_INDEX]->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DAC1) ||
           (info->encoders[ATOM_DEVICE_TV1_INDEX]->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1)))
          dac_data.sDacload.ucDacType = ATOM_DAC_A;
      else
          dac_data.sDacload.ucDacType = ATOM_DAC_B;
      if (minor >= 3)
          dac_data.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb;
    } else
      return ATOM_NOT_IMPLEMENTED;

    data.exec.index = index;
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &dac_data;

    if (RHDAtomBiosFunc(atomBIOS->scrnIndex, atomBIOS, ATOMBIOS_EXEC, &data) == ATOM_SUCCESS) {
      ErrorF("Dac detection success\n");
      return ATOM_SUCCESS ;
    }

    ErrorF("DAC detection failed\n");
    return ATOM_NOT_IMPLEMENTED;
}

RADEONMonitorType
atombios_dac_detect(xf86OutputPtr output)
{
    ScrnInfoPtr pScrn = output->scrn;
    RADEONInfoPtr info       = RADEONPTR(pScrn);
    unsigned char *RADEONMMIO = info->MMIO;
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONMonitorType MonType = MT_NONE;
    AtomBiosResult ret;
    RADEONSavePtr save = info->ModeReg;

    if (radeon_output->devices & ATOM_DEVICE_TV1_SUPPORT) {
      if (xf86ReturnOptValBool(info->Options, OPTION_FORCE_TVOUT, FALSE)) {
          if (radeon_output->ConnectorType == CONNECTOR_STV)
            return MT_STV;
          else
            return MT_CTV;
      }
    }

    ret = atom_bios_dac_load_detect(info->atomBIOS, output);
    if (ret == ATOM_SUCCESS) {
      if (info->ChipFamily >= CHIP_FAMILY_R600)
          save->bios_0_scratch = INREG(R600_BIOS_0_SCRATCH);
      else
          save->bios_0_scratch = INREG(RADEON_BIOS_0_SCRATCH);
      /*ErrorF("DAC connect %08X\n", (unsigned int)save->bios_0_scratch);*/

      if (radeon_output->devices & ATOM_DEVICE_CRT1_SUPPORT) {
          if (save->bios_0_scratch & ATOM_S0_CRT1_MASK)
            MonType = MT_CRT;
      } else if (radeon_output->devices & ATOM_DEVICE_CRT2_SUPPORT) {
          if (save->bios_0_scratch & ATOM_S0_CRT2_MASK)
            MonType = MT_CRT;
      } else if (radeon_output->devices & ATOM_DEVICE_CV_SUPPORT) {
          if (save->bios_0_scratch & (ATOM_S0_CV_MASK | ATOM_S0_CV_MASK_A))
            MonType = MT_CV;
      } else if (radeon_output->devices & ATOM_DEVICE_TV1_SUPPORT) {
          if (save->bios_0_scratch & (ATOM_S0_TV1_COMPOSITE | ATOM_S0_TV1_COMPOSITE_A))
            MonType = MT_CTV;
          else if (save->bios_0_scratch & (ATOM_S0_TV1_SVIDEO | ATOM_S0_TV1_SVIDEO_A))
            MonType = MT_STV;
      }
    }

    return MonType;
}

#define AUX_NATIVE_WRITE                    0x8
#define AUX_NATIVE_READ                     0x9

#define AUX_I2C_WRITE                       0x0
#define AUX_I2C_READ                        0x1
#define AUX_I2C_STATUS                      0x2
#define AUX_I2C_MOT                         0x4

#define DP_DPCD_REV                         0x0
#define DP_MAX_LINK_RATE                    0x1
#define DP_MAX_LANE_COUNT                   0x2
#define DP_MAX_DOWNSPREAD                   0x3
#define DP_NORP                             0x4
#define DP_DOWNSTREAMPORT_PRESENT           0x5
#define DP_MAIN_LINK_CHANNEL_CONFIG         0x6
#define DP_DP11_DOWNSTREAM_PORT_COUNT       0x7

/* from intel i830_dp.h */
#define DP_LINK_BW_SET                      0x100
//# define DP_LINK_BW_1_62                    0x06
//# define DP_LINK_BW_2_7                     0x0a
#define DP_LANE_COUNT_SET                   0x101
# define DP_LANE_COUNT_MASK                 0x0f
# define DP_LANE_COUNT_ENHANCED_FRAME_EN    (1 << 7)

#define DP_TRAINING_PATTERN_SET             0x102

# define DP_TRAINING_PATTERN_DISABLE        0
# define DP_TRAINING_PATTERN_1              1
# define DP_TRAINING_PATTERN_2              2
# define DP_TRAINING_PATTERN_MASK           0x3

# define DP_LINK_QUAL_PATTERN_DISABLE       (0 << 2)
# define DP_LINK_QUAL_PATTERN_D10_2         (1 << 2)
# define DP_LINK_QUAL_PATTERN_ERROR_RATE    (2 << 2)
# define DP_LINK_QUAL_PATTERN_PRBS7         (3 << 2)
# define DP_LINK_QUAL_PATTERN_MASK          (3 << 2)
# define DP_RECOVERED_CLOCK_OUT_EN          (1 << 4)
# define DP_LINK_SCRAMBLING_DISABLE         (1 << 5)

# define DP_SYMBOL_ERROR_COUNT_BOTH         (0 << 6)
# define DP_SYMBOL_ERROR_COUNT_DISPARITY    (1 << 6)
# define DP_SYMBOL_ERROR_COUNT_SYMBOL       (2 << 6)
# define DP_SYMBOL_ERROR_COUNT_MASK         (3 << 6)

#define DP_TRAINING_LANE0_SET               0x103
#define DP_TRAINING_LANE1_SET               0x104
#define DP_TRAINING_LANE2_SET               0x105
#define DP_TRAINING_LANE3_SET               0x106
# define DP_TRAIN_VOLTAGE_SWING_MASK        0x3
# define DP_TRAIN_VOLTAGE_SWING_SHIFT       0
# define DP_TRAIN_MAX_SWING_REACHED         (1 << 2)
# define DP_TRAIN_VOLTAGE_SWING_400         (0 << 0)
# define DP_TRAIN_VOLTAGE_SWING_600         (1 << 0)
# define DP_TRAIN_VOLTAGE_SWING_800         (2 << 0)
# define DP_TRAIN_VOLTAGE_SWING_1200        (3 << 0)

# define DP_TRAIN_PRE_EMPHASIS_MASK         (3 << 3)
# define DP_TRAIN_PRE_EMPHASIS_0            (0 << 3)
# define DP_TRAIN_PRE_EMPHASIS_3_5          (1 << 3)
# define DP_TRAIN_PRE_EMPHASIS_6            (2 << 3)
# define DP_TRAIN_PRE_EMPHASIS_9_5          (3 << 3)

# define DP_TRAIN_PRE_EMPHASIS_SHIFT        3
# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED  (1 << 5)
#define DP_DOWNSPREAD_CTRL                  0x107
# define DP_SPREAD_AMP_0_5                  (1 << 4)

#define DP_MAIN_LINK_CHANNEL_CODING_SET     0x108
# define DP_SET_ANSI_8B10B                  (1 << 0)

#define DP_LANE0_1_STATUS                   0x202
#define DP_LANE2_3_STATUS                   0x203

# define DP_LANE_CR_DONE                    (1 << 0)
# define DP_LANE_CHANNEL_EQ_DONE            (1 << 1)
# define DP_LANE_SYMBOL_LOCKED              (1 << 2)

#define DP_LANE_ALIGN_STATUS_UPDATED        0x204
#define DP_INTERLANE_ALIGN_DONE             (1 << 0)
#define DP_DOWNSTREAM_PORT_STATUS_CHANGED   (1 << 6)
#define DP_LINK_STATUS_UPDATED              (1 << 7)

#define DP_SINK_STATUS                      0x205

#define DP_RECEIVE_PORT_0_STATUS            (1 << 0)
#define DP_RECEIVE_PORT_1_STATUS            (1 << 1)

#define DP_ADJUST_REQUEST_LANE0_1           0x206
#define DP_ADJUST_REQUEST_LANE2_3           0x207

#define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK  0x03
#define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
#define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK   0x0c
#define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT  2
#define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK  0x30
#define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK   0xc0
#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT  6

#define DP_LINK_STATUS_SIZE                 6
#define DP_LINK_CONFIGURATION_SIZE          9

#define DP_SET_POWER_D0  0x1
#define DP_SET_POWER_D3  0x2

static inline int atom_dp_get_encoder_id(xf86OutputPtr output)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    int ret = 0;
    if (radeon_output->dig_encoder)
        ret |= ATOM_DP_CONFIG_DIG2_ENCODER;
    else
        ret |= ATOM_DP_CONFIG_DIG1_ENCODER;
    if (radeon_output->linkb)
        ret |= ATOM_DP_CONFIG_LINK_B;
    else
        ret |= ATOM_DP_CONFIG_LINK_A;
    return ret;
}

union aux_channel_transaction {
    PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1;
    PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2;
};

Bool
RADEONProcessAuxCH(xf86OutputPtr output, uint8_t *req_bytes, uint8_t num_bytes,
               uint8_t *read_byte, uint8_t read_buf_len, uint8_t delay)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    RADEONInfoPtr info       = RADEONPTR(output->scrn);
    union aux_channel_transaction args;
    AtomBiosArgRec data;
    unsigned char *space;
    unsigned char *base;

    memset(&args, 0, sizeof(args));
    if (info->atomBIOS->fbBase)
      base = info->FB + info->atomBIOS->fbBase;
    else if (info->atomBIOS->scratchBase)
      base = (unsigned char *)info->atomBIOS->scratchBase;
    else
      return FALSE;

    memcpy(base, req_bytes, num_bytes);

    args.v1.lpAuxRequest = 0;
    args.v1.lpDataOut = 16;
    args.v1.ucDataOutLen = 0;
    args.v1.ucChannelID = radeon_output->ucI2cId;
    args.v1.ucDelay = delay / 10; /* 10 usec */
    if (IS_DCE4_VARIANT)
      args.v2.ucHPD_ID = radeon_output->hpd_id;

    data.exec.index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &args;

    RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data);
    if (args.v1.ucReplyStatus) {
      ErrorF("failed to get auxch %02x%02x %02x %02x %02x\n",
             req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3], args.v1.ucReplyStatus);
      return FALSE;
    }
    if (args.v1.ucDataOutLen && read_byte && read_buf_len) {
      if (read_buf_len < args.v1.ucDataOutLen) {
          ErrorF("%s: Buffer too small for return answer %d %d\n", __func__, read_buf_len, args.v1.ucDataOutLen);
          return FALSE;
      }
      {
          int len = read_buf_len < args.v1.ucDataOutLen ? read_buf_len : args.v1.ucDataOutLen;
          memcpy(read_byte, base+16, len);
      }
    }
    return TRUE;
}

static int
RADEONDPEncoderService(xf86OutputPtr output, int action, uint8_t ucconfig, uint8_t lane_num)
{
    RADEONInfoPtr info = RADEONPTR(output->scrn);
    DP_ENCODER_SERVICE_PARAMETERS args;
    AtomBiosArgRec data;
    unsigned char *space;

    memset(&args, 0, sizeof(args));

    args.ucLinkClock = 0;
    args.ucConfig = ucconfig;
    args.ucAction = action;
    args.ucLaneNum = lane_num;
    args.ucStatus = 0;

    data.exec.index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
    data.exec.dataSpace = (void *)&space;
    data.exec.pspace = &args;

    RHDAtomBiosFunc(info->atomBIOS->scrnIndex, info->atomBIOS, ATOMBIOS_EXEC, &data);

    ErrorF("%s: %d\n", __func__, args.ucStatus);
    return args.ucStatus;
}

int RADEON_DP_GetSinkType(xf86OutputPtr output)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;

    return RADEONDPEncoderService(output, ATOM_DP_ACTION_GET_SINK_TYPE, radeon_output->ucI2cId, 0);
}

static Bool atom_dp_aux_native_write(xf86OutputPtr output, uint16_t address,
                             uint8_t send_bytes, uint8_t *send)
{
    uint8_t msg[20];
    uint8_t msg_len, dp_msg_len;
    int ret;

    dp_msg_len = 4;
    msg[0] = address;
    msg[1] = address >> 8;
    msg[2] = AUX_NATIVE_WRITE << 4;
    dp_msg_len += send_bytes;
    msg[3] = (dp_msg_len << 4)| (send_bytes - 1);

    if (0)
      ErrorF("writing %02x %02x %02x, %d, %d\n", msg[0], msg[1], msg[3], send_bytes, dp_msg_len);
    if (send_bytes > 16)
      return FALSE;

    memcpy(&msg[4], send, send_bytes);
    msg_len = 4 + send_bytes;
    ret = RADEONProcessAuxCH(output, msg, msg_len, NULL, 0, 0);
    return ret;
}

static Bool atom_dp_aux_native_read(xf86OutputPtr output, uint16_t address,
                            uint8_t delay,
                            uint8_t expected_bytes, uint8_t *read_p)
{
    uint8_t msg[20];
    uint8_t msg_len, dp_msg_len;
    int ret;

    msg_len = 4;
    dp_msg_len = 4;
    msg[0] = address;
    msg[1] = address >> 8;
    msg[2] = AUX_NATIVE_READ << 4;
    msg[3] = (dp_msg_len) << 4;
    msg[3] |= expected_bytes - 1;

    if (0)
      ErrorF("reading %02x %02x %02x, %d, %d\n", msg[0], msg[1], msg[3], expected_bytes, dp_msg_len);
    ret = RADEONProcessAuxCH(output, msg, msg_len, read_p, expected_bytes, delay);
    return ret;
}

/* fill out the DPCD structure */
void RADEON_DP_GetDPCD(xf86OutputPtr output)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    uint8_t msg[25];
    int ret;

    ret = atom_dp_aux_native_read(output, DP_DPCD_REV, 0, 8, msg);
    if (ret) {
      memcpy(radeon_output->dpcd, msg, 8);
      if (0) {
          int i;
          ErrorF("DPCD: ");
          for (i = 0; i < 8; i++)
            ErrorF("%02x ", radeon_output->dpcd[i]);
          ErrorF("\n");
      }
      ret = atom_dp_aux_native_read(output, DP_LINK_BW_SET, 0, 2, msg);
      if (0) {
          ErrorF("0x200: %02x %02x\n", msg[0], msg[1]);
      }
      return;
    }
    radeon_output->dpcd[0] = 0;
    return;
}


enum dp_aux_i2c_mode {
    dp_aux_i2c_start,
    dp_aux_i2c_write,
    dp_aux_i2c_read,
    dp_aux_i2c_stop,
};


static Bool atom_dp_aux_i2c_transaction(xf86OutputPtr output, uint16_t address,
                               enum dp_aux_i2c_mode mode,
                               uint8_t write_byte, uint8_t *read_byte)
{
    uint8_t msg[8], msg_len, dp_msg_len;
    int ret;
    int auxch_cmd = 0;

    memset(msg, 0, 8);

    if (mode != dp_aux_i2c_stop)
      auxch_cmd = AUX_I2C_MOT;

    if (address & 1)
      auxch_cmd |= AUX_I2C_READ;
    else
      auxch_cmd |= AUX_I2C_WRITE;

    msg[2] = auxch_cmd << 4;

    msg[4] = 0;
    msg[0] = (address >> 1);
    msg[1] = (address >> 9);

    msg_len = 4;
    dp_msg_len = 3;
    switch (mode) {
    case dp_aux_i2c_read:
      /* bottom bits is byte count - 1 so for 1 byte == 0 */
      dp_msg_len += 1;
      break;
    case dp_aux_i2c_write:
      dp_msg_len += 2;
      msg[4] = write_byte;
      msg_len++;
      break;
    default:
      break;
    }
    msg[3] = dp_msg_len << 4;

    ret = RADEONProcessAuxCH(output, msg, msg_len, read_byte, 1, 0);
    return ret;
}

static Bool
atom_dp_i2c_address(I2CDevPtr dev, I2CSlaveAddr addr)
{
    I2CBusPtr bus = dev->pI2CBus;
    xf86OutputPtr output = bus->DriverPrivate.ptr;
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    int ret;

    radeon_output->dp_i2c_addr = addr;
    radeon_output->dp_i2c_running = TRUE;

    /* call i2c start */
    ret = atom_dp_aux_i2c_transaction(output, radeon_output->dp_i2c_addr,
                              dp_aux_i2c_start, 0, NULL);

    return ret;
}
static Bool
atom_dp_i2c_start(I2CBusPtr bus, int timeout)
{
    ErrorF("%s\n", __func__);
    return TRUE;
}

static void
atom_dp_i2c_stop(I2CDevPtr dev)
{
    I2CBusPtr bus = dev->pI2CBus;
    xf86OutputPtr output = bus->DriverPrivate.ptr;
    RADEONOutputPrivatePtr radeon_output = output->driver_private;

    if (radeon_output->dp_i2c_running)
      atom_dp_aux_i2c_transaction(output, radeon_output->dp_i2c_addr,
                            dp_aux_i2c_stop, 0, NULL);
    radeon_output->dp_i2c_running = FALSE;
}


static Bool
atom_dp_i2c_put_byte(I2CDevPtr dev, I2CByte byte)
{
    I2CBusPtr bus = dev->pI2CBus;
    xf86OutputPtr output = bus->DriverPrivate.ptr;
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    Bool ret;

    ret = (atom_dp_aux_i2c_transaction(output, radeon_output->dp_i2c_addr,
                               dp_aux_i2c_write, byte, NULL));
    return ret;
}

static Bool
atom_dp_i2c_get_byte(I2CDevPtr dev, I2CByte *byte_ret, Bool last)
{
    I2CBusPtr bus = dev->pI2CBus;
    xf86OutputPtr output = bus->DriverPrivate.ptr;
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    Bool ret;

    ret = (atom_dp_aux_i2c_transaction(output, radeon_output->dp_i2c_addr,
                               dp_aux_i2c_read, 0, byte_ret));
    return ret;
}

Bool
RADEON_DP_I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, char *name, xf86OutputPtr output)
{
    I2CBusPtr pI2CBus;

    pI2CBus = xf86CreateI2CBusRec();
    if (!pI2CBus) return FALSE;

    pI2CBus->BusName = name;
    pI2CBus->scrnIndex = pScrn->scrnIndex;
    pI2CBus->I2CGetByte = atom_dp_i2c_get_byte;
    pI2CBus->I2CPutByte = atom_dp_i2c_put_byte;
    pI2CBus->I2CAddress = atom_dp_i2c_address;
    pI2CBus->I2CStart = atom_dp_i2c_start;
    pI2CBus->I2CStop = atom_dp_i2c_stop;
    pI2CBus->DriverPrivate.ptr = output;

    /*
     * These were set incorrectly in the server pre-1.3, Having
     * duplicate settings is sub-optimal, but this lets the driver
     * work with older servers
     */
    pI2CBus->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
    pI2CBus->StartTimeout = 550;
    pI2CBus->BitTimeout = 40;
    pI2CBus->AcknTimeout = 40;
    pI2CBus->RiseFallTime = 20;

    if (!xf86I2CBusInit(pI2CBus))
      return FALSE;

    *bus_ptr = pI2CBus;
    return TRUE;
}


static uint8_t dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int r)
{
    return link_status[r - DP_LANE0_1_STATUS];
}

static uint8_t dp_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane)
{
    int i = DP_LANE0_1_STATUS + (lane >> 1);
    int s = (lane & 1) * 4;
    uint8_t l = dp_link_status(link_status, i);
    return (l >> s) & 0xf;
}

static Bool dp_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
{
    int lane;

    uint8_t lane_status;

    for (lane = 0; lane < lane_count; lane++) {
      lane_status = dp_get_lane_status(link_status, lane);
      if ((lane_status & DP_LANE_CR_DONE) == 0)
          return FALSE;
    }
    return TRUE;
}


/* Check to see if channel eq is done on all channels */
#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\
                   DP_LANE_CHANNEL_EQ_DONE|\
                   DP_LANE_SYMBOL_LOCKED)
static Bool
dp_channel_eq_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
{
    uint8_t lane_align;
    uint8_t lane_status;
    int lane;

    lane_align = dp_link_status(link_status,
                        DP_LANE_ALIGN_STATUS_UPDATED);
    if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
      return FALSE;
    for (lane = 0; lane < lane_count; lane++) {
      lane_status = dp_get_lane_status(link_status, lane);
      if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS)
          return FALSE;
    }
    return TRUE;
}

/*
 * Fetch AUX CH registers 0x202 - 0x207 which contain
 * link status information
 */
static Bool
atom_dp_get_link_status(xf86OutputPtr output,
                    uint8_t link_status[DP_LINK_STATUS_SIZE])
{
    ScrnInfoPtr pScrn = output->scrn;
    int ret;
    ret = atom_dp_aux_native_read(output, DP_LANE0_1_STATUS, 100,
                          DP_LINK_STATUS_SIZE, link_status);
    if (!ret) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "dp link status failed\n");
      return FALSE;
    }
    ErrorF("link status %02x %02x %02x %02x %02x %02x\n", link_status[0], link_status[1],
         link_status[2], link_status[3], link_status[4], link_status[5]);

    return TRUE;
}

static uint8_t
dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
                        int lane)

{
    int     i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
    int     s = ((lane & 1) ?
                 DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
                 DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
    uint8_t l = dp_link_status(link_status, i);

    return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
}

static uint8_t
dp_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
                           int lane)
{
    int     i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
    int     s = ((lane & 1) ?
                 DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
                 DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
    uint8_t l = dp_link_status(link_status, i);

    return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
}

static char     *voltage_names[] = {
        "0.4V", "0.6V", "0.8V", "1.2V"
};
static char     *pre_emph_names[] = {
        "0dB", "3.5dB", "6dB", "9.5dB"
};

/*
 * These are source-specific values; current Intel hardware supports
 * a maximum voltage of 800mV and a maximum pre-emphasis of 6dB
 */
#define DP_VOLTAGE_MAX         DP_TRAIN_VOLTAGE_SWING_1200

static uint8_t
dp_pre_emphasis_max(uint8_t voltage_swing)
{
    switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
    case DP_TRAIN_VOLTAGE_SWING_400:
        return DP_TRAIN_PRE_EMPHASIS_6;
    case DP_TRAIN_VOLTAGE_SWING_600:
        return DP_TRAIN_PRE_EMPHASIS_6;
    case DP_TRAIN_VOLTAGE_SWING_800:
        return DP_TRAIN_PRE_EMPHASIS_3_5;
    case DP_TRAIN_VOLTAGE_SWING_1200:
    default:
        return DP_TRAIN_PRE_EMPHASIS_0;
    }
}

static void dp_set_training(xf86OutputPtr output, uint8_t training)
{
    atom_dp_aux_native_write(output, DP_TRAINING_PATTERN_SET, 1, &training);
}

static void dp_set_power(xf86OutputPtr output, uint8_t power_state)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;

    if (radeon_output->dpcd[0] >= 0x11) {
      atom_dp_aux_native_write(output, 0x600, 1, &power_state);
    }
}

static void
dp_get_adjust_train(xf86OutputPtr output,
                  uint8_t link_status[DP_LINK_STATUS_SIZE],
                  int lane_count,
                  uint8_t train_set[4])
{
    ScrnInfoPtr pScrn = output->scrn;
    uint8_t v = 0;
    uint8_t p = 0;
    int lane;

    for (lane = 0; lane < lane_count; lane++) {
      uint8_t this_v = dp_get_adjust_request_voltage(link_status, lane);
      uint8_t this_p = dp_get_adjust_request_pre_emphasis(link_status, lane);

      if (0) {
          xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                   "requested signal parameters: lane %d voltage %s pre_emph %s\n",
                   lane,
                   voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
                   pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
      }
      if (this_v > v)
          v = this_v;
      if (this_p > p)
          p = this_p;
    }

    if (v >= DP_VOLTAGE_MAX)
      v = DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;

    if (p >= dp_pre_emphasis_max(v))
      p = dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;

    if (0) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
               "using signal parameters: voltage %s pre_emph %s\n",
               voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
               pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
    }
    for (lane = 0; lane < 4; lane++)
      train_set[lane] = v | p;
}

static int radeon_dp_max_lane_count(xf86OutputPtr output)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    int max_lane_count = 4;

    if (radeon_output->dpcd[0] >= 0x11) {
      max_lane_count = radeon_output->dpcd[2] & 0x1f;
      switch(max_lane_count) {
      case 1: case 2: case 4:
          break;
      default:
          max_lane_count = 4;
      }
    }
    return max_lane_count;
}

static int radeon_dp_max_link_bw(xf86OutputPtr output)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    int max_link_bw = radeon_output->dpcd[1];
    switch(max_link_bw) {
    case DP_LINK_BW_1_62:
    case DP_LINK_BW_2_7:
      break;
    default:
      max_link_bw = DP_LINK_BW_1_62;
      break;
    }
    return max_link_bw;
}

static int radeon_dp_link_clock(uint8_t link_bw)
{
    if (link_bw == DP_LINK_BW_2_7)
      return 270000;
    else
      return 162000;
}


/* I think this is a fiction */
static int radeon_dp_link_required(int pixel_clock)
{
    return pixel_clock * 3;
}

Bool radeon_dp_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    int lane_count, clock;
    int max_lane_count = radeon_dp_max_lane_count(output);
    int max_clock = radeon_dp_max_link_bw(output) == DP_LINK_BW_2_7 ? 1 : 0;
    static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };

    for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
      for (clock = 0; clock <= max_clock; clock++) {
          int link_avail = radeon_dp_link_clock(bws[clock]) * lane_count;

          if (radeon_dp_link_required(mode->Clock) <= link_avail) {
            radeon_output->dp_lane_count = lane_count;
            radeon_output->dp_clock = radeon_dp_link_clock(bws[clock]);
            if (0)
                  xf86DrvMsg(0, X_INFO,
                           "lane_count %d clock %d\n",
                           radeon_output->dp_lane_count,
                           radeon_output->dp_clock);
            return TRUE;
          }
      }
    }
    return FALSE;
}

static void dp_update_dpvs_emph(xf86OutputPtr output, uint8_t train_set[4])
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    int i;
    for (i = 0; i < radeon_output->dp_lane_count; i++)
      atombios_output_dig_transmitter_setup(output, ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH, i, train_set[i]);

    atom_dp_aux_native_write(output, DP_TRAINING_LANE0_SET, radeon_output->dp_lane_count, train_set);
}

static void do_displayport_link_train(xf86OutputPtr output)
{
    ScrnInfoPtr pScrn = output->scrn;
    RADEONInfoPtr info = RADEONPTR(pScrn);
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    int enc_id = atom_dp_get_encoder_id(output);
    Bool clock_recovery;
    uint8_t link_status[DP_LINK_STATUS_SIZE];
    uint8_t tries, voltage, ss_cntl;
    uint8_t train_set[4];
    int i;
    Bool channel_eq;
    uint8_t dp_link_configuration[DP_LINK_CONFIGURATION_SIZE];

    memset(train_set, 0, 4);

    /* set up link configuration */
    memset(dp_link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);

    if (radeon_output->dp_clock == 270000)
      dp_link_configuration[0] = DP_LINK_BW_2_7;
    else
      dp_link_configuration[0] = DP_LINK_BW_1_62;
    dp_link_configuration[1] = radeon_output->dp_lane_count;

    if (radeon_output->dpcd[0] >= 0x11) {
      dp_link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
    }

    /* power up to D0 */
    dp_set_power(output, DP_SET_POWER_D0);

    /* disable training */
    dp_set_training(output, DP_TRAINING_PATTERN_DISABLE);

    /* write link rate / num / eh framing */
    atom_dp_aux_native_write(output, DP_LINK_BW_SET, 2,
                       dp_link_configuration);

    /* write ss cntl */
    ss_cntl = 0;
    atom_dp_aux_native_write(output, DP_DOWNSPREAD_CTRL, 1,
                       &ss_cntl);

    /* start local training start */
    if (IS_DCE4_VARIANT) {
      atombios_dce4_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START);
      atombios_dce4_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1);
    } else {
      RADEONDPEncoderService(output, ATOM_DP_ACTION_TRAINING_START, enc_id, 0);
      RADEONDPEncoderService(output, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, enc_id, 0);
    }

    usleep(400);
    dp_set_training(output, DP_TRAINING_PATTERN_1);
    dp_update_dpvs_emph(output, train_set);

    /* loop around doing configuration reads and DP encoder setups */
    clock_recovery = FALSE;
    tries = 0;
    voltage = 0xff;
    for (;;) {
            usleep(100);
      if (!atom_dp_get_link_status(output, link_status))
          break;

      if (dp_clock_recovery_ok(link_status, radeon_output->dp_lane_count)) {
          clock_recovery = TRUE;
          break;
      }

      for (i = 0; i < radeon_output->dp_lane_count; i++)
          if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
            break;
      if (i == radeon_output->dp_lane_count) {
          xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                   "clock recovery reached max voltage\n");
          break;
      }

      /* Check to see if we've tried the same voltage 5 times */
      if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
          ++tries;
          if (tries == 5) {
            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                     "clock recovery tried 5 times\n");
            break;
          }
      } else
          tries = 0;

      voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;

        dp_get_adjust_train(output, link_status, radeon_output->dp_lane_count, train_set);
      dp_update_dpvs_emph(output, train_set);

    }

    if (!clock_recovery)
      xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
               "clock recovery failed\n");

    /* channel equalization */
    tries = 0;
    channel_eq = FALSE;
    dp_set_training(output, DP_TRAINING_PATTERN_2);
    if (IS_DCE4_VARIANT)
      atombios_dce4_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2);
    else
      RADEONDPEncoderService(output, ATOM_DP_ACTION_TRAINING_PATTERN_SEL, enc_id, 1);

    for (;;) {
      usleep(400);
      if (!atom_dp_get_link_status(output, link_status))
          break;

      if (dp_channel_eq_ok(link_status, radeon_output->dp_lane_count)) {
          channel_eq = TRUE;
          break;
      }

      /* Try 5 times */
      if (tries > 5) {
          xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                   "channel eq failed: 5 tries\n");
          break;
      }

      /* Compute new train_set as requested by target */
        dp_get_adjust_train(output, link_status, radeon_output->dp_lane_count, train_set);
      dp_update_dpvs_emph(output, train_set);

      ++tries;
    }

    if (!channel_eq)
      xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
               "channel eq failed\n");

    dp_set_training(output, DP_TRAINING_PATTERN_DISABLE);
    if (IS_DCE4_VARIANT)
      atombios_dce4_output_dig_encoder_setup(output, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE);
    else
      RADEONDPEncoderService(output, ATOM_DP_ACTION_TRAINING_COMPLETE, enc_id, 0);

}


Generated by  Doxygen 1.6.0   Back to index