/* * Copyright 2007-8 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Dave Airlie * Alex Deucher
*/
WREG8(AVIVO_DC_LUT_RW_INDEX, 0);
r = crtc->gamma_store;
g = r + crtc->gamma_size;
b = g + crtc->gamma_size; for (i = 0; i < 256; i++) {
WREG32(AVIVO_DC_LUT_30_COLOR,
((*r++ & 0xffc0) << 14) |
((*g++ & 0xffc0) << 4) |
(*b++ >> 6));
}
/* Only change bit 0 of LUT_SEL, other bits are set elsewhere */
WREG32_P(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id, ~1);
}
WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0);
r = crtc->gamma_store;
g = r + crtc->gamma_size;
b = g + crtc->gamma_size; for (i = 0; i < 256; i++) {
WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset,
((*r++ & 0xffc0) << 14) |
((*g++ & 0xffc0) << 4) |
(*b++ >> 6));
}
WREG32(NI_DEGAMMA_CONTROL + radeon_crtc->crtc_offset,
(NI_GRPH_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
NI_OVL_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
NI_ICON_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
NI_CURSOR_DEGAMMA_MODE(NI_DEGAMMA_BYPASS)));
WREG32(NI_GAMUT_REMAP_CONTROL + radeon_crtc->crtc_offset,
(NI_GRPH_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS) |
NI_OVL_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS)));
WREG32(NI_REGAMMA_CONTROL + radeon_crtc->crtc_offset,
(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(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); if (ASIC_IS_DCE8(rdev)) { /* XXX this only needs to be programmed once per crtc at startup, * not sure where the best place for it is
*/
WREG32(CIK_ALPHA_CONTROL + radeon_crtc->crtc_offset,
CIK_CURSOR_ALPHA_BLND_ENA);
}
}
/** * radeon_unpin_work_func - unpin old buffer object * * @__work: kernel work item * * Unpin the old frame buffer object outside of the interrupt handler
*/ staticvoid radeon_unpin_work_func(struct work_struct *__work)
{ struct radeon_flip_work *work =
container_of(__work, struct radeon_flip_work, unpin_work); int r;
/* unpin of the old buffer */
r = radeon_bo_reserve(work->old_rbo, false); if (likely(r == 0)) {
radeon_bo_unpin(work->old_rbo);
radeon_bo_unreserve(work->old_rbo);
} else
DRM_ERROR("failed to reserve buffer after flip\n");
void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id)
{ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; unsignedlong 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;
/* Has the pageflip already completed in crtc, or is it certain * to complete in this vblank? GET_DISTANCE_TO_VBLANKSTART provides * distance to start of "fudged earlier" vblank in vpos, distance to * start of real vblank in hpos. vpos >= 0 && hpos < 0 means we are in * the last few scanlines before start of real vblank, where the vblank * irq can fire, so we have sampled update_pending a bit too early and * know the flip will complete at leading edge of the upcoming real * vblank. On pre-AVIVO hardware, flips also complete inside the real * vblank, not only at leading edge, so if update_pending for hpos >= 0 * == inside real vblank, the flip will complete almost immediately. * Note that this method of completion handling is still not 100% race * free, as we could execute before the radeon_flip_work_func managed * to run and set the RADEON_FLIP_SUBMITTED status, thereby we no-op, * but the flip still gets programmed into hw and completed during * vblank, leading to a delayed emission of the flip completion event. * This applies at least to pre-AVIVO hardware, where flips are always * completing inside vblank, not only at leading edge of vblank.
*/ if (update_pending &&
(DRM_SCANOUTPOS_VALID &
radeon_get_crtc_scanoutpos(rdev_to_drm(rdev), crtc_id,
GET_DISTANCE_TO_VBLANKSTART,
&vpos, &hpos, NULL, NULL,
&rdev->mode_info.crtcs[crtc_id]->base.hwmode)) &&
((vpos >= 0 && hpos < 0) || (hpos >= 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_to_drm(rdev)->event_lock, flags); if (!update_pending)
radeon_crtc_handle_flip(rdev, crtc_id);
}
/** * radeon_crtc_handle_flip - page flip completed * * @rdev: radeon device pointer * @crtc_id: crtc number this event is for * * Called when we are sure that a page flip for this crtc is completed.
*/ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
{ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; struct radeon_flip_work *work; unsignedlong flags;
/* this can happen at init */ if (radeon_crtc == NULL) return;
/** * radeon_flip_work_func - page flip framebuffer * * @__work: kernel work item * * Wait for the buffer object to become idle and do the actual page flip
*/ staticvoid radeon_flip_work_func(struct work_struct *__work)
{ struct radeon_flip_work *work =
container_of(__work, struct radeon_flip_work, flip_work); struct radeon_device *rdev = work->rdev; struct drm_device *dev = rdev_to_drm(rdev); struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id];
struct drm_crtc *crtc = &radeon_crtc->base; unsignedlong flags; int r; int vpos, hpos;
down_read(&rdev->exclusive_lock); if (work->fence) { struct radeon_fence *fence;
fence = to_radeon_fence(work->fence); if (fence && fence->rdev == rdev) {
r = radeon_fence_wait(fence, false); if (r == -EDEADLK) {
up_read(&rdev->exclusive_lock); do {
r = radeon_gpu_reset(rdev);
} while (r == -EAGAIN);
down_read(&rdev->exclusive_lock);
}
} else
r = dma_fence_wait(work->fence, false);
if (r)
DRM_ERROR("failed to wait on page flip fence (%d)!\n", r);
/* We continue with the page flip even if we failed to wait on * the fence, otherwise the DRM core and userspace will be * confused about which BO the CRTC is scanning out
*/
dma_fence_put(work->fence);
work->fence = NULL;
}
/* Wait until we're out of the vertical blank period before the one * targeted by the flip. Always wait on pre DCE4 to avoid races with * flip completion handling from vblank irq, as these old asics don't * have reliable pageflip completion interrupts.
*/ while (radeon_crtc->enabled &&
(radeon_get_crtc_scanoutpos(dev, work->crtc_id, 0,
&vpos, &hpos, NULL, NULL,
&crtc->hwmode)
& (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
(DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
(!ASIC_IS_AVIVO(rdev) ||
((int) (work->target_vblank -
crtc->funcs->get_vblank_counter(crtc)) > 0)))
usleep_range(1000, 2000);
/* We borrow the event spin lock for protecting flip_status */
spin_lock_irqsave(&crtc->dev->event_lock, flags);
/* set the proper interrupt */
radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id);
/* do the flip (mmio) */
radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base, work->async);
/* pin the new buffer */
DRM_DEBUG_DRIVER("flip-ioctl() cur_rbo = %p, new_rbo = %p\n",
work->old_rbo, new_rbo);
r = radeon_bo_reserve(new_rbo, false); if (unlikely(r != 0)) {
DRM_ERROR("failed to reserve new rbo buffer before flip\n"); goto cleanup;
} /* Only 27 bit offset for legacy CRTC */
r = radeon_bo_pin_restricted(new_rbo, RADEON_GEM_DOMAIN_VRAM,
ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base); if (unlikely(r != 0)) {
radeon_bo_unreserve(new_rbo);
r = -EINVAL;
DRM_ERROR("failed to pin new rbo buffer before flip\n"); goto cleanup;
}
r = dma_resv_get_singleton(new_rbo->tbo.base.resv, DMA_RESV_USAGE_WRITE,
&work->fence); if (r) {
radeon_bo_unreserve(new_rbo);
DRM_ERROR("failed to get new rbo buffer fences\n"); goto cleanup;
}
radeon_bo_get_tiling_flags(new_rbo, &tiling_flags, NULL);
radeon_bo_unreserve(new_rbo);
if (!ASIC_IS_AVIVO(rdev)) { /* crtc offset is from display base addr not FB location */
base -= radeon_crtc->legacy_display_base_addr;
pitch_pixels = fb->pitches[0] / fb->format->cpp[0];
if (tiling_flags & RADEON_TILING_MACRO) { if (ASIC_IS_R300(rdev)) {
base &= ~0x7ff;
} else { int byteshift = fb->format->cpp[0] * 8 >> 4; int tile_addr = (((crtc->y >> 3) * pitch_pixels + crtc->x) >> (8 - byteshift)) << 11;
base += tile_addr + ((crtc->x << byteshift) % 256) + ((crtc->y % 8) << 8);
}
} else { int offset = crtc->y * pitch_pixels + crtc->x; switch (fb->format->cpp[0] * 8) { case 8: default:
offset *= 1; break; case 15: case 16:
offset *= 2; break; case 24:
offset *= 3; break; case 32:
offset *= 4; break;
}
base += offset;
}
base &= ~7;
}
work->base = base;
work->target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) +
crtc->funcs->get_vblank_counter(crtc);
/* We borrow the event spin lock for protecting flip_work */
spin_lock_irqsave(&crtc->dev->event_lock, flags);
pflip_cleanup: if (unlikely(radeon_bo_reserve(new_rbo, false) != 0)) {
DRM_ERROR("failed to reserve new rbo in error path\n"); goto cleanup;
}
radeon_bo_unpin(new_rbo);
radeon_bo_unreserve(new_rbo);
ret = pm_runtime_get_sync(dev->dev); if (ret < 0) {
pm_runtime_put_autosuspend(dev->dev); return ret;
}
ret = drm_crtc_helper_set_config(set, ctx);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) if (crtc->enabled)
active = true;
pm_runtime_mark_last_busy(dev->dev);
rdev = dev->dev_private; /* if we have active crtcs and we don't have a power ref,
take the current one */ if (active && !rdev->have_disp_power_ref) {
rdev->have_disp_power_ref = true; return ret;
} /* if we have no active crtcs, then drop the power ref
we got before */ if (!active && rdev->have_disp_power_ref) {
pm_runtime_put_autosuspend(dev->dev);
rdev->have_disp_power_ref = false;
}
/* drop the power reference we got coming in here */
pm_runtime_put_autosuspend(dev->dev); return ret;
}
if (rdev->bios) { if (rdev->is_atom_bios) {
ret = radeon_get_atom_connector_info_from_supported_devices_table(dev); if (!ret)
ret = radeon_get_atom_connector_info_from_object_table(dev);
} else {
ret = radeon_get_legacy_connector_info_from_bios(dev); if (!ret)
ret = radeon_get_legacy_connector_info_from_table(dev);
}
} else { if (!ASIC_IS_AVIVO(rdev))
ret = radeon_get_legacy_connector_info_from_table(dev);
} if (ret) {
radeon_setup_encoder_clones(dev);
radeon_print_display_setup(dev);
}
return ret;
}
/* avivo */
/** * avivo_reduce_ratio - fractional number reduction * * @nom: nominator * @den: denominator * @nom_min: minimum value for nominator * @den_min: minimum value for denominator * * Find the greatest common divisor and apply it on both nominator and * denominator, but make nominator and denominator are at least as large * as their minimum values.
*/ staticvoid avivo_reduce_ratio(unsigned *nom, unsigned *den, unsigned nom_min, unsigned den_min)
{ unsigned tmp;
/* reduce the numbers to a simpler ratio */
tmp = gcd(*nom, *den);
*nom /= tmp;
*den /= tmp;
/* make sure nominator is large enough */ if (*nom < nom_min) {
tmp = DIV_ROUND_UP(nom_min, *nom);
*nom *= tmp;
*den *= tmp;
}
/* make sure the denominator is large enough */ if (*den < den_min) {
tmp = DIV_ROUND_UP(den_min, *den);
*nom *= tmp;
*den *= tmp;
}
}
/** * avivo_get_fb_ref_div - feedback and ref divider calculation * * @nom: nominator * @den: denominator * @post_div: post divider * @fb_div_max: feedback divider maximum * @ref_div_max: reference divider maximum * @fb_div: resulting feedback divider * @ref_div: resulting reference divider * * Calculate feedback and reference divider for a given post divider. Makes * sure we stay within the limits.
*/ staticvoid avivo_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div, unsigned fb_div_max, unsigned ref_div_max, unsigned *fb_div, unsigned *ref_div)
{ /* limit reference * post divider to a maximum */
ref_div_max = max(min(100 / post_div, ref_div_max), 1u);
post_div_min = vco_min / target_clock; if ((target_clock * post_div_min) < vco_min)
++post_div_min; if (post_div_min < pll->min_post_div)
post_div_min = pll->min_post_div;
post_div_max = vco_max / target_clock; if ((target_clock * post_div_max) > vco_max)
--post_div_max; if (post_div_max > pll->max_post_div)
post_div_max = pll->max_post_div;
}
/* represent the searched ratio as fractional number */
nom = target_clock;
den = pll->reference_freq;
/* reduce the numbers to a simpler ratio */
avivo_reduce_ratio(&nom, &den, fb_div_min, post_div_min);
/* now search for a post divider */ if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP)
post_div_best = post_div_min; else
post_div_best = post_div_max;
diff_best = ~0;
/* get the feedback and reference divider for the optimal value */
avivo_get_fb_ref_div(nom, den, post_div, fb_div_max, ref_div_max,
&fb_div, &ref_div);
/* reduce the numbers to a simpler ratio once more */ /* this also makes sure that the reference divider is large enough */
avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min);
/* avoid high jitter with small fractional dividers */ if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) {
fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 50); if (fb_div < fb_div_min) { unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div);
fb_div *= tmp;
ref_div *= tmp;
}
}
/* and finally save the result */ if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
*fb_div_p = fb_div / 10;
*frac_fb_div_p = fb_div % 10;
} else {
*fb_div_p = fb_div;
*frac_fb_div_p = 0;
}
obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]); if (obj == NULL) {
dev_err(dev->dev, "No GEM object associated to handle 0x%08X, " "can't create framebuffer\n", mode_cmd->handles[0]); return ERR_PTR(-ENOENT);
}
/* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */ if (obj->import_attach) {
DRM_DEBUG_KMS("Cannot create framebuffer from imported dma_buf\n");
drm_gem_object_put(obj); return ERR_PTR(-EINVAL);
}
void radeon_update_display_priority(struct radeon_device *rdev)
{ /* adjustment options for the display watermarks */ if ((radeon_disp_priority == 0) || (radeon_disp_priority > 2)) { /* set display priority to high for r3xx, rv515 chips * this avoids flickering due to underflow to the * display controllers during heavy acceleration. * Don't force high on rs4xx igp chips as it seems to * affect the sound card. See kernel bug 15982.
*/ if ((ASIC_IS_R300(rdev) || (rdev->family == CHIP_RV515)) &&
!(rdev->flags & RADEON_IS_IGP))
rdev->disp_priority = 2; else
rdev->disp_priority = 0;
} else
rdev->disp_priority = radeon_disp_priority;
}
/* * Allocate hdmi structs and determine register offsets
*/ staticvoid radeon_afmt_init(struct radeon_device *rdev)
{ int i;
for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++)
rdev->mode_info.afmt[i] = NULL;
if (ASIC_IS_NODCE(rdev)) { /* nothing to do */
} elseif (ASIC_IS_DCE4(rdev)) { static uint32_t eg_offsets[] = {
EVERGREEN_CRTC0_REGISTER_OFFSET,
EVERGREEN_CRTC1_REGISTER_OFFSET,
EVERGREEN_CRTC2_REGISTER_OFFSET,
EVERGREEN_CRTC3_REGISTER_OFFSET,
EVERGREEN_CRTC4_REGISTER_OFFSET,
EVERGREEN_CRTC5_REGISTER_OFFSET,
0x13830 - 0x7030,
}; int num_afmt;
/* DCE8 has 7 audio blocks tied to DIG encoders */ /* DCE6 has 6 audio blocks tied to DIG encoders */ /* DCE4/5 has 6 audio blocks tied to DIG encoders */ /* DCE4.1 has 2 audio blocks tied to DIG encoders */ if (ASIC_IS_DCE8(rdev))
num_afmt = 7; elseif (ASIC_IS_DCE6(rdev))
num_afmt = 6; elseif (ASIC_IS_DCE5(rdev))
num_afmt = 6; elseif (ASIC_IS_DCE41(rdev))
num_afmt = 2; else/* DCE4 */
num_afmt = 6;
BUG_ON(num_afmt > ARRAY_SIZE(eg_offsets)); for (i = 0; i < num_afmt; i++) {
rdev->mode_info.afmt[i] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); if (rdev->mode_info.afmt[i]) {
rdev->mode_info.afmt[i]->offset = eg_offsets[i];
rdev->mode_info.afmt[i]->id = i;
}
}
} elseif (ASIC_IS_DCE3(rdev)) { /* DCE3.x has 2 audio blocks tied to DIG encoders */
rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); if (rdev->mode_info.afmt[0]) {
rdev->mode_info.afmt[0]->offset = DCE3_HDMI_OFFSET0;
rdev->mode_info.afmt[0]->id = 0;
}
rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); if (rdev->mode_info.afmt[1]) {
rdev->mode_info.afmt[1]->offset = DCE3_HDMI_OFFSET1;
rdev->mode_info.afmt[1]->id = 1;
}
} elseif (ASIC_IS_DCE2(rdev)) { /* DCE2 has at least 1 routable audio block */
rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); if (rdev->mode_info.afmt[0]) {
rdev->mode_info.afmt[0]->offset = DCE2_HDMI_OFFSET0;
rdev->mode_info.afmt[0]->id = 0;
} /* r6xx has 2 routable audio blocks */ if (rdev->family >= CHIP_R600) {
rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); if (rdev->mode_info.afmt[1]) {
rdev->mode_info.afmt[1]->offset = DCE2_HDMI_OFFSET1;
rdev->mode_info.afmt[1]->id = 1;
}
}
}
}
staticvoid radeon_afmt_fini(struct radeon_device *rdev)
{ int i;
for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) {
kfree(rdev->mode_info.afmt[i]);
rdev->mode_info.afmt[i] = NULL;
}
}
int radeon_modeset_init(struct radeon_device *rdev)
{ int i; int ret;
ret = radeon_modeset_create_props(rdev); if (ret) { return ret;
}
/* init i2c buses */
radeon_i2c_init(rdev);
/* check combios for a valid hardcoded EDID - Sun servers */ if (!rdev->is_atom_bios) { /* check for hardcoded EDID in BIOS */
radeon_combios_check_hardcoded_edid(rdev);
}
/* allocate crtcs */ for (i = 0; i < rdev->num_crtc; i++) {
radeon_crtc_init(rdev_to_drm(rdev), i);
}
/* okay we should have all the bios connectors */
ret = radeon_setup_enc_conn(rdev_to_drm(rdev)); if (!ret) { return ret;
}
/* init dig PHYs, disp eng pll */ if (rdev->is_atom_bios) {
radeon_atom_encoder_init(rdev);
radeon_atom_disp_eng_pll_init(rdev);
}
/* initialize hpd */
radeon_hpd_init(rdev);
/* setup afmt */
radeon_afmt_init(rdev);
drm_kms_helper_poll_init(rdev_to_drm(rdev));
/* do pm late init */
ret = radeon_pm_late_init(rdev);
/* fix up for overscan on hdmi */ if (ASIC_IS_AVIVO(rdev) &&
(!(mode->flags & DRM_MODE_FLAG_INTERLACE)) &&
((radeon_encoder->underscan_type == UNDERSCAN_ON) ||
((radeon_encoder->underscan_type == UNDERSCAN_AUTO) &&
connector->display_info.is_hdmi &&
is_hdtv_mode(mode)))) { 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);
src_h = crtc->mode.hdisplay;
dst_h = crtc->mode.hdisplay - (radeon_crtc->h_border * 2);
}
first = false;
} else { if (radeon_crtc->rmx_type != radeon_encoder->rmx_type) { /* WARNING: Right now this can't happen but * in the future we need to check that scaling * are consistent across different encoder * (ie all encoder can work with the same * scaling).
*/
DRM_ERROR("Scaling not consistent across encoder.\n"); returnfalse;
}
}
} if (radeon_crtc->rmx_type != RMX_OFF) {
fixed20_12 a, b;
a.full = dfixed_const(src_v);
b.full = dfixed_const(dst_v);
radeon_crtc->vsc.full = dfixed_div(a, b);
a.full = dfixed_const(src_h);
b.full = dfixed_const(dst_h);
radeon_crtc->hsc.full = dfixed_div(a, b);
} else {
radeon_crtc->vsc.full = dfixed_const(1);
radeon_crtc->hsc.full = dfixed_const(1);
} returntrue;
}
/* * Retrieve current video scanout position of crtc on a given gpu, and * an optional accurate timestamp of when query happened. * * \param dev Device to query. * \param crtc Crtc to query. * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). * For driver internal use only also supports these flags: * * USE_REAL_VBLANKSTART to use the real start of vblank instead * of a fudged earlier start of vblank. * * GET_DISTANCE_TO_VBLANKSTART to return distance to the * fudged earlier start of vblank in *vpos and the distance * to true start of vblank in *hpos. * * \param *vpos Location where vertical scanout position should be stored. * \param *hpos Location where horizontal scanout position should go. * \param *stime Target location for timestamp taken immediately before * scanout position query. Can be NULL to skip timestamp. * \param *etime Target location for timestamp taken immediately after * scanout position query. Can be NULL to skip timestamp. * * 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: * * DRM_SCANOUTPOS_VALID = Query successful. * DRM_SCANOUTPOS_INVBL = Inside vblank. * DRM_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 drm_device *dev, unsignedint pipe, unsignedint flags, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime, conststruct drm_display_mode *mode)
{
u32 stat_crtc = 0, vbl = 0, position = 0; int vbl_start, vbl_end, vtotal, ret = 0; bool in_vbl = true;
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 (pipe == 0) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC0_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC0_REGISTER_OFFSET);
ret |= DRM_SCANOUTPOS_VALID;
} if (pipe == 1) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC1_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC1_REGISTER_OFFSET);
ret |= DRM_SCANOUTPOS_VALID;
} if (pipe == 2) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC2_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC2_REGISTER_OFFSET);
ret |= DRM_SCANOUTPOS_VALID;
} if (pipe == 3) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC3_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC3_REGISTER_OFFSET);
ret |= DRM_SCANOUTPOS_VALID;
} if (pipe == 4) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC4_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC4_REGISTER_OFFSET);
ret |= DRM_SCANOUTPOS_VALID;
} if (pipe == 5) {
vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC5_REGISTER_OFFSET);
position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC5_REGISTER_OFFSET);
ret |= DRM_SCANOUTPOS_VALID;
}
} elseif (ASIC_IS_AVIVO(rdev)) { if (pipe == 0) {
vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END);
position = RREG32(AVIVO_D1CRTC_STATUS_POSITION);
ret |= DRM_SCANOUTPOS_VALID;
} if (pipe == 1) {
vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END);
position = RREG32(AVIVO_D2CRTC_STATUS_POSITION);
ret |= DRM_SCANOUTPOS_VALID;
}
} else { /* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */ if (pipe == 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 |= DRM_SCANOUTPOS_VALID;
} 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;
stat_crtc = RREG32(RADEON_CRTC2_STATUS); if (!(stat_crtc & 1))
in_vbl = false;
ret |= DRM_SCANOUTPOS_VALID;
}
}
/* 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;
/* Valid vblank area boundaries from gpu retrieved? */ if (vbl > 0) { /* Yes: Decode. */
ret |= DRM_SCANOUTPOS_ACCURATE;
vbl_start = vbl & 0x1fff;
vbl_end = (vbl >> 16) & 0x1fff;
} else { /* No: Fake something reasonable which gives at least ok results. */
vbl_start = mode->crtc_vdisplay;
vbl_end = 0;
}
/* Called from driver internal vblank counter query code? */ if (flags & GET_DISTANCE_TO_VBLANKSTART) { /* Caller wants distance from real vbl_start in *hpos */
*hpos = *vpos - vbl_start;
}
/* Fudge vblank to start a few scanlines earlier to handle the * problem that vblank irqs fire a few scanlines before start * of vblank. Some driver internal callers need the true vblank * start to be used and signal this via the USE_REAL_VBLANKSTART flag. * * The cause of the "early" vblank irq is that the irq is triggered * by the line buffer logic when the line buffer read position enters * the vblank, whereas our crtc scanout position naturally lags the * line buffer read position.
*/ if (!(flags & USE_REAL_VBLANKSTART))
vbl_start -= rdev->mode_info.crtcs[pipe]->lb_vblank_lead_lines;
/* Test scanout position against vblank region. */ if ((*vpos < vbl_start) && (*vpos >= vbl_end))
in_vbl = false;
/* In vblank? */ if (in_vbl)
ret |= DRM_SCANOUTPOS_IN_VBLANK;
/* Called from driver internal vblank counter query code? */ if (flags & GET_DISTANCE_TO_VBLANKSTART) { /* Caller wants distance from fudged earlier vbl_start */
*vpos -= vbl_start; return ret;
}
/* 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 = mode->crtc_vtotal;
*vpos = *vpos - vtotal;
}
/* Correct for shifted end of vbl at vbl_end. */
*vpos = *vpos - vbl_end;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.