From 1d8297e79bdef515c9724c1dbd6800f66b2a3790 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 11 Nov 2009 13:52:26 +1000 Subject: [PATCH] drm/radeon/kms: add legacy LVDS spread spectrum support. TODO: Figure out the percentage->delay translation so we can avoid reading back the regs at bootup, and program the hw from scratch. --- drivers/gpu/drm/radeon/radeon_combios.c | 31 ++- drivers/gpu/drm/radeon/radeon_legacy_crtc.c | 503 +++++++++++++++++---------- drivers/gpu/drm/radeon/radeon_mode.h | 7 + drivers/gpu/drm/radeon/radeon_reg.h | 24 ++ 4 files changed, 387 insertions(+), 178 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index a36ede0..be494e5 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -782,6 +782,27 @@ out: return tv_dac; } +static void radeon_legacy_get_ss_info_from_regs(struct radeon_device *rdev, + struct radeon_encoder_lvds *lvds) +{ + uint32_t lvds_en = RREG32(RADEON_LVDS_SS_GEN_CNTL); + + /* we can't deal with external SS yet */ + if (lvds_en & RADEON_LVDS_SS_EXT_EN) { + WARN(1, "LVDS External spread spectrum enabled - TODO\n"); + return; + } + + lvds_en = RREG32_PLL(RADEON_SS_INT_CNTL); + if (!(lvds_en & 0x1)) + return; + + lvds->ss_valid_boot = true; + lvds->ss_delay = (lvds_en >> 4) & 0x7; + lvds->ss_step_size = (lvds_en >> 2) & 0x3; + DRM_INFO("ss valid %d %d\n", lvds->ss_delay, lvds->ss_step_size); +} + static struct radeon_encoder_lvds *radeon_legacy_get_lvds_info_from_regs(struct radeon_device *rdev) @@ -903,6 +924,12 @@ struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder (lvds->panel_fb_divider > 3)) lvds->use_bios_dividers = true; + lvds->ss_external = !(RBIOS8(lcd_info + 0x33) & (1 << 7)); + lvds->ss_center = RBIOS8(lcd_info + 0x33) & (1 << 0); + lvds->ss_percentage = RBIOS16(lcd_info + 0x34); + DRM_INFO("ss %d %d %d\n", lvds->ss_external, lvds->ss_center, + lvds->ss_percentage); + panel_setup = RBIOS32(lcd_info + 0x39); lvds->lvds_gen_cntl = 0xff00; if (panel_setup & 0x1) @@ -970,8 +997,10 @@ struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder lvds = radeon_legacy_get_lvds_info_from_regs(rdev); } out: - if (lvds) + if (lvds) { + radeon_legacy_get_ss_info_from_regs(rdev, lvds); encoder->native_mode = lvds->native_mode; + } return lvds; } diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index 8d0b7aa..2b11a59 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -200,9 +200,8 @@ void radeon_restore_common_regs(struct drm_device *dev) /* don't need this yet */ } -static void radeon_pll_wait_for_read_update_complete(struct drm_device *dev) +static void radeon_pll_wait_for_read_update_complete(struct radeon_device *rdev) { - struct radeon_device *rdev = dev->dev_private; int i = 0; /* FIXME: Certain revisions of R300 can't recover here. Not sure of @@ -215,10 +214,8 @@ static void radeon_pll_wait_for_read_update_complete(struct drm_device *dev) i++); } -static void radeon_pll_write_update(struct drm_device *dev) +static void radeon_pll_write_update(struct radeon_device *rdev) { - struct radeon_device *rdev = dev->dev_private; - while (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); WREG32_PLL_P(RADEON_PPLL_REF_DIV, @@ -226,9 +223,8 @@ static void radeon_pll_write_update(struct drm_device *dev) ~(RADEON_PPLL_ATOMIC_UPDATE_W)); } -static void radeon_pll2_wait_for_read_update_complete(struct drm_device *dev) +static void radeon_pll2_wait_for_read_update_complete(struct radeon_device *rdev) { - struct radeon_device *rdev = dev->dev_private; int i = 0; @@ -242,10 +238,8 @@ static void radeon_pll2_wait_for_read_update_complete(struct drm_device *dev) i++); } -static void radeon_pll2_write_update(struct drm_device *dev) +static void radeon_pll2_write_update(struct radeon_device *rdev) { - struct radeon_device *rdev = dev->dev_private; - while (RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); WREG32_PLL_P(RADEON_P2PLL_REF_DIV, @@ -253,6 +247,35 @@ static void radeon_pll2_write_update(struct drm_device *dev) ~(RADEON_P2PLL_ATOMIC_UPDATE_W)); } + +static void radeon_sspll_wait_for_read_update_complete(struct radeon_device *rdev) +{ + int i = 0; + + /* FIXME: Certain revisions of R300 can't recover here. Not sure of + the cause yet, but this workaround will mask the problem for now. + Other chips usually will pass at the very first test, so the + workaround shouldn't have any effect on them. */ + for (i = 0; + (i < 10000 && + RREG32_PLL(RADEON_SSPLL_REF_DIV) & RADEON_SSPLL_ATOMIC_UPDATE_R); + i++); +} + +static void radeon_sspll_write_update(struct radeon_device *rdev) +{ + int i = 0; + + while ((RREG32_PLL(RADEON_SSPLL_REF_DIV) & RADEON_SSPLL_ATOMIC_UPDATE_R) && (i++ < 10000)); + + if (i >= 10000) + printk("timed out setting sspll\n"); + + WREG32_PLL_P(RADEON_SSPLL_REF_DIV, + RADEON_SSPLL_ATOMIC_UPDATE_W, + ~(RADEON_SSPLL_ATOMIC_UPDATE_W)); +} + static uint8_t radeon_compute_pll_gain(uint16_t ref_freq, uint16_t ref_div, uint16_t fb_div) { @@ -716,6 +739,278 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod return true; } +static void radeon_program_pll1(struct radeon_device *rdev, + struct drm_encoder *encoder, + uint32_t htotal_cntl, + uint32_t pll_ref_div, uint32_t pll_fb_post_div, + uint32_t pll_gain, + bool is_tv) +{ + uint32_t pixclks_cntl; + + + if (is_tv) { + pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL); + radeon_legacy_tv_adjust_pll1(encoder, &htotal_cntl, &pll_ref_div, + &pll_fb_post_div, &pixclks_cntl); + } + + if (rdev->flags & RADEON_IS_MOBILITY) { + /* A temporal workaround for the occational blanking on certain laptop panels. + This appears to related to the PLL divider registers (fail to lock?). + It occurs even when all dividers are the same with their old settings. + In this case we really don't need to fiddle with PLL registers. + By doing this we can avoid the blanking problem with some panels. + */ + if ((pll_ref_div == (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_REF_DIV_MASK)) && + (pll_fb_post_div == (RREG32_PLL(RADEON_PPLL_DIV_3) & + (RADEON_PPLL_POST3_DIV_MASK | RADEON_PPLL_FB3_DIV_MASK)))) { + WREG32_P(RADEON_CLOCK_CNTL_INDEX, + RADEON_PLL_DIV_SEL, + ~(RADEON_PLL_DIV_SEL)); + r100_pll_errata_after_index(rdev); + return; + } + } + + WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, + RADEON_VCLK_SRC_SEL_CPUCLK, + ~(RADEON_VCLK_SRC_SEL_MASK)); + WREG32_PLL_P(RADEON_PPLL_CNTL, + RADEON_PPLL_RESET + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN + | ((uint32_t)pll_gain << RADEON_PPLL_PVG_SHIFT), + ~(RADEON_PPLL_RESET + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN + | RADEON_PPLL_PVG_MASK)); + + WREG32_P(RADEON_CLOCK_CNTL_INDEX, + RADEON_PLL_DIV_SEL, + ~(RADEON_PLL_DIV_SEL)); + r100_pll_errata_after_index(rdev); + + if (ASIC_IS_R300(rdev) || + (rdev->family == CHIP_RS300) || + (rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { + if (pll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { + /* When restoring console mode, use saved PPLL_REF_DIV + * setting. + */ + WREG32_PLL_P(RADEON_PPLL_REF_DIV, + pll_ref_div, + 0); + } else { + /* R300 uses ref_div_acc field as real ref divider */ + WREG32_PLL_P(RADEON_PPLL_REF_DIV, + (pll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), + ~R300_PPLL_REF_DIV_ACC_MASK); + } + } else + WREG32_PLL_P(RADEON_PPLL_REF_DIV, + pll_ref_div, + ~RADEON_PPLL_REF_DIV_MASK); + + WREG32_PLL_P(RADEON_PPLL_DIV_3, + pll_fb_post_div, + ~RADEON_PPLL_FB3_DIV_MASK); + + WREG32_PLL_P(RADEON_PPLL_DIV_3, + pll_fb_post_div, + ~RADEON_PPLL_POST3_DIV_MASK); + + radeon_pll_write_update(rdev); + radeon_pll_wait_for_read_update_complete(rdev); + + WREG32_PLL(RADEON_HTOTAL_CNTL, htotal_cntl); + + WREG32_PLL_P(RADEON_PPLL_CNTL, + 0, + ~(RADEON_PPLL_RESET + | RADEON_PPLL_SLEEP + | RADEON_PPLL_ATOMIC_UPDATE_EN + | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN)); + + DRM_DEBUG("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n", + pll_ref_div, + pll_fb_post_div, + (unsigned)htotal_cntl, + RREG32_PLL(RADEON_PPLL_CNTL)); + DRM_DEBUG("Wrote: rd=%d, fd=%d, pd=%d\n", + pll_ref_div & RADEON_PPLL_REF_DIV_MASK, + pll_fb_post_div & RADEON_PPLL_FB3_DIV_MASK, + (pll_fb_post_div & RADEON_PPLL_POST3_DIV_MASK) >> 16); + + mdelay(50); /* Let the clock to lock */ + + WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, + RADEON_VCLK_SRC_SEL_PPLLCLK, + ~(RADEON_VCLK_SRC_SEL_MASK)); + + if (is_tv) + WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); +} + +static void radeon_program_pll2(struct radeon_device *rdev, + struct drm_encoder *encoder, + uint32_t htotal_cntl, + uint32_t pll_ref_div, uint32_t pll_fb_post_div, + uint32_t pll_gain, + bool is_tv) +{ + uint32_t pixclks_cntl = ((RREG32_PLL(RADEON_PIXCLKS_CNTL) & + ~(RADEON_PIX2CLK_SRC_SEL_MASK)) | + RADEON_PIX2CLK_SRC_SEL_P2PLLCLK); + + if (is_tv) { + radeon_legacy_tv_adjust_pll2(encoder, &htotal_cntl, + &pll_ref_div, &pll_fb_post_div, + &pixclks_cntl); + } + + WREG32_PLL_P(RADEON_PIXCLKS_CNTL, + RADEON_PIX2CLK_SRC_SEL_CPUCLK, + ~(RADEON_PIX2CLK_SRC_SEL_MASK)); + + WREG32_PLL_P(RADEON_P2PLL_CNTL, + RADEON_P2PLL_RESET + | RADEON_P2PLL_ATOMIC_UPDATE_EN + | ((uint32_t)pll_gain << RADEON_P2PLL_PVG_SHIFT), + ~(RADEON_P2PLL_RESET + | RADEON_P2PLL_ATOMIC_UPDATE_EN + | RADEON_P2PLL_PVG_MASK)); + + WREG32_PLL_P(RADEON_P2PLL_REF_DIV, + pll_ref_div, + ~RADEON_P2PLL_REF_DIV_MASK); + + WREG32_PLL_P(RADEON_P2PLL_DIV_0, + pll_fb_post_div, + ~RADEON_P2PLL_FB0_DIV_MASK); + + WREG32_PLL_P(RADEON_P2PLL_DIV_0, + pll_fb_post_div, + ~RADEON_P2PLL_POST0_DIV_MASK); + + radeon_pll2_write_update(rdev); + radeon_pll2_wait_for_read_update_complete(rdev); + + WREG32_PLL(RADEON_HTOTAL2_CNTL, htotal_cntl); + + WREG32_PLL_P(RADEON_P2PLL_CNTL, + 0, + ~(RADEON_P2PLL_RESET + | RADEON_P2PLL_SLEEP + | RADEON_P2PLL_ATOMIC_UPDATE_EN)); + + DRM_DEBUG("Wrote2: 0x%08x 0x%08x 0x%08x (0x%08x)\n", + (unsigned)pll_ref_div, + (unsigned)pll_fb_post_div, + (unsigned)htotal_cntl, + RREG32_PLL(RADEON_P2PLL_CNTL)); + DRM_DEBUG("Wrote2: rd=%u, fd=%u, pd=%u\n", + (unsigned)pll_ref_div & RADEON_P2PLL_REF_DIV_MASK, + (unsigned)pll_fb_post_div & RADEON_P2PLL_FB0_DIV_MASK, + (unsigned)((pll_fb_post_div & + RADEON_P2PLL_POST0_DIV_MASK) >> 16)); + + mdelay(50); /* Let the clock to lock */ + + WREG32_PLL_P(RADEON_PIXCLKS_CNTL, + RADEON_PIX2CLK_SRC_SEL_P2PLLCLK, + ~(RADEON_PIX2CLK_SRC_SEL_MASK)); + + WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); +} + +static void radeon_program_sspll(struct radeon_device *rdev, + uint32_t pll_ref_div, uint32_t pll_fb_post_div, + uint32_t pll_gain) +{ + /* definitely not need tv */ + + WREG32_PLL_P(RADEON_SSPLL_CNTL, + RADEON_SSPLL_RESET + | RADEON_SSPLL_ATOMIC_UPDATE_EN + | ((uint32_t)pll_gain << RADEON_SSPLL_PVG_SHIFT), + ~(RADEON_SSPLL_RESET + | RADEON_SSPLL_ATOMIC_UPDATE_EN + | RADEON_SSPLL_PVG_MASK)); + + WREG32_PLL_P(RADEON_SSPLL_REF_DIV, + pll_ref_div, + ~RADEON_SSPLL_REF_DIV_MASK); + + WREG32_PLL_P(RADEON_SSPLL_DIV_0, + pll_fb_post_div, + ~RADEON_SSPLL_FB0_DIV_MASK); + + WREG32_PLL_P(RADEON_SSPLL_DIV_0, + pll_fb_post_div, + ~RADEON_SSPLL_POST0_DIV_MASK); + + radeon_sspll_write_update(rdev); + radeon_sspll_wait_for_read_update_complete(rdev); + + WREG32_PLL_P(RADEON_SSPLL_CNTL, + 0, + ~(RADEON_SSPLL_RESET + | RADEON_SSPLL_SLEEP + | RADEON_SSPLL_ATOMIC_UPDATE_EN)); + + DRM_DEBUG("WroteSS: 0x%08x 0x%08x (0x%08x)\n", + (unsigned)pll_ref_div, + (unsigned)pll_fb_post_div, + RREG32_PLL(RADEON_SSPLL_CNTL)); + DRM_DEBUG("Wrote2: rd=%u, fd=%u, pd=%u\n", + (unsigned)pll_ref_div & RADEON_SSPLL_REF_DIV_MASK, + (unsigned)pll_fb_post_div & RADEON_SSPLL_FB0_DIV_MASK, + (unsigned)((pll_fb_post_div & + RADEON_SSPLL_POST0_DIV_MASK) >> 16)); + + mdelay(50); /* Let the clock to lock */ + +} + + +static void radeon_crtc_set_ss(struct drm_crtc *crtc, struct radeon_encoder_lvds *lvds, bool enable) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + u32 tmp; + + if (!enable) { + tmp = RREG32(RADEON_LVDS_SS_GEN_CNTL); + + if (((tmp & RADEON_LVDS_SS_EXT_SEL) && (radeon_crtc->crtc_id == 1)) || + ((!(tmp & RADEON_LVDS_SS_EXT_SEL)) && (radeon_crtc->crtc_id == 0))) + WREG32(RADEON_LVDS_SS_GEN_CNTL, tmp & ~RADEON_LVDS_SS_EXT_EN); + if (rdev->family >= CHIP_R200) { + tmp = RREG32_PLL(RADEON_SS_INT_CNTL); + if (((tmp & RADEON_SS_INT_SEL) && (radeon_crtc->crtc_id == 1)) || + ((!(tmp & RADEON_SS_INT_SEL)) && (radeon_crtc->crtc_id == 0))) { + WREG32_PLL(RADEON_SSPLL_CNTL, RADEON_SSPLL_RESET | RADEON_SSPLL_SLEEP); + WREG32_PLL_P(RADEON_SS_INT_CNTL, 0, ~(RADEON_SS_INT_EN|RADEON_SS_INT_SEL)); + } + } + return; + } + + tmp = RREG32_PLL(RADEON_SS_INT_CNTL); + tmp &= (0x7 << RADEON_SS_DELAY_SHIFT) | (0x3 << RADEON_SS_STEP_SIZE_SHIFT); + + tmp |= (lvds->ss_delay << RADEON_SS_DELAY_SHIFT) | + (lvds->ss_step_size << RADEON_SS_STEP_SIZE_SHIFT); + tmp &= ~(RADEON_SS_INT_SEL | RADEON_SS_INT_EN); + + tmp |= RADEON_SS_INT_EN | ((radeon_crtc->crtc_id == 1) ? RADEON_SS_INT_SEL : 0); + /* we've already programmed the ss pll */ + WREG32_PLL(RADEON_SS_INT_CNTL, tmp); +} + static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) { struct drm_device *dev = crtc->dev; @@ -736,6 +1031,7 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) uint32_t htotal_cntl = 0; bool is_tv = false; struct radeon_pll *pll; + struct radeon_encoder_lvds *lvds = NULL; struct { int divider; @@ -780,7 +1076,7 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) pll_flags |= RADEON_PLL_NO_ODD_POST_DIV; if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv; + lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv; if (lvds) { if (lvds->use_bios_dividers) { pll_ref_div = lvds->panel_ref_divider; @@ -790,6 +1086,7 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) use_bios_divs = true; } } + pll_flags |= RADEON_PLL_USE_REF_DIV; } } @@ -830,183 +1127,35 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) } + pll_gain = radeon_compute_pll_gain(pll->reference_freq, pll_ref_div & 0x3ff, pll_fb_post_div & 0x7ff); - if (radeon_crtc->crtc_id) { - uint32_t pixclks_cntl = ((RREG32_PLL(RADEON_PIXCLKS_CNTL) & - ~(RADEON_PIX2CLK_SRC_SEL_MASK)) | - RADEON_PIX2CLK_SRC_SEL_P2PLLCLK); - - if (is_tv) { - radeon_legacy_tv_adjust_pll2(encoder, &htotal_cntl, - &pll_ref_div, &pll_fb_post_div, - &pixclks_cntl); - } - - WREG32_PLL_P(RADEON_PIXCLKS_CNTL, - RADEON_PIX2CLK_SRC_SEL_CPUCLK, - ~(RADEON_PIX2CLK_SRC_SEL_MASK)); - - WREG32_PLL_P(RADEON_P2PLL_CNTL, - RADEON_P2PLL_RESET - | RADEON_P2PLL_ATOMIC_UPDATE_EN - | ((uint32_t)pll_gain << RADEON_P2PLL_PVG_SHIFT), - ~(RADEON_P2PLL_RESET - | RADEON_P2PLL_ATOMIC_UPDATE_EN - | RADEON_P2PLL_PVG_MASK)); - - WREG32_PLL_P(RADEON_P2PLL_REF_DIV, - pll_ref_div, - ~RADEON_P2PLL_REF_DIV_MASK); - - WREG32_PLL_P(RADEON_P2PLL_DIV_0, - pll_fb_post_div, - ~RADEON_P2PLL_FB0_DIV_MASK); - - WREG32_PLL_P(RADEON_P2PLL_DIV_0, - pll_fb_post_div, - ~RADEON_P2PLL_POST0_DIV_MASK); - - radeon_pll2_write_update(dev); - radeon_pll2_wait_for_read_update_complete(dev); - - WREG32_PLL(RADEON_HTOTAL2_CNTL, htotal_cntl); - - WREG32_PLL_P(RADEON_P2PLL_CNTL, - 0, - ~(RADEON_P2PLL_RESET - | RADEON_P2PLL_SLEEP - | RADEON_P2PLL_ATOMIC_UPDATE_EN)); - - DRM_DEBUG("Wrote2: 0x%08x 0x%08x 0x%08x (0x%08x)\n", - (unsigned)pll_ref_div, - (unsigned)pll_fb_post_div, - (unsigned)htotal_cntl, - RREG32_PLL(RADEON_P2PLL_CNTL)); - DRM_DEBUG("Wrote2: rd=%u, fd=%u, pd=%u\n", - (unsigned)pll_ref_div & RADEON_P2PLL_REF_DIV_MASK, - (unsigned)pll_fb_post_div & RADEON_P2PLL_FB0_DIV_MASK, - (unsigned)((pll_fb_post_div & - RADEON_P2PLL_POST0_DIV_MASK) >> 16)); - - mdelay(50); /* Let the clock to lock */ - - WREG32_PLL_P(RADEON_PIXCLKS_CNTL, - RADEON_PIX2CLK_SRC_SEL_P2PLLCLK, - ~(RADEON_PIX2CLK_SRC_SEL_MASK)); + if (lvds) { + radeon_crtc_set_ss(crtc, NULL, false); + /* disable SS */ + } - WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); + if (radeon_crtc->crtc_id) { + radeon_program_pll2(rdev, encoder, htotal_cntl, pll_ref_div, + pll_fb_post_div, pll_gain, is_tv); } else { - uint32_t pixclks_cntl; - - - if (is_tv) { - pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL); - radeon_legacy_tv_adjust_pll1(encoder, &htotal_cntl, &pll_ref_div, - &pll_fb_post_div, &pixclks_cntl); - } + radeon_program_pll1(rdev, encoder, htotal_cntl, pll_ref_div, + pll_fb_post_div, pll_gain, is_tv); + } - if (rdev->flags & RADEON_IS_MOBILITY) { - /* A temporal workaround for the occational blanking on certain laptop panels. - This appears to related to the PLL divider registers (fail to lock?). - It occurs even when all dividers are the same with their old settings. - In this case we really don't need to fiddle with PLL registers. - By doing this we can avoid the blanking problem with some panels. - */ - if ((pll_ref_div == (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_REF_DIV_MASK)) && - (pll_fb_post_div == (RREG32_PLL(RADEON_PPLL_DIV_3) & - (RADEON_PPLL_POST3_DIV_MASK | RADEON_PPLL_FB3_DIV_MASK)))) { - WREG32_P(RADEON_CLOCK_CNTL_INDEX, - RADEON_PLL_DIV_SEL, - ~(RADEON_PLL_DIV_SEL)); - r100_pll_errata_after_index(rdev); - return; - } + if (lvds) { + if (lvds->ss_external == false && lvds->ss_valid_boot == true) { + /* program and enable SS LVDS */ + radeon_program_sspll(rdev, pll_ref_div, + pll_fb_post_div, pll_gain); + radeon_crtc_set_ss(crtc, lvds, true); } - - WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, - RADEON_VCLK_SRC_SEL_CPUCLK, - ~(RADEON_VCLK_SRC_SEL_MASK)); - WREG32_PLL_P(RADEON_PPLL_CNTL, - RADEON_PPLL_RESET - | RADEON_PPLL_ATOMIC_UPDATE_EN - | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN - | ((uint32_t)pll_gain << RADEON_PPLL_PVG_SHIFT), - ~(RADEON_PPLL_RESET - | RADEON_PPLL_ATOMIC_UPDATE_EN - | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN - | RADEON_PPLL_PVG_MASK)); - - WREG32_P(RADEON_CLOCK_CNTL_INDEX, - RADEON_PLL_DIV_SEL, - ~(RADEON_PLL_DIV_SEL)); - r100_pll_errata_after_index(rdev); - - if (ASIC_IS_R300(rdev) || - (rdev->family == CHIP_RS300) || - (rdev->family == CHIP_RS400) || - (rdev->family == CHIP_RS480)) { - if (pll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { - /* When restoring console mode, use saved PPLL_REF_DIV - * setting. - */ - WREG32_PLL_P(RADEON_PPLL_REF_DIV, - pll_ref_div, - 0); - } else { - /* R300 uses ref_div_acc field as real ref divider */ - WREG32_PLL_P(RADEON_PPLL_REF_DIV, - (pll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), - ~R300_PPLL_REF_DIV_ACC_MASK); - } - } else - WREG32_PLL_P(RADEON_PPLL_REF_DIV, - pll_ref_div, - ~RADEON_PPLL_REF_DIV_MASK); - - WREG32_PLL_P(RADEON_PPLL_DIV_3, - pll_fb_post_div, - ~RADEON_PPLL_FB3_DIV_MASK); - - WREG32_PLL_P(RADEON_PPLL_DIV_3, - pll_fb_post_div, - ~RADEON_PPLL_POST3_DIV_MASK); - - radeon_pll_write_update(dev); - radeon_pll_wait_for_read_update_complete(dev); - - WREG32_PLL(RADEON_HTOTAL_CNTL, htotal_cntl); - - WREG32_PLL_P(RADEON_PPLL_CNTL, - 0, - ~(RADEON_PPLL_RESET - | RADEON_PPLL_SLEEP - | RADEON_PPLL_ATOMIC_UPDATE_EN - | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN)); - - DRM_DEBUG("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n", - pll_ref_div, - pll_fb_post_div, - (unsigned)htotal_cntl, - RREG32_PLL(RADEON_PPLL_CNTL)); - DRM_DEBUG("Wrote: rd=%d, fd=%d, pd=%d\n", - pll_ref_div & RADEON_PPLL_REF_DIV_MASK, - pll_fb_post_div & RADEON_PPLL_FB3_DIV_MASK, - (pll_fb_post_div & RADEON_PPLL_POST3_DIV_MASK) >> 16); - - mdelay(50); /* Let the clock to lock */ - - WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, - RADEON_VCLK_SRC_SEL_PPLLCLK, - ~(RADEON_VCLK_SRC_SEL_MASK)); - - if (is_tv) - WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); } } + static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index ccb7838..b208984 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -239,6 +239,13 @@ struct radeon_encoder_lvds { uint32_t lvds_gen_cntl; /* panel mode */ struct drm_display_mode native_mode; + + bool ss_valid_boot; + bool ss_external; + bool ss_center; + uint16_t ss_percentage; + uint32_t ss_delay; /* readback at boot */ + uint32_t ss_step_size; /* readback */ }; struct radeon_encoder_tv_dac { diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h index 29ab759..01603d3 100644 --- a/drivers/gpu/drm/radeon/radeon_reg.h +++ b/drivers/gpu/drm/radeon/radeon_reg.h @@ -1115,6 +1115,8 @@ # define R300_LVDS_SRC_SEL_CRTC2 (1 << 18) # define R300_LVDS_SRC_SEL_RMX (2 << 18) #define RADEON_LVDS_SS_GEN_CNTL 0x02ec +# define RADEON_LVDS_SS_EXT_EN (1 << 1) +# define RADEON_LVDS_SS_EXT_SEL (1 << 2) # define RADEON_LVDS_PWRSEQ_DELAY1_SHIFT 16 # define RADEON_LVDS_PWRSEQ_DELAY2_SHIFT 20 @@ -3658,4 +3660,26 @@ #define RV530_GB_PIPE_SELECT2 0x4124 +#define RADEON_SSPLL_CNTL 0x30 +# define RADEON_SSPLL_RESET (1 << 0) +# define RADEON_SSPLL_SLEEP (1 << 1) +# define RADEON_SSPLL_PVG_MASK (7 << 11) +# define RADEON_SSPLL_PVG_SHIFT 11 +# define RADEON_SSPLL_ATOMIC_UPDATE_EN (1 << 16) +# define RADEON_SSPLL_VGA_ATOMIC_UPDATE_EN (1 << 17) +# define RADEON_SSPLL_ATOMIC_UPDATE_VSYNC (1 << 18) +#define RADEON_SSPLL_DIV_0 0x32 +# define RADEON_SSPLL_FB0_DIV_MASK 0x07ff +# define RADEON_SSPLL_POST0_DIV_MASK 0x00070000 +#define RADEON_SSPLL_REF_DIV 0x31 +# define RADEON_SSPLL_REF_DIV_MASK 0x03ff +# define RADEON_SSPLL_ATOMIC_UPDATE_R (1 << 15) /* same as _W */ +# define RADEON_SSPLL_ATOMIC_UPDATE_W (1 << 15) /* same as _R */ + +#define RADEON_SS_INT_CNTL 0x33 +# define RADEON_SS_INT_EN (1 << 0) +# define RADEON_SS_INT_SEL (1 << 1) +# define RADEON_SS_STEP_SIZE_SHIFT 2 +# define RADEON_SS_DELAY_SHIFT 4 + #endif -- 1.6.5.2