30,26 → 30,13 |
#include "atom.h" |
#include <asm/div64.h> |
|
#include <linux/pm_runtime.h> |
#include <drm/drm_crtc_helper.h> |
#include <drm/drm_plane_helper.h> |
#include <drm/drm_edid.h> |
|
/* Greatest common divisor */ |
unsigned long gcd(unsigned long a, unsigned long b) |
{ |
unsigned long r; |
#include <linux/gcd.h> |
|
if (a < b) |
swap(a, b); |
|
if (!b) |
return a; |
while ((r = a % b) != 0) { |
a = b; |
b = r; |
} |
return b; |
} |
|
static void avivo_crtc_load_lut(struct drm_crtc *crtc) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
167,7 → 154,7 |
(NI_GRPH_REGAMMA_MODE(NI_REGAMMA_BYPASS) | |
NI_OVL_REGAMMA_MODE(NI_REGAMMA_BYPASS))); |
WREG32(NI_OUTPUT_CSC_CONTROL + radeon_crtc->crtc_offset, |
(NI_OUTPUT_CSC_GRPH_MODE(NI_OUTPUT_CSC_BYPASS) | |
(NI_OUTPUT_CSC_GRPH_MODE(radeon_crtc->output_csc) | |
NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS))); |
/* XXX match this to the depth of the crtc fmt block, move to modeset? */ |
WREG32(0x6940 + radeon_crtc->crtc_offset, 0); |
267,6 → 254,65 |
kfree(radeon_crtc); |
} |
|
void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id) |
{ |
struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; |
unsigned long flags; |
u32 update_pending; |
int vpos, hpos; |
|
/* can happen during initialization */ |
if (radeon_crtc == NULL) |
return; |
|
/* Skip the pageflip completion check below (based on polling) on |
* asics which reliably support hw pageflip completion irqs. pflip |
* irqs are a reliable and race-free method of handling pageflip |
* completion detection. A use_pflipirq module parameter < 2 allows |
* to override this in case of asics with faulty pflip irqs. |
* A module parameter of 0 would only use this polling based path, |
* a parameter of 1 would use pflip irq only as a backup to this |
* path, as in Linux 3.16. |
*/ |
if ((radeon_use_pflipirq == 2) && ASIC_IS_DCE4(rdev)) |
return; |
|
spin_lock_irqsave(&rdev->ddev->event_lock, flags); |
if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) { |
DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != " |
"RADEON_FLIP_SUBMITTED(%d)\n", |
radeon_crtc->flip_status, |
RADEON_FLIP_SUBMITTED); |
spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); |
return; |
} |
|
update_pending = radeon_page_flip_pending(rdev, crtc_id); |
|
/* Has the pageflip already completed in crtc, or is it certain |
* to complete in this vblank? |
*/ |
if (update_pending && |
(DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, |
crtc_id, |
USE_REAL_VBLANKSTART, |
&vpos, &hpos, NULL, NULL, |
&rdev->mode_info.crtcs[crtc_id]->base.hwmode)) && |
((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) || |
(vpos < 0 && !ASIC_IS_AVIVO(rdev)))) { |
/* crtc didn't flip in this target vblank interval, |
* but flip is pending in crtc. Based on the current |
* scanout position we know that the current frame is |
* (nearly) complete and the flip will (likely) |
* complete before the start of the next frame. |
*/ |
update_pending = 0; |
} |
spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); |
// if (!update_pending) |
// radeon_crtc_handle_flip(rdev, crtc_id); |
} |
|
static int |
radeon_crtc_set_config(struct drm_mode_set *set) |
{ |
632,6 → 678,9 |
if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && |
pll->flags & RADEON_PLL_USE_REF_DIV) |
ref_div_max = pll->reference_div; |
else if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) |
/* fix for problems on RS880 */ |
ref_div_max = min(pll->max_ref_div, 7u); |
else |
ref_div_max = pll->max_ref_div; |
|
1016,6 → 1065,13 |
{ RADEON_FMT_DITHER_ENABLE, "on" }, |
}; |
|
static struct drm_prop_enum_list radeon_output_csc_enum_list[] = |
{ { RADEON_OUTPUT_CSC_BYPASS, "bypass" }, |
{ RADEON_OUTPUT_CSC_TVRGB, "tvrgb" }, |
{ RADEON_OUTPUT_CSC_YCBCR601, "ycbcr601" }, |
{ RADEON_OUTPUT_CSC_YCBCR709, "ycbcr709" }, |
}; |
|
static int radeon_modeset_create_props(struct radeon_device *rdev) |
{ |
int sz; |
1078,6 → 1134,12 |
"dither", |
radeon_dither_enum_list, sz); |
|
sz = ARRAY_SIZE(radeon_output_csc_enum_list); |
rdev->mode_info.output_csc_property = |
drm_property_create_enum(rdev->ddev, 0, |
"output_csc", |
radeon_output_csc_enum_list, sz); |
|
return 0; |
} |
|
1408,8 → 1470,10 |
* unknown small number of scanlines wrt. real scanout position. |
* |
*/ |
int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, |
int *vpos, int *hpos, void *stime, void *etime) |
int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, |
unsigned int flags, int *vpos, int *hpos, |
ktime_t *stime, ktime_t *etime, |
const struct drm_display_mode *mode) |
{ |
u32 stat_crtc = 0, vbl = 0, position = 0; |
int vbl_start, vbl_end, vtotal, ret = 0; |
1417,8 → 1481,14 |
|
struct radeon_device *rdev = dev->dev_private; |
|
/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ |
|
/* Get optional system timestamp before query. */ |
if (stime) |
*stime = ktime_get(); |
|
if (ASIC_IS_DCE4(rdev)) { |
if (crtc == 0) { |
if (pipe == 0) { |
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + |
EVERGREEN_CRTC0_REGISTER_OFFSET); |
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + |
1425,7 → 1495,7 |
EVERGREEN_CRTC0_REGISTER_OFFSET); |
ret |= DRM_SCANOUTPOS_VALID; |
} |
if (crtc == 1) { |
if (pipe == 1) { |
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + |
EVERGREEN_CRTC1_REGISTER_OFFSET); |
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + |
1432,7 → 1502,7 |
EVERGREEN_CRTC1_REGISTER_OFFSET); |
ret |= DRM_SCANOUTPOS_VALID; |
} |
if (crtc == 2) { |
if (pipe == 2) { |
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + |
EVERGREEN_CRTC2_REGISTER_OFFSET); |
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + |
1439,7 → 1509,7 |
EVERGREEN_CRTC2_REGISTER_OFFSET); |
ret |= DRM_SCANOUTPOS_VALID; |
} |
if (crtc == 3) { |
if (pipe == 3) { |
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + |
EVERGREEN_CRTC3_REGISTER_OFFSET); |
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + |
1446,7 → 1516,7 |
EVERGREEN_CRTC3_REGISTER_OFFSET); |
ret |= DRM_SCANOUTPOS_VALID; |
} |
if (crtc == 4) { |
if (pipe == 4) { |
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + |
EVERGREEN_CRTC4_REGISTER_OFFSET); |
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + |
1453,7 → 1523,7 |
EVERGREEN_CRTC4_REGISTER_OFFSET); |
ret |= DRM_SCANOUTPOS_VALID; |
} |
if (crtc == 5) { |
if (pipe == 5) { |
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + |
EVERGREEN_CRTC5_REGISTER_OFFSET); |
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + |
1461,12 → 1531,12 |
ret |= DRM_SCANOUTPOS_VALID; |
} |
} else if (ASIC_IS_AVIVO(rdev)) { |
if (crtc == 0) { |
if (pipe == 0) { |
vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END); |
position = RREG32(AVIVO_D1CRTC_STATUS_POSITION); |
ret |= DRM_SCANOUTPOS_VALID; |
} |
if (crtc == 1) { |
if (pipe == 1) { |
vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END); |
position = RREG32(AVIVO_D2CRTC_STATUS_POSITION); |
ret |= DRM_SCANOUTPOS_VALID; |
1473,7 → 1543,7 |
} |
} else { |
/* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */ |
if (crtc == 0) { |
if (pipe == 0) { |
/* Assume vbl_end == 0, get vbl_start from |
* upper 16 bits. |
*/ |
1487,7 → 1557,7 |
|
ret |= DRM_SCANOUTPOS_VALID; |
} |
if (crtc == 1) { |
if (pipe == 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; |
1499,6 → 1569,12 |
} |
} |
|
/* Get optional system timestamp after query. */ |
if (etime) |
*etime = ktime_get(); |
|
/* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ |
|
/* Decode into vertical and horizontal scanout position. */ |
*vpos = position & 0x1fff; |
*hpos = (position >> 16) & 0x1fff; |
1512,7 → 1588,7 |
} |
else { |
/* No: Fake something reasonable which gives at least ok results. */ |
vbl_start = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; |
vbl_start = mode->crtc_vdisplay; |
vbl_end = 0; |
} |
|
1528,7 → 1604,7 |
|
/* Inside "upper part" of vblank area? Apply corrective offset if so: */ |
if (in_vbl && (*vpos >= vbl_start)) { |
vtotal = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; |
vtotal = mode->crtc_vtotal; |
*vpos = *vpos - vtotal; |
} |
|
1550,8 → 1626,8 |
* We only do this if DRM_CALLED_FROM_VBLIRQ. |
*/ |
if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) { |
vbl_start = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; |
vtotal = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; |
vbl_start = mode->crtc_vdisplay; |
vtotal = mode->crtc_vtotal; |
|
if (vbl_start - *vpos < vtotal / 100) { |
*vpos -= vtotal; |