summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/radeon/radeon_display.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_display.c')
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c405
1 files changed, 187 insertions, 218 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index b92d2f2fcbed..0383631da69c 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -454,13 +454,13 @@ static inline uint32_t radeon_div(uint64_t n, uint32_t d)
return n;
}
-static void radeon_compute_pll_legacy(struct radeon_pll *pll,
- uint64_t freq,
- uint32_t *dot_clock_p,
- uint32_t *fb_div_p,
- uint32_t *frac_fb_div_p,
- uint32_t *ref_div_p,
- uint32_t *post_div_p)
+void radeon_compute_pll(struct radeon_pll *pll,
+ uint64_t freq,
+ uint32_t *dot_clock_p,
+ uint32_t *fb_div_p,
+ uint32_t *frac_fb_div_p,
+ uint32_t *ref_div_p,
+ uint32_t *post_div_p)
{
uint32_t min_ref_div = pll->min_ref_div;
uint32_t max_ref_div = pll->max_ref_div;
@@ -513,7 +513,7 @@ static void radeon_compute_pll_legacy(struct radeon_pll *pll,
max_fractional_feed_div = pll->max_frac_feedback_div;
}
- for (post_div = min_post_div; post_div <= max_post_div; ++post_div) {
+ for (post_div = max_post_div; post_div >= min_post_div; --post_div) {
uint32_t ref_div;
if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
@@ -631,214 +631,6 @@ static void radeon_compute_pll_legacy(struct radeon_pll *pll,
*post_div_p = best_post_div;
}
-static bool
-calc_fb_div(struct radeon_pll *pll,
- uint32_t freq,
- uint32_t post_div,
- uint32_t ref_div,
- uint32_t *fb_div,
- uint32_t *fb_div_frac)
-{
- fixed20_12 feedback_divider, a, b;
- u32 vco_freq;
-
- vco_freq = freq * post_div;
- /* feedback_divider = vco_freq * ref_div / pll->reference_freq; */
- a.full = dfixed_const(pll->reference_freq);
- feedback_divider.full = dfixed_const(vco_freq);
- feedback_divider.full = dfixed_div(feedback_divider, a);
- a.full = dfixed_const(ref_div);
- feedback_divider.full = dfixed_mul(feedback_divider, a);
-
- if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
- /* feedback_divider = floor((feedback_divider * 10.0) + 0.5) * 0.1; */
- a.full = dfixed_const(10);
- feedback_divider.full = dfixed_mul(feedback_divider, a);
- feedback_divider.full += dfixed_const_half(0);
- feedback_divider.full = dfixed_floor(feedback_divider);
- feedback_divider.full = dfixed_div(feedback_divider, a);
-
- /* *fb_div = floor(feedback_divider); */
- a.full = dfixed_floor(feedback_divider);
- *fb_div = dfixed_trunc(a);
- /* *fb_div_frac = fmod(feedback_divider, 1.0) * 10.0; */
- a.full = dfixed_const(10);
- b.full = dfixed_mul(feedback_divider, a);
-
- feedback_divider.full = dfixed_floor(feedback_divider);
- feedback_divider.full = dfixed_mul(feedback_divider, a);
- feedback_divider.full = b.full - feedback_divider.full;
- *fb_div_frac = dfixed_trunc(feedback_divider);
- } else {
- /* *fb_div = floor(feedback_divider + 0.5); */
- feedback_divider.full += dfixed_const_half(0);
- feedback_divider.full = dfixed_floor(feedback_divider);
-
- *fb_div = dfixed_trunc(feedback_divider);
- *fb_div_frac = 0;
- }
-
- if (((*fb_div) < pll->min_feedback_div) || ((*fb_div) > pll->max_feedback_div))
- return false;
- else
- return true;
-}
-
-static bool
-calc_fb_ref_div(struct radeon_pll *pll,
- uint32_t freq,
- uint32_t post_div,
- uint32_t *fb_div,
- uint32_t *fb_div_frac,
- uint32_t *ref_div)
-{
- fixed20_12 ffreq, max_error, error, pll_out, a;
- u32 vco;
- u32 pll_out_min, pll_out_max;
-
- if (pll->flags & RADEON_PLL_IS_LCD) {
- pll_out_min = pll->lcd_pll_out_min;
- pll_out_max = pll->lcd_pll_out_max;
- } else {
- pll_out_min = pll->pll_out_min;
- pll_out_max = pll->pll_out_max;
- }
-
- ffreq.full = dfixed_const(freq);
- /* max_error = ffreq * 0.0025; */
- a.full = dfixed_const(400);
- max_error.full = dfixed_div(ffreq, a);
-
- for ((*ref_div) = pll->min_ref_div; (*ref_div) < pll->max_ref_div; ++(*ref_div)) {
- if (calc_fb_div(pll, freq, post_div, (*ref_div), fb_div, fb_div_frac)) {
- vco = pll->reference_freq * (((*fb_div) * 10) + (*fb_div_frac));
- vco = vco / ((*ref_div) * 10);
-
- if ((vco < pll_out_min) || (vco > pll_out_max))
- continue;
-
- /* pll_out = vco / post_div; */
- a.full = dfixed_const(post_div);
- pll_out.full = dfixed_const(vco);
- pll_out.full = dfixed_div(pll_out, a);
-
- if (pll_out.full >= ffreq.full) {
- error.full = pll_out.full - ffreq.full;
- if (error.full <= max_error.full)
- return true;
- }
- }
- }
- return false;
-}
-
-static void radeon_compute_pll_new(struct radeon_pll *pll,
- uint64_t freq,
- uint32_t *dot_clock_p,
- uint32_t *fb_div_p,
- uint32_t *frac_fb_div_p,
- uint32_t *ref_div_p,
- uint32_t *post_div_p)
-{
- u32 fb_div = 0, fb_div_frac = 0, post_div = 0, ref_div = 0;
- u32 best_freq = 0, vco_frequency;
- u32 pll_out_min, pll_out_max;
-
- if (pll->flags & RADEON_PLL_IS_LCD) {
- pll_out_min = pll->lcd_pll_out_min;
- pll_out_max = pll->lcd_pll_out_max;
- } else {
- pll_out_min = pll->pll_out_min;
- pll_out_max = pll->pll_out_max;
- }
-
- /* freq = freq / 10; */
- do_div(freq, 10);
-
- if (pll->flags & RADEON_PLL_USE_POST_DIV) {
- post_div = pll->post_div;
- if ((post_div < pll->min_post_div) || (post_div > pll->max_post_div))
- goto done;
-
- vco_frequency = freq * post_div;
- if ((vco_frequency < pll_out_min) || (vco_frequency > pll_out_max))
- goto done;
-
- if (pll->flags & RADEON_PLL_USE_REF_DIV) {
- ref_div = pll->reference_div;
- if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div))
- goto done;
- if (!calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac))
- goto done;
- }
- } else {
- for (post_div = pll->max_post_div; post_div >= pll->min_post_div; --post_div) {
- if (pll->flags & RADEON_PLL_LEGACY) {
- if ((post_div == 5) ||
- (post_div == 7) ||
- (post_div == 9) ||
- (post_div == 10) ||
- (post_div == 11))
- continue;
- }
-
- if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
- continue;
-
- vco_frequency = freq * post_div;
- if ((vco_frequency < pll_out_min) || (vco_frequency > pll_out_max))
- continue;
- if (pll->flags & RADEON_PLL_USE_REF_DIV) {
- ref_div = pll->reference_div;
- if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div))
- goto done;
- if (calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac))
- break;
- } else {
- if (calc_fb_ref_div(pll, freq, post_div, &fb_div, &fb_div_frac, &ref_div))
- break;
- }
- }
- }
-
- best_freq = pll->reference_freq * 10 * fb_div;
- best_freq += pll->reference_freq * fb_div_frac;
- best_freq = best_freq / (ref_div * post_div);
-
-done:
- if (best_freq == 0)
- DRM_ERROR("Couldn't find valid PLL dividers\n");
-
- *dot_clock_p = best_freq / 10;
- *fb_div_p = fb_div;
- *frac_fb_div_p = fb_div_frac;
- *ref_div_p = ref_div;
- *post_div_p = post_div;
-
- DRM_DEBUG_KMS("%u %d.%d, %d, %d\n", *dot_clock_p, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p);
-}
-
-void radeon_compute_pll(struct radeon_pll *pll,
- uint64_t freq,
- uint32_t *dot_clock_p,
- uint32_t *fb_div_p,
- uint32_t *frac_fb_div_p,
- uint32_t *ref_div_p,
- uint32_t *post_div_p)
-{
- switch (pll->algo) {
- case PLL_ALGO_NEW:
- radeon_compute_pll_new(pll, freq, dot_clock_p, fb_div_p,
- frac_fb_div_p, ref_div_p, post_div_p);
- break;
- case PLL_ALGO_LEGACY:
- default:
- radeon_compute_pll_legacy(pll, freq, dot_clock_p, fb_div_p,
- frac_fb_div_p, ref_div_p, post_div_p);
- break;
- }
-}
-
static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
{
struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb);
@@ -1002,6 +794,24 @@ static int radeon_modeset_create_props(struct radeon_device *rdev)
radeon_underscan_enum_list[i].name);
}
+ rdev->mode_info.underscan_hborder_property =
+ drm_property_create(rdev->ddev,
+ DRM_MODE_PROP_RANGE,
+ "underscan hborder", 2);
+ if (!rdev->mode_info.underscan_hborder_property)
+ return -ENOMEM;
+ rdev->mode_info.underscan_hborder_property->values[0] = 0;
+ rdev->mode_info.underscan_hborder_property->values[1] = 128;
+
+ rdev->mode_info.underscan_vborder_property =
+ drm_property_create(rdev->ddev,
+ DRM_MODE_PROP_RANGE,
+ "underscan vborder", 2);
+ if (!rdev->mode_info.underscan_vborder_property)
+ return -ENOMEM;
+ rdev->mode_info.underscan_vborder_property->values[0] = 0;
+ rdev->mode_info.underscan_vborder_property->values[1] = 128;
+
return 0;
}
@@ -1159,8 +969,14 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
((radeon_encoder->underscan_type == UNDERSCAN_AUTO) &&
drm_detect_hdmi_monitor(radeon_connector->edid) &&
is_hdtv_mode(mode)))) {
- radeon_crtc->h_border = (mode->hdisplay >> 5) + 16;
- radeon_crtc->v_border = (mode->vdisplay >> 5) + 16;
+ if (radeon_encoder->underscan_hborder != 0)
+ radeon_crtc->h_border = radeon_encoder->underscan_hborder;
+ else
+ radeon_crtc->h_border = (mode->hdisplay >> 5) + 16;
+ if (radeon_encoder->underscan_vborder != 0)
+ radeon_crtc->v_border = radeon_encoder->underscan_vborder;
+ else
+ radeon_crtc->v_border = (mode->vdisplay >> 5) + 16;
radeon_crtc->rmx_type = RMX_FULL;
src_v = crtc->mode.vdisplay;
dst_v = crtc->mode.vdisplay - (radeon_crtc->v_border * 2);
@@ -1195,3 +1011,156 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
}
return true;
}
+
+/*
+ * Retrieve current video scanout position of crtc on a given gpu.
+ *
+ * \param rdev Device to query.
+ * \param crtc Crtc to query.
+ * \param *vpos Location where vertical scanout position should be stored.
+ * \param *hpos Location where horizontal scanout position should go.
+ *
+ * Returns vpos as a positive number while in active scanout area.
+ * Returns vpos as a negative number inside vblank, counting the number
+ * of scanlines to go until end of vblank, e.g., -1 means "one scanline
+ * until start of active scanout / end of vblank."
+ *
+ * \return Flags, or'ed together as follows:
+ *
+ * RADEON_SCANOUTPOS_VALID = Query successfull.
+ * RADEON_SCANOUTPOS_INVBL = Inside vblank.
+ * RADEON_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of
+ * this flag means that returned position may be offset by a constant but
+ * unknown small number of scanlines wrt. real scanout position.
+ *
+ */
+int radeon_get_crtc_scanoutpos(struct radeon_device *rdev, int crtc, int *vpos, int *hpos)
+{
+ u32 stat_crtc = 0, vbl = 0, position = 0;
+ int vbl_start, vbl_end, vtotal, ret = 0;
+ bool in_vbl = true;
+
+ if (ASIC_IS_DCE4(rdev)) {
+ if (crtc == 0) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC0_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC0_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 1) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC1_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC1_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 2) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC2_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC2_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 3) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC3_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC3_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 4) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC4_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC4_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 5) {
+ vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
+ EVERGREEN_CRTC5_REGISTER_OFFSET);
+ position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
+ EVERGREEN_CRTC5_REGISTER_OFFSET);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ } else if (ASIC_IS_AVIVO(rdev)) {
+ if (crtc == 0) {
+ vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END);
+ position = RREG32(AVIVO_D1CRTC_STATUS_POSITION);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 1) {
+ vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END);
+ position = RREG32(AVIVO_D2CRTC_STATUS_POSITION);
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ } else {
+ /* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */
+ if (crtc == 0) {
+ /* Assume vbl_end == 0, get vbl_start from
+ * upper 16 bits.
+ */
+ vbl = (RREG32(RADEON_CRTC_V_TOTAL_DISP) &
+ RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
+ /* Only retrieve vpos from upper 16 bits, set hpos == 0. */
+ position = (RREG32(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL;
+ stat_crtc = RREG32(RADEON_CRTC_STATUS);
+ if (!(stat_crtc & 1))
+ in_vbl = false;
+
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ if (crtc == 1) {
+ vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) &
+ RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
+ position = (RREG32(RADEON_CRTC2_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL;
+ stat_crtc = RREG32(RADEON_CRTC2_STATUS);
+ if (!(stat_crtc & 1))
+ in_vbl = false;
+
+ ret |= RADEON_SCANOUTPOS_VALID;
+ }
+ }
+
+ /* Decode into vertical and horizontal scanout position. */
+ *vpos = position & 0x1fff;
+ *hpos = (position >> 16) & 0x1fff;
+
+ /* Valid vblank area boundaries from gpu retrieved? */
+ if (vbl > 0) {
+ /* Yes: Decode. */
+ ret |= RADEON_SCANOUTPOS_ACCURATE;
+ vbl_start = vbl & 0x1fff;
+ vbl_end = (vbl >> 16) & 0x1fff;
+ }
+ else {
+ /* No: Fake something reasonable which gives at least ok results. */
+ vbl_start = rdev->mode_info.crtcs[crtc]->base.mode.crtc_vdisplay;
+ vbl_end = 0;
+ }
+
+ /* Test scanout position against vblank region. */
+ if ((*vpos < vbl_start) && (*vpos >= vbl_end))
+ in_vbl = false;
+
+ /* Check if inside vblank area and apply corrective offsets:
+ * vpos will then be >=0 in video scanout area, but negative
+ * within vblank area, counting down the number of lines until
+ * start of scanout.
+ */
+
+ /* Inside "upper part" of vblank area? Apply corrective offset if so: */
+ if (in_vbl && (*vpos >= vbl_start)) {
+ vtotal = rdev->mode_info.crtcs[crtc]->base.mode.crtc_vtotal;
+ *vpos = *vpos - vtotal;
+ }
+
+ /* Correct for shifted end of vbl at vbl_end. */
+ *vpos = *vpos - vbl_end;
+
+ /* In vblank? */
+ if (in_vbl)
+ ret |= RADEON_SCANOUTPOS_INVBL;
+
+ return ret;
+}