26,7 → 26,7 |
#include <drm/drmP.h> |
#include <drm/drm_crtc_helper.h> |
#include <drm/radeon_drm.h> |
#include "radeon_fixed.h" |
#include <drm/drm_fixed.h> |
#include "radeon.h" |
#include "atom.h" |
#include "atom-bits.h" |
44,19 → 44,14 |
|
memset(&args, 0, sizeof(args)); |
|
args.usOverscanRight = 0; |
args.usOverscanLeft = 0; |
args.usOverscanBottom = 0; |
args.usOverscanTop = 0; |
args.ucCRTC = radeon_crtc->crtc_id; |
|
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; |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
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; |
63,23 → 58,22 |
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); |
} |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
break; |
case RMX_FULL: |
default: |
args.usOverscanRight = 0; |
args.usOverscanLeft = 0; |
args.usOverscanBottom = 0; |
args.usOverscanTop = 0; |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
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); |
} |
|
static void atombios_scaler_setup(struct drm_crtc *crtc) |
245,12 → 239,13 |
|
switch (mode) { |
case DRM_MODE_DPMS_ON: |
radeon_crtc->enabled = true; |
/* adjust pm to dpms changes BEFORE enabling crtcs */ |
radeon_pm_compute_clocks(rdev); |
atombios_enable_crtc(crtc, ATOM_ENABLE); |
if (ASIC_IS_DCE3(rdev)) |
atombios_enable_crtc_memreq(crtc, ATOM_ENABLE); |
atombios_blank_crtc(crtc, ATOM_DISABLE); |
/* XXX re-enable when interrupt support is added */ |
if (!ASIC_IS_DCE4(rdev)) |
drm_vblank_post_modeset(dev, radeon_crtc->crtc_id); |
radeon_crtc_load_lut(crtc); |
break; |
257,13 → 252,15 |
case DRM_MODE_DPMS_STANDBY: |
case DRM_MODE_DPMS_SUSPEND: |
case DRM_MODE_DPMS_OFF: |
/* XXX re-enable when interrupt support is added */ |
if (!ASIC_IS_DCE4(rdev)) |
drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id); |
if (radeon_crtc->enabled) |
atombios_blank_crtc(crtc, ATOM_ENABLE); |
if (ASIC_IS_DCE3(rdev)) |
atombios_enable_crtc_memreq(crtc, ATOM_DISABLE); |
atombios_enable_crtc(crtc, ATOM_DISABLE); |
radeon_crtc->enabled = false; |
/* adjust pm to dpms changes AFTER disabling crtcs */ |
radeon_pm_compute_clocks(rdev); |
break; |
} |
} |
280,22 → 277,22 |
u16 misc = 0; |
|
memset(&args, 0, sizeof(args)); |
args.usH_Size = cpu_to_le16(mode->crtc_hdisplay); |
args.usH_Size = cpu_to_le16(mode->crtc_hdisplay - (radeon_crtc->h_border * 2)); |
args.usH_Blanking_Time = |
cpu_to_le16(mode->crtc_hblank_end - mode->crtc_hdisplay); |
args.usV_Size = cpu_to_le16(mode->crtc_vdisplay); |
cpu_to_le16(mode->crtc_hblank_end - mode->crtc_hdisplay + (radeon_crtc->h_border * 2)); |
args.usV_Size = cpu_to_le16(mode->crtc_vdisplay - (radeon_crtc->v_border * 2)); |
args.usV_Blanking_Time = |
cpu_to_le16(mode->crtc_vblank_end - mode->crtc_vdisplay); |
cpu_to_le16(mode->crtc_vblank_end - mode->crtc_vdisplay + (radeon_crtc->v_border * 2)); |
args.usH_SyncOffset = |
cpu_to_le16(mode->crtc_hsync_start - mode->crtc_hdisplay); |
cpu_to_le16(mode->crtc_hsync_start - mode->crtc_hdisplay + radeon_crtc->h_border); |
args.usH_SyncWidth = |
cpu_to_le16(mode->crtc_hsync_end - mode->crtc_hsync_start); |
args.usV_SyncOffset = |
cpu_to_le16(mode->crtc_vsync_start - mode->crtc_vdisplay); |
cpu_to_le16(mode->crtc_vsync_start - mode->crtc_vdisplay + radeon_crtc->v_border); |
args.usV_SyncWidth = |
cpu_to_le16(mode->crtc_vsync_end - mode->crtc_vsync_start); |
/*args.ucH_Border = mode->hborder;*/ |
/*args.ucV_Border = mode->vborder;*/ |
args.ucH_Border = radeon_crtc->h_border; |
args.ucV_Border = radeon_crtc->v_border; |
|
if (mode->flags & DRM_MODE_FLAG_NVSYNC) |
misc |= ATOM_VSYNC_POLARITY; |
336,6 → 333,11 |
args.usV_SyncWidth = |
cpu_to_le16(mode->crtc_vsync_end - mode->crtc_vsync_start); |
|
args.ucOverscanRight = radeon_crtc->h_border; |
args.ucOverscanLeft = radeon_crtc->h_border; |
args.ucOverscanBottom = radeon_crtc->v_border; |
args.ucOverscanTop = radeon_crtc->v_border; |
|
if (mode->flags & DRM_MODE_FLAG_NVSYNC) |
misc |= ATOM_VSYNC_POLARITY; |
if (mode->flags & DRM_MODE_FLAG_NHSYNC) |
353,67 → 355,151 |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
} |
|
static void atombios_disable_ss(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; |
u32 ss_cntl; |
|
if (ASIC_IS_DCE4(rdev)) { |
switch (radeon_crtc->pll_id) { |
case ATOM_PPLL1: |
ss_cntl = RREG32(EVERGREEN_P1PLL_SS_CNTL); |
ss_cntl &= ~EVERGREEN_PxPLL_SS_EN; |
WREG32(EVERGREEN_P1PLL_SS_CNTL, ss_cntl); |
break; |
case ATOM_PPLL2: |
ss_cntl = RREG32(EVERGREEN_P2PLL_SS_CNTL); |
ss_cntl &= ~EVERGREEN_PxPLL_SS_EN; |
WREG32(EVERGREEN_P2PLL_SS_CNTL, ss_cntl); |
break; |
case ATOM_DCPLL: |
case ATOM_PPLL_INVALID: |
return; |
} |
} else if (ASIC_IS_AVIVO(rdev)) { |
switch (radeon_crtc->pll_id) { |
case ATOM_PPLL1: |
ss_cntl = RREG32(AVIVO_P1PLL_INT_SS_CNTL); |
ss_cntl &= ~1; |
WREG32(AVIVO_P1PLL_INT_SS_CNTL, ss_cntl); |
break; |
case ATOM_PPLL2: |
ss_cntl = RREG32(AVIVO_P2PLL_INT_SS_CNTL); |
ss_cntl &= ~1; |
WREG32(AVIVO_P2PLL_INT_SS_CNTL, ss_cntl); |
break; |
case ATOM_DCPLL: |
case ATOM_PPLL_INVALID: |
return; |
} |
} |
} |
|
|
union atom_enable_ss { |
ENABLE_LVDS_SS_PARAMETERS legacy; |
ENABLE_LVDS_SS_PARAMETERS lvds_ss; |
ENABLE_LVDS_SS_PARAMETERS_V2 lvds_ss_2; |
ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION v1; |
ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 v2; |
ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 v3; |
}; |
|
static void atombios_set_ss(struct drm_crtc *crtc, int enable) |
static void atombios_crtc_program_ss(struct drm_crtc *crtc, |
int enable, |
int pll_id, |
struct radeon_atom_ss *ss) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct drm_encoder *encoder = NULL; |
struct radeon_encoder *radeon_encoder = NULL; |
struct radeon_encoder_atom_dig *dig = NULL; |
int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL); |
union atom_enable_ss args; |
uint16_t percentage = 0; |
uint8_t type = 0, step = 0, delay = 0, range = 0; |
|
/* XXX add ss support for DCE4 */ |
if (ASIC_IS_DCE4(rdev)) |
return; |
memset(&args, 0, sizeof(args)); |
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
if (encoder->crtc == crtc) { |
radeon_encoder = to_radeon_encoder(encoder); |
/* only enable spread spectrum on LVDS */ |
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { |
dig = radeon_encoder->enc_priv; |
if (dig && dig->ss) { |
percentage = dig->ss->percentage; |
type = dig->ss->type; |
step = dig->ss->step; |
delay = dig->ss->delay; |
range = dig->ss->range; |
} else if (enable) |
if (ASIC_IS_DCE5(rdev)) { |
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 = 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 = 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 = cpu_to_le16(0); |
args.v3.usSpreadSpectrumStep = cpu_to_le16(0); |
break; |
case ATOM_PPLL_INVALID: |
return; |
} else if (enable) |
} |
args.v3.ucEnable = enable; |
if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK)) |
args.v3.ucEnable = ATOM_DISABLE; |
} else if (ASIC_IS_DCE4(rdev)) { |
args.v2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); |
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 = 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 = 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 = cpu_to_le16(0); |
args.v2.usSpreadSpectrumStep = cpu_to_le16(0); |
break; |
case ATOM_PPLL_INVALID: |
return; |
break; |
} |
args.v2.ucEnable = enable; |
if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK)) |
args.v2.ucEnable = ATOM_DISABLE; |
} else if (ASIC_IS_DCE3(rdev)) { |
args.v1.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); |
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) || (ss->percentage == 0) || |
(ss->type & ATOM_EXTERNAL_SS_MASK)) { |
atombios_disable_ss(crtc); |
return; |
} |
|
if (!radeon_encoder) |
args.lvds_ss_2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); |
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) || (ss->percentage == 0) || |
(ss->type & ATOM_EXTERNAL_SS_MASK)) { |
atombios_disable_ss(crtc); |
return; |
|
memset(&args, 0, sizeof(args)); |
if (ASIC_IS_AVIVO(rdev)) { |
args.v1.usSpreadSpectrumPercentage = cpu_to_le16(percentage); |
args.v1.ucSpreadSpectrumType = type; |
args.v1.ucSpreadSpectrumStep = step; |
args.v1.ucSpreadSpectrumDelay = delay; |
args.v1.ucSpreadSpectrumRange = range; |
args.v1.ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; |
args.v1.ucEnable = enable; |
} else { |
args.legacy.usSpreadSpectrumPercentage = cpu_to_le16(percentage); |
args.legacy.ucSpreadSpectrumType = type; |
args.legacy.ucSpreadSpectrumStepSize_Delay = (step & 3) << 2; |
args.legacy.ucSpreadSpectrumStepSize_Delay |= (delay & 7) << 4; |
args.legacy.ucEnable = enable; |
} |
args.lvds_ss.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage); |
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; |
} |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
} |
|
424,36 → 510,28 |
|
static u32 atombios_adjust_pll(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct radeon_pll *pll) |
struct radeon_pll *pll, |
bool ss_enabled, |
struct radeon_atom_ss *ss) |
{ |
struct drm_device *dev = crtc->dev; |
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; |
|
/* reset the pll flags */ |
pll->flags = 0; |
|
/* select the PLL algo */ |
if (ASIC_IS_AVIVO(rdev)) { |
if (radeon_new_pll == 0) |
pll->algo = PLL_ALGO_LEGACY; |
else |
pll->algo = PLL_ALGO_NEW; |
} else { |
if (radeon_new_pll == 1) |
pll->algo = PLL_ALGO_NEW; |
else |
pll->algo = PLL_ALGO_LEGACY; |
} |
|
if (ASIC_IS_AVIVO(rdev)) { |
if ((rdev->family == CHIP_RS600) || |
(rdev->family == CHIP_RS690) || |
(rdev->family == CHIP_RS740)) |
pll->flags |= (RADEON_PLL_USE_FRAC_FB_DIV | |
pll->flags |= (/*RADEON_PLL_USE_FRAC_FB_DIV |*/ |
RADEON_PLL_PREFER_CLOSEST_LOWER); |
|
if (ASIC_IS_DCE32(rdev) && mode->clock > 200000) /* range limits??? */ |
460,6 → 538,9 |
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; |
} else { |
pll->flags |= RADEON_PLL_LEGACY; |
|
467,22 → 548,46 |
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) |
bpc = connector->display_info.bpc; |
encoder_mode = atombios_get_encoder_mode(encoder); |
if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) || |
radeon_encoder_is_dp_bridge(encoder)) { |
if (connector) { |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
struct radeon_connector_atom_dig *dig_connector = |
radeon_connector->con_priv; |
|
dp_clock = dig_connector->dp_clock; |
} |
} |
|
/* 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; |
} |
} |
} |
|
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; |
/* LVDS PLL quirks */ |
if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) { |
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; |
pll->algo = dig->pll_algo; |
} |
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; |
503,8 → 608,9 |
int index; |
|
index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll); |
atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, |
&crev); |
if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, |
&crev)) |
return adjusted_clock; |
|
memset(&args, 0, sizeof(args)); |
|
516,6 → 622,9 |
args.v1.usPixelClock = cpu_to_le16(mode->clock / 10); |
args.v1.ucTransmitterID = radeon_encoder->encoder_id; |
args.v1.ucEncodeMode = encoder_mode; |
if (ss_enabled && ss->percentage) |
args.v1.ucConfig |= |
ADJUST_DISPLAY_CONFIG_SS_ENABLE; |
|
atom_execute_table(rdev->mode_info.atom_context, |
index, (uint32_t *)&args); |
526,13 → 635,23 |
args.v3.sInput.ucTransmitterID = radeon_encoder->encoder_id; |
args.v3.sInput.ucEncodeMode = encoder_mode; |
args.v3.sInput.ucDispPllConfig = 0; |
if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { |
if (ss_enabled && ss->percentage) |
args.v3.sInput.ucDispPllConfig |= |
DISPPLL_CONFIG_SS_ENABLE; |
if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT) || |
radeon_encoder_is_dp_bridge(encoder)) { |
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; |
|
if (encoder_mode == ATOM_ENCODER_MODE_DP) |
if (encoder_mode == ATOM_ENCODER_MODE_DP) { |
args.v3.sInput.ucDispPllConfig |= |
DISPPLL_CONFIG_COHERENT_MODE; |
else { |
/* 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; |
541,21 → 660,27 |
DISPPLL_CONFIG_DUAL_LINK; |
} |
} else if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { |
/* may want to enable SS on DP/eDP eventually */ |
if (encoder_mode == ATOM_ENCODER_MODE_DP) { |
args.v3.sInput.ucDispPllConfig |= |
DISPPLL_CONFIG_SS_ENABLE; |
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; |
} |
} |
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; |
} |
579,9 → 704,14 |
PIXEL_CLOCK_PARAMETERS_V2 v2; |
PIXEL_CLOCK_PARAMETERS_V3 v3; |
PIXEL_CLOCK_PARAMETERS_V5 v5; |
PIXEL_CLOCK_PARAMETERS_V6 v6; |
}; |
|
static void atombios_crtc_set_dcpll(struct drm_crtc *crtc) |
/* 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, |
u32 dispclk) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
592,8 → 722,9 |
memset(&args, 0, sizeof(args)); |
|
index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); |
atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, |
&crev); |
if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, |
&crev)) |
return; |
|
switch (frev) { |
case 1: |
603,9 → 734,16 |
* SetPixelClock provides the dividers |
*/ |
args.v5.ucCRTC = ATOM_CRTC_INVALID; |
args.v5.usPixelClock = rdev->clock.default_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 = cpu_to_le32(dispclk); |
args.v6.ucPpll = ATOM_DCPLL; |
break; |
default: |
DRM_ERROR("Unknown table version %d %d\n", frev, crev); |
return; |
618,105 → 756,123 |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
} |
|
static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) |
static void atombios_crtc_program_pll(struct drm_crtc *crtc, |
int crtc_id, |
int pll_id, |
u32 encoder_mode, |
u32 encoder_id, |
u32 clock, |
u32 ref_div, |
u32 fb_div, |
u32 frac_fb_div, |
u32 post_div, |
int bpc, |
bool ss_enabled, |
struct radeon_atom_ss *ss) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct drm_encoder *encoder = NULL; |
struct radeon_encoder *radeon_encoder = NULL; |
u8 frev, crev; |
int index; |
int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); |
union set_pixel_clock args; |
u32 pll_clock = mode->clock; |
u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0; |
struct radeon_pll *pll; |
u32 adjusted_clock; |
int encoder_mode = 0; |
|
memset(&args, 0, sizeof(args)); |
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
if (encoder->crtc == crtc) { |
radeon_encoder = to_radeon_encoder(encoder); |
encoder_mode = atombios_get_encoder_mode(encoder); |
break; |
} |
} |
|
if (!radeon_encoder) |
if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, |
&crev)) |
return; |
|
switch (radeon_crtc->pll_id) { |
case ATOM_PPLL1: |
pll = &rdev->clock.p1pll; |
break; |
case ATOM_PPLL2: |
pll = &rdev->clock.p2pll; |
break; |
case ATOM_DCPLL: |
case ATOM_PPLL_INVALID: |
pll = &rdev->clock.dcpll; |
break; |
} |
|
/* adjust pixel clock as needed */ |
adjusted_clock = atombios_adjust_pll(crtc, mode, pll); |
|
radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, |
&ref_div, &post_div); |
|
index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); |
atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, |
&crev); |
|
switch (frev) { |
case 1: |
switch (crev) { |
case 1: |
args.v1.usPixelClock = cpu_to_le16(mode->clock / 10); |
if (clock == ATOM_DISABLE) |
return; |
args.v1.usPixelClock = cpu_to_le16(clock / 10); |
args.v1.usRefDiv = cpu_to_le16(ref_div); |
args.v1.usFbDiv = cpu_to_le16(fb_div); |
args.v1.ucFracFbDiv = frac_fb_div; |
args.v1.ucPostDiv = post_div; |
args.v1.ucPpll = radeon_crtc->pll_id; |
args.v1.ucCRTC = radeon_crtc->crtc_id; |
args.v1.ucPpll = pll_id; |
args.v1.ucCRTC = crtc_id; |
args.v1.ucRefDivSrc = 1; |
break; |
case 2: |
args.v2.usPixelClock = cpu_to_le16(mode->clock / 10); |
args.v2.usPixelClock = cpu_to_le16(clock / 10); |
args.v2.usRefDiv = cpu_to_le16(ref_div); |
args.v2.usFbDiv = cpu_to_le16(fb_div); |
args.v2.ucFracFbDiv = frac_fb_div; |
args.v2.ucPostDiv = post_div; |
args.v2.ucPpll = radeon_crtc->pll_id; |
args.v2.ucCRTC = radeon_crtc->crtc_id; |
args.v2.ucPpll = pll_id; |
args.v2.ucCRTC = crtc_id; |
args.v2.ucRefDivSrc = 1; |
break; |
case 3: |
args.v3.usPixelClock = cpu_to_le16(mode->clock / 10); |
args.v3.usPixelClock = cpu_to_le16(clock / 10); |
args.v3.usRefDiv = cpu_to_le16(ref_div); |
args.v3.usFbDiv = cpu_to_le16(fb_div); |
args.v3.ucFracFbDiv = frac_fb_div; |
args.v3.ucPostDiv = post_div; |
args.v3.ucPpll = radeon_crtc->pll_id; |
args.v3.ucMiscInfo = (radeon_crtc->pll_id << 2); |
args.v3.ucTransmitterId = radeon_encoder->encoder_id; |
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; |
case 5: |
args.v5.ucCRTC = radeon_crtc->crtc_id; |
args.v5.usPixelClock = cpu_to_le16(mode->clock / 10); |
args.v5.ucCRTC = crtc_id; |
args.v5.usPixelClock = cpu_to_le16(clock / 10); |
args.v5.ucRefDiv = ref_div; |
args.v5.usFbDiv = cpu_to_le16(fb_div); |
args.v5.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000); |
args.v5.ucPostDiv = post_div; |
args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */ |
args.v5.ucTransmitterID = radeon_encoder->encoder_id; |
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 = radeon_crtc->pll_id; |
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.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; |
break; |
default: |
DRM_ERROR("Unknown table version %d %d\n", frev, crev); |
return; |
} |
729,42 → 885,207 |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
} |
|
static int evergreen_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
struct drm_framebuffer *old_fb) |
static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct drm_encoder *encoder = NULL; |
struct radeon_encoder *radeon_encoder = NULL; |
u32 pll_clock = mode->clock; |
u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0; |
struct radeon_pll *pll; |
u32 adjusted_clock; |
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) { |
radeon_encoder = to_radeon_encoder(encoder); |
encoder_mode = atombios_get_encoder_mode(encoder); |
break; |
} |
} |
|
if (!radeon_encoder) |
return; |
|
switch (radeon_crtc->pll_id) { |
case ATOM_PPLL1: |
pll = &rdev->clock.p1pll; |
break; |
case ATOM_PPLL2: |
pll = &rdev->clock.p2pll; |
break; |
case ATOM_DCPLL: |
case ATOM_PPLL_INVALID: |
default: |
pll = &rdev->clock.dcpll; |
break; |
} |
|
if (radeon_encoder->active_device & |
(ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) { |
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; |
struct drm_connector *connector = |
radeon_get_connector_for_encoder(encoder); |
struct radeon_connector *radeon_connector = |
to_radeon_connector(connector); |
struct radeon_connector_atom_dig *dig_connector = |
radeon_connector->con_priv; |
int dp_clock; |
bpc = connector->display_info.bpc; |
|
switch (encoder_mode) { |
case ATOM_ENCODER_MODE_DP: |
/* DP/eDP */ |
dp_clock = dig_connector->dp_clock / 10; |
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 |
ss_enabled = |
radeon_atombios_get_ppll_ss_info(rdev, &ss, |
ATOM_DP_SS_ID1); |
} |
break; |
case ATOM_ENCODER_MODE_LVDS: |
if (ASIC_IS_DCE4(rdev)) |
ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss, |
dig->lcd_ss_id, |
mode->clock / 10); |
else |
ss_enabled = radeon_atombios_get_ppll_ss_info(rdev, &ss, |
dig->lcd_ss_id); |
break; |
case ATOM_ENCODER_MODE_DVI: |
if (ASIC_IS_DCE4(rdev)) |
ss_enabled = |
radeon_atombios_get_asic_ss_info(rdev, &ss, |
ASIC_INTERNAL_SS_ON_TMDS, |
mode->clock / 10); |
break; |
case ATOM_ENCODER_MODE_HDMI: |
if (ASIC_IS_DCE4(rdev)) |
ss_enabled = |
radeon_atombios_get_asic_ss_info(rdev, &ss, |
ASIC_INTERNAL_SS_ON_HDMI, |
mode->clock / 10); |
break; |
default: |
break; |
} |
} |
|
/* adjust pixel clock as needed */ |
adjusted_clock = atombios_adjust_pll(crtc, mode, pll, ss_enabled, &ss); |
|
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_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, bpc, ss_enabled, &ss); |
|
if (ss_enabled) { |
/* calculate ss amount and step size */ |
if (ASIC_IS_DCE4(rdev)) { |
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 - (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)) / |
(125 * 25 * pll->reference_freq / 100); |
else |
step_size = (2 * amount * ref_div * (ss.rate * 2048)) / |
(125 * 25 * pll->reference_freq / 100); |
ss.step = step_size; |
} |
|
atombios_crtc_program_ss(crtc, ATOM_ENABLE, radeon_crtc->pll_id, &ss); |
} |
} |
|
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; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_framebuffer *radeon_fb; |
struct drm_framebuffer *target_fb; |
struct drm_gem_object *obj; |
struct radeon_bo *rbo; |
uint64_t fb_location; |
uint32_t fb_format, fb_pitch_pixels, tiling_flags; |
u32 fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE); |
u32 tmp, viewport_w, viewport_h; |
int r; |
|
/* no fb bound */ |
if (!crtc->fb) { |
DRM_DEBUG("No FB bound\n"); |
if (!atomic && !crtc->fb) { |
DRM_DEBUG_KMS("No FB bound\n"); |
return 0; |
} |
|
if (atomic) { |
radeon_fb = to_radeon_framebuffer(fb); |
target_fb = fb; |
} |
else { |
radeon_fb = to_radeon_framebuffer(crtc->fb); |
target_fb = crtc->fb; |
} |
|
/* Pin framebuffer & get tilling informations */ |
/* If atomic, assume fb object is pinned & idle & fenced and |
* 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; |
|
if (atomic) |
fb_location = radeon_bo_gpu_offset(rbo); |
else { |
r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location); |
if (unlikely(r != 0)) { |
radeon_bo_unreserve(rbo); |
return -EINVAL; |
} |
} |
|
radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); |
radeon_bo_unreserve(rbo); |
|
switch (crtc->fb->bits_per_pixel) { |
switch (target_fb->bits_per_pixel) { |
case 8: |
fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP) | |
EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED)); |
776,18 → 1097,29 |
case 16: |
fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) | |
EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB565)); |
#ifdef __BIG_ENDIAN |
fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16); |
#endif |
break; |
case 24: |
case 32: |
fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP) | |
EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB8888)); |
#ifdef __BIG_ENDIAN |
fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32); |
#endif |
break; |
default: |
DRM_ERROR("Unsupported screen depth %d\n", |
crtc->fb->bits_per_pixel); |
target_fb->bits_per_pixel); |
return -EINVAL; |
} |
|
if (tiling_flags & RADEON_TILING_MACRO) |
fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_2D_TILED_THIN1); |
else if (tiling_flags & RADEON_TILING_MICRO) |
fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1); |
|
switch (radeon_crtc->crtc_id) { |
case 0: |
WREG32(AVIVO_D1VGA_CONTROL, 0); |
820,15 → 1152,16 |
WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, |
(u32) fb_location & EVERGREEN_GRPH_SURFACE_ADDRESS_MASK); |
WREG32(EVERGREEN_GRPH_CONTROL + radeon_crtc->crtc_offset, fb_format); |
WREG32(EVERGREEN_GRPH_SWAP_CONTROL + radeon_crtc->crtc_offset, fb_swap); |
|
WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0); |
WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0); |
WREG32(EVERGREEN_GRPH_X_START + radeon_crtc->crtc_offset, 0); |
WREG32(EVERGREEN_GRPH_Y_START + radeon_crtc->crtc_offset, 0); |
WREG32(EVERGREEN_GRPH_X_END + radeon_crtc->crtc_offset, crtc->fb->width); |
WREG32(EVERGREEN_GRPH_Y_END + radeon_crtc->crtc_offset, crtc->fb->height); |
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 = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8); |
fb_pitch_pixels = target_fb->pitch / (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); |
|
838,18 → 1171,23 |
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); |
|
if (old_fb && old_fb != crtc->fb) { |
radeon_fb = to_radeon_framebuffer(old_fb); |
rbo = radeon_fb->obj->driver_private; |
/* 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 = gem_to_radeon_bo(radeon_fb->obj); |
r = radeon_bo_reserve(rbo, false); |
if (unlikely(r != 0)) |
return r; |
863,8 → 1201,9 |
return 0; |
} |
|
static int avivo_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
struct drm_framebuffer *old_fb) |
static int avivo_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; |
872,33 → 1211,50 |
struct radeon_framebuffer *radeon_fb; |
struct drm_gem_object *obj; |
struct radeon_bo *rbo; |
struct drm_framebuffer *target_fb; |
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 */ |
if (!crtc->fb) { |
DRM_DEBUG("No FB bound\n"); |
if (!atomic && !crtc->fb) { |
DRM_DEBUG_KMS("No FB bound\n"); |
return 0; |
} |
|
if (atomic) { |
radeon_fb = to_radeon_framebuffer(fb); |
target_fb = fb; |
} |
else { |
radeon_fb = to_radeon_framebuffer(crtc->fb); |
target_fb = crtc->fb; |
} |
|
/* Pin framebuffer & get tilling informations */ |
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; |
|
/* If atomic, assume fb object is pinned & idle & fenced and |
* just update base pointers |
*/ |
if (atomic) |
fb_location = radeon_bo_gpu_offset(rbo); |
else { |
r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location); |
if (unlikely(r != 0)) { |
radeon_bo_unreserve(rbo); |
return -EINVAL; |
} |
} |
radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); |
radeon_bo_unreserve(rbo); |
|
switch (crtc->fb->bits_per_pixel) { |
switch (target_fb->bits_per_pixel) { |
case 8: |
fb_format = |
AVIVO_D1GRPH_CONTROL_DEPTH_8BPP | |
913,6 → 1269,9 |
fb_format = |
AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | |
AVIVO_D1GRPH_CONTROL_16BPP_RGB565; |
#ifdef __BIG_ENDIAN |
fb_swap = R600_D1GRPH_SWAP_ENDIAN_16BIT; |
#endif |
break; |
case 24: |
case 32: |
919,18 → 1278,28 |
fb_format = |
AVIVO_D1GRPH_CONTROL_DEPTH_32BPP | |
AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888; |
#ifdef __BIG_ENDIAN |
fb_swap = R600_D1GRPH_SWAP_ENDIAN_32BIT; |
#endif |
break; |
default: |
DRM_ERROR("Unsupported screen depth %d\n", |
crtc->fb->bits_per_pixel); |
target_fb->bits_per_pixel); |
return -EINVAL; |
} |
|
if (rdev->family >= CHIP_R600) { |
if (tiling_flags & RADEON_TILING_MACRO) |
fb_format |= R600_D1GRPH_ARRAY_MODE_2D_TILED_THIN1; |
else if (tiling_flags & RADEON_TILING_MICRO) |
fb_format |= R600_D1GRPH_ARRAY_MODE_1D_TILED_THIN1; |
} else { |
if (tiling_flags & RADEON_TILING_MACRO) |
fb_format |= AVIVO_D1GRPH_MACRO_ADDRESS_MODE; |
|
if (tiling_flags & RADEON_TILING_MICRO) |
fb_format |= AVIVO_D1GRPH_TILED; |
} |
|
if (radeon_crtc->crtc_id == 0) |
WREG32(AVIVO_D1VGA_CONTROL, 0); |
939,11 → 1308,11 |
|
if (rdev->family >= CHIP_RV770) { |
if (radeon_crtc->crtc_id) { |
WREG32(R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0); |
WREG32(R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0); |
WREG32(R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, upper_32_bits(fb_location)); |
WREG32(R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, upper_32_bits(fb_location)); |
} else { |
WREG32(R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0); |
WREG32(R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0); |
WREG32(R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, upper_32_bits(fb_location)); |
WREG32(R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, upper_32_bits(fb_location)); |
} |
} |
WREG32(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, |
951,15 → 1320,17 |
WREG32(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + |
radeon_crtc->crtc_offset, (u32) fb_location); |
WREG32(AVIVO_D1GRPH_CONTROL + radeon_crtc->crtc_offset, fb_format); |
if (rdev->family >= CHIP_R600) |
WREG32(R600_D1GRPH_SWAP_CONTROL + radeon_crtc->crtc_offset, fb_swap); |
|
WREG32(AVIVO_D1GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_D1GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_D1GRPH_X_START + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_D1GRPH_Y_START + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_D1GRPH_X_END + radeon_crtc->crtc_offset, crtc->fb->width); |
WREG32(AVIVO_D1GRPH_Y_END + radeon_crtc->crtc_offset, crtc->fb->height); |
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 = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8); |
fb_pitch_pixels = target_fb->pitch / (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); |
|
969,18 → 1340,23 |
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); |
|
if (old_fb && old_fb != crtc->fb) { |
radeon_fb = to_radeon_framebuffer(old_fb); |
rbo = radeon_fb->obj->driver_private; |
/* 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 = gem_to_radeon_bo(radeon_fb->obj); |
r = radeon_bo_reserve(rbo, false); |
if (unlikely(r != 0)) |
return r; |
1001,13 → 1377,28 |
struct radeon_device *rdev = dev->dev_private; |
|
if (ASIC_IS_DCE4(rdev)) |
return evergreen_crtc_set_base(crtc, x, y, old_fb); |
return dce4_crtc_do_set_base(crtc, old_fb, x, y, 0); |
else if (ASIC_IS_AVIVO(rdev)) |
return avivo_crtc_set_base(crtc, x, y, old_fb); |
return avivo_crtc_do_set_base(crtc, old_fb, x, y, 0); |
else |
return radeon_crtc_set_base(crtc, x, y, old_fb); |
return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0); |
} |
|
int atombios_crtc_set_base_atomic(struct drm_crtc *crtc, |
struct drm_framebuffer *fb, |
int x, int y, enum mode_set_atomic state) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
|
if (ASIC_IS_DCE4(rdev)) |
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 |
return radeon_crtc_do_set_base(crtc, fb, x, y, 1); |
} |
|
/* properly set additional regs when using atombios */ |
static void radeon_legacy_atom_fixup(struct drm_crtc *crtc) |
{ |
1042,11 → 1433,19 |
uint32_t pll_in_use = 0; |
|
if (ASIC_IS_DCE4(rdev)) { |
/* if crtc is driving DP and we have an ext clock, use that */ |
list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) { |
if (test_encoder->crtc && (test_encoder->crtc == crtc)) { |
/* 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 (atombios_get_encoder_mode(test_encoder) == ATOM_ENCODER_MODE_DP) { |
if (rdev->clock.dp_extclk) |
if (ASIC_IS_DCE5(rdev) || rdev->clock.dp_extclk) |
return ATOM_PPLL_INVALID; |
} |
} |
1080,24 → 1479,42 |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct drm_encoder *encoder; |
bool is_tvcv = false; |
|
/* TODO color tiling */ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
/* find tv std */ |
if (encoder->crtc == crtc) { |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
if (radeon_encoder->active_device & |
(ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) |
is_tvcv = true; |
} |
} |
|
/* pick pll */ |
radeon_crtc->pll_id = radeon_atom_pick_pll(crtc); |
|
atombios_set_ss(crtc, 0); |
/* always set DCPLL */ |
if (ASIC_IS_DCE4(rdev)) |
atombios_crtc_set_dcpll(crtc); |
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); |
atombios_set_ss(crtc, 1); |
|
if (ASIC_IS_DCE4(rdev)) |
atombios_set_crtc_dtd_timing(crtc, adjusted_mode); |
else if (ASIC_IS_AVIVO(rdev)) |
else if (ASIC_IS_AVIVO(rdev)) { |
if (is_tvcv) |
atombios_crtc_set_timing(crtc, adjusted_mode); |
else { |
else |
atombios_set_crtc_dtd_timing(crtc, adjusted_mode); |
} else { |
atombios_crtc_set_timing(crtc, adjusted_mode); |
if (radeon_crtc->crtc_id == 0) |
atombios_set_crtc_dtd_timing(crtc, adjusted_mode); |
1113,6 → 1530,12 |
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; |
1120,6 → 1543,11 |
|
static void atombios_crtc_prepare(struct drm_crtc *crtc) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
|
/* pick pll */ |
radeon_crtc->pll_id = radeon_atom_pick_pll(crtc); |
|
atombios_lock_crtc(crtc, ATOM_ENABLE); |
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); |
} |
1130,14 → 1558,36 |
atombios_lock_crtc(crtc, ATOM_DISABLE); |
} |
|
static void atombios_crtc_disable(struct drm_crtc *crtc) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct radeon_atom_ss ss; |
|
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); |
|
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, false, &ss); |
break; |
default: |
break; |
} |
radeon_crtc->pll_id = -1; |
} |
|
static const struct drm_crtc_helper_funcs atombios_helper_funcs = { |
.dpms = atombios_crtc_dpms, |
.mode_fixup = atombios_crtc_mode_fixup, |
.mode_set = atombios_crtc_mode_set, |
.mode_set_base = atombios_crtc_set_base, |
.mode_set_base_atomic = atombios_crtc_set_base_atomic, |
.prepare = atombios_crtc_prepare, |
.commit = atombios_crtc_commit, |
.load_lut = radeon_crtc_load_lut, |
.disable = atombios_crtc_disable, |
}; |
|
void radeon_atombios_init_crtc(struct drm_device *dev, |