]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/gpu/drm/radeon/atombios_crtc.c
drm/radeon: fix up pll selection on DCE5/6
[linux-2.6.git] / drivers / gpu / drm / radeon / atombios_crtc.c
index 4374168b75b8816d4042719b23730d85583951c6..ca2548b26500f3441c2a053fac6952ed2bf9efa5 100644 (file)
@@ -48,29 +48,29 @@ static void atombios_overscan_setup(struct drm_crtc *crtc,
 
        switch (radeon_crtc->rmx_type) {
        case RMX_CENTER:
-               args.usOverscanTop = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2;
-               args.usOverscanBottom = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2;
-               args.usOverscanLeft = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2;
-               args.usOverscanRight = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2;
+               args.usOverscanTop = cpu_to_le16((adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2);
+               args.usOverscanBottom = cpu_to_le16((adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2);
+               args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2);
+               args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2);
                break;
        case RMX_ASPECT:
                a1 = mode->crtc_vdisplay * adjusted_mode->crtc_hdisplay;
                a2 = adjusted_mode->crtc_vdisplay * mode->crtc_hdisplay;
 
                if (a1 > a2) {
-                       args.usOverscanLeft = (adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2;
-                       args.usOverscanRight = (adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2;
+                       args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2);
+                       args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2);
                } else if (a2 > a1) {
-                       args.usOverscanLeft = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2;
-                       args.usOverscanRight = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2;
+                       args.usOverscanTop = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2);
+                       args.usOverscanBottom = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2);
                }
                break;
        case RMX_FULL:
        default:
-               args.usOverscanRight = radeon_crtc->h_border;
-               args.usOverscanLeft = radeon_crtc->h_border;
-               args.usOverscanBottom = radeon_crtc->v_border;
-               args.usOverscanTop = radeon_crtc->v_border;
+               args.usOverscanRight = cpu_to_le16(radeon_crtc->h_border);
+               args.usOverscanLeft = cpu_to_le16(radeon_crtc->h_border);
+               args.usOverscanBottom = cpu_to_le16(radeon_crtc->v_border);
+               args.usOverscanTop = cpu_to_le16(radeon_crtc->v_border);
                break;
        }
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
@@ -231,6 +231,22 @@ static void atombios_blank_crtc(struct drm_crtc *crtc, int state)
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
+static void atombios_powergate_crtc(struct drm_crtc *crtc, int state)
+{
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       int index = GetIndexIntoMasterTable(COMMAND, EnableDispPowerGating);
+       ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 args;
+
+       memset(&args, 0, sizeof(args));
+
+       args.ucDispPipeId = radeon_crtc->crtc_id;
+       args.ucEnable = state;
+
+       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
 void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
        struct drm_device *dev = crtc->dev;
@@ -242,8 +258,10 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
                radeon_crtc->enabled = true;
                /* adjust pm to dpms changes BEFORE enabling crtcs */
                radeon_pm_compute_clocks(rdev);
+               if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set)
+                       atombios_powergate_crtc(crtc, ATOM_DISABLE);
                atombios_enable_crtc(crtc, ATOM_ENABLE);
-               if (ASIC_IS_DCE3(rdev))
+               if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev))
                        atombios_enable_crtc_memreq(crtc, ATOM_ENABLE);
                atombios_blank_crtc(crtc, ATOM_DISABLE);
                drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
@@ -255,10 +273,12 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
                drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
                if (radeon_crtc->enabled)
                        atombios_blank_crtc(crtc, ATOM_ENABLE);
-               if (ASIC_IS_DCE3(rdev))
+               if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev))
                        atombios_enable_crtc_memreq(crtc, ATOM_DISABLE);
                atombios_enable_crtc(crtc, ATOM_DISABLE);
                radeon_crtc->enabled = false;
+               if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set)
+                       atombios_powergate_crtc(crtc, ATOM_ENABLE);
                /* adjust pm to dpms changes AFTER disabling crtcs */
                radeon_pm_compute_clocks(rdev);
                break;
@@ -355,15 +375,12 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc,
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
-static void atombios_disable_ss(struct drm_crtc *crtc)
+static void atombios_disable_ss(struct radeon_device *rdev, int pll_id)
 {
-       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
-       struct drm_device *dev = crtc->dev;
-       struct radeon_device *rdev = dev->dev_private;
        u32 ss_cntl;
 
        if (ASIC_IS_DCE4(rdev)) {
-               switch (radeon_crtc->pll_id) {
+               switch (pll_id) {
                case ATOM_PPLL1:
                        ss_cntl = RREG32(EVERGREEN_P1PLL_SS_CNTL);
                        ss_cntl &= ~EVERGREEN_PxPLL_SS_EN;
@@ -379,7 +396,7 @@ static void atombios_disable_ss(struct drm_crtc *crtc)
                        return;
                }
        } else if (ASIC_IS_AVIVO(rdev)) {
-               switch (radeon_crtc->pll_id) {
+               switch (pll_id) {
                case ATOM_PPLL1:
                        ss_cntl = RREG32(AVIVO_P1PLL_INT_SS_CNTL);
                        ss_cntl &= ~1;
@@ -406,90 +423,111 @@ union atom_enable_ss {
        ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 v3;
 };
 
-static void atombios_crtc_program_ss(struct drm_crtc *crtc,
+static void atombios_crtc_program_ss(struct radeon_device *rdev,
                                     int enable,
                                     int pll_id,
+                                    int crtc_id,
                                     struct radeon_atom_ss *ss)
 {
-       struct drm_device *dev = crtc->dev;
-       struct radeon_device *rdev = dev->dev_private;
+       unsigned i;
        int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
        union atom_enable_ss args;
 
+       if (!enable) {
+               for (i = 0; i < rdev->num_crtc; i++) {
+                       if (rdev->mode_info.crtcs[i] &&
+                           rdev->mode_info.crtcs[i]->enabled &&
+                           i != crtc_id &&
+                           pll_id == rdev->mode_info.crtcs[i]->pll_id) {
+                               /* one other crtc is using this pll don't turn
+                                * off spread spectrum as it might turn off
+                                * display on active crtc
+                                */
+                               return;
+                       }
+               }
+       }
+
        memset(&args, 0, sizeof(args));
 
        if (ASIC_IS_DCE5(rdev)) {
-               args.v3.usSpreadSpectrumAmountFrac = 0;
-               args.v3.ucSpreadSpectrumType = ss->type;
+               args.v3.usSpreadSpectrumAmountFrac = cpu_to_le16(0);
+               args.v3.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
                switch (pll_id) {
                case ATOM_PPLL1:
                        args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P1PLL;
-                       args.v3.usSpreadSpectrumAmount = ss->amount;
-                       args.v3.usSpreadSpectrumStep = ss->step;
+                       args.v3.usSpreadSpectrumAmount = cpu_to_le16(ss->amount);
+                       args.v3.usSpreadSpectrumStep = cpu_to_le16(ss->step);
                        break;
                case ATOM_PPLL2:
                        args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P2PLL;
-                       args.v3.usSpreadSpectrumAmount = ss->amount;
-                       args.v3.usSpreadSpectrumStep = ss->step;
+                       args.v3.usSpreadSpectrumAmount = cpu_to_le16(ss->amount);
+                       args.v3.usSpreadSpectrumStep = cpu_to_le16(ss->step);
                        break;
                case ATOM_DCPLL:
                        args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_DCPLL;
-                       args.v3.usSpreadSpectrumAmount = 0;
-                       args.v3.usSpreadSpectrumStep = 0;
+                       args.v3.usSpreadSpectrumAmount = cpu_to_le16(0);
+                       args.v3.usSpreadSpectrumStep = cpu_to_le16(0);
                        break;
                case ATOM_PPLL_INVALID:
                        return;
                }
-               args.v2.ucEnable = enable;
+               args.v3.ucEnable = enable;
+               if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK) || ASIC_IS_DCE61(rdev))
+                       args.v3.ucEnable = ATOM_DISABLE;
        } else if (ASIC_IS_DCE4(rdev)) {
                args.v2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
-               args.v2.ucSpreadSpectrumType = ss->type;
+               args.v2.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
                switch (pll_id) {
                case ATOM_PPLL1:
                        args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P1PLL;
-                       args.v2.usSpreadSpectrumAmount = ss->amount;
-                       args.v2.usSpreadSpectrumStep = ss->step;
+                       args.v2.usSpreadSpectrumAmount = cpu_to_le16(ss->amount);
+                       args.v2.usSpreadSpectrumStep = cpu_to_le16(ss->step);
                        break;
                case ATOM_PPLL2:
                        args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P2PLL;
-                       args.v2.usSpreadSpectrumAmount = ss->amount;
-                       args.v2.usSpreadSpectrumStep = ss->step;
+                       args.v2.usSpreadSpectrumAmount = cpu_to_le16(ss->amount);
+                       args.v2.usSpreadSpectrumStep = cpu_to_le16(ss->step);
                        break;
                case ATOM_DCPLL:
                        args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_DCPLL;
-                       args.v2.usSpreadSpectrumAmount = 0;
-                       args.v2.usSpreadSpectrumStep = 0;
+                       args.v2.usSpreadSpectrumAmount = cpu_to_le16(0);
+                       args.v2.usSpreadSpectrumStep = cpu_to_le16(0);
                        break;
                case ATOM_PPLL_INVALID:
                        return;
                }
                args.v2.ucEnable = enable;
+               if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK) || ASIC_IS_DCE41(rdev))
+                       args.v2.ucEnable = ATOM_DISABLE;
        } else if (ASIC_IS_DCE3(rdev)) {
                args.v1.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
-               args.v1.ucSpreadSpectrumType = ss->type;
+               args.v1.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
                args.v1.ucSpreadSpectrumStep = ss->step;
                args.v1.ucSpreadSpectrumDelay = ss->delay;
                args.v1.ucSpreadSpectrumRange = ss->range;
                args.v1.ucPpll = pll_id;
                args.v1.ucEnable = enable;
        } else if (ASIC_IS_AVIVO(rdev)) {
-               if (enable == ATOM_DISABLE) {
-                       atombios_disable_ss(crtc);
+               if ((enable == ATOM_DISABLE) || (ss->percentage == 0) ||
+                   (ss->type & ATOM_EXTERNAL_SS_MASK)) {
+                       atombios_disable_ss(rdev, pll_id);
                        return;
                }
                args.lvds_ss_2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
-               args.lvds_ss_2.ucSpreadSpectrumType = ss->type;
+               args.lvds_ss_2.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
                args.lvds_ss_2.ucSpreadSpectrumStep = ss->step;
                args.lvds_ss_2.ucSpreadSpectrumDelay = ss->delay;
                args.lvds_ss_2.ucSpreadSpectrumRange = ss->range;
                args.lvds_ss_2.ucEnable = enable;
        } else {
-               if (enable == ATOM_DISABLE) {
-                       atombios_disable_ss(crtc);
+               if ((enable == ATOM_DISABLE) || (ss->percentage == 0) ||
+                   (ss->type & ATOM_EXTERNAL_SS_MASK)) {
+                       atombios_disable_ss(rdev, pll_id);
                        return;
                }
                args.lvds_ss.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
-               args.lvds_ss.ucSpreadSpectrumType = ss->type;
+               args.lvds_ss.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
                args.lvds_ss.ucSpreadSpectrumStepSize_Delay = (ss->step & 3) << 2;
                args.lvds_ss.ucSpreadSpectrumStepSize_Delay |= (ss->delay & 7) << 4;
                args.lvds_ss.ucEnable = enable;
@@ -512,10 +550,12 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
        struct radeon_device *rdev = dev->dev_private;
        struct drm_encoder *encoder = NULL;
        struct radeon_encoder *radeon_encoder = NULL;
+       struct drm_connector *connector = NULL;
        u32 adjusted_clock = mode->clock;
        int encoder_mode = 0;
        u32 dp_clock = mode->clock;
        int bpc = 8;
+       bool is_duallink = false;
 
        /* reset the pll flags */
        pll->flags = 0;
@@ -531,6 +571,12 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                        pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
                else
                        pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
+
+               if (rdev->family < CHIP_RV770)
+                       pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP;
+               /* use frac fb div on APUs */
+               if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev))
+                       pll->flags |= RADEON_PLL_USE_FRAC_FB_DIV;
        } else {
                pll->flags |= RADEON_PLL_LEGACY;
 
@@ -538,15 +584,18 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                        pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
                else
                        pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
-
        }
 
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                if (encoder->crtc == crtc) {
                        radeon_encoder = to_radeon_encoder(encoder);
+                       connector = radeon_get_connector_for_encoder(encoder);
+                       /* if (connector && connector->display_info.bpc)
+                               bpc = connector->display_info.bpc; */
                        encoder_mode = atombios_get_encoder_mode(encoder);
-                       if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) {
-                               struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+                       is_duallink = radeon_dig_monitor_is_duallink(encoder, mode->clock);
+                       if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) ||
+                           (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) {
                                if (connector) {
                                        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
                                        struct radeon_connector_atom_dig *dig_connector =
@@ -555,23 +604,27 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                                        dp_clock = dig_connector->dp_clock;
                                }
                        }
-#if 0 /* doesn't work properly on some laptops */
+
                        /* use recommended ref_div for ss */
                        if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
                                if (ss_enabled) {
                                        if (ss->refdiv) {
                                                pll->flags |= RADEON_PLL_USE_REF_DIV;
                                                pll->reference_div = ss->refdiv;
+                                               if (ASIC_IS_AVIVO(rdev))
+                                                       pll->flags |= RADEON_PLL_USE_FRAC_FB_DIV;
                                        }
                                }
                        }
-#endif
+
                        if (ASIC_IS_AVIVO(rdev)) {
                                /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */
                                if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1)
                                        adjusted_clock = mode->clock * 2;
                                if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
                                        pll->flags |= RADEON_PLL_PREFER_CLOSEST_LOWER;
+                               if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+                                       pll->flags |= RADEON_PLL_IS_LCD;
                        } else {
                                if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
                                        pll->flags |= RADEON_PLL_NO_ODD_POST_DIV;
@@ -606,7 +659,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                                args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
                                args.v1.ucTransmitterID = radeon_encoder->encoder_id;
                                args.v1.ucEncodeMode = encoder_mode;
-                               if (ss_enabled)
+                               if (ss_enabled && ss->percentage)
                                        args.v1.ucConfig |=
                                                ADJUST_DISPLAY_CONFIG_SS_ENABLE;
 
@@ -619,49 +672,44 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                                args.v3.sInput.ucTransmitterID = radeon_encoder->encoder_id;
                                args.v3.sInput.ucEncodeMode = encoder_mode;
                                args.v3.sInput.ucDispPllConfig = 0;
-                               if (ss_enabled)
+                               if (ss_enabled && ss->percentage)
                                        args.v3.sInput.ucDispPllConfig |=
                                                DISPPLL_CONFIG_SS_ENABLE;
-                               if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+                               if (ENCODER_MODE_IS_DP(encoder_mode)) {
+                                       args.v3.sInput.ucDispPllConfig |=
+                                               DISPPLL_CONFIG_COHERENT_MODE;
+                                       /* 16200 or 27000 */
+                                       args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10);
+                               } else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
                                        struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
-                                       if (encoder_mode == ATOM_ENCODER_MODE_DP) {
+                                       if (encoder_mode == ATOM_ENCODER_MODE_HDMI)
+                                               /* deep color support */
+                                               args.v3.sInput.usPixelClock =
+                                                       cpu_to_le16((mode->clock * bpc / 8) / 10);
+                                       if (dig->coherent_mode)
                                                args.v3.sInput.ucDispPllConfig |=
                                                        DISPPLL_CONFIG_COHERENT_MODE;
-                                               /* 16200 or 27000 */
-                                               args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10);
-                                       } else {
-                                               if (encoder_mode == ATOM_ENCODER_MODE_HDMI) {
-                                                       /* deep color support */
-                                                       args.v3.sInput.usPixelClock =
-                                                               cpu_to_le16((mode->clock * bpc / 8) / 10);
-                                               }
-                                               if (dig->coherent_mode)
-                                                       args.v3.sInput.ucDispPllConfig |=
-                                                               DISPPLL_CONFIG_COHERENT_MODE;
-                                               if (mode->clock > 165000)
-                                                       args.v3.sInput.ucDispPllConfig |=
-                                                               DISPPLL_CONFIG_DUAL_LINK;
-                                       }
-                               } else if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
-                                       if (encoder_mode == ATOM_ENCODER_MODE_DP) {
+                                       if (is_duallink)
                                                args.v3.sInput.ucDispPllConfig |=
-                                                       DISPPLL_CONFIG_COHERENT_MODE;
-                                               /* 16200 or 27000 */
-                                               args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10);
-                                       } else if (encoder_mode != ATOM_ENCODER_MODE_LVDS) {
-                                               if (mode->clock > 165000)
-                                                       args.v3.sInput.ucDispPllConfig |=
-                                                               DISPPLL_CONFIG_DUAL_LINK;
-                                       }
+                                                       DISPPLL_CONFIG_DUAL_LINK;
                                }
+                               if (radeon_encoder_get_dp_bridge_encoder_id(encoder) !=
+                                   ENCODER_OBJECT_ID_NONE)
+                                       args.v3.sInput.ucExtTransmitterID =
+                                               radeon_encoder_get_dp_bridge_encoder_id(encoder);
+                               else
+                                       args.v3.sInput.ucExtTransmitterID = 0;
+
                                atom_execute_table(rdev->mode_info.atom_context,
                                                   index, (uint32_t *)&args);
                                adjusted_clock = le32_to_cpu(args.v3.sOutput.ulDispPllFreq) * 10;
                                if (args.v3.sOutput.ucRefDiv) {
+                                       pll->flags |= RADEON_PLL_USE_FRAC_FB_DIV;
                                        pll->flags |= RADEON_PLL_USE_REF_DIV;
                                        pll->reference_div = args.v3.sOutput.ucRefDiv;
                                }
                                if (args.v3.sOutput.ucPostDiv) {
+                                       pll->flags |= RADEON_PLL_USE_FRAC_FB_DIV;
                                        pll->flags |= RADEON_PLL_USE_POST_DIV;
                                        pll->post_div = args.v3.sOutput.ucPostDiv;
                                }
@@ -691,11 +739,9 @@ union set_pixel_clock {
 /* on DCE5, make sure the voltage is high enough to support the
  * required disp clk.
  */
-static void atombios_crtc_set_dcpll(struct drm_crtc *crtc,
+static void atombios_crtc_set_disp_eng_pll(struct radeon_device *rdev,
                                    u32 dispclk)
 {
-       struct drm_device *dev = crtc->dev;
-       struct radeon_device *rdev = dev->dev_private;
        u8 frev, crev;
        int index;
        union set_pixel_clock args;
@@ -715,15 +761,20 @@ static void atombios_crtc_set_dcpll(struct drm_crtc *crtc,
                         * SetPixelClock provides the dividers
                         */
                        args.v5.ucCRTC = ATOM_CRTC_INVALID;
-                       args.v5.usPixelClock = dispclk;
+                       args.v5.usPixelClock = cpu_to_le16(dispclk);
                        args.v5.ucPpll = ATOM_DCPLL;
                        break;
                case 6:
                        /* if the default dcpll clock is specified,
                         * SetPixelClock provides the dividers
                         */
-                       args.v6.ulDispEngClkFreq = dispclk;
-                       args.v6.ucPpll = ATOM_DCPLL;
+                       args.v6.ulDispEngClkFreq = cpu_to_le32(dispclk);
+                       if (ASIC_IS_DCE61(rdev))
+                               args.v6.ucPpll = ATOM_EXT_PLL1;
+                       else if (ASIC_IS_DCE6(rdev))
+                               args.v6.ucPpll = ATOM_PPLL0;
+                       else
+                               args.v6.ucPpll = ATOM_DCPLL;
                        break;
                default:
                        DRM_ERROR("Unknown table version %d %d\n", frev, crev);
@@ -738,7 +789,7 @@ static void atombios_crtc_set_dcpll(struct drm_crtc *crtc,
 }
 
 static void atombios_crtc_program_pll(struct drm_crtc *crtc,
-                                     int crtc_id,
+                                     u32 crtc_id,
                                      int pll_id,
                                      u32 encoder_mode,
                                      u32 encoder_id,
@@ -746,7 +797,10 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
                                      u32 ref_div,
                                      u32 fb_div,
                                      u32 frac_fb_div,
-                                     u32 post_div)
+                                     u32 post_div,
+                                     int bpc,
+                                     bool ss_enabled,
+                                     struct radeon_atom_ss *ss)
 {
        struct drm_device *dev = crtc->dev;
        struct radeon_device *rdev = dev->dev_private;
@@ -793,6 +847,8 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
                        args.v3.ucPostDiv = post_div;
                        args.v3.ucPpll = pll_id;
                        args.v3.ucMiscInfo = (pll_id << 2);
+                       if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
+                               args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC;
                        args.v3.ucTransmitterId = encoder_id;
                        args.v3.ucEncoderMode = encoder_mode;
                        break;
@@ -804,18 +860,45 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
                        args.v5.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000);
                        args.v5.ucPostDiv = post_div;
                        args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */
+                       if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
+                               args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC;
+                       switch (bpc) {
+                       case 8:
+                       default:
+                               args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP;
+                               break;
+                       case 10:
+                               args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP;
+                               break;
+                       }
                        args.v5.ucTransmitterID = encoder_id;
                        args.v5.ucEncoderMode = encoder_mode;
                        args.v5.ucPpll = pll_id;
                        break;
                case 6:
-                       args.v6.ulCrtcPclkFreq.ucCRTC = crtc_id;
-                       args.v6.ulCrtcPclkFreq.ulPixelClock = cpu_to_le32(clock / 10);
+                       args.v6.ulDispEngClkFreq = cpu_to_le32(crtc_id << 24 | clock / 10);
                        args.v6.ucRefDiv = ref_div;
                        args.v6.usFbDiv = cpu_to_le16(fb_div);
                        args.v6.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000);
                        args.v6.ucPostDiv = post_div;
                        args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */
+                       if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
+                               args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC;
+                       switch (bpc) {
+                       case 8:
+                       default:
+                               args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP;
+                               break;
+                       case 10:
+                               args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP;
+                               break;
+                       case 12:
+                               args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP;
+                               break;
+                       case 16:
+                               args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP;
+                               break;
+                       }
                        args.v6.ucTransmitterID = encoder_id;
                        args.v6.ucEncoderMode = encoder_mode;
                        args.v6.ucPpll = pll_id;
@@ -847,6 +930,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
        int encoder_mode = 0;
        struct radeon_atom_ss ss;
        bool ss_enabled = false;
+       int bpc = 8;
 
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                if (encoder->crtc == crtc) {
@@ -873,8 +957,8 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
                break;
        }
 
-       if (radeon_encoder->active_device &
-           (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) {
+       if ((radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) ||
+           (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)) {
                struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
                struct drm_connector *connector =
                        radeon_get_connector_for_encoder(encoder);
@@ -884,40 +968,32 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
                        radeon_connector->con_priv;
                int dp_clock;
 
+               /* if (connector->display_info.bpc)
+                       bpc = connector->display_info.bpc; */
+
                switch (encoder_mode) {
+               case ATOM_ENCODER_MODE_DP_MST:
                case ATOM_ENCODER_MODE_DP:
                        /* DP/eDP */
                        dp_clock = dig_connector->dp_clock / 10;
-                       if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
-                               if (ASIC_IS_DCE4(rdev))
-                                       ss_enabled =
-                                               radeon_atombios_get_asic_ss_info(rdev, &ss,
-                                                                                dig->lcd_ss_id,
-                                                                                dp_clock);
-                               else
+                       if (ASIC_IS_DCE4(rdev))
+                               ss_enabled =
+                                       radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                                        ASIC_INTERNAL_SS_ON_DP,
+                                                                        dp_clock);
+                       else {
+                               if (dp_clock == 16200) {
                                        ss_enabled =
                                                radeon_atombios_get_ppll_ss_info(rdev, &ss,
-                                                                                dig->lcd_ss_id);
-                       } else {
-                               if (ASIC_IS_DCE4(rdev))
-                                       ss_enabled =
-                                               radeon_atombios_get_asic_ss_info(rdev, &ss,
-                                                                                ASIC_INTERNAL_SS_ON_DP,
-                                                                                dp_clock);
-                               else {
-                                       if (dp_clock == 16200) {
-                                               ss_enabled =
-                                                       radeon_atombios_get_ppll_ss_info(rdev, &ss,
-                                                                                        ATOM_DP_SS_ID2);
-                                               if (!ss_enabled)
-                                                       ss_enabled =
-                                                               radeon_atombios_get_ppll_ss_info(rdev, &ss,
-                                                                                                ATOM_DP_SS_ID1);
-                                       } else
+                                                                                ATOM_DP_SS_ID2);
+                                       if (!ss_enabled)
                                                ss_enabled =
                                                        radeon_atombios_get_ppll_ss_info(rdev, &ss,
                                                                                         ATOM_DP_SS_ID1);
-                               }
+                               } else
+                                       ss_enabled =
+                                               radeon_atombios_get_ppll_ss_info(rdev, &ss,
+                                                                                ATOM_DP_SS_ID1);
                        }
                        break;
                case ATOM_ENCODER_MODE_LVDS:
@@ -951,14 +1027,22 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
        /* adjust pixel clock as needed */
        adjusted_clock = atombios_adjust_pll(crtc, mode, pll, ss_enabled, &ss);
 
-       radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
-                                 &ref_div, &post_div);
+       if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
+               /* TV seems to prefer the legacy algo on some boards */
+               radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
+                                         &ref_div, &post_div);
+       else if (ASIC_IS_AVIVO(rdev))
+               radeon_compute_pll_avivo(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
+                                        &ref_div, &post_div);
+       else
+               radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
+                                         &ref_div, &post_div);
 
-       atombios_crtc_program_ss(crtc, ATOM_DISABLE, radeon_crtc->pll_id, &ss);
+       atombios_crtc_program_ss(rdev, ATOM_DISABLE, radeon_crtc->pll_id, radeon_crtc->crtc_id, &ss);
 
        atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
                                  encoder_mode, radeon_encoder->encoder_id, mode->clock,
-                                 ref_div, fb_div, frac_fb_div, post_div);
+                                 ref_div, fb_div, frac_fb_div, post_div, bpc, ss_enabled, &ss);
 
        if (ss_enabled) {
                /* calculate ss amount and step size */
@@ -966,7 +1050,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
                        u32 step_size;
                        u32 amount = (((fb_div * 10) + frac_fb_div) * ss.percentage) / 10000;
                        ss.amount = (amount / 10) & ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK;
-                       ss.amount |= ((amount - (ss.amount * 10)) << ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) &
+                       ss.amount |= ((amount - (amount / 10)) << ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) &
                                ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK;
                        if (ss.type & ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD)
                                step_size = (4 * amount * ref_div * (ss.rate * 2048)) /
@@ -977,13 +1061,13 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
                        ss.step = step_size;
                }
 
-               atombios_crtc_program_ss(crtc, ATOM_ENABLE, radeon_crtc->pll_id, &ss);
+               atombios_crtc_program_ss(rdev, ATOM_ENABLE, radeon_crtc->pll_id, radeon_crtc->crtc_id, &ss);
        }
 }
 
-static int evergreen_crtc_do_set_base(struct drm_crtc *crtc,
-                                     struct drm_framebuffer *fb,
-                                     int x, int y, int atomic)
+static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
+                                struct drm_framebuffer *fb,
+                                int x, int y, int atomic)
 {
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
        struct drm_device *dev = crtc->dev;
@@ -994,7 +1078,9 @@ static int evergreen_crtc_do_set_base(struct drm_crtc *crtc,
        struct radeon_bo *rbo;
        uint64_t fb_location;
        uint32_t fb_format, fb_pitch_pixels, tiling_flags;
+       unsigned bankw, bankh, mtaspect, tile_split;
        u32 fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE);
+       u32 tmp, viewport_w, viewport_h;
        int r;
 
        /* no fb bound */
@@ -1016,7 +1102,7 @@ static int evergreen_crtc_do_set_base(struct drm_crtc *crtc,
         * just update base pointers
         */
        obj = radeon_fb->obj;
-       rbo = obj->driver_private;
+       rbo = gem_to_radeon_bo(obj);
        r = radeon_bo_reserve(rbo, false);
        if (unlikely(r != 0))
                return r;
@@ -1064,9 +1150,33 @@ static int evergreen_crtc_do_set_base(struct drm_crtc *crtc,
                return -EINVAL;
        }
 
-       if (tiling_flags & RADEON_TILING_MACRO)
+       if (tiling_flags & RADEON_TILING_MACRO) {
+               if (rdev->family >= CHIP_CAYMAN)
+                       tmp = rdev->config.cayman.tile_config;
+               else
+                       tmp = rdev->config.evergreen.tile_config;
+
+               switch ((tmp & 0xf0) >> 4) {
+               case 0: /* 4 banks */
+                       fb_format |= EVERGREEN_GRPH_NUM_BANKS(EVERGREEN_ADDR_SURF_4_BANK);
+                       break;
+               case 1: /* 8 banks */
+               default:
+                       fb_format |= EVERGREEN_GRPH_NUM_BANKS(EVERGREEN_ADDR_SURF_8_BANK);
+                       break;
+               case 2: /* 16 banks */
+                       fb_format |= EVERGREEN_GRPH_NUM_BANKS(EVERGREEN_ADDR_SURF_16_BANK);
+                       break;
+               }
+
                fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_2D_TILED_THIN1);
-       else if (tiling_flags & RADEON_TILING_MICRO)
+
+               evergreen_tiling_fields(tiling_flags, &bankw, &bankh, &mtaspect, &tile_split);
+               fb_format |= EVERGREEN_GRPH_TILE_SPLIT(tile_split);
+               fb_format |= EVERGREEN_GRPH_BANK_WIDTH(bankw);
+               fb_format |= EVERGREEN_GRPH_BANK_HEIGHT(bankh);
+               fb_format |= EVERGREEN_GRPH_MACRO_TILE_ASPECT(mtaspect);
+       } else if (tiling_flags & RADEON_TILING_MICRO)
                fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1);
 
        switch (radeon_crtc->crtc_id) {
@@ -1110,28 +1220,33 @@ static int evergreen_crtc_do_set_base(struct drm_crtc *crtc,
        WREG32(EVERGREEN_GRPH_X_END + radeon_crtc->crtc_offset, target_fb->width);
        WREG32(EVERGREEN_GRPH_Y_END + radeon_crtc->crtc_offset, target_fb->height);
 
-       fb_pitch_pixels = target_fb->pitch / (target_fb->bits_per_pixel / 8);
+       fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8);
        WREG32(EVERGREEN_GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels);
        WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1);
 
        WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset,
-              crtc->mode.vdisplay);
+              target_fb->height);
        x &= ~3;
        y &= ~1;
        WREG32(EVERGREEN_VIEWPORT_START + radeon_crtc->crtc_offset,
               (x << 16) | y);
+       viewport_w = crtc->mode.hdisplay;
+       viewport_h = (crtc->mode.vdisplay + 1) & ~1;
        WREG32(EVERGREEN_VIEWPORT_SIZE + radeon_crtc->crtc_offset,
-              (crtc->mode.hdisplay << 16) | crtc->mode.vdisplay);
+              (viewport_w << 16) | viewport_h);
 
-       if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE)
-               WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset,
-                      EVERGREEN_INTERLEAVE_EN);
-       else
-               WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
+       /* pageflip setup */
+       /* make sure flip is at vb rather than hb */
+       tmp = RREG32(EVERGREEN_GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset);
+       tmp &= ~EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN;
+       WREG32(EVERGREEN_GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset, tmp);
+
+       /* set pageflip to happen anywhere in vblank interval */
+       WREG32(EVERGREEN_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0);
 
        if (!atomic && fb && fb != crtc->fb) {
                radeon_fb = to_radeon_framebuffer(fb);
-               rbo = radeon_fb->obj->driver_private;
+               rbo = gem_to_radeon_bo(radeon_fb->obj);
                r = radeon_bo_reserve(rbo, false);
                if (unlikely(r != 0))
                        return r;
@@ -1159,6 +1274,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc,
        uint64_t fb_location;
        uint32_t fb_format, fb_pitch_pixels, tiling_flags;
        u32 fb_swap = R600_D1GRPH_SWAP_ENDIAN_NONE;
+       u32 tmp, viewport_w, viewport_h;
        int r;
 
        /* no fb bound */
@@ -1177,7 +1293,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc,
        }
 
        obj = radeon_fb->obj;
-       rbo = obj->driver_private;
+       rbo = gem_to_radeon_bo(obj);
        r = radeon_bo_reserve(rbo, false);
        if (unlikely(r != 0))
                return r;
@@ -1273,28 +1389,33 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc,
        WREG32(AVIVO_D1GRPH_X_END + radeon_crtc->crtc_offset, target_fb->width);
        WREG32(AVIVO_D1GRPH_Y_END + radeon_crtc->crtc_offset, target_fb->height);
 
-       fb_pitch_pixels = target_fb->pitch / (target_fb->bits_per_pixel / 8);
+       fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8);
        WREG32(AVIVO_D1GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels);
        WREG32(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1);
 
        WREG32(AVIVO_D1MODE_DESKTOP_HEIGHT + radeon_crtc->crtc_offset,
-              crtc->mode.vdisplay);
+              target_fb->height);
        x &= ~3;
        y &= ~1;
        WREG32(AVIVO_D1MODE_VIEWPORT_START + radeon_crtc->crtc_offset,
               (x << 16) | y);
+       viewport_w = crtc->mode.hdisplay;
+       viewport_h = (crtc->mode.vdisplay + 1) & ~1;
        WREG32(AVIVO_D1MODE_VIEWPORT_SIZE + radeon_crtc->crtc_offset,
-              (crtc->mode.hdisplay << 16) | crtc->mode.vdisplay);
+              (viewport_w << 16) | viewport_h);
 
-       if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE)
-               WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset,
-                      AVIVO_D1MODE_INTERLEAVE_EN);
-       else
-               WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
+       /* pageflip setup */
+       /* make sure flip is at vb rather than hb */
+       tmp = RREG32(AVIVO_D1GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset);
+       tmp &= ~AVIVO_D1GRPH_SURFACE_UPDATE_H_RETRACE_EN;
+       WREG32(AVIVO_D1GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset, tmp);
+
+       /* set pageflip to happen anywhere in vblank interval */
+       WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0);
 
        if (!atomic && fb && fb != crtc->fb) {
                radeon_fb = to_radeon_framebuffer(fb);
-               rbo = radeon_fb->obj->driver_private;
+               rbo = gem_to_radeon_bo(radeon_fb->obj);
                r = radeon_bo_reserve(rbo, false);
                if (unlikely(r != 0))
                        return r;
@@ -1315,7 +1436,7 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
        struct radeon_device *rdev = dev->dev_private;
 
        if (ASIC_IS_DCE4(rdev))
-               return evergreen_crtc_do_set_base(crtc, old_fb, x, y, 0);
+               return dce4_crtc_do_set_base(crtc, old_fb, x, y, 0);
        else if (ASIC_IS_AVIVO(rdev))
                return avivo_crtc_do_set_base(crtc, old_fb, x, y, 0);
        else
@@ -1330,7 +1451,7 @@ int atombios_crtc_set_base_atomic(struct drm_crtc *crtc,
        struct radeon_device *rdev = dev->dev_private;
 
        if (ASIC_IS_DCE4(rdev))
-               return evergreen_crtc_do_set_base(crtc, fb, x, y, 1);
+               return dce4_crtc_do_set_base(crtc, fb, x, y, 1);
        else if (ASIC_IS_AVIVO(rdev))
                return avivo_crtc_do_set_base(crtc, fb, x, y, 1);
        else
@@ -1370,12 +1491,53 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
        struct drm_crtc *test_crtc;
        uint32_t pll_in_use = 0;
 
-       if (ASIC_IS_DCE4(rdev)) {
-               /* if crtc is driving DP and we have an ext clock, use that */
+       if (ASIC_IS_DCE61(rdev)) {
+               list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
+                       if (test_encoder->crtc && (test_encoder->crtc == crtc)) {
+                               struct radeon_encoder *test_radeon_encoder =
+                                       to_radeon_encoder(test_encoder);
+                               struct radeon_encoder_atom_dig *dig =
+                                       test_radeon_encoder->enc_priv;
+
+                               if ((test_radeon_encoder->encoder_id ==
+                                    ENCODER_OBJECT_ID_INTERNAL_UNIPHY) &&
+                                   (dig->linkb == false)) /* UNIPHY A uses PPLL2 */
+                                       return ATOM_PPLL2;
+                       }
+               }
+               /* UNIPHY B/C/D/E/F */
+               list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
+                       struct radeon_crtc *radeon_test_crtc;
+
+                       if (crtc == test_crtc)
+                               continue;
+
+                       radeon_test_crtc = to_radeon_crtc(test_crtc);
+                       if ((radeon_test_crtc->pll_id == ATOM_PPLL0) ||
+                           (radeon_test_crtc->pll_id == ATOM_PPLL1))
+                               pll_in_use |= (1 << radeon_test_crtc->pll_id);
+               }
+               if (!(pll_in_use & 4))
+                       return ATOM_PPLL0;
+               return ATOM_PPLL1;
+       } else if (ASIC_IS_DCE4(rdev)) {
                list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
                        if (test_encoder->crtc && (test_encoder->crtc == crtc)) {
-                               if (atombios_get_encoder_mode(test_encoder) == ATOM_ENCODER_MODE_DP) {
-                                       if (rdev->clock.dp_extclk)
+                               /* in DP mode, the DP ref clock can come from PPLL, DCPLL, or ext clock,
+                                * depending on the asic:
+                                * DCE4: PPLL or ext clock
+                                * DCE5: DCPLL or ext clock
+                                *
+                                * Setting ATOM_PPLL_INVALID will cause SetPixelClock to skip
+                                * PPLL/DCPLL programming and only program the DP DTO for the
+                                * crtc virtual pixel clock.
+                                */
+                               if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
+                                       if (ASIC_IS_DCE5(rdev))
+                                               return ATOM_DCPLL;
+                                       else if (ASIC_IS_DCE6(rdev))
+                                               return ATOM_PPLL0;
+                                       else if (rdev->clock.dp_extclk)
                                                return ATOM_PPLL_INVALID;
                                }
                        }
@@ -1401,6 +1563,26 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
 
 }
 
+void radeon_atom_disp_eng_pll_init(struct radeon_device *rdev)
+{
+       /* always set DCPLL */
+       if (ASIC_IS_DCE6(rdev))
+               atombios_crtc_set_disp_eng_pll(rdev, rdev->clock.default_dispclk);
+       else if (ASIC_IS_DCE4(rdev)) {
+               struct radeon_atom_ss ss;
+               bool ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                                  ASIC_INTERNAL_SS_ON_DCPLL,
+                                                                  rdev->clock.default_dispclk);
+               if (ss_enabled)
+                       atombios_crtc_program_ss(rdev, ATOM_DISABLE, ATOM_DCPLL, -1, &ss);
+               /* XXX: DCE5, make sure voltage, dispclk is high enough */
+               atombios_crtc_set_disp_eng_pll(rdev, rdev->clock.default_dispclk);
+               if (ss_enabled)
+                       atombios_crtc_program_ss(rdev, ATOM_ENABLE, ATOM_DCPLL, -1, &ss);
+       }
+
+}
+
 int atombios_crtc_mode_set(struct drm_crtc *crtc,
                           struct drm_display_mode *mode,
                           struct drm_display_mode *adjusted_mode,
@@ -1422,19 +1604,6 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,
                }
        }
 
-       /* always set DCPLL */
-       if (ASIC_IS_DCE4(rdev)) {
-               struct radeon_atom_ss ss;
-               bool ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss,
-                                                                  ASIC_INTERNAL_SS_ON_DCPLL,
-                                                                  rdev->clock.default_dispclk);
-               if (ss_enabled)
-                       atombios_crtc_program_ss(crtc, ATOM_DISABLE, ATOM_DCPLL, &ss);
-               /* XXX: DCE5, make sure voltage, dispclk is high enough */
-               atombios_crtc_set_dcpll(crtc, rdev->clock.default_dispclk);
-               if (ss_enabled)
-                       atombios_crtc_program_ss(crtc, ATOM_ENABLE, ATOM_DCPLL, &ss);
-       }
        atombios_crtc_set_pll(crtc, adjusted_mode);
 
        if (ASIC_IS_DCE4(rdev))
@@ -1460,12 +1629,6 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc,
                                     struct drm_display_mode *mode,
                                     struct drm_display_mode *adjusted_mode)
 {
-       struct drm_device *dev = crtc->dev;
-       struct radeon_device *rdev = dev->dev_private;
-
-       /* adjust pm to upcoming mode change */
-       radeon_pm_compute_clocks(rdev);
-
        if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode))
                return false;
        return true;
@@ -1474,35 +1637,69 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc,
 static void atombios_crtc_prepare(struct drm_crtc *crtc)
 {
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
 
+       radeon_crtc->in_mode_set = true;
        /* pick pll */
        radeon_crtc->pll_id = radeon_atom_pick_pll(crtc);
 
+       /* disable crtc pair power gating before programming */
+       if (ASIC_IS_DCE6(rdev))
+               atombios_powergate_crtc(crtc, ATOM_DISABLE);
+
        atombios_lock_crtc(crtc, ATOM_ENABLE);
        atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
 }
 
 static void atombios_crtc_commit(struct drm_crtc *crtc)
 {
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+
        atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
        atombios_lock_crtc(crtc, ATOM_DISABLE);
+       radeon_crtc->in_mode_set = false;
 }
 
 static void atombios_crtc_disable(struct drm_crtc *crtc)
 {
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_atom_ss ss;
+       int i;
+
        atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
 
+       for (i = 0; i < rdev->num_crtc; i++) {
+               if (rdev->mode_info.crtcs[i] &&
+                   rdev->mode_info.crtcs[i]->enabled &&
+                   i != radeon_crtc->crtc_id &&
+                   radeon_crtc->pll_id == rdev->mode_info.crtcs[i]->pll_id) {
+                       /* one other crtc is using this pll don't turn
+                        * off the pll
+                        */
+                       goto done;
+               }
+       }
+
        switch (radeon_crtc->pll_id) {
        case ATOM_PPLL1:
        case ATOM_PPLL2:
                /* disable the ppll */
                atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
-                                         0, 0, ATOM_DISABLE, 0, 0, 0, 0);
+                                         0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss);
+               break;
+       case ATOM_PPLL0:
+               /* disable the ppll */
+               if (ASIC_IS_DCE61(rdev))
+                       atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
+                                                 0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss);
                break;
        default:
                break;
        }
+done:
        radeon_crtc->pll_id = -1;
 }