162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2007-8 Advanced Micro Devices, Inc. 362306a36Sopenharmony_ci * Copyright 2008 Red Hat Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 662306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 762306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 862306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 962306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1062306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1362306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1862306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1962306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2062306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2162306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Authors: Dave Airlie 2462306a36Sopenharmony_ci * Alex Deucher 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/pci.h> 2862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2962306a36Sopenharmony_ci#include <linux/gcd.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <asm/div64.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h> 3462306a36Sopenharmony_ci#include <drm/drm_device.h> 3562306a36Sopenharmony_ci#include <drm/drm_drv.h> 3662306a36Sopenharmony_ci#include <drm/drm_edid.h> 3762306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 3862306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 3962306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 4062306a36Sopenharmony_ci#include <drm/drm_modeset_helper.h> 4162306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 4262306a36Sopenharmony_ci#include <drm/drm_vblank.h> 4362306a36Sopenharmony_ci#include <drm/radeon_drm.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include "atom.h" 4662306a36Sopenharmony_ci#include "radeon.h" 4762306a36Sopenharmony_ci#include "radeon_kms.h" 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void avivo_crtc_load_lut(struct drm_crtc *crtc) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 5262306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 5362306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 5462306a36Sopenharmony_ci u16 *r, *g, *b; 5562306a36Sopenharmony_ci int i; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); 5862306a36Sopenharmony_ci WREG32(AVIVO_DC_LUTA_CONTROL + radeon_crtc->crtc_offset, 0); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); 6162306a36Sopenharmony_ci WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); 6262306a36Sopenharmony_ci WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); 6562306a36Sopenharmony_ci WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); 6662306a36Sopenharmony_ci WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci WREG32(AVIVO_DC_LUT_RW_SELECT, radeon_crtc->crtc_id); 6962306a36Sopenharmony_ci WREG32(AVIVO_DC_LUT_RW_MODE, 0); 7062306a36Sopenharmony_ci WREG32(AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci WREG8(AVIVO_DC_LUT_RW_INDEX, 0); 7362306a36Sopenharmony_ci r = crtc->gamma_store; 7462306a36Sopenharmony_ci g = r + crtc->gamma_size; 7562306a36Sopenharmony_ci b = g + crtc->gamma_size; 7662306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 7762306a36Sopenharmony_ci WREG32(AVIVO_DC_LUT_30_COLOR, 7862306a36Sopenharmony_ci ((*r++ & 0xffc0) << 14) | 7962306a36Sopenharmony_ci ((*g++ & 0xffc0) << 4) | 8062306a36Sopenharmony_ci (*b++ >> 6)); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Only change bit 0 of LUT_SEL, other bits are set elsewhere */ 8462306a36Sopenharmony_ci WREG32_P(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id, ~1); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void dce4_crtc_load_lut(struct drm_crtc *crtc) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 9062306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 9162306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 9262306a36Sopenharmony_ci u16 *r, *g, *b; 9362306a36Sopenharmony_ci int i; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); 9662306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); 9962306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); 10062306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); 10362306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); 10462306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_RW_MODE + radeon_crtc->crtc_offset, 0); 10762306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x00000007); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0); 11062306a36Sopenharmony_ci r = crtc->gamma_store; 11162306a36Sopenharmony_ci g = r + crtc->gamma_size; 11262306a36Sopenharmony_ci b = g + crtc->gamma_size; 11362306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 11462306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset, 11562306a36Sopenharmony_ci ((*r++ & 0xffc0) << 14) | 11662306a36Sopenharmony_ci ((*g++ & 0xffc0) << 4) | 11762306a36Sopenharmony_ci (*b++ >> 6)); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void dce5_crtc_load_lut(struct drm_crtc *crtc) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 12462306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 12562306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 12662306a36Sopenharmony_ci u16 *r, *g, *b; 12762306a36Sopenharmony_ci int i; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci msleep(10); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci WREG32(NI_INPUT_CSC_CONTROL + radeon_crtc->crtc_offset, 13462306a36Sopenharmony_ci (NI_INPUT_CSC_GRPH_MODE(NI_INPUT_CSC_BYPASS) | 13562306a36Sopenharmony_ci NI_INPUT_CSC_OVL_MODE(NI_INPUT_CSC_BYPASS))); 13662306a36Sopenharmony_ci WREG32(NI_PRESCALE_GRPH_CONTROL + radeon_crtc->crtc_offset, 13762306a36Sopenharmony_ci NI_GRPH_PRESCALE_BYPASS); 13862306a36Sopenharmony_ci WREG32(NI_PRESCALE_OVL_CONTROL + radeon_crtc->crtc_offset, 13962306a36Sopenharmony_ci NI_OVL_PRESCALE_BYPASS); 14062306a36Sopenharmony_ci WREG32(NI_INPUT_GAMMA_CONTROL + radeon_crtc->crtc_offset, 14162306a36Sopenharmony_ci (NI_GRPH_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT) | 14262306a36Sopenharmony_ci NI_OVL_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT))); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); 14762306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); 14862306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); 15162306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); 15262306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_RW_MODE + radeon_crtc->crtc_offset, 0); 15562306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x00000007); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0); 15862306a36Sopenharmony_ci r = crtc->gamma_store; 15962306a36Sopenharmony_ci g = r + crtc->gamma_size; 16062306a36Sopenharmony_ci b = g + crtc->gamma_size; 16162306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 16262306a36Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset, 16362306a36Sopenharmony_ci ((*r++ & 0xffc0) << 14) | 16462306a36Sopenharmony_ci ((*g++ & 0xffc0) << 4) | 16562306a36Sopenharmony_ci (*b++ >> 6)); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci WREG32(NI_DEGAMMA_CONTROL + radeon_crtc->crtc_offset, 16962306a36Sopenharmony_ci (NI_GRPH_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | 17062306a36Sopenharmony_ci NI_OVL_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | 17162306a36Sopenharmony_ci NI_ICON_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | 17262306a36Sopenharmony_ci NI_CURSOR_DEGAMMA_MODE(NI_DEGAMMA_BYPASS))); 17362306a36Sopenharmony_ci WREG32(NI_GAMUT_REMAP_CONTROL + radeon_crtc->crtc_offset, 17462306a36Sopenharmony_ci (NI_GRPH_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS) | 17562306a36Sopenharmony_ci NI_OVL_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS))); 17662306a36Sopenharmony_ci WREG32(NI_REGAMMA_CONTROL + radeon_crtc->crtc_offset, 17762306a36Sopenharmony_ci (NI_GRPH_REGAMMA_MODE(NI_REGAMMA_BYPASS) | 17862306a36Sopenharmony_ci NI_OVL_REGAMMA_MODE(NI_REGAMMA_BYPASS))); 17962306a36Sopenharmony_ci WREG32(NI_OUTPUT_CSC_CONTROL + radeon_crtc->crtc_offset, 18062306a36Sopenharmony_ci (NI_OUTPUT_CSC_GRPH_MODE(radeon_crtc->output_csc) | 18162306a36Sopenharmony_ci NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS))); 18262306a36Sopenharmony_ci /* XXX match this to the depth of the crtc fmt block, move to modeset? */ 18362306a36Sopenharmony_ci WREG32(0x6940 + radeon_crtc->crtc_offset, 0); 18462306a36Sopenharmony_ci if (ASIC_IS_DCE8(rdev)) { 18562306a36Sopenharmony_ci /* XXX this only needs to be programmed once per crtc at startup, 18662306a36Sopenharmony_ci * not sure where the best place for it is 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci WREG32(CIK_ALPHA_CONTROL + radeon_crtc->crtc_offset, 18962306a36Sopenharmony_ci CIK_CURSOR_ALPHA_BLND_ENA); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void legacy_crtc_load_lut(struct drm_crtc *crtc) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 19662306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 19762306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 19862306a36Sopenharmony_ci u16 *r, *g, *b; 19962306a36Sopenharmony_ci int i; 20062306a36Sopenharmony_ci uint32_t dac2_cntl; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci dac2_cntl = RREG32(RADEON_DAC_CNTL2); 20362306a36Sopenharmony_ci if (radeon_crtc->crtc_id == 0) 20462306a36Sopenharmony_ci dac2_cntl &= (uint32_t)~RADEON_DAC2_PALETTE_ACC_CTL; 20562306a36Sopenharmony_ci else 20662306a36Sopenharmony_ci dac2_cntl |= RADEON_DAC2_PALETTE_ACC_CTL; 20762306a36Sopenharmony_ci WREG32(RADEON_DAC_CNTL2, dac2_cntl); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci WREG8(RADEON_PALETTE_INDEX, 0); 21062306a36Sopenharmony_ci r = crtc->gamma_store; 21162306a36Sopenharmony_ci g = r + crtc->gamma_size; 21262306a36Sopenharmony_ci b = g + crtc->gamma_size; 21362306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 21462306a36Sopenharmony_ci WREG32(RADEON_PALETTE_30_DATA, 21562306a36Sopenharmony_ci ((*r++ & 0xffc0) << 14) | 21662306a36Sopenharmony_ci ((*g++ & 0xffc0) << 4) | 21762306a36Sopenharmony_ci (*b++ >> 6)); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_civoid radeon_crtc_load_lut(struct drm_crtc *crtc) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 22462306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (!crtc->enabled) 22762306a36Sopenharmony_ci return; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (ASIC_IS_DCE5(rdev)) 23062306a36Sopenharmony_ci dce5_crtc_load_lut(crtc); 23162306a36Sopenharmony_ci else if (ASIC_IS_DCE4(rdev)) 23262306a36Sopenharmony_ci dce4_crtc_load_lut(crtc); 23362306a36Sopenharmony_ci else if (ASIC_IS_AVIVO(rdev)) 23462306a36Sopenharmony_ci avivo_crtc_load_lut(crtc); 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci legacy_crtc_load_lut(crtc); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, 24062306a36Sopenharmony_ci u16 *blue, uint32_t size, 24162306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci radeon_crtc_load_lut(crtc); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void radeon_crtc_destroy(struct drm_crtc *crtc) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 25362306a36Sopenharmony_ci destroy_workqueue(radeon_crtc->flip_queue); 25462306a36Sopenharmony_ci kfree(radeon_crtc); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/** 25862306a36Sopenharmony_ci * radeon_unpin_work_func - unpin old buffer object 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * @__work: kernel work item 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * Unpin the old frame buffer object outside of the interrupt handler 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_cistatic void radeon_unpin_work_func(struct work_struct *__work) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct radeon_flip_work *work = 26762306a36Sopenharmony_ci container_of(__work, struct radeon_flip_work, unpin_work); 26862306a36Sopenharmony_ci int r; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* unpin of the old buffer */ 27162306a36Sopenharmony_ci r = radeon_bo_reserve(work->old_rbo, false); 27262306a36Sopenharmony_ci if (likely(r == 0)) { 27362306a36Sopenharmony_ci radeon_bo_unpin(work->old_rbo); 27462306a36Sopenharmony_ci radeon_bo_unreserve(work->old_rbo); 27562306a36Sopenharmony_ci } else 27662306a36Sopenharmony_ci DRM_ERROR("failed to reserve buffer after flip\n"); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci drm_gem_object_put(&work->old_rbo->tbo.base); 27962306a36Sopenharmony_ci kfree(work); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_civoid radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; 28562306a36Sopenharmony_ci unsigned long flags; 28662306a36Sopenharmony_ci u32 update_pending; 28762306a36Sopenharmony_ci int vpos, hpos; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* can happen during initialization */ 29062306a36Sopenharmony_ci if (radeon_crtc == NULL) 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Skip the pageflip completion check below (based on polling) on 29462306a36Sopenharmony_ci * asics which reliably support hw pageflip completion irqs. pflip 29562306a36Sopenharmony_ci * irqs are a reliable and race-free method of handling pageflip 29662306a36Sopenharmony_ci * completion detection. A use_pflipirq module parameter < 2 allows 29762306a36Sopenharmony_ci * to override this in case of asics with faulty pflip irqs. 29862306a36Sopenharmony_ci * A module parameter of 0 would only use this polling based path, 29962306a36Sopenharmony_ci * a parameter of 1 would use pflip irq only as a backup to this 30062306a36Sopenharmony_ci * path, as in Linux 3.16. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci if ((radeon_use_pflipirq == 2) && ASIC_IS_DCE4(rdev)) 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci spin_lock_irqsave(&rdev->ddev->event_lock, flags); 30662306a36Sopenharmony_ci if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) { 30762306a36Sopenharmony_ci DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != " 30862306a36Sopenharmony_ci "RADEON_FLIP_SUBMITTED(%d)\n", 30962306a36Sopenharmony_ci radeon_crtc->flip_status, 31062306a36Sopenharmony_ci RADEON_FLIP_SUBMITTED); 31162306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); 31262306a36Sopenharmony_ci return; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci update_pending = radeon_page_flip_pending(rdev, crtc_id); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Has the pageflip already completed in crtc, or is it certain 31862306a36Sopenharmony_ci * to complete in this vblank? GET_DISTANCE_TO_VBLANKSTART provides 31962306a36Sopenharmony_ci * distance to start of "fudged earlier" vblank in vpos, distance to 32062306a36Sopenharmony_ci * start of real vblank in hpos. vpos >= 0 && hpos < 0 means we are in 32162306a36Sopenharmony_ci * the last few scanlines before start of real vblank, where the vblank 32262306a36Sopenharmony_ci * irq can fire, so we have sampled update_pending a bit too early and 32362306a36Sopenharmony_ci * know the flip will complete at leading edge of the upcoming real 32462306a36Sopenharmony_ci * vblank. On pre-AVIVO hardware, flips also complete inside the real 32562306a36Sopenharmony_ci * vblank, not only at leading edge, so if update_pending for hpos >= 0 32662306a36Sopenharmony_ci * == inside real vblank, the flip will complete almost immediately. 32762306a36Sopenharmony_ci * Note that this method of completion handling is still not 100% race 32862306a36Sopenharmony_ci * free, as we could execute before the radeon_flip_work_func managed 32962306a36Sopenharmony_ci * to run and set the RADEON_FLIP_SUBMITTED status, thereby we no-op, 33062306a36Sopenharmony_ci * but the flip still gets programmed into hw and completed during 33162306a36Sopenharmony_ci * vblank, leading to a delayed emission of the flip completion event. 33262306a36Sopenharmony_ci * This applies at least to pre-AVIVO hardware, where flips are always 33362306a36Sopenharmony_ci * completing inside vblank, not only at leading edge of vblank. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci if (update_pending && 33662306a36Sopenharmony_ci (DRM_SCANOUTPOS_VALID & 33762306a36Sopenharmony_ci radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, 33862306a36Sopenharmony_ci GET_DISTANCE_TO_VBLANKSTART, 33962306a36Sopenharmony_ci &vpos, &hpos, NULL, NULL, 34062306a36Sopenharmony_ci &rdev->mode_info.crtcs[crtc_id]->base.hwmode)) && 34162306a36Sopenharmony_ci ((vpos >= 0 && hpos < 0) || (hpos >= 0 && !ASIC_IS_AVIVO(rdev)))) { 34262306a36Sopenharmony_ci /* crtc didn't flip in this target vblank interval, 34362306a36Sopenharmony_ci * but flip is pending in crtc. Based on the current 34462306a36Sopenharmony_ci * scanout position we know that the current frame is 34562306a36Sopenharmony_ci * (nearly) complete and the flip will (likely) 34662306a36Sopenharmony_ci * complete before the start of the next frame. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci update_pending = 0; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); 35162306a36Sopenharmony_ci if (!update_pending) 35262306a36Sopenharmony_ci radeon_crtc_handle_flip(rdev, crtc_id); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/** 35662306a36Sopenharmony_ci * radeon_crtc_handle_flip - page flip completed 35762306a36Sopenharmony_ci * 35862306a36Sopenharmony_ci * @rdev: radeon device pointer 35962306a36Sopenharmony_ci * @crtc_id: crtc number this event is for 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * Called when we are sure that a page flip for this crtc is completed. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_civoid radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; 36662306a36Sopenharmony_ci struct radeon_flip_work *work; 36762306a36Sopenharmony_ci unsigned long flags; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* this can happen at init */ 37062306a36Sopenharmony_ci if (radeon_crtc == NULL) 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci spin_lock_irqsave(&rdev->ddev->event_lock, flags); 37462306a36Sopenharmony_ci work = radeon_crtc->flip_work; 37562306a36Sopenharmony_ci if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) { 37662306a36Sopenharmony_ci DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != " 37762306a36Sopenharmony_ci "RADEON_FLIP_SUBMITTED(%d)\n", 37862306a36Sopenharmony_ci radeon_crtc->flip_status, 37962306a36Sopenharmony_ci RADEON_FLIP_SUBMITTED); 38062306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); 38162306a36Sopenharmony_ci return; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Pageflip completed. Clean up. */ 38562306a36Sopenharmony_ci radeon_crtc->flip_status = RADEON_FLIP_NONE; 38662306a36Sopenharmony_ci radeon_crtc->flip_work = NULL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* wakeup userspace */ 38962306a36Sopenharmony_ci if (work->event) 39062306a36Sopenharmony_ci drm_crtc_send_vblank_event(&radeon_crtc->base, work->event); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci drm_crtc_vblank_put(&radeon_crtc->base); 39562306a36Sopenharmony_ci radeon_irq_kms_pflip_irq_put(rdev, work->crtc_id); 39662306a36Sopenharmony_ci queue_work(radeon_crtc->flip_queue, &work->unpin_work); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/** 40062306a36Sopenharmony_ci * radeon_flip_work_func - page flip framebuffer 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * @__work: kernel work item 40362306a36Sopenharmony_ci * 40462306a36Sopenharmony_ci * Wait for the buffer object to become idle and do the actual page flip 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_cistatic void radeon_flip_work_func(struct work_struct *__work) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct radeon_flip_work *work = 40962306a36Sopenharmony_ci container_of(__work, struct radeon_flip_work, flip_work); 41062306a36Sopenharmony_ci struct radeon_device *rdev = work->rdev; 41162306a36Sopenharmony_ci struct drm_device *dev = rdev->ddev; 41262306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id]; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci struct drm_crtc *crtc = &radeon_crtc->base; 41562306a36Sopenharmony_ci unsigned long flags; 41662306a36Sopenharmony_ci int r; 41762306a36Sopenharmony_ci int vpos, hpos; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci down_read(&rdev->exclusive_lock); 42062306a36Sopenharmony_ci if (work->fence) { 42162306a36Sopenharmony_ci struct radeon_fence *fence; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci fence = to_radeon_fence(work->fence); 42462306a36Sopenharmony_ci if (fence && fence->rdev == rdev) { 42562306a36Sopenharmony_ci r = radeon_fence_wait(fence, false); 42662306a36Sopenharmony_ci if (r == -EDEADLK) { 42762306a36Sopenharmony_ci up_read(&rdev->exclusive_lock); 42862306a36Sopenharmony_ci do { 42962306a36Sopenharmony_ci r = radeon_gpu_reset(rdev); 43062306a36Sopenharmony_ci } while (r == -EAGAIN); 43162306a36Sopenharmony_ci down_read(&rdev->exclusive_lock); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci } else 43462306a36Sopenharmony_ci r = dma_fence_wait(work->fence, false); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (r) 43762306a36Sopenharmony_ci DRM_ERROR("failed to wait on page flip fence (%d)!\n", r); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* We continue with the page flip even if we failed to wait on 44062306a36Sopenharmony_ci * the fence, otherwise the DRM core and userspace will be 44162306a36Sopenharmony_ci * confused about which BO the CRTC is scanning out 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci dma_fence_put(work->fence); 44562306a36Sopenharmony_ci work->fence = NULL; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Wait until we're out of the vertical blank period before the one 44962306a36Sopenharmony_ci * targeted by the flip. Always wait on pre DCE4 to avoid races with 45062306a36Sopenharmony_ci * flip completion handling from vblank irq, as these old asics don't 45162306a36Sopenharmony_ci * have reliable pageflip completion interrupts. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci while (radeon_crtc->enabled && 45462306a36Sopenharmony_ci (radeon_get_crtc_scanoutpos(dev, work->crtc_id, 0, 45562306a36Sopenharmony_ci &vpos, &hpos, NULL, NULL, 45662306a36Sopenharmony_ci &crtc->hwmode) 45762306a36Sopenharmony_ci & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) == 45862306a36Sopenharmony_ci (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) && 45962306a36Sopenharmony_ci (!ASIC_IS_AVIVO(rdev) || 46062306a36Sopenharmony_ci ((int) (work->target_vblank - 46162306a36Sopenharmony_ci crtc->funcs->get_vblank_counter(crtc)) > 0))) 46262306a36Sopenharmony_ci usleep_range(1000, 2000); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* We borrow the event spin lock for protecting flip_status */ 46562306a36Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* set the proper interrupt */ 46862306a36Sopenharmony_ci radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* do the flip (mmio) */ 47162306a36Sopenharmony_ci radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base, work->async); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci radeon_crtc->flip_status = RADEON_FLIP_SUBMITTED; 47462306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 47562306a36Sopenharmony_ci up_read(&rdev->exclusive_lock); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int radeon_crtc_page_flip_target(struct drm_crtc *crtc, 47962306a36Sopenharmony_ci struct drm_framebuffer *fb, 48062306a36Sopenharmony_ci struct drm_pending_vblank_event *event, 48162306a36Sopenharmony_ci uint32_t page_flip_flags, 48262306a36Sopenharmony_ci uint32_t target, 48362306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 48662306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 48762306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 48862306a36Sopenharmony_ci struct drm_gem_object *obj; 48962306a36Sopenharmony_ci struct radeon_flip_work *work; 49062306a36Sopenharmony_ci struct radeon_bo *new_rbo; 49162306a36Sopenharmony_ci uint32_t tiling_flags, pitch_pixels; 49262306a36Sopenharmony_ci uint64_t base; 49362306a36Sopenharmony_ci unsigned long flags; 49462306a36Sopenharmony_ci int r; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci work = kzalloc(sizeof *work, GFP_KERNEL); 49762306a36Sopenharmony_ci if (work == NULL) 49862306a36Sopenharmony_ci return -ENOMEM; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci INIT_WORK(&work->flip_work, radeon_flip_work_func); 50162306a36Sopenharmony_ci INIT_WORK(&work->unpin_work, radeon_unpin_work_func); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci work->rdev = rdev; 50462306a36Sopenharmony_ci work->crtc_id = radeon_crtc->crtc_id; 50562306a36Sopenharmony_ci work->event = event; 50662306a36Sopenharmony_ci work->async = (page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC) != 0; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* schedule unpin of the old buffer */ 50962306a36Sopenharmony_ci obj = crtc->primary->fb->obj[0]; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* take a reference to the old object */ 51262306a36Sopenharmony_ci drm_gem_object_get(obj); 51362306a36Sopenharmony_ci work->old_rbo = gem_to_radeon_bo(obj); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci obj = fb->obj[0]; 51662306a36Sopenharmony_ci new_rbo = gem_to_radeon_bo(obj); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* pin the new buffer */ 51962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("flip-ioctl() cur_rbo = %p, new_rbo = %p\n", 52062306a36Sopenharmony_ci work->old_rbo, new_rbo); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci r = radeon_bo_reserve(new_rbo, false); 52362306a36Sopenharmony_ci if (unlikely(r != 0)) { 52462306a36Sopenharmony_ci DRM_ERROR("failed to reserve new rbo buffer before flip\n"); 52562306a36Sopenharmony_ci goto cleanup; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci /* Only 27 bit offset for legacy CRTC */ 52862306a36Sopenharmony_ci r = radeon_bo_pin_restricted(new_rbo, RADEON_GEM_DOMAIN_VRAM, 52962306a36Sopenharmony_ci ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base); 53062306a36Sopenharmony_ci if (unlikely(r != 0)) { 53162306a36Sopenharmony_ci radeon_bo_unreserve(new_rbo); 53262306a36Sopenharmony_ci r = -EINVAL; 53362306a36Sopenharmony_ci DRM_ERROR("failed to pin new rbo buffer before flip\n"); 53462306a36Sopenharmony_ci goto cleanup; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci r = dma_resv_get_singleton(new_rbo->tbo.base.resv, DMA_RESV_USAGE_WRITE, 53762306a36Sopenharmony_ci &work->fence); 53862306a36Sopenharmony_ci if (r) { 53962306a36Sopenharmony_ci radeon_bo_unreserve(new_rbo); 54062306a36Sopenharmony_ci DRM_ERROR("failed to get new rbo buffer fences\n"); 54162306a36Sopenharmony_ci goto cleanup; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci radeon_bo_get_tiling_flags(new_rbo, &tiling_flags, NULL); 54462306a36Sopenharmony_ci radeon_bo_unreserve(new_rbo); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (!ASIC_IS_AVIVO(rdev)) { 54762306a36Sopenharmony_ci /* crtc offset is from display base addr not FB location */ 54862306a36Sopenharmony_ci base -= radeon_crtc->legacy_display_base_addr; 54962306a36Sopenharmony_ci pitch_pixels = fb->pitches[0] / fb->format->cpp[0]; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (tiling_flags & RADEON_TILING_MACRO) { 55262306a36Sopenharmony_ci if (ASIC_IS_R300(rdev)) { 55362306a36Sopenharmony_ci base &= ~0x7ff; 55462306a36Sopenharmony_ci } else { 55562306a36Sopenharmony_ci int byteshift = fb->format->cpp[0] * 8 >> 4; 55662306a36Sopenharmony_ci int tile_addr = (((crtc->y >> 3) * pitch_pixels + crtc->x) >> (8 - byteshift)) << 11; 55762306a36Sopenharmony_ci base += tile_addr + ((crtc->x << byteshift) % 256) + ((crtc->y % 8) << 8); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci } else { 56062306a36Sopenharmony_ci int offset = crtc->y * pitch_pixels + crtc->x; 56162306a36Sopenharmony_ci switch (fb->format->cpp[0] * 8) { 56262306a36Sopenharmony_ci case 8: 56362306a36Sopenharmony_ci default: 56462306a36Sopenharmony_ci offset *= 1; 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci case 15: 56762306a36Sopenharmony_ci case 16: 56862306a36Sopenharmony_ci offset *= 2; 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci case 24: 57162306a36Sopenharmony_ci offset *= 3; 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci case 32: 57462306a36Sopenharmony_ci offset *= 4; 57562306a36Sopenharmony_ci break; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci base += offset; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci base &= ~7; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci work->base = base; 58262306a36Sopenharmony_ci work->target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) + 58362306a36Sopenharmony_ci crtc->funcs->get_vblank_counter(crtc); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* We borrow the event spin lock for protecting flip_work */ 58662306a36Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (radeon_crtc->flip_status != RADEON_FLIP_NONE) { 58962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); 59062306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 59162306a36Sopenharmony_ci r = -EBUSY; 59262306a36Sopenharmony_ci goto pflip_cleanup; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci radeon_crtc->flip_status = RADEON_FLIP_PENDING; 59562306a36Sopenharmony_ci radeon_crtc->flip_work = work; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* update crtc fb */ 59862306a36Sopenharmony_ci crtc->primary->fb = fb; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci queue_work(radeon_crtc->flip_queue, &work->flip_work); 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cipflip_cleanup: 60662306a36Sopenharmony_ci if (unlikely(radeon_bo_reserve(new_rbo, false) != 0)) { 60762306a36Sopenharmony_ci DRM_ERROR("failed to reserve new rbo in error path\n"); 60862306a36Sopenharmony_ci goto cleanup; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci radeon_bo_unpin(new_rbo); 61162306a36Sopenharmony_ci radeon_bo_unreserve(new_rbo); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cicleanup: 61462306a36Sopenharmony_ci drm_gem_object_put(&work->old_rbo->tbo.base); 61562306a36Sopenharmony_ci dma_fence_put(work->fence); 61662306a36Sopenharmony_ci kfree(work); 61762306a36Sopenharmony_ci return r; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int 62162306a36Sopenharmony_ciradeon_crtc_set_config(struct drm_mode_set *set, 62262306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct drm_device *dev; 62562306a36Sopenharmony_ci struct radeon_device *rdev; 62662306a36Sopenharmony_ci struct drm_crtc *crtc; 62762306a36Sopenharmony_ci bool active = false; 62862306a36Sopenharmony_ci int ret; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (!set || !set->crtc) 63162306a36Sopenharmony_ci return -EINVAL; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci dev = set->crtc->dev; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev->dev); 63662306a36Sopenharmony_ci if (ret < 0) { 63762306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci ret = drm_crtc_helper_set_config(set, ctx); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 64462306a36Sopenharmony_ci if (crtc->enabled) 64562306a36Sopenharmony_ci active = true; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci rdev = dev->dev_private; 65062306a36Sopenharmony_ci /* if we have active crtcs and we don't have a power ref, 65162306a36Sopenharmony_ci take the current one */ 65262306a36Sopenharmony_ci if (active && !rdev->have_disp_power_ref) { 65362306a36Sopenharmony_ci rdev->have_disp_power_ref = true; 65462306a36Sopenharmony_ci return ret; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci /* if we have no active crtcs, then drop the power ref 65762306a36Sopenharmony_ci we got before */ 65862306a36Sopenharmony_ci if (!active && rdev->have_disp_power_ref) { 65962306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 66062306a36Sopenharmony_ci rdev->have_disp_power_ref = false; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* drop the power reference we got coming in here */ 66462306a36Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 66562306a36Sopenharmony_ci return ret; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic const struct drm_crtc_funcs radeon_crtc_funcs = { 66962306a36Sopenharmony_ci .cursor_set2 = radeon_crtc_cursor_set2, 67062306a36Sopenharmony_ci .cursor_move = radeon_crtc_cursor_move, 67162306a36Sopenharmony_ci .gamma_set = radeon_crtc_gamma_set, 67262306a36Sopenharmony_ci .set_config = radeon_crtc_set_config, 67362306a36Sopenharmony_ci .destroy = radeon_crtc_destroy, 67462306a36Sopenharmony_ci .page_flip_target = radeon_crtc_page_flip_target, 67562306a36Sopenharmony_ci .get_vblank_counter = radeon_get_vblank_counter_kms, 67662306a36Sopenharmony_ci .enable_vblank = radeon_enable_vblank_kms, 67762306a36Sopenharmony_ci .disable_vblank = radeon_disable_vblank_kms, 67862306a36Sopenharmony_ci .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, 67962306a36Sopenharmony_ci}; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void radeon_crtc_init(struct drm_device *dev, int index) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 68462306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci radeon_crtc = kzalloc(sizeof(struct radeon_crtc) + (RADEONFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); 68762306a36Sopenharmony_ci if (radeon_crtc == NULL) 68862306a36Sopenharmony_ci return; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci radeon_crtc->flip_queue = alloc_workqueue("radeon-crtc", WQ_HIGHPRI, 0); 69162306a36Sopenharmony_ci if (!radeon_crtc->flip_queue) { 69262306a36Sopenharmony_ci kfree(radeon_crtc); 69362306a36Sopenharmony_ci return; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci drm_crtc_init(dev, &radeon_crtc->base, &radeon_crtc_funcs); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256); 69962306a36Sopenharmony_ci radeon_crtc->crtc_id = index; 70062306a36Sopenharmony_ci rdev->mode_info.crtcs[index] = radeon_crtc; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (rdev->family >= CHIP_BONAIRE) { 70362306a36Sopenharmony_ci radeon_crtc->max_cursor_width = CIK_CURSOR_WIDTH; 70462306a36Sopenharmony_ci radeon_crtc->max_cursor_height = CIK_CURSOR_HEIGHT; 70562306a36Sopenharmony_ci } else { 70662306a36Sopenharmony_ci radeon_crtc->max_cursor_width = CURSOR_WIDTH; 70762306a36Sopenharmony_ci radeon_crtc->max_cursor_height = CURSOR_HEIGHT; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci dev->mode_config.cursor_width = radeon_crtc->max_cursor_width; 71062306a36Sopenharmony_ci dev->mode_config.cursor_height = radeon_crtc->max_cursor_height; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci#if 0 71362306a36Sopenharmony_ci radeon_crtc->mode_set.crtc = &radeon_crtc->base; 71462306a36Sopenharmony_ci radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1); 71562306a36Sopenharmony_ci radeon_crtc->mode_set.num_connectors = 0; 71662306a36Sopenharmony_ci#endif 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (rdev->is_atom_bios && (ASIC_IS_AVIVO(rdev) || radeon_r4xx_atom)) 71962306a36Sopenharmony_ci radeon_atombios_init_crtc(dev, radeon_crtc); 72062306a36Sopenharmony_ci else 72162306a36Sopenharmony_ci radeon_legacy_init_crtc(dev, radeon_crtc); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic const char *encoder_names[38] = { 72562306a36Sopenharmony_ci "NONE", 72662306a36Sopenharmony_ci "INTERNAL_LVDS", 72762306a36Sopenharmony_ci "INTERNAL_TMDS1", 72862306a36Sopenharmony_ci "INTERNAL_TMDS2", 72962306a36Sopenharmony_ci "INTERNAL_DAC1", 73062306a36Sopenharmony_ci "INTERNAL_DAC2", 73162306a36Sopenharmony_ci "INTERNAL_SDVOA", 73262306a36Sopenharmony_ci "INTERNAL_SDVOB", 73362306a36Sopenharmony_ci "SI170B", 73462306a36Sopenharmony_ci "CH7303", 73562306a36Sopenharmony_ci "CH7301", 73662306a36Sopenharmony_ci "INTERNAL_DVO1", 73762306a36Sopenharmony_ci "EXTERNAL_SDVOA", 73862306a36Sopenharmony_ci "EXTERNAL_SDVOB", 73962306a36Sopenharmony_ci "TITFP513", 74062306a36Sopenharmony_ci "INTERNAL_LVTM1", 74162306a36Sopenharmony_ci "VT1623", 74262306a36Sopenharmony_ci "HDMI_SI1930", 74362306a36Sopenharmony_ci "HDMI_INTERNAL", 74462306a36Sopenharmony_ci "INTERNAL_KLDSCP_TMDS1", 74562306a36Sopenharmony_ci "INTERNAL_KLDSCP_DVO1", 74662306a36Sopenharmony_ci "INTERNAL_KLDSCP_DAC1", 74762306a36Sopenharmony_ci "INTERNAL_KLDSCP_DAC2", 74862306a36Sopenharmony_ci "SI178", 74962306a36Sopenharmony_ci "MVPU_FPGA", 75062306a36Sopenharmony_ci "INTERNAL_DDI", 75162306a36Sopenharmony_ci "VT1625", 75262306a36Sopenharmony_ci "HDMI_SI1932", 75362306a36Sopenharmony_ci "DP_AN9801", 75462306a36Sopenharmony_ci "DP_DP501", 75562306a36Sopenharmony_ci "INTERNAL_UNIPHY", 75662306a36Sopenharmony_ci "INTERNAL_KLDSCP_LVTMA", 75762306a36Sopenharmony_ci "INTERNAL_UNIPHY1", 75862306a36Sopenharmony_ci "INTERNAL_UNIPHY2", 75962306a36Sopenharmony_ci "NUTMEG", 76062306a36Sopenharmony_ci "TRAVIS", 76162306a36Sopenharmony_ci "INTERNAL_VCE", 76262306a36Sopenharmony_ci "INTERNAL_UNIPHY3", 76362306a36Sopenharmony_ci}; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic const char *hpd_names[6] = { 76662306a36Sopenharmony_ci "HPD1", 76762306a36Sopenharmony_ci "HPD2", 76862306a36Sopenharmony_ci "HPD3", 76962306a36Sopenharmony_ci "HPD4", 77062306a36Sopenharmony_ci "HPD5", 77162306a36Sopenharmony_ci "HPD6", 77262306a36Sopenharmony_ci}; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic void radeon_print_display_setup(struct drm_device *dev) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci struct drm_connector *connector; 77762306a36Sopenharmony_ci struct radeon_connector *radeon_connector; 77862306a36Sopenharmony_ci struct drm_encoder *encoder; 77962306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder; 78062306a36Sopenharmony_ci uint32_t devices; 78162306a36Sopenharmony_ci int i = 0; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci DRM_INFO("Radeon Display Connectors\n"); 78462306a36Sopenharmony_ci list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 78562306a36Sopenharmony_ci radeon_connector = to_radeon_connector(connector); 78662306a36Sopenharmony_ci DRM_INFO("Connector %d:\n", i); 78762306a36Sopenharmony_ci DRM_INFO(" %s\n", connector->name); 78862306a36Sopenharmony_ci if (radeon_connector->hpd.hpd != RADEON_HPD_NONE) 78962306a36Sopenharmony_ci DRM_INFO(" %s\n", hpd_names[radeon_connector->hpd.hpd]); 79062306a36Sopenharmony_ci if (radeon_connector->ddc_bus) { 79162306a36Sopenharmony_ci DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", 79262306a36Sopenharmony_ci radeon_connector->ddc_bus->rec.mask_clk_reg, 79362306a36Sopenharmony_ci radeon_connector->ddc_bus->rec.mask_data_reg, 79462306a36Sopenharmony_ci radeon_connector->ddc_bus->rec.a_clk_reg, 79562306a36Sopenharmony_ci radeon_connector->ddc_bus->rec.a_data_reg, 79662306a36Sopenharmony_ci radeon_connector->ddc_bus->rec.en_clk_reg, 79762306a36Sopenharmony_ci radeon_connector->ddc_bus->rec.en_data_reg, 79862306a36Sopenharmony_ci radeon_connector->ddc_bus->rec.y_clk_reg, 79962306a36Sopenharmony_ci radeon_connector->ddc_bus->rec.y_data_reg); 80062306a36Sopenharmony_ci if (radeon_connector->router.ddc_valid) 80162306a36Sopenharmony_ci DRM_INFO(" DDC Router 0x%x/0x%x\n", 80262306a36Sopenharmony_ci radeon_connector->router.ddc_mux_control_pin, 80362306a36Sopenharmony_ci radeon_connector->router.ddc_mux_state); 80462306a36Sopenharmony_ci if (radeon_connector->router.cd_valid) 80562306a36Sopenharmony_ci DRM_INFO(" Clock/Data Router 0x%x/0x%x\n", 80662306a36Sopenharmony_ci radeon_connector->router.cd_mux_control_pin, 80762306a36Sopenharmony_ci radeon_connector->router.cd_mux_state); 80862306a36Sopenharmony_ci } else { 80962306a36Sopenharmony_ci if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || 81062306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVII || 81162306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVID || 81262306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVIA || 81362306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || 81462306a36Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) 81562306a36Sopenharmony_ci DRM_INFO(" DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n"); 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci DRM_INFO(" Encoders:\n"); 81862306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 81962306a36Sopenharmony_ci radeon_encoder = to_radeon_encoder(encoder); 82062306a36Sopenharmony_ci devices = radeon_encoder->devices & radeon_connector->devices; 82162306a36Sopenharmony_ci if (devices) { 82262306a36Sopenharmony_ci if (devices & ATOM_DEVICE_CRT1_SUPPORT) 82362306a36Sopenharmony_ci DRM_INFO(" CRT1: %s\n", encoder_names[radeon_encoder->encoder_id]); 82462306a36Sopenharmony_ci if (devices & ATOM_DEVICE_CRT2_SUPPORT) 82562306a36Sopenharmony_ci DRM_INFO(" CRT2: %s\n", encoder_names[radeon_encoder->encoder_id]); 82662306a36Sopenharmony_ci if (devices & ATOM_DEVICE_LCD1_SUPPORT) 82762306a36Sopenharmony_ci DRM_INFO(" LCD1: %s\n", encoder_names[radeon_encoder->encoder_id]); 82862306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP1_SUPPORT) 82962306a36Sopenharmony_ci DRM_INFO(" DFP1: %s\n", encoder_names[radeon_encoder->encoder_id]); 83062306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP2_SUPPORT) 83162306a36Sopenharmony_ci DRM_INFO(" DFP2: %s\n", encoder_names[radeon_encoder->encoder_id]); 83262306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP3_SUPPORT) 83362306a36Sopenharmony_ci DRM_INFO(" DFP3: %s\n", encoder_names[radeon_encoder->encoder_id]); 83462306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP4_SUPPORT) 83562306a36Sopenharmony_ci DRM_INFO(" DFP4: %s\n", encoder_names[radeon_encoder->encoder_id]); 83662306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP5_SUPPORT) 83762306a36Sopenharmony_ci DRM_INFO(" DFP5: %s\n", encoder_names[radeon_encoder->encoder_id]); 83862306a36Sopenharmony_ci if (devices & ATOM_DEVICE_DFP6_SUPPORT) 83962306a36Sopenharmony_ci DRM_INFO(" DFP6: %s\n", encoder_names[radeon_encoder->encoder_id]); 84062306a36Sopenharmony_ci if (devices & ATOM_DEVICE_TV1_SUPPORT) 84162306a36Sopenharmony_ci DRM_INFO(" TV1: %s\n", encoder_names[radeon_encoder->encoder_id]); 84262306a36Sopenharmony_ci if (devices & ATOM_DEVICE_CV_SUPPORT) 84362306a36Sopenharmony_ci DRM_INFO(" CV: %s\n", encoder_names[radeon_encoder->encoder_id]); 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci i++; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic bool radeon_setup_enc_conn(struct drm_device *dev) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 85362306a36Sopenharmony_ci bool ret = false; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (rdev->bios) { 85662306a36Sopenharmony_ci if (rdev->is_atom_bios) { 85762306a36Sopenharmony_ci ret = radeon_get_atom_connector_info_from_supported_devices_table(dev); 85862306a36Sopenharmony_ci if (!ret) 85962306a36Sopenharmony_ci ret = radeon_get_atom_connector_info_from_object_table(dev); 86062306a36Sopenharmony_ci } else { 86162306a36Sopenharmony_ci ret = radeon_get_legacy_connector_info_from_bios(dev); 86262306a36Sopenharmony_ci if (!ret) 86362306a36Sopenharmony_ci ret = radeon_get_legacy_connector_info_from_table(dev); 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci } else { 86662306a36Sopenharmony_ci if (!ASIC_IS_AVIVO(rdev)) 86762306a36Sopenharmony_ci ret = radeon_get_legacy_connector_info_from_table(dev); 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci if (ret) { 87062306a36Sopenharmony_ci radeon_setup_encoder_clones(dev); 87162306a36Sopenharmony_ci radeon_print_display_setup(dev); 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci return ret; 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci/* avivo */ 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci/** 88062306a36Sopenharmony_ci * avivo_reduce_ratio - fractional number reduction 88162306a36Sopenharmony_ci * 88262306a36Sopenharmony_ci * @nom: nominator 88362306a36Sopenharmony_ci * @den: denominator 88462306a36Sopenharmony_ci * @nom_min: minimum value for nominator 88562306a36Sopenharmony_ci * @den_min: minimum value for denominator 88662306a36Sopenharmony_ci * 88762306a36Sopenharmony_ci * Find the greatest common divisor and apply it on both nominator and 88862306a36Sopenharmony_ci * denominator, but make nominator and denominator are at least as large 88962306a36Sopenharmony_ci * as their minimum values. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_cistatic void avivo_reduce_ratio(unsigned *nom, unsigned *den, 89262306a36Sopenharmony_ci unsigned nom_min, unsigned den_min) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci unsigned tmp; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* reduce the numbers to a simpler ratio */ 89762306a36Sopenharmony_ci tmp = gcd(*nom, *den); 89862306a36Sopenharmony_ci *nom /= tmp; 89962306a36Sopenharmony_ci *den /= tmp; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* make sure nominator is large enough */ 90262306a36Sopenharmony_ci if (*nom < nom_min) { 90362306a36Sopenharmony_ci tmp = DIV_ROUND_UP(nom_min, *nom); 90462306a36Sopenharmony_ci *nom *= tmp; 90562306a36Sopenharmony_ci *den *= tmp; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* make sure the denominator is large enough */ 90962306a36Sopenharmony_ci if (*den < den_min) { 91062306a36Sopenharmony_ci tmp = DIV_ROUND_UP(den_min, *den); 91162306a36Sopenharmony_ci *nom *= tmp; 91262306a36Sopenharmony_ci *den *= tmp; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci/** 91762306a36Sopenharmony_ci * avivo_get_fb_ref_div - feedback and ref divider calculation 91862306a36Sopenharmony_ci * 91962306a36Sopenharmony_ci * @nom: nominator 92062306a36Sopenharmony_ci * @den: denominator 92162306a36Sopenharmony_ci * @post_div: post divider 92262306a36Sopenharmony_ci * @fb_div_max: feedback divider maximum 92362306a36Sopenharmony_ci * @ref_div_max: reference divider maximum 92462306a36Sopenharmony_ci * @fb_div: resulting feedback divider 92562306a36Sopenharmony_ci * @ref_div: resulting reference divider 92662306a36Sopenharmony_ci * 92762306a36Sopenharmony_ci * Calculate feedback and reference divider for a given post divider. Makes 92862306a36Sopenharmony_ci * sure we stay within the limits. 92962306a36Sopenharmony_ci */ 93062306a36Sopenharmony_cistatic void avivo_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div, 93162306a36Sopenharmony_ci unsigned fb_div_max, unsigned ref_div_max, 93262306a36Sopenharmony_ci unsigned *fb_div, unsigned *ref_div) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci /* limit reference * post divider to a maximum */ 93562306a36Sopenharmony_ci ref_div_max = max(min(100 / post_div, ref_div_max), 1u); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* get matching reference and feedback divider */ 93862306a36Sopenharmony_ci *ref_div = min(max(den/post_div, 1u), ref_div_max); 93962306a36Sopenharmony_ci *fb_div = DIV_ROUND_CLOSEST(nom * *ref_div * post_div, den); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* limit fb divider to its maximum */ 94262306a36Sopenharmony_ci if (*fb_div > fb_div_max) { 94362306a36Sopenharmony_ci *ref_div = (*ref_div * fb_div_max)/(*fb_div); 94462306a36Sopenharmony_ci *fb_div = fb_div_max; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/** 94962306a36Sopenharmony_ci * radeon_compute_pll_avivo - compute PLL paramaters 95062306a36Sopenharmony_ci * 95162306a36Sopenharmony_ci * @pll: information about the PLL 95262306a36Sopenharmony_ci * @freq: target frequency 95362306a36Sopenharmony_ci * @dot_clock_p: resulting pixel clock 95462306a36Sopenharmony_ci * @fb_div_p: resulting feedback divider 95562306a36Sopenharmony_ci * @frac_fb_div_p: fractional part of the feedback divider 95662306a36Sopenharmony_ci * @ref_div_p: resulting reference divider 95762306a36Sopenharmony_ci * @post_div_p: resulting reference divider 95862306a36Sopenharmony_ci * 95962306a36Sopenharmony_ci * Try to calculate the PLL parameters to generate the given frequency: 96062306a36Sopenharmony_ci * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div) 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_civoid radeon_compute_pll_avivo(struct radeon_pll *pll, 96362306a36Sopenharmony_ci u32 freq, 96462306a36Sopenharmony_ci u32 *dot_clock_p, 96562306a36Sopenharmony_ci u32 *fb_div_p, 96662306a36Sopenharmony_ci u32 *frac_fb_div_p, 96762306a36Sopenharmony_ci u32 *ref_div_p, 96862306a36Sopenharmony_ci u32 *post_div_p) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci unsigned target_clock = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ? 97162306a36Sopenharmony_ci freq : freq / 10; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci unsigned fb_div_min, fb_div_max, fb_div; 97462306a36Sopenharmony_ci unsigned post_div_min, post_div_max, post_div; 97562306a36Sopenharmony_ci unsigned ref_div_min, ref_div_max, ref_div; 97662306a36Sopenharmony_ci unsigned post_div_best, diff_best; 97762306a36Sopenharmony_ci unsigned nom, den; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* determine allowed feedback divider range */ 98062306a36Sopenharmony_ci fb_div_min = pll->min_feedback_div; 98162306a36Sopenharmony_ci fb_div_max = pll->max_feedback_div; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 98462306a36Sopenharmony_ci fb_div_min *= 10; 98562306a36Sopenharmony_ci fb_div_max *= 10; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* determine allowed ref divider range */ 98962306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_REF_DIV) 99062306a36Sopenharmony_ci ref_div_min = pll->reference_div; 99162306a36Sopenharmony_ci else 99262306a36Sopenharmony_ci ref_div_min = pll->min_ref_div; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && 99562306a36Sopenharmony_ci pll->flags & RADEON_PLL_USE_REF_DIV) 99662306a36Sopenharmony_ci ref_div_max = pll->reference_div; 99762306a36Sopenharmony_ci else if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) 99862306a36Sopenharmony_ci /* fix for problems on RS880 */ 99962306a36Sopenharmony_ci ref_div_max = min(pll->max_ref_div, 7u); 100062306a36Sopenharmony_ci else 100162306a36Sopenharmony_ci ref_div_max = pll->max_ref_div; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* determine allowed post divider range */ 100462306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_POST_DIV) { 100562306a36Sopenharmony_ci post_div_min = pll->post_div; 100662306a36Sopenharmony_ci post_div_max = pll->post_div; 100762306a36Sopenharmony_ci } else { 100862306a36Sopenharmony_ci unsigned vco_min, vco_max; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_IS_LCD) { 101162306a36Sopenharmony_ci vco_min = pll->lcd_pll_out_min; 101262306a36Sopenharmony_ci vco_max = pll->lcd_pll_out_max; 101362306a36Sopenharmony_ci } else { 101462306a36Sopenharmony_ci vco_min = pll->pll_out_min; 101562306a36Sopenharmony_ci vco_max = pll->pll_out_max; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 101962306a36Sopenharmony_ci vco_min *= 10; 102062306a36Sopenharmony_ci vco_max *= 10; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci post_div_min = vco_min / target_clock; 102462306a36Sopenharmony_ci if ((target_clock * post_div_min) < vco_min) 102562306a36Sopenharmony_ci ++post_div_min; 102662306a36Sopenharmony_ci if (post_div_min < pll->min_post_div) 102762306a36Sopenharmony_ci post_div_min = pll->min_post_div; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci post_div_max = vco_max / target_clock; 103062306a36Sopenharmony_ci if ((target_clock * post_div_max) > vco_max) 103162306a36Sopenharmony_ci --post_div_max; 103262306a36Sopenharmony_ci if (post_div_max > pll->max_post_div) 103362306a36Sopenharmony_ci post_div_max = pll->max_post_div; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci /* represent the searched ratio as fractional number */ 103762306a36Sopenharmony_ci nom = target_clock; 103862306a36Sopenharmony_ci den = pll->reference_freq; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* reduce the numbers to a simpler ratio */ 104162306a36Sopenharmony_ci avivo_reduce_ratio(&nom, &den, fb_div_min, post_div_min); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci /* now search for a post divider */ 104462306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) 104562306a36Sopenharmony_ci post_div_best = post_div_min; 104662306a36Sopenharmony_ci else 104762306a36Sopenharmony_ci post_div_best = post_div_max; 104862306a36Sopenharmony_ci diff_best = ~0; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci for (post_div = post_div_min; post_div <= post_div_max; ++post_div) { 105162306a36Sopenharmony_ci unsigned diff; 105262306a36Sopenharmony_ci avivo_get_fb_ref_div(nom, den, post_div, fb_div_max, 105362306a36Sopenharmony_ci ref_div_max, &fb_div, &ref_div); 105462306a36Sopenharmony_ci diff = abs(target_clock - (pll->reference_freq * fb_div) / 105562306a36Sopenharmony_ci (ref_div * post_div)); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (diff < diff_best || (diff == diff_best && 105862306a36Sopenharmony_ci !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) { 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci post_div_best = post_div; 106162306a36Sopenharmony_ci diff_best = diff; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci post_div = post_div_best; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* get the feedback and reference divider for the optimal value */ 106762306a36Sopenharmony_ci avivo_get_fb_ref_div(nom, den, post_div, fb_div_max, ref_div_max, 106862306a36Sopenharmony_ci &fb_div, &ref_div); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci /* reduce the numbers to a simpler ratio once more */ 107162306a36Sopenharmony_ci /* this also makes sure that the reference divider is large enough */ 107262306a36Sopenharmony_ci avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* avoid high jitter with small fractional dividers */ 107562306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) { 107662306a36Sopenharmony_ci fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 50); 107762306a36Sopenharmony_ci if (fb_div < fb_div_min) { 107862306a36Sopenharmony_ci unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div); 107962306a36Sopenharmony_ci fb_div *= tmp; 108062306a36Sopenharmony_ci ref_div *= tmp; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* and finally save the result */ 108562306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 108662306a36Sopenharmony_ci *fb_div_p = fb_div / 10; 108762306a36Sopenharmony_ci *frac_fb_div_p = fb_div % 10; 108862306a36Sopenharmony_ci } else { 108962306a36Sopenharmony_ci *fb_div_p = fb_div; 109062306a36Sopenharmony_ci *frac_fb_div_p = 0; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) + 109462306a36Sopenharmony_ci (pll->reference_freq * *frac_fb_div_p)) / 109562306a36Sopenharmony_ci (ref_div * post_div * 10); 109662306a36Sopenharmony_ci *ref_div_p = ref_div; 109762306a36Sopenharmony_ci *post_div_p = post_div; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n", 110062306a36Sopenharmony_ci freq, *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, 110162306a36Sopenharmony_ci ref_div, post_div); 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci/* pre-avivo */ 110562306a36Sopenharmony_cistatic inline uint32_t radeon_div(uint64_t n, uint32_t d) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci n += d / 2; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci do_div(n, d); 111062306a36Sopenharmony_ci return n; 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_civoid radeon_compute_pll_legacy(struct radeon_pll *pll, 111462306a36Sopenharmony_ci uint64_t freq, 111562306a36Sopenharmony_ci uint32_t *dot_clock_p, 111662306a36Sopenharmony_ci uint32_t *fb_div_p, 111762306a36Sopenharmony_ci uint32_t *frac_fb_div_p, 111862306a36Sopenharmony_ci uint32_t *ref_div_p, 111962306a36Sopenharmony_ci uint32_t *post_div_p) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci uint32_t min_ref_div = pll->min_ref_div; 112262306a36Sopenharmony_ci uint32_t max_ref_div = pll->max_ref_div; 112362306a36Sopenharmony_ci uint32_t min_post_div = pll->min_post_div; 112462306a36Sopenharmony_ci uint32_t max_post_div = pll->max_post_div; 112562306a36Sopenharmony_ci uint32_t min_fractional_feed_div = 0; 112662306a36Sopenharmony_ci uint32_t max_fractional_feed_div = 0; 112762306a36Sopenharmony_ci uint32_t best_vco = pll->best_vco; 112862306a36Sopenharmony_ci uint32_t best_post_div = 1; 112962306a36Sopenharmony_ci uint32_t best_ref_div = 1; 113062306a36Sopenharmony_ci uint32_t best_feedback_div = 1; 113162306a36Sopenharmony_ci uint32_t best_frac_feedback_div = 0; 113262306a36Sopenharmony_ci uint32_t best_freq = -1; 113362306a36Sopenharmony_ci uint32_t best_error = 0xffffffff; 113462306a36Sopenharmony_ci uint32_t best_vco_diff = 1; 113562306a36Sopenharmony_ci uint32_t post_div; 113662306a36Sopenharmony_ci u32 pll_out_min, pll_out_max; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci DRM_DEBUG_KMS("PLL freq %llu %u %u\n", freq, pll->min_ref_div, pll->max_ref_div); 113962306a36Sopenharmony_ci freq = freq * 1000; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_IS_LCD) { 114262306a36Sopenharmony_ci pll_out_min = pll->lcd_pll_out_min; 114362306a36Sopenharmony_ci pll_out_max = pll->lcd_pll_out_max; 114462306a36Sopenharmony_ci } else { 114562306a36Sopenharmony_ci pll_out_min = pll->pll_out_min; 114662306a36Sopenharmony_ci pll_out_max = pll->pll_out_max; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (pll_out_min > 64800) 115062306a36Sopenharmony_ci pll_out_min = 64800; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_REF_DIV) 115362306a36Sopenharmony_ci min_ref_div = max_ref_div = pll->reference_div; 115462306a36Sopenharmony_ci else { 115562306a36Sopenharmony_ci while (min_ref_div < max_ref_div-1) { 115662306a36Sopenharmony_ci uint32_t mid = (min_ref_div + max_ref_div) / 2; 115762306a36Sopenharmony_ci uint32_t pll_in = pll->reference_freq / mid; 115862306a36Sopenharmony_ci if (pll_in < pll->pll_in_min) 115962306a36Sopenharmony_ci max_ref_div = mid; 116062306a36Sopenharmony_ci else if (pll_in > pll->pll_in_max) 116162306a36Sopenharmony_ci min_ref_div = mid; 116262306a36Sopenharmony_ci else 116362306a36Sopenharmony_ci break; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_POST_DIV) 116862306a36Sopenharmony_ci min_post_div = max_post_div = pll->post_div; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 117162306a36Sopenharmony_ci min_fractional_feed_div = pll->min_frac_feedback_div; 117262306a36Sopenharmony_ci max_fractional_feed_div = pll->max_frac_feedback_div; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci for (post_div = max_post_div; post_div >= min_post_div; --post_div) { 117662306a36Sopenharmony_ci uint32_t ref_div; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) 117962306a36Sopenharmony_ci continue; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* legacy radeons only have a few post_divs */ 118262306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_LEGACY) { 118362306a36Sopenharmony_ci if ((post_div == 5) || 118462306a36Sopenharmony_ci (post_div == 7) || 118562306a36Sopenharmony_ci (post_div == 9) || 118662306a36Sopenharmony_ci (post_div == 10) || 118762306a36Sopenharmony_ci (post_div == 11) || 118862306a36Sopenharmony_ci (post_div == 13) || 118962306a36Sopenharmony_ci (post_div == 14) || 119062306a36Sopenharmony_ci (post_div == 15)) 119162306a36Sopenharmony_ci continue; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci for (ref_div = min_ref_div; ref_div <= max_ref_div; ++ref_div) { 119562306a36Sopenharmony_ci uint32_t feedback_div, current_freq = 0, error, vco_diff; 119662306a36Sopenharmony_ci uint32_t pll_in = pll->reference_freq / ref_div; 119762306a36Sopenharmony_ci uint32_t min_feed_div = pll->min_feedback_div; 119862306a36Sopenharmony_ci uint32_t max_feed_div = pll->max_feedback_div + 1; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max) 120162306a36Sopenharmony_ci continue; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci while (min_feed_div < max_feed_div) { 120462306a36Sopenharmony_ci uint32_t vco; 120562306a36Sopenharmony_ci uint32_t min_frac_feed_div = min_fractional_feed_div; 120662306a36Sopenharmony_ci uint32_t max_frac_feed_div = max_fractional_feed_div + 1; 120762306a36Sopenharmony_ci uint32_t frac_feedback_div; 120862306a36Sopenharmony_ci uint64_t tmp; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci feedback_div = (min_feed_div + max_feed_div) / 2; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci tmp = (uint64_t)pll->reference_freq * feedback_div; 121362306a36Sopenharmony_ci vco = radeon_div(tmp, ref_div); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (vco < pll_out_min) { 121662306a36Sopenharmony_ci min_feed_div = feedback_div + 1; 121762306a36Sopenharmony_ci continue; 121862306a36Sopenharmony_ci } else if (vco > pll_out_max) { 121962306a36Sopenharmony_ci max_feed_div = feedback_div; 122062306a36Sopenharmony_ci continue; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci while (min_frac_feed_div < max_frac_feed_div) { 122462306a36Sopenharmony_ci frac_feedback_div = (min_frac_feed_div + max_frac_feed_div) / 2; 122562306a36Sopenharmony_ci tmp = (uint64_t)pll->reference_freq * 10000 * feedback_div; 122662306a36Sopenharmony_ci tmp += (uint64_t)pll->reference_freq * 1000 * frac_feedback_div; 122762306a36Sopenharmony_ci current_freq = radeon_div(tmp, ref_div * post_div); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci if (pll->flags & RADEON_PLL_PREFER_CLOSEST_LOWER) { 123062306a36Sopenharmony_ci if (freq < current_freq) 123162306a36Sopenharmony_ci error = 0xffffffff; 123262306a36Sopenharmony_ci else 123362306a36Sopenharmony_ci error = freq - current_freq; 123462306a36Sopenharmony_ci } else 123562306a36Sopenharmony_ci error = abs(current_freq - freq); 123662306a36Sopenharmony_ci vco_diff = abs(vco - best_vco); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci if ((best_vco == 0 && error < best_error) || 123962306a36Sopenharmony_ci (best_vco != 0 && 124062306a36Sopenharmony_ci ((best_error > 100 && error < best_error - 100) || 124162306a36Sopenharmony_ci (abs(error - best_error) < 100 && vco_diff < best_vco_diff)))) { 124262306a36Sopenharmony_ci best_post_div = post_div; 124362306a36Sopenharmony_ci best_ref_div = ref_div; 124462306a36Sopenharmony_ci best_feedback_div = feedback_div; 124562306a36Sopenharmony_ci best_frac_feedback_div = frac_feedback_div; 124662306a36Sopenharmony_ci best_freq = current_freq; 124762306a36Sopenharmony_ci best_error = error; 124862306a36Sopenharmony_ci best_vco_diff = vco_diff; 124962306a36Sopenharmony_ci } else if (current_freq == freq) { 125062306a36Sopenharmony_ci if (best_freq == -1) { 125162306a36Sopenharmony_ci best_post_div = post_div; 125262306a36Sopenharmony_ci best_ref_div = ref_div; 125362306a36Sopenharmony_ci best_feedback_div = feedback_div; 125462306a36Sopenharmony_ci best_frac_feedback_div = frac_feedback_div; 125562306a36Sopenharmony_ci best_freq = current_freq; 125662306a36Sopenharmony_ci best_error = error; 125762306a36Sopenharmony_ci best_vco_diff = vco_diff; 125862306a36Sopenharmony_ci } else if (((pll->flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) || 125962306a36Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) || 126062306a36Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) || 126162306a36Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) || 126262306a36Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) || 126362306a36Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) { 126462306a36Sopenharmony_ci best_post_div = post_div; 126562306a36Sopenharmony_ci best_ref_div = ref_div; 126662306a36Sopenharmony_ci best_feedback_div = feedback_div; 126762306a36Sopenharmony_ci best_frac_feedback_div = frac_feedback_div; 126862306a36Sopenharmony_ci best_freq = current_freq; 126962306a36Sopenharmony_ci best_error = error; 127062306a36Sopenharmony_ci best_vco_diff = vco_diff; 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci if (current_freq < freq) 127462306a36Sopenharmony_ci min_frac_feed_div = frac_feedback_div + 1; 127562306a36Sopenharmony_ci else 127662306a36Sopenharmony_ci max_frac_feed_div = frac_feedback_div; 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci if (current_freq < freq) 127962306a36Sopenharmony_ci min_feed_div = feedback_div + 1; 128062306a36Sopenharmony_ci else 128162306a36Sopenharmony_ci max_feed_div = feedback_div; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci *dot_clock_p = best_freq / 10000; 128762306a36Sopenharmony_ci *fb_div_p = best_feedback_div; 128862306a36Sopenharmony_ci *frac_fb_div_p = best_frac_feedback_div; 128962306a36Sopenharmony_ci *ref_div_p = best_ref_div; 129062306a36Sopenharmony_ci *post_div_p = best_post_div; 129162306a36Sopenharmony_ci DRM_DEBUG_KMS("%lld %d, pll dividers - fb: %d.%d ref: %d, post %d\n", 129262306a36Sopenharmony_ci (long long)freq, 129362306a36Sopenharmony_ci best_freq / 1000, best_feedback_div, best_frac_feedback_div, 129462306a36Sopenharmony_ci best_ref_div, best_post_div); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs radeon_fb_funcs = { 129962306a36Sopenharmony_ci .destroy = drm_gem_fb_destroy, 130062306a36Sopenharmony_ci .create_handle = drm_gem_fb_create_handle, 130162306a36Sopenharmony_ci}; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ciint 130462306a36Sopenharmony_ciradeon_framebuffer_init(struct drm_device *dev, 130562306a36Sopenharmony_ci struct drm_framebuffer *fb, 130662306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 130762306a36Sopenharmony_ci struct drm_gem_object *obj) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci int ret; 131062306a36Sopenharmony_ci fb->obj[0] = obj; 131162306a36Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); 131262306a36Sopenharmony_ci ret = drm_framebuffer_init(dev, fb, &radeon_fb_funcs); 131362306a36Sopenharmony_ci if (ret) { 131462306a36Sopenharmony_ci fb->obj[0] = NULL; 131562306a36Sopenharmony_ci return ret; 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci return 0; 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cistatic struct drm_framebuffer * 132162306a36Sopenharmony_ciradeon_user_framebuffer_create(struct drm_device *dev, 132262306a36Sopenharmony_ci struct drm_file *file_priv, 132362306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 132462306a36Sopenharmony_ci{ 132562306a36Sopenharmony_ci struct drm_gem_object *obj; 132662306a36Sopenharmony_ci struct drm_framebuffer *fb; 132762306a36Sopenharmony_ci int ret; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]); 133062306a36Sopenharmony_ci if (obj == NULL) { 133162306a36Sopenharmony_ci dev_err(dev->dev, "No GEM object associated to handle 0x%08X, " 133262306a36Sopenharmony_ci "can't create framebuffer\n", mode_cmd->handles[0]); 133362306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci /* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */ 133762306a36Sopenharmony_ci if (obj->import_attach) { 133862306a36Sopenharmony_ci DRM_DEBUG_KMS("Cannot create framebuffer from imported dma_buf\n"); 133962306a36Sopenharmony_ci drm_gem_object_put(obj); 134062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci fb = kzalloc(sizeof(*fb), GFP_KERNEL); 134462306a36Sopenharmony_ci if (fb == NULL) { 134562306a36Sopenharmony_ci drm_gem_object_put(obj); 134662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci ret = radeon_framebuffer_init(dev, fb, mode_cmd, obj); 135062306a36Sopenharmony_ci if (ret) { 135162306a36Sopenharmony_ci kfree(fb); 135262306a36Sopenharmony_ci drm_gem_object_put(obj); 135362306a36Sopenharmony_ci return ERR_PTR(ret); 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci return fb; 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cistatic const struct drm_mode_config_funcs radeon_mode_funcs = { 136062306a36Sopenharmony_ci .fb_create = radeon_user_framebuffer_create, 136162306a36Sopenharmony_ci}; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_tmds_pll_enum_list[] = 136462306a36Sopenharmony_ci{ { 0, "driver" }, 136562306a36Sopenharmony_ci { 1, "bios" }, 136662306a36Sopenharmony_ci}; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_tv_std_enum_list[] = 136962306a36Sopenharmony_ci{ { TV_STD_NTSC, "ntsc" }, 137062306a36Sopenharmony_ci { TV_STD_PAL, "pal" }, 137162306a36Sopenharmony_ci { TV_STD_PAL_M, "pal-m" }, 137262306a36Sopenharmony_ci { TV_STD_PAL_60, "pal-60" }, 137362306a36Sopenharmony_ci { TV_STD_NTSC_J, "ntsc-j" }, 137462306a36Sopenharmony_ci { TV_STD_SCART_PAL, "scart-pal" }, 137562306a36Sopenharmony_ci { TV_STD_PAL_CN, "pal-cn" }, 137662306a36Sopenharmony_ci { TV_STD_SECAM, "secam" }, 137762306a36Sopenharmony_ci}; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_underscan_enum_list[] = 138062306a36Sopenharmony_ci{ { UNDERSCAN_OFF, "off" }, 138162306a36Sopenharmony_ci { UNDERSCAN_ON, "on" }, 138262306a36Sopenharmony_ci { UNDERSCAN_AUTO, "auto" }, 138362306a36Sopenharmony_ci}; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_audio_enum_list[] = 138662306a36Sopenharmony_ci{ { RADEON_AUDIO_DISABLE, "off" }, 138762306a36Sopenharmony_ci { RADEON_AUDIO_ENABLE, "on" }, 138862306a36Sopenharmony_ci { RADEON_AUDIO_AUTO, "auto" }, 138962306a36Sopenharmony_ci}; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci/* XXX support different dither options? spatial, temporal, both, etc. */ 139262306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_dither_enum_list[] = 139362306a36Sopenharmony_ci{ { RADEON_FMT_DITHER_DISABLE, "off" }, 139462306a36Sopenharmony_ci { RADEON_FMT_DITHER_ENABLE, "on" }, 139562306a36Sopenharmony_ci}; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_cistatic const struct drm_prop_enum_list radeon_output_csc_enum_list[] = 139862306a36Sopenharmony_ci{ { RADEON_OUTPUT_CSC_BYPASS, "bypass" }, 139962306a36Sopenharmony_ci { RADEON_OUTPUT_CSC_TVRGB, "tvrgb" }, 140062306a36Sopenharmony_ci { RADEON_OUTPUT_CSC_YCBCR601, "ycbcr601" }, 140162306a36Sopenharmony_ci { RADEON_OUTPUT_CSC_YCBCR709, "ycbcr709" }, 140262306a36Sopenharmony_ci}; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_cistatic int radeon_modeset_create_props(struct radeon_device *rdev) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci int sz; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci if (rdev->is_atom_bios) { 140962306a36Sopenharmony_ci rdev->mode_info.coherent_mode_property = 141062306a36Sopenharmony_ci drm_property_create_range(rdev->ddev, 0 , "coherent", 0, 1); 141162306a36Sopenharmony_ci if (!rdev->mode_info.coherent_mode_property) 141262306a36Sopenharmony_ci return -ENOMEM; 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (!ASIC_IS_AVIVO(rdev)) { 141662306a36Sopenharmony_ci sz = ARRAY_SIZE(radeon_tmds_pll_enum_list); 141762306a36Sopenharmony_ci rdev->mode_info.tmds_pll_property = 141862306a36Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 141962306a36Sopenharmony_ci "tmds_pll", 142062306a36Sopenharmony_ci radeon_tmds_pll_enum_list, sz); 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci rdev->mode_info.load_detect_property = 142462306a36Sopenharmony_ci drm_property_create_range(rdev->ddev, 0, "load detection", 0, 1); 142562306a36Sopenharmony_ci if (!rdev->mode_info.load_detect_property) 142662306a36Sopenharmony_ci return -ENOMEM; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci drm_mode_create_scaling_mode_property(rdev->ddev); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci sz = ARRAY_SIZE(radeon_tv_std_enum_list); 143162306a36Sopenharmony_ci rdev->mode_info.tv_std_property = 143262306a36Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 143362306a36Sopenharmony_ci "tv standard", 143462306a36Sopenharmony_ci radeon_tv_std_enum_list, sz); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci sz = ARRAY_SIZE(radeon_underscan_enum_list); 143762306a36Sopenharmony_ci rdev->mode_info.underscan_property = 143862306a36Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 143962306a36Sopenharmony_ci "underscan", 144062306a36Sopenharmony_ci radeon_underscan_enum_list, sz); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci rdev->mode_info.underscan_hborder_property = 144362306a36Sopenharmony_ci drm_property_create_range(rdev->ddev, 0, 144462306a36Sopenharmony_ci "underscan hborder", 0, 128); 144562306a36Sopenharmony_ci if (!rdev->mode_info.underscan_hborder_property) 144662306a36Sopenharmony_ci return -ENOMEM; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci rdev->mode_info.underscan_vborder_property = 144962306a36Sopenharmony_ci drm_property_create_range(rdev->ddev, 0, 145062306a36Sopenharmony_ci "underscan vborder", 0, 128); 145162306a36Sopenharmony_ci if (!rdev->mode_info.underscan_vborder_property) 145262306a36Sopenharmony_ci return -ENOMEM; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci sz = ARRAY_SIZE(radeon_audio_enum_list); 145562306a36Sopenharmony_ci rdev->mode_info.audio_property = 145662306a36Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 145762306a36Sopenharmony_ci "audio", 145862306a36Sopenharmony_ci radeon_audio_enum_list, sz); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci sz = ARRAY_SIZE(radeon_dither_enum_list); 146162306a36Sopenharmony_ci rdev->mode_info.dither_property = 146262306a36Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 146362306a36Sopenharmony_ci "dither", 146462306a36Sopenharmony_ci radeon_dither_enum_list, sz); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci sz = ARRAY_SIZE(radeon_output_csc_enum_list); 146762306a36Sopenharmony_ci rdev->mode_info.output_csc_property = 146862306a36Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 146962306a36Sopenharmony_ci "output_csc", 147062306a36Sopenharmony_ci radeon_output_csc_enum_list, sz); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci return 0; 147362306a36Sopenharmony_ci} 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_civoid radeon_update_display_priority(struct radeon_device *rdev) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci /* adjustment options for the display watermarks */ 147862306a36Sopenharmony_ci if ((radeon_disp_priority == 0) || (radeon_disp_priority > 2)) { 147962306a36Sopenharmony_ci /* set display priority to high for r3xx, rv515 chips 148062306a36Sopenharmony_ci * this avoids flickering due to underflow to the 148162306a36Sopenharmony_ci * display controllers during heavy acceleration. 148262306a36Sopenharmony_ci * Don't force high on rs4xx igp chips as it seems to 148362306a36Sopenharmony_ci * affect the sound card. See kernel bug 15982. 148462306a36Sopenharmony_ci */ 148562306a36Sopenharmony_ci if ((ASIC_IS_R300(rdev) || (rdev->family == CHIP_RV515)) && 148662306a36Sopenharmony_ci !(rdev->flags & RADEON_IS_IGP)) 148762306a36Sopenharmony_ci rdev->disp_priority = 2; 148862306a36Sopenharmony_ci else 148962306a36Sopenharmony_ci rdev->disp_priority = 0; 149062306a36Sopenharmony_ci } else 149162306a36Sopenharmony_ci rdev->disp_priority = radeon_disp_priority; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci} 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci/* 149662306a36Sopenharmony_ci * Allocate hdmi structs and determine register offsets 149762306a36Sopenharmony_ci */ 149862306a36Sopenharmony_cistatic void radeon_afmt_init(struct radeon_device *rdev) 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci int i; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) 150362306a36Sopenharmony_ci rdev->mode_info.afmt[i] = NULL; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci if (ASIC_IS_NODCE(rdev)) { 150662306a36Sopenharmony_ci /* nothing to do */ 150762306a36Sopenharmony_ci } else if (ASIC_IS_DCE4(rdev)) { 150862306a36Sopenharmony_ci static uint32_t eg_offsets[] = { 150962306a36Sopenharmony_ci EVERGREEN_CRTC0_REGISTER_OFFSET, 151062306a36Sopenharmony_ci EVERGREEN_CRTC1_REGISTER_OFFSET, 151162306a36Sopenharmony_ci EVERGREEN_CRTC2_REGISTER_OFFSET, 151262306a36Sopenharmony_ci EVERGREEN_CRTC3_REGISTER_OFFSET, 151362306a36Sopenharmony_ci EVERGREEN_CRTC4_REGISTER_OFFSET, 151462306a36Sopenharmony_ci EVERGREEN_CRTC5_REGISTER_OFFSET, 151562306a36Sopenharmony_ci 0x13830 - 0x7030, 151662306a36Sopenharmony_ci }; 151762306a36Sopenharmony_ci int num_afmt; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci /* DCE8 has 7 audio blocks tied to DIG encoders */ 152062306a36Sopenharmony_ci /* DCE6 has 6 audio blocks tied to DIG encoders */ 152162306a36Sopenharmony_ci /* DCE4/5 has 6 audio blocks tied to DIG encoders */ 152262306a36Sopenharmony_ci /* DCE4.1 has 2 audio blocks tied to DIG encoders */ 152362306a36Sopenharmony_ci if (ASIC_IS_DCE8(rdev)) 152462306a36Sopenharmony_ci num_afmt = 7; 152562306a36Sopenharmony_ci else if (ASIC_IS_DCE6(rdev)) 152662306a36Sopenharmony_ci num_afmt = 6; 152762306a36Sopenharmony_ci else if (ASIC_IS_DCE5(rdev)) 152862306a36Sopenharmony_ci num_afmt = 6; 152962306a36Sopenharmony_ci else if (ASIC_IS_DCE41(rdev)) 153062306a36Sopenharmony_ci num_afmt = 2; 153162306a36Sopenharmony_ci else /* DCE4 */ 153262306a36Sopenharmony_ci num_afmt = 6; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci BUG_ON(num_afmt > ARRAY_SIZE(eg_offsets)); 153562306a36Sopenharmony_ci for (i = 0; i < num_afmt; i++) { 153662306a36Sopenharmony_ci rdev->mode_info.afmt[i] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 153762306a36Sopenharmony_ci if (rdev->mode_info.afmt[i]) { 153862306a36Sopenharmony_ci rdev->mode_info.afmt[i]->offset = eg_offsets[i]; 153962306a36Sopenharmony_ci rdev->mode_info.afmt[i]->id = i; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci } else if (ASIC_IS_DCE3(rdev)) { 154362306a36Sopenharmony_ci /* DCE3.x has 2 audio blocks tied to DIG encoders */ 154462306a36Sopenharmony_ci rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 154562306a36Sopenharmony_ci if (rdev->mode_info.afmt[0]) { 154662306a36Sopenharmony_ci rdev->mode_info.afmt[0]->offset = DCE3_HDMI_OFFSET0; 154762306a36Sopenharmony_ci rdev->mode_info.afmt[0]->id = 0; 154862306a36Sopenharmony_ci } 154962306a36Sopenharmony_ci rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 155062306a36Sopenharmony_ci if (rdev->mode_info.afmt[1]) { 155162306a36Sopenharmony_ci rdev->mode_info.afmt[1]->offset = DCE3_HDMI_OFFSET1; 155262306a36Sopenharmony_ci rdev->mode_info.afmt[1]->id = 1; 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci } else if (ASIC_IS_DCE2(rdev)) { 155562306a36Sopenharmony_ci /* DCE2 has at least 1 routable audio block */ 155662306a36Sopenharmony_ci rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 155762306a36Sopenharmony_ci if (rdev->mode_info.afmt[0]) { 155862306a36Sopenharmony_ci rdev->mode_info.afmt[0]->offset = DCE2_HDMI_OFFSET0; 155962306a36Sopenharmony_ci rdev->mode_info.afmt[0]->id = 0; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci /* r6xx has 2 routable audio blocks */ 156262306a36Sopenharmony_ci if (rdev->family >= CHIP_R600) { 156362306a36Sopenharmony_ci rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 156462306a36Sopenharmony_ci if (rdev->mode_info.afmt[1]) { 156562306a36Sopenharmony_ci rdev->mode_info.afmt[1]->offset = DCE2_HDMI_OFFSET1; 156662306a36Sopenharmony_ci rdev->mode_info.afmt[1]->id = 1; 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci} 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_cistatic void radeon_afmt_fini(struct radeon_device *rdev) 157362306a36Sopenharmony_ci{ 157462306a36Sopenharmony_ci int i; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) { 157762306a36Sopenharmony_ci kfree(rdev->mode_info.afmt[i]); 157862306a36Sopenharmony_ci rdev->mode_info.afmt[i] = NULL; 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci} 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ciint radeon_modeset_init(struct radeon_device *rdev) 158362306a36Sopenharmony_ci{ 158462306a36Sopenharmony_ci int i; 158562306a36Sopenharmony_ci int ret; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci drm_mode_config_init(rdev->ddev); 158862306a36Sopenharmony_ci rdev->mode_info.mode_config_initialized = true; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci rdev->ddev->mode_config.funcs = &radeon_mode_funcs; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci if (radeon_use_pflipirq == 2 && rdev->family >= CHIP_R600) 159362306a36Sopenharmony_ci rdev->ddev->mode_config.async_page_flip = true; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (ASIC_IS_DCE5(rdev)) { 159662306a36Sopenharmony_ci rdev->ddev->mode_config.max_width = 16384; 159762306a36Sopenharmony_ci rdev->ddev->mode_config.max_height = 16384; 159862306a36Sopenharmony_ci } else if (ASIC_IS_AVIVO(rdev)) { 159962306a36Sopenharmony_ci rdev->ddev->mode_config.max_width = 8192; 160062306a36Sopenharmony_ci rdev->ddev->mode_config.max_height = 8192; 160162306a36Sopenharmony_ci } else { 160262306a36Sopenharmony_ci rdev->ddev->mode_config.max_width = 4096; 160362306a36Sopenharmony_ci rdev->ddev->mode_config.max_height = 4096; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci rdev->ddev->mode_config.preferred_depth = 24; 160762306a36Sopenharmony_ci rdev->ddev->mode_config.prefer_shadow = 1; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci rdev->ddev->mode_config.fb_modifiers_not_supported = true; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci ret = radeon_modeset_create_props(rdev); 161262306a36Sopenharmony_ci if (ret) { 161362306a36Sopenharmony_ci return ret; 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci /* init i2c buses */ 161762306a36Sopenharmony_ci radeon_i2c_init(rdev); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci /* check combios for a valid hardcoded EDID - Sun servers */ 162062306a36Sopenharmony_ci if (!rdev->is_atom_bios) { 162162306a36Sopenharmony_ci /* check for hardcoded EDID in BIOS */ 162262306a36Sopenharmony_ci radeon_combios_check_hardcoded_edid(rdev); 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci /* allocate crtcs */ 162662306a36Sopenharmony_ci for (i = 0; i < rdev->num_crtc; i++) { 162762306a36Sopenharmony_ci radeon_crtc_init(rdev->ddev, i); 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci /* okay we should have all the bios connectors */ 163162306a36Sopenharmony_ci ret = radeon_setup_enc_conn(rdev->ddev); 163262306a36Sopenharmony_ci if (!ret) { 163362306a36Sopenharmony_ci return ret; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci /* init dig PHYs, disp eng pll */ 163762306a36Sopenharmony_ci if (rdev->is_atom_bios) { 163862306a36Sopenharmony_ci radeon_atom_encoder_init(rdev); 163962306a36Sopenharmony_ci radeon_atom_disp_eng_pll_init(rdev); 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci /* initialize hpd */ 164362306a36Sopenharmony_ci radeon_hpd_init(rdev); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci /* setup afmt */ 164662306a36Sopenharmony_ci radeon_afmt_init(rdev); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci drm_kms_helper_poll_init(rdev->ddev); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci /* do pm late init */ 165162306a36Sopenharmony_ci ret = radeon_pm_late_init(rdev); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci return 0; 165462306a36Sopenharmony_ci} 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_civoid radeon_modeset_fini(struct radeon_device *rdev) 165762306a36Sopenharmony_ci{ 165862306a36Sopenharmony_ci if (rdev->mode_info.mode_config_initialized) { 165962306a36Sopenharmony_ci drm_kms_helper_poll_fini(rdev->ddev); 166062306a36Sopenharmony_ci radeon_hpd_fini(rdev); 166162306a36Sopenharmony_ci drm_helper_force_disable_all(rdev->ddev); 166262306a36Sopenharmony_ci radeon_afmt_fini(rdev); 166362306a36Sopenharmony_ci drm_mode_config_cleanup(rdev->ddev); 166462306a36Sopenharmony_ci rdev->mode_info.mode_config_initialized = false; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci kfree(rdev->mode_info.bios_hardcoded_edid); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci /* free i2c buses */ 167062306a36Sopenharmony_ci radeon_i2c_fini(rdev); 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_cistatic bool is_hdtv_mode(const struct drm_display_mode *mode) 167462306a36Sopenharmony_ci{ 167562306a36Sopenharmony_ci /* try and guess if this is a tv or a monitor */ 167662306a36Sopenharmony_ci if ((mode->vdisplay == 480 && mode->hdisplay == 720) || /* 480p */ 167762306a36Sopenharmony_ci (mode->vdisplay == 576) || /* 576p */ 167862306a36Sopenharmony_ci (mode->vdisplay == 720) || /* 720p */ 167962306a36Sopenharmony_ci (mode->vdisplay == 1080)) /* 1080p */ 168062306a36Sopenharmony_ci return true; 168162306a36Sopenharmony_ci else 168262306a36Sopenharmony_ci return false; 168362306a36Sopenharmony_ci} 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_cibool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, 168662306a36Sopenharmony_ci const struct drm_display_mode *mode, 168762306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 168862306a36Sopenharmony_ci{ 168962306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 169062306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 169162306a36Sopenharmony_ci struct drm_encoder *encoder; 169262306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 169362306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder; 169462306a36Sopenharmony_ci struct drm_connector *connector; 169562306a36Sopenharmony_ci bool first = true; 169662306a36Sopenharmony_ci u32 src_v = 1, dst_v = 1; 169762306a36Sopenharmony_ci u32 src_h = 1, dst_h = 1; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci radeon_crtc->h_border = 0; 170062306a36Sopenharmony_ci radeon_crtc->v_border = 0; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 170362306a36Sopenharmony_ci if (encoder->crtc != crtc) 170462306a36Sopenharmony_ci continue; 170562306a36Sopenharmony_ci radeon_encoder = to_radeon_encoder(encoder); 170662306a36Sopenharmony_ci connector = radeon_get_connector_for_encoder(encoder); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci if (first) { 170962306a36Sopenharmony_ci /* set scaling */ 171062306a36Sopenharmony_ci if (radeon_encoder->rmx_type == RMX_OFF) 171162306a36Sopenharmony_ci radeon_crtc->rmx_type = RMX_OFF; 171262306a36Sopenharmony_ci else if (mode->hdisplay < radeon_encoder->native_mode.hdisplay || 171362306a36Sopenharmony_ci mode->vdisplay < radeon_encoder->native_mode.vdisplay) 171462306a36Sopenharmony_ci radeon_crtc->rmx_type = radeon_encoder->rmx_type; 171562306a36Sopenharmony_ci else 171662306a36Sopenharmony_ci radeon_crtc->rmx_type = RMX_OFF; 171762306a36Sopenharmony_ci /* copy native mode */ 171862306a36Sopenharmony_ci memcpy(&radeon_crtc->native_mode, 171962306a36Sopenharmony_ci &radeon_encoder->native_mode, 172062306a36Sopenharmony_ci sizeof(struct drm_display_mode)); 172162306a36Sopenharmony_ci src_v = crtc->mode.vdisplay; 172262306a36Sopenharmony_ci dst_v = radeon_crtc->native_mode.vdisplay; 172362306a36Sopenharmony_ci src_h = crtc->mode.hdisplay; 172462306a36Sopenharmony_ci dst_h = radeon_crtc->native_mode.hdisplay; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* fix up for overscan on hdmi */ 172762306a36Sopenharmony_ci if (ASIC_IS_AVIVO(rdev) && 172862306a36Sopenharmony_ci (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) && 172962306a36Sopenharmony_ci ((radeon_encoder->underscan_type == UNDERSCAN_ON) || 173062306a36Sopenharmony_ci ((radeon_encoder->underscan_type == UNDERSCAN_AUTO) && 173162306a36Sopenharmony_ci drm_detect_hdmi_monitor(radeon_connector_edid(connector)) && 173262306a36Sopenharmony_ci is_hdtv_mode(mode)))) { 173362306a36Sopenharmony_ci if (radeon_encoder->underscan_hborder != 0) 173462306a36Sopenharmony_ci radeon_crtc->h_border = radeon_encoder->underscan_hborder; 173562306a36Sopenharmony_ci else 173662306a36Sopenharmony_ci radeon_crtc->h_border = (mode->hdisplay >> 5) + 16; 173762306a36Sopenharmony_ci if (radeon_encoder->underscan_vborder != 0) 173862306a36Sopenharmony_ci radeon_crtc->v_border = radeon_encoder->underscan_vborder; 173962306a36Sopenharmony_ci else 174062306a36Sopenharmony_ci radeon_crtc->v_border = (mode->vdisplay >> 5) + 16; 174162306a36Sopenharmony_ci radeon_crtc->rmx_type = RMX_FULL; 174262306a36Sopenharmony_ci src_v = crtc->mode.vdisplay; 174362306a36Sopenharmony_ci dst_v = crtc->mode.vdisplay - (radeon_crtc->v_border * 2); 174462306a36Sopenharmony_ci src_h = crtc->mode.hdisplay; 174562306a36Sopenharmony_ci dst_h = crtc->mode.hdisplay - (radeon_crtc->h_border * 2); 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci first = false; 174862306a36Sopenharmony_ci } else { 174962306a36Sopenharmony_ci if (radeon_crtc->rmx_type != radeon_encoder->rmx_type) { 175062306a36Sopenharmony_ci /* WARNING: Right now this can't happen but 175162306a36Sopenharmony_ci * in the future we need to check that scaling 175262306a36Sopenharmony_ci * are consistent across different encoder 175362306a36Sopenharmony_ci * (ie all encoder can work with the same 175462306a36Sopenharmony_ci * scaling). 175562306a36Sopenharmony_ci */ 175662306a36Sopenharmony_ci DRM_ERROR("Scaling not consistent across encoder.\n"); 175762306a36Sopenharmony_ci return false; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci if (radeon_crtc->rmx_type != RMX_OFF) { 176262306a36Sopenharmony_ci fixed20_12 a, b; 176362306a36Sopenharmony_ci a.full = dfixed_const(src_v); 176462306a36Sopenharmony_ci b.full = dfixed_const(dst_v); 176562306a36Sopenharmony_ci radeon_crtc->vsc.full = dfixed_div(a, b); 176662306a36Sopenharmony_ci a.full = dfixed_const(src_h); 176762306a36Sopenharmony_ci b.full = dfixed_const(dst_h); 176862306a36Sopenharmony_ci radeon_crtc->hsc.full = dfixed_div(a, b); 176962306a36Sopenharmony_ci } else { 177062306a36Sopenharmony_ci radeon_crtc->vsc.full = dfixed_const(1); 177162306a36Sopenharmony_ci radeon_crtc->hsc.full = dfixed_const(1); 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci return true; 177462306a36Sopenharmony_ci} 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci/* 177762306a36Sopenharmony_ci * Retrieve current video scanout position of crtc on a given gpu, and 177862306a36Sopenharmony_ci * an optional accurate timestamp of when query happened. 177962306a36Sopenharmony_ci * 178062306a36Sopenharmony_ci * \param dev Device to query. 178162306a36Sopenharmony_ci * \param crtc Crtc to query. 178262306a36Sopenharmony_ci * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). 178362306a36Sopenharmony_ci * For driver internal use only also supports these flags: 178462306a36Sopenharmony_ci * 178562306a36Sopenharmony_ci * USE_REAL_VBLANKSTART to use the real start of vblank instead 178662306a36Sopenharmony_ci * of a fudged earlier start of vblank. 178762306a36Sopenharmony_ci * 178862306a36Sopenharmony_ci * GET_DISTANCE_TO_VBLANKSTART to return distance to the 178962306a36Sopenharmony_ci * fudged earlier start of vblank in *vpos and the distance 179062306a36Sopenharmony_ci * to true start of vblank in *hpos. 179162306a36Sopenharmony_ci * 179262306a36Sopenharmony_ci * \param *vpos Location where vertical scanout position should be stored. 179362306a36Sopenharmony_ci * \param *hpos Location where horizontal scanout position should go. 179462306a36Sopenharmony_ci * \param *stime Target location for timestamp taken immediately before 179562306a36Sopenharmony_ci * scanout position query. Can be NULL to skip timestamp. 179662306a36Sopenharmony_ci * \param *etime Target location for timestamp taken immediately after 179762306a36Sopenharmony_ci * scanout position query. Can be NULL to skip timestamp. 179862306a36Sopenharmony_ci * 179962306a36Sopenharmony_ci * Returns vpos as a positive number while in active scanout area. 180062306a36Sopenharmony_ci * Returns vpos as a negative number inside vblank, counting the number 180162306a36Sopenharmony_ci * of scanlines to go until end of vblank, e.g., -1 means "one scanline 180262306a36Sopenharmony_ci * until start of active scanout / end of vblank." 180362306a36Sopenharmony_ci * 180462306a36Sopenharmony_ci * \return Flags, or'ed together as follows: 180562306a36Sopenharmony_ci * 180662306a36Sopenharmony_ci * DRM_SCANOUTPOS_VALID = Query successful. 180762306a36Sopenharmony_ci * DRM_SCANOUTPOS_INVBL = Inside vblank. 180862306a36Sopenharmony_ci * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of 180962306a36Sopenharmony_ci * this flag means that returned position may be offset by a constant but 181062306a36Sopenharmony_ci * unknown small number of scanlines wrt. real scanout position. 181162306a36Sopenharmony_ci * 181262306a36Sopenharmony_ci */ 181362306a36Sopenharmony_ciint radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, 181462306a36Sopenharmony_ci unsigned int flags, int *vpos, int *hpos, 181562306a36Sopenharmony_ci ktime_t *stime, ktime_t *etime, 181662306a36Sopenharmony_ci const struct drm_display_mode *mode) 181762306a36Sopenharmony_ci{ 181862306a36Sopenharmony_ci u32 stat_crtc = 0, vbl = 0, position = 0; 181962306a36Sopenharmony_ci int vbl_start, vbl_end, vtotal, ret = 0; 182062306a36Sopenharmony_ci bool in_vbl = true; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci /* Get optional system timestamp before query. */ 182762306a36Sopenharmony_ci if (stime) 182862306a36Sopenharmony_ci *stime = ktime_get(); 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci if (ASIC_IS_DCE4(rdev)) { 183162306a36Sopenharmony_ci if (pipe == 0) { 183262306a36Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 183362306a36Sopenharmony_ci EVERGREEN_CRTC0_REGISTER_OFFSET); 183462306a36Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 183562306a36Sopenharmony_ci EVERGREEN_CRTC0_REGISTER_OFFSET); 183662306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 183762306a36Sopenharmony_ci } 183862306a36Sopenharmony_ci if (pipe == 1) { 183962306a36Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 184062306a36Sopenharmony_ci EVERGREEN_CRTC1_REGISTER_OFFSET); 184162306a36Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 184262306a36Sopenharmony_ci EVERGREEN_CRTC1_REGISTER_OFFSET); 184362306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci if (pipe == 2) { 184662306a36Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 184762306a36Sopenharmony_ci EVERGREEN_CRTC2_REGISTER_OFFSET); 184862306a36Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 184962306a36Sopenharmony_ci EVERGREEN_CRTC2_REGISTER_OFFSET); 185062306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 185162306a36Sopenharmony_ci } 185262306a36Sopenharmony_ci if (pipe == 3) { 185362306a36Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 185462306a36Sopenharmony_ci EVERGREEN_CRTC3_REGISTER_OFFSET); 185562306a36Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 185662306a36Sopenharmony_ci EVERGREEN_CRTC3_REGISTER_OFFSET); 185762306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci if (pipe == 4) { 186062306a36Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 186162306a36Sopenharmony_ci EVERGREEN_CRTC4_REGISTER_OFFSET); 186262306a36Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 186362306a36Sopenharmony_ci EVERGREEN_CRTC4_REGISTER_OFFSET); 186462306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 186562306a36Sopenharmony_ci } 186662306a36Sopenharmony_ci if (pipe == 5) { 186762306a36Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 186862306a36Sopenharmony_ci EVERGREEN_CRTC5_REGISTER_OFFSET); 186962306a36Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 187062306a36Sopenharmony_ci EVERGREEN_CRTC5_REGISTER_OFFSET); 187162306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci } else if (ASIC_IS_AVIVO(rdev)) { 187462306a36Sopenharmony_ci if (pipe == 0) { 187562306a36Sopenharmony_ci vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END); 187662306a36Sopenharmony_ci position = RREG32(AVIVO_D1CRTC_STATUS_POSITION); 187762306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci if (pipe == 1) { 188062306a36Sopenharmony_ci vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END); 188162306a36Sopenharmony_ci position = RREG32(AVIVO_D2CRTC_STATUS_POSITION); 188262306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci } else { 188562306a36Sopenharmony_ci /* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */ 188662306a36Sopenharmony_ci if (pipe == 0) { 188762306a36Sopenharmony_ci /* Assume vbl_end == 0, get vbl_start from 188862306a36Sopenharmony_ci * upper 16 bits. 188962306a36Sopenharmony_ci */ 189062306a36Sopenharmony_ci vbl = (RREG32(RADEON_CRTC_V_TOTAL_DISP) & 189162306a36Sopenharmony_ci RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT; 189262306a36Sopenharmony_ci /* Only retrieve vpos from upper 16 bits, set hpos == 0. */ 189362306a36Sopenharmony_ci position = (RREG32(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL; 189462306a36Sopenharmony_ci stat_crtc = RREG32(RADEON_CRTC_STATUS); 189562306a36Sopenharmony_ci if (!(stat_crtc & 1)) 189662306a36Sopenharmony_ci in_vbl = false; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci if (pipe == 1) { 190162306a36Sopenharmony_ci vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) & 190262306a36Sopenharmony_ci RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT; 190362306a36Sopenharmony_ci position = (RREG32(RADEON_CRTC2_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL; 190462306a36Sopenharmony_ci stat_crtc = RREG32(RADEON_CRTC2_STATUS); 190562306a36Sopenharmony_ci if (!(stat_crtc & 1)) 190662306a36Sopenharmony_ci in_vbl = false; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci } 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci /* Get optional system timestamp after query. */ 191362306a36Sopenharmony_ci if (etime) 191462306a36Sopenharmony_ci *etime = ktime_get(); 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci /* Decode into vertical and horizontal scanout position. */ 191962306a36Sopenharmony_ci *vpos = position & 0x1fff; 192062306a36Sopenharmony_ci *hpos = (position >> 16) & 0x1fff; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci /* Valid vblank area boundaries from gpu retrieved? */ 192362306a36Sopenharmony_ci if (vbl > 0) { 192462306a36Sopenharmony_ci /* Yes: Decode. */ 192562306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_ACCURATE; 192662306a36Sopenharmony_ci vbl_start = vbl & 0x1fff; 192762306a36Sopenharmony_ci vbl_end = (vbl >> 16) & 0x1fff; 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci else { 193062306a36Sopenharmony_ci /* No: Fake something reasonable which gives at least ok results. */ 193162306a36Sopenharmony_ci vbl_start = mode->crtc_vdisplay; 193262306a36Sopenharmony_ci vbl_end = 0; 193362306a36Sopenharmony_ci } 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci /* Called from driver internal vblank counter query code? */ 193662306a36Sopenharmony_ci if (flags & GET_DISTANCE_TO_VBLANKSTART) { 193762306a36Sopenharmony_ci /* Caller wants distance from real vbl_start in *hpos */ 193862306a36Sopenharmony_ci *hpos = *vpos - vbl_start; 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci /* Fudge vblank to start a few scanlines earlier to handle the 194262306a36Sopenharmony_ci * problem that vblank irqs fire a few scanlines before start 194362306a36Sopenharmony_ci * of vblank. Some driver internal callers need the true vblank 194462306a36Sopenharmony_ci * start to be used and signal this via the USE_REAL_VBLANKSTART flag. 194562306a36Sopenharmony_ci * 194662306a36Sopenharmony_ci * The cause of the "early" vblank irq is that the irq is triggered 194762306a36Sopenharmony_ci * by the line buffer logic when the line buffer read position enters 194862306a36Sopenharmony_ci * the vblank, whereas our crtc scanout position naturally lags the 194962306a36Sopenharmony_ci * line buffer read position. 195062306a36Sopenharmony_ci */ 195162306a36Sopenharmony_ci if (!(flags & USE_REAL_VBLANKSTART)) 195262306a36Sopenharmony_ci vbl_start -= rdev->mode_info.crtcs[pipe]->lb_vblank_lead_lines; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci /* Test scanout position against vblank region. */ 195562306a36Sopenharmony_ci if ((*vpos < vbl_start) && (*vpos >= vbl_end)) 195662306a36Sopenharmony_ci in_vbl = false; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci /* In vblank? */ 195962306a36Sopenharmony_ci if (in_vbl) 196062306a36Sopenharmony_ci ret |= DRM_SCANOUTPOS_IN_VBLANK; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci /* Called from driver internal vblank counter query code? */ 196362306a36Sopenharmony_ci if (flags & GET_DISTANCE_TO_VBLANKSTART) { 196462306a36Sopenharmony_ci /* Caller wants distance from fudged earlier vbl_start */ 196562306a36Sopenharmony_ci *vpos -= vbl_start; 196662306a36Sopenharmony_ci return ret; 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci /* Check if inside vblank area and apply corrective offsets: 197062306a36Sopenharmony_ci * vpos will then be >=0 in video scanout area, but negative 197162306a36Sopenharmony_ci * within vblank area, counting down the number of lines until 197262306a36Sopenharmony_ci * start of scanout. 197362306a36Sopenharmony_ci */ 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci /* Inside "upper part" of vblank area? Apply corrective offset if so: */ 197662306a36Sopenharmony_ci if (in_vbl && (*vpos >= vbl_start)) { 197762306a36Sopenharmony_ci vtotal = mode->crtc_vtotal; 197862306a36Sopenharmony_ci *vpos = *vpos - vtotal; 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci /* Correct for shifted end of vbl at vbl_end. */ 198262306a36Sopenharmony_ci *vpos = *vpos - vbl_end; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci return ret; 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_cibool 198862306a36Sopenharmony_ciradeon_get_crtc_scanout_position(struct drm_crtc *crtc, 198962306a36Sopenharmony_ci bool in_vblank_irq, int *vpos, int *hpos, 199062306a36Sopenharmony_ci ktime_t *stime, ktime_t *etime, 199162306a36Sopenharmony_ci const struct drm_display_mode *mode) 199262306a36Sopenharmony_ci{ 199362306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 199462306a36Sopenharmony_ci unsigned int pipe = crtc->index; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci return radeon_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos, 199762306a36Sopenharmony_ci stime, etime, mode); 199862306a36Sopenharmony_ci} 1999