18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2007-8 Advanced Micro Devices, Inc. 38c2ecf20Sopenharmony_ci * Copyright 2008 Red Hat Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 68c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 78c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 88c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 98c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 108c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 138c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 198c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 208c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 218c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Authors: Dave Airlie 248c2ecf20Sopenharmony_ci * Alex Deucher 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/pci.h> 288c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 298c2ecf20Sopenharmony_ci#include <linux/gcd.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <asm/div64.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 368c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 378c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 388c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 398c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 408c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 418c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 428c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 438c2ecf20Sopenharmony_ci#include <drm/radeon_drm.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include "atom.h" 468c2ecf20Sopenharmony_ci#include "radeon.h" 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciu32 radeon_get_vblank_counter_kms(struct drm_crtc *crtc); 498c2ecf20Sopenharmony_ciint radeon_enable_vblank_kms(struct drm_crtc *crtc); 508c2ecf20Sopenharmony_civoid radeon_disable_vblank_kms(struct drm_crtc *crtc); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void avivo_crtc_load_lut(struct drm_crtc *crtc) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 558c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 568c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 578c2ecf20Sopenharmony_ci u16 *r, *g, *b; 588c2ecf20Sopenharmony_ci int i; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); 618c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUTA_CONTROL + radeon_crtc->crtc_offset, 0); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); 648c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); 658c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); 688c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); 698c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUT_RW_SELECT, radeon_crtc->crtc_id); 728c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUT_RW_MODE, 0); 738c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci WREG8(AVIVO_DC_LUT_RW_INDEX, 0); 768c2ecf20Sopenharmony_ci r = crtc->gamma_store; 778c2ecf20Sopenharmony_ci g = r + crtc->gamma_size; 788c2ecf20Sopenharmony_ci b = g + crtc->gamma_size; 798c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 808c2ecf20Sopenharmony_ci WREG32(AVIVO_DC_LUT_30_COLOR, 818c2ecf20Sopenharmony_ci ((*r++ & 0xffc0) << 14) | 828c2ecf20Sopenharmony_ci ((*g++ & 0xffc0) << 4) | 838c2ecf20Sopenharmony_ci (*b++ >> 6)); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Only change bit 0 of LUT_SEL, other bits are set elsewhere */ 878c2ecf20Sopenharmony_ci WREG32_P(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id, ~1); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void dce4_crtc_load_lut(struct drm_crtc *crtc) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 938c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 948c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 958c2ecf20Sopenharmony_ci u16 *r, *g, *b; 968c2ecf20Sopenharmony_ci int i; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); 998c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); 1028c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); 1038c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); 1068c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); 1078c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_RW_MODE + radeon_crtc->crtc_offset, 0); 1108c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x00000007); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0); 1138c2ecf20Sopenharmony_ci r = crtc->gamma_store; 1148c2ecf20Sopenharmony_ci g = r + crtc->gamma_size; 1158c2ecf20Sopenharmony_ci b = g + crtc->gamma_size; 1168c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 1178c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset, 1188c2ecf20Sopenharmony_ci ((*r++ & 0xffc0) << 14) | 1198c2ecf20Sopenharmony_ci ((*g++ & 0xffc0) << 4) | 1208c2ecf20Sopenharmony_ci (*b++ >> 6)); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void dce5_crtc_load_lut(struct drm_crtc *crtc) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 1278c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 1288c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 1298c2ecf20Sopenharmony_ci u16 *r, *g, *b; 1308c2ecf20Sopenharmony_ci int i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%d\n", radeon_crtc->crtc_id); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci msleep(10); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci WREG32(NI_INPUT_CSC_CONTROL + radeon_crtc->crtc_offset, 1378c2ecf20Sopenharmony_ci (NI_INPUT_CSC_GRPH_MODE(NI_INPUT_CSC_BYPASS) | 1388c2ecf20Sopenharmony_ci NI_INPUT_CSC_OVL_MODE(NI_INPUT_CSC_BYPASS))); 1398c2ecf20Sopenharmony_ci WREG32(NI_PRESCALE_GRPH_CONTROL + radeon_crtc->crtc_offset, 1408c2ecf20Sopenharmony_ci NI_GRPH_PRESCALE_BYPASS); 1418c2ecf20Sopenharmony_ci WREG32(NI_PRESCALE_OVL_CONTROL + radeon_crtc->crtc_offset, 1428c2ecf20Sopenharmony_ci NI_OVL_PRESCALE_BYPASS); 1438c2ecf20Sopenharmony_ci WREG32(NI_INPUT_GAMMA_CONTROL + radeon_crtc->crtc_offset, 1448c2ecf20Sopenharmony_ci (NI_GRPH_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT) | 1458c2ecf20Sopenharmony_ci NI_OVL_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT))); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); 1508c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); 1518c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); 1548c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); 1558c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_RW_MODE + radeon_crtc->crtc_offset, 0); 1588c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc->crtc_offset, 0x00000007); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc->crtc_offset, 0); 1618c2ecf20Sopenharmony_ci r = crtc->gamma_store; 1628c2ecf20Sopenharmony_ci g = r + crtc->gamma_size; 1638c2ecf20Sopenharmony_ci b = g + crtc->gamma_size; 1648c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 1658c2ecf20Sopenharmony_ci WREG32(EVERGREEN_DC_LUT_30_COLOR + radeon_crtc->crtc_offset, 1668c2ecf20Sopenharmony_ci ((*r++ & 0xffc0) << 14) | 1678c2ecf20Sopenharmony_ci ((*g++ & 0xffc0) << 4) | 1688c2ecf20Sopenharmony_ci (*b++ >> 6)); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci WREG32(NI_DEGAMMA_CONTROL + radeon_crtc->crtc_offset, 1728c2ecf20Sopenharmony_ci (NI_GRPH_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | 1738c2ecf20Sopenharmony_ci NI_OVL_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | 1748c2ecf20Sopenharmony_ci NI_ICON_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) | 1758c2ecf20Sopenharmony_ci NI_CURSOR_DEGAMMA_MODE(NI_DEGAMMA_BYPASS))); 1768c2ecf20Sopenharmony_ci WREG32(NI_GAMUT_REMAP_CONTROL + radeon_crtc->crtc_offset, 1778c2ecf20Sopenharmony_ci (NI_GRPH_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS) | 1788c2ecf20Sopenharmony_ci NI_OVL_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS))); 1798c2ecf20Sopenharmony_ci WREG32(NI_REGAMMA_CONTROL + radeon_crtc->crtc_offset, 1808c2ecf20Sopenharmony_ci (NI_GRPH_REGAMMA_MODE(NI_REGAMMA_BYPASS) | 1818c2ecf20Sopenharmony_ci NI_OVL_REGAMMA_MODE(NI_REGAMMA_BYPASS))); 1828c2ecf20Sopenharmony_ci WREG32(NI_OUTPUT_CSC_CONTROL + radeon_crtc->crtc_offset, 1838c2ecf20Sopenharmony_ci (NI_OUTPUT_CSC_GRPH_MODE(radeon_crtc->output_csc) | 1848c2ecf20Sopenharmony_ci NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS))); 1858c2ecf20Sopenharmony_ci /* XXX match this to the depth of the crtc fmt block, move to modeset? */ 1868c2ecf20Sopenharmony_ci WREG32(0x6940 + radeon_crtc->crtc_offset, 0); 1878c2ecf20Sopenharmony_ci if (ASIC_IS_DCE8(rdev)) { 1888c2ecf20Sopenharmony_ci /* XXX this only needs to be programmed once per crtc at startup, 1898c2ecf20Sopenharmony_ci * not sure where the best place for it is 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci WREG32(CIK_ALPHA_CONTROL + radeon_crtc->crtc_offset, 1928c2ecf20Sopenharmony_ci CIK_CURSOR_ALPHA_BLND_ENA); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void legacy_crtc_load_lut(struct drm_crtc *crtc) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 1998c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 2008c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 2018c2ecf20Sopenharmony_ci u16 *r, *g, *b; 2028c2ecf20Sopenharmony_ci int i; 2038c2ecf20Sopenharmony_ci uint32_t dac2_cntl; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci dac2_cntl = RREG32(RADEON_DAC_CNTL2); 2068c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id == 0) 2078c2ecf20Sopenharmony_ci dac2_cntl &= (uint32_t)~RADEON_DAC2_PALETTE_ACC_CTL; 2088c2ecf20Sopenharmony_ci else 2098c2ecf20Sopenharmony_ci dac2_cntl |= RADEON_DAC2_PALETTE_ACC_CTL; 2108c2ecf20Sopenharmony_ci WREG32(RADEON_DAC_CNTL2, dac2_cntl); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci WREG8(RADEON_PALETTE_INDEX, 0); 2138c2ecf20Sopenharmony_ci r = crtc->gamma_store; 2148c2ecf20Sopenharmony_ci g = r + crtc->gamma_size; 2158c2ecf20Sopenharmony_ci b = g + crtc->gamma_size; 2168c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 2178c2ecf20Sopenharmony_ci WREG32(RADEON_PALETTE_30_DATA, 2188c2ecf20Sopenharmony_ci ((*r++ & 0xffc0) << 14) | 2198c2ecf20Sopenharmony_ci ((*g++ & 0xffc0) << 4) | 2208c2ecf20Sopenharmony_ci (*b++ >> 6)); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_civoid radeon_crtc_load_lut(struct drm_crtc *crtc) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 2278c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (!crtc->enabled) 2308c2ecf20Sopenharmony_ci return; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (ASIC_IS_DCE5(rdev)) 2338c2ecf20Sopenharmony_ci dce5_crtc_load_lut(crtc); 2348c2ecf20Sopenharmony_ci else if (ASIC_IS_DCE4(rdev)) 2358c2ecf20Sopenharmony_ci dce4_crtc_load_lut(crtc); 2368c2ecf20Sopenharmony_ci else if (ASIC_IS_AVIVO(rdev)) 2378c2ecf20Sopenharmony_ci avivo_crtc_load_lut(crtc); 2388c2ecf20Sopenharmony_ci else 2398c2ecf20Sopenharmony_ci legacy_crtc_load_lut(crtc); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, 2438c2ecf20Sopenharmony_ci u16 *blue, uint32_t size, 2448c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci radeon_crtc_load_lut(crtc); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void radeon_crtc_destroy(struct drm_crtc *crtc) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci drm_crtc_cleanup(crtc); 2568c2ecf20Sopenharmony_ci destroy_workqueue(radeon_crtc->flip_queue); 2578c2ecf20Sopenharmony_ci kfree(radeon_crtc); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/** 2618c2ecf20Sopenharmony_ci * radeon_unpin_work_func - unpin old buffer object 2628c2ecf20Sopenharmony_ci * 2638c2ecf20Sopenharmony_ci * @__work - kernel work item 2648c2ecf20Sopenharmony_ci * 2658c2ecf20Sopenharmony_ci * Unpin the old frame buffer object outside of the interrupt handler 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_cistatic void radeon_unpin_work_func(struct work_struct *__work) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct radeon_flip_work *work = 2708c2ecf20Sopenharmony_ci container_of(__work, struct radeon_flip_work, unpin_work); 2718c2ecf20Sopenharmony_ci int r; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* unpin of the old buffer */ 2748c2ecf20Sopenharmony_ci r = radeon_bo_reserve(work->old_rbo, false); 2758c2ecf20Sopenharmony_ci if (likely(r == 0)) { 2768c2ecf20Sopenharmony_ci r = radeon_bo_unpin(work->old_rbo); 2778c2ecf20Sopenharmony_ci if (unlikely(r != 0)) { 2788c2ecf20Sopenharmony_ci DRM_ERROR("failed to unpin buffer after flip\n"); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci radeon_bo_unreserve(work->old_rbo); 2818c2ecf20Sopenharmony_ci } else 2828c2ecf20Sopenharmony_ci DRM_ERROR("failed to reserve buffer after flip\n"); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci drm_gem_object_put(&work->old_rbo->tbo.base); 2858c2ecf20Sopenharmony_ci kfree(work); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_civoid radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; 2918c2ecf20Sopenharmony_ci unsigned long flags; 2928c2ecf20Sopenharmony_ci u32 update_pending; 2938c2ecf20Sopenharmony_ci int vpos, hpos; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* can happen during initialization */ 2968c2ecf20Sopenharmony_ci if (radeon_crtc == NULL) 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* Skip the pageflip completion check below (based on polling) on 3008c2ecf20Sopenharmony_ci * asics which reliably support hw pageflip completion irqs. pflip 3018c2ecf20Sopenharmony_ci * irqs are a reliable and race-free method of handling pageflip 3028c2ecf20Sopenharmony_ci * completion detection. A use_pflipirq module parameter < 2 allows 3038c2ecf20Sopenharmony_ci * to override this in case of asics with faulty pflip irqs. 3048c2ecf20Sopenharmony_ci * A module parameter of 0 would only use this polling based path, 3058c2ecf20Sopenharmony_ci * a parameter of 1 would use pflip irq only as a backup to this 3068c2ecf20Sopenharmony_ci * path, as in Linux 3.16. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci if ((radeon_use_pflipirq == 2) && ASIC_IS_DCE4(rdev)) 3098c2ecf20Sopenharmony_ci return; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->ddev->event_lock, flags); 3128c2ecf20Sopenharmony_ci if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) { 3138c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != " 3148c2ecf20Sopenharmony_ci "RADEON_FLIP_SUBMITTED(%d)\n", 3158c2ecf20Sopenharmony_ci radeon_crtc->flip_status, 3168c2ecf20Sopenharmony_ci RADEON_FLIP_SUBMITTED); 3178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); 3188c2ecf20Sopenharmony_ci return; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci update_pending = radeon_page_flip_pending(rdev, crtc_id); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Has the pageflip already completed in crtc, or is it certain 3248c2ecf20Sopenharmony_ci * to complete in this vblank? GET_DISTANCE_TO_VBLANKSTART provides 3258c2ecf20Sopenharmony_ci * distance to start of "fudged earlier" vblank in vpos, distance to 3268c2ecf20Sopenharmony_ci * start of real vblank in hpos. vpos >= 0 && hpos < 0 means we are in 3278c2ecf20Sopenharmony_ci * the last few scanlines before start of real vblank, where the vblank 3288c2ecf20Sopenharmony_ci * irq can fire, so we have sampled update_pending a bit too early and 3298c2ecf20Sopenharmony_ci * know the flip will complete at leading edge of the upcoming real 3308c2ecf20Sopenharmony_ci * vblank. On pre-AVIVO hardware, flips also complete inside the real 3318c2ecf20Sopenharmony_ci * vblank, not only at leading edge, so if update_pending for hpos >= 0 3328c2ecf20Sopenharmony_ci * == inside real vblank, the flip will complete almost immediately. 3338c2ecf20Sopenharmony_ci * Note that this method of completion handling is still not 100% race 3348c2ecf20Sopenharmony_ci * free, as we could execute before the radeon_flip_work_func managed 3358c2ecf20Sopenharmony_ci * to run and set the RADEON_FLIP_SUBMITTED status, thereby we no-op, 3368c2ecf20Sopenharmony_ci * but the flip still gets programmed into hw and completed during 3378c2ecf20Sopenharmony_ci * vblank, leading to a delayed emission of the flip completion event. 3388c2ecf20Sopenharmony_ci * This applies at least to pre-AVIVO hardware, where flips are always 3398c2ecf20Sopenharmony_ci * completing inside vblank, not only at leading edge of vblank. 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci if (update_pending && 3428c2ecf20Sopenharmony_ci (DRM_SCANOUTPOS_VALID & 3438c2ecf20Sopenharmony_ci radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, 3448c2ecf20Sopenharmony_ci GET_DISTANCE_TO_VBLANKSTART, 3458c2ecf20Sopenharmony_ci &vpos, &hpos, NULL, NULL, 3468c2ecf20Sopenharmony_ci &rdev->mode_info.crtcs[crtc_id]->base.hwmode)) && 3478c2ecf20Sopenharmony_ci ((vpos >= 0 && hpos < 0) || (hpos >= 0 && !ASIC_IS_AVIVO(rdev)))) { 3488c2ecf20Sopenharmony_ci /* crtc didn't flip in this target vblank interval, 3498c2ecf20Sopenharmony_ci * but flip is pending in crtc. Based on the current 3508c2ecf20Sopenharmony_ci * scanout position we know that the current frame is 3518c2ecf20Sopenharmony_ci * (nearly) complete and the flip will (likely) 3528c2ecf20Sopenharmony_ci * complete before the start of the next frame. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci update_pending = 0; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); 3578c2ecf20Sopenharmony_ci if (!update_pending) 3588c2ecf20Sopenharmony_ci radeon_crtc_handle_flip(rdev, crtc_id); 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/** 3628c2ecf20Sopenharmony_ci * radeon_crtc_handle_flip - page flip completed 3638c2ecf20Sopenharmony_ci * 3648c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 3658c2ecf20Sopenharmony_ci * @crtc_id: crtc number this event is for 3668c2ecf20Sopenharmony_ci * 3678c2ecf20Sopenharmony_ci * Called when we are sure that a page flip for this crtc is completed. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_civoid radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; 3728c2ecf20Sopenharmony_ci struct radeon_flip_work *work; 3738c2ecf20Sopenharmony_ci unsigned long flags; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* this can happen at init */ 3768c2ecf20Sopenharmony_ci if (radeon_crtc == NULL) 3778c2ecf20Sopenharmony_ci return; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->ddev->event_lock, flags); 3808c2ecf20Sopenharmony_ci work = radeon_crtc->flip_work; 3818c2ecf20Sopenharmony_ci if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) { 3828c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != " 3838c2ecf20Sopenharmony_ci "RADEON_FLIP_SUBMITTED(%d)\n", 3848c2ecf20Sopenharmony_ci radeon_crtc->flip_status, 3858c2ecf20Sopenharmony_ci RADEON_FLIP_SUBMITTED); 3868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); 3878c2ecf20Sopenharmony_ci return; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* Pageflip completed. Clean up. */ 3918c2ecf20Sopenharmony_ci radeon_crtc->flip_status = RADEON_FLIP_NONE; 3928c2ecf20Sopenharmony_ci radeon_crtc->flip_work = NULL; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* wakeup userspace */ 3958c2ecf20Sopenharmony_ci if (work->event) 3968c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(&radeon_crtc->base, work->event); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci drm_crtc_vblank_put(&radeon_crtc->base); 4018c2ecf20Sopenharmony_ci radeon_irq_kms_pflip_irq_put(rdev, work->crtc_id); 4028c2ecf20Sopenharmony_ci queue_work(radeon_crtc->flip_queue, &work->unpin_work); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/** 4068c2ecf20Sopenharmony_ci * radeon_flip_work_func - page flip framebuffer 4078c2ecf20Sopenharmony_ci * 4088c2ecf20Sopenharmony_ci * @work - kernel work item 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * Wait for the buffer object to become idle and do the actual page flip 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_cistatic void radeon_flip_work_func(struct work_struct *__work) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct radeon_flip_work *work = 4158c2ecf20Sopenharmony_ci container_of(__work, struct radeon_flip_work, flip_work); 4168c2ecf20Sopenharmony_ci struct radeon_device *rdev = work->rdev; 4178c2ecf20Sopenharmony_ci struct drm_device *dev = rdev->ddev; 4188c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id]; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &radeon_crtc->base; 4218c2ecf20Sopenharmony_ci unsigned long flags; 4228c2ecf20Sopenharmony_ci int r; 4238c2ecf20Sopenharmony_ci int vpos, hpos; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci down_read(&rdev->exclusive_lock); 4268c2ecf20Sopenharmony_ci if (work->fence) { 4278c2ecf20Sopenharmony_ci struct radeon_fence *fence; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci fence = to_radeon_fence(work->fence); 4308c2ecf20Sopenharmony_ci if (fence && fence->rdev == rdev) { 4318c2ecf20Sopenharmony_ci r = radeon_fence_wait(fence, false); 4328c2ecf20Sopenharmony_ci if (r == -EDEADLK) { 4338c2ecf20Sopenharmony_ci up_read(&rdev->exclusive_lock); 4348c2ecf20Sopenharmony_ci do { 4358c2ecf20Sopenharmony_ci r = radeon_gpu_reset(rdev); 4368c2ecf20Sopenharmony_ci } while (r == -EAGAIN); 4378c2ecf20Sopenharmony_ci down_read(&rdev->exclusive_lock); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci } else 4408c2ecf20Sopenharmony_ci r = dma_fence_wait(work->fence, false); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (r) 4438c2ecf20Sopenharmony_ci DRM_ERROR("failed to wait on page flip fence (%d)!\n", r); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* We continue with the page flip even if we failed to wait on 4468c2ecf20Sopenharmony_ci * the fence, otherwise the DRM core and userspace will be 4478c2ecf20Sopenharmony_ci * confused about which BO the CRTC is scanning out 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci dma_fence_put(work->fence); 4518c2ecf20Sopenharmony_ci work->fence = NULL; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Wait until we're out of the vertical blank period before the one 4558c2ecf20Sopenharmony_ci * targeted by the flip. Always wait on pre DCE4 to avoid races with 4568c2ecf20Sopenharmony_ci * flip completion handling from vblank irq, as these old asics don't 4578c2ecf20Sopenharmony_ci * have reliable pageflip completion interrupts. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci while (radeon_crtc->enabled && 4608c2ecf20Sopenharmony_ci (radeon_get_crtc_scanoutpos(dev, work->crtc_id, 0, 4618c2ecf20Sopenharmony_ci &vpos, &hpos, NULL, NULL, 4628c2ecf20Sopenharmony_ci &crtc->hwmode) 4638c2ecf20Sopenharmony_ci & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) == 4648c2ecf20Sopenharmony_ci (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) && 4658c2ecf20Sopenharmony_ci (!ASIC_IS_AVIVO(rdev) || 4668c2ecf20Sopenharmony_ci ((int) (work->target_vblank - 4678c2ecf20Sopenharmony_ci crtc->funcs->get_vblank_counter(crtc)) > 0))) 4688c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* We borrow the event spin lock for protecting flip_status */ 4718c2ecf20Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* set the proper interrupt */ 4748c2ecf20Sopenharmony_ci radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* do the flip (mmio) */ 4778c2ecf20Sopenharmony_ci radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base, work->async); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci radeon_crtc->flip_status = RADEON_FLIP_SUBMITTED; 4808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 4818c2ecf20Sopenharmony_ci up_read(&rdev->exclusive_lock); 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int radeon_crtc_page_flip_target(struct drm_crtc *crtc, 4858c2ecf20Sopenharmony_ci struct drm_framebuffer *fb, 4868c2ecf20Sopenharmony_ci struct drm_pending_vblank_event *event, 4878c2ecf20Sopenharmony_ci uint32_t page_flip_flags, 4888c2ecf20Sopenharmony_ci uint32_t target, 4898c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 4928c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 4938c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 4948c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 4958c2ecf20Sopenharmony_ci struct radeon_flip_work *work; 4968c2ecf20Sopenharmony_ci struct radeon_bo *new_rbo; 4978c2ecf20Sopenharmony_ci uint32_t tiling_flags, pitch_pixels; 4988c2ecf20Sopenharmony_ci uint64_t base; 4998c2ecf20Sopenharmony_ci unsigned long flags; 5008c2ecf20Sopenharmony_ci int r; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci work = kzalloc(sizeof *work, GFP_KERNEL); 5038c2ecf20Sopenharmony_ci if (work == NULL) 5048c2ecf20Sopenharmony_ci return -ENOMEM; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci INIT_WORK(&work->flip_work, radeon_flip_work_func); 5078c2ecf20Sopenharmony_ci INIT_WORK(&work->unpin_work, radeon_unpin_work_func); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci work->rdev = rdev; 5108c2ecf20Sopenharmony_ci work->crtc_id = radeon_crtc->crtc_id; 5118c2ecf20Sopenharmony_ci work->event = event; 5128c2ecf20Sopenharmony_ci work->async = (page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC) != 0; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* schedule unpin of the old buffer */ 5158c2ecf20Sopenharmony_ci obj = crtc->primary->fb->obj[0]; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* take a reference to the old object */ 5188c2ecf20Sopenharmony_ci drm_gem_object_get(obj); 5198c2ecf20Sopenharmony_ci work->old_rbo = gem_to_radeon_bo(obj); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci obj = fb->obj[0]; 5228c2ecf20Sopenharmony_ci new_rbo = gem_to_radeon_bo(obj); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* pin the new buffer */ 5258c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("flip-ioctl() cur_rbo = %p, new_rbo = %p\n", 5268c2ecf20Sopenharmony_ci work->old_rbo, new_rbo); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci r = radeon_bo_reserve(new_rbo, false); 5298c2ecf20Sopenharmony_ci if (unlikely(r != 0)) { 5308c2ecf20Sopenharmony_ci DRM_ERROR("failed to reserve new rbo buffer before flip\n"); 5318c2ecf20Sopenharmony_ci goto cleanup; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci /* Only 27 bit offset for legacy CRTC */ 5348c2ecf20Sopenharmony_ci r = radeon_bo_pin_restricted(new_rbo, RADEON_GEM_DOMAIN_VRAM, 5358c2ecf20Sopenharmony_ci ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base); 5368c2ecf20Sopenharmony_ci if (unlikely(r != 0)) { 5378c2ecf20Sopenharmony_ci radeon_bo_unreserve(new_rbo); 5388c2ecf20Sopenharmony_ci r = -EINVAL; 5398c2ecf20Sopenharmony_ci DRM_ERROR("failed to pin new rbo buffer before flip\n"); 5408c2ecf20Sopenharmony_ci goto cleanup; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci work->fence = dma_fence_get(dma_resv_get_excl(new_rbo->tbo.base.resv)); 5438c2ecf20Sopenharmony_ci radeon_bo_get_tiling_flags(new_rbo, &tiling_flags, NULL); 5448c2ecf20Sopenharmony_ci radeon_bo_unreserve(new_rbo); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (!ASIC_IS_AVIVO(rdev)) { 5478c2ecf20Sopenharmony_ci /* crtc offset is from display base addr not FB location */ 5488c2ecf20Sopenharmony_ci base -= radeon_crtc->legacy_display_base_addr; 5498c2ecf20Sopenharmony_ci pitch_pixels = fb->pitches[0] / fb->format->cpp[0]; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (tiling_flags & RADEON_TILING_MACRO) { 5528c2ecf20Sopenharmony_ci if (ASIC_IS_R300(rdev)) { 5538c2ecf20Sopenharmony_ci base &= ~0x7ff; 5548c2ecf20Sopenharmony_ci } else { 5558c2ecf20Sopenharmony_ci int byteshift = fb->format->cpp[0] * 8 >> 4; 5568c2ecf20Sopenharmony_ci int tile_addr = (((crtc->y >> 3) * pitch_pixels + crtc->x) >> (8 - byteshift)) << 11; 5578c2ecf20Sopenharmony_ci base += tile_addr + ((crtc->x << byteshift) % 256) + ((crtc->y % 8) << 8); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci } else { 5608c2ecf20Sopenharmony_ci int offset = crtc->y * pitch_pixels + crtc->x; 5618c2ecf20Sopenharmony_ci switch (fb->format->cpp[0] * 8) { 5628c2ecf20Sopenharmony_ci case 8: 5638c2ecf20Sopenharmony_ci default: 5648c2ecf20Sopenharmony_ci offset *= 1; 5658c2ecf20Sopenharmony_ci break; 5668c2ecf20Sopenharmony_ci case 15: 5678c2ecf20Sopenharmony_ci case 16: 5688c2ecf20Sopenharmony_ci offset *= 2; 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci case 24: 5718c2ecf20Sopenharmony_ci offset *= 3; 5728c2ecf20Sopenharmony_ci break; 5738c2ecf20Sopenharmony_ci case 32: 5748c2ecf20Sopenharmony_ci offset *= 4; 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci base += offset; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci base &= ~7; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci work->base = base; 5828c2ecf20Sopenharmony_ci work->target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) + 5838c2ecf20Sopenharmony_ci crtc->funcs->get_vblank_counter(crtc); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* We borrow the event spin lock for protecting flip_work */ 5868c2ecf20Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (radeon_crtc->flip_status != RADEON_FLIP_NONE) { 5898c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); 5908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 5918c2ecf20Sopenharmony_ci r = -EBUSY; 5928c2ecf20Sopenharmony_ci goto pflip_cleanup; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci radeon_crtc->flip_status = RADEON_FLIP_PENDING; 5958c2ecf20Sopenharmony_ci radeon_crtc->flip_work = work; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* update crtc fb */ 5988c2ecf20Sopenharmony_ci crtc->primary->fb = fb; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci queue_work(radeon_crtc->flip_queue, &work->flip_work); 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cipflip_cleanup: 6068c2ecf20Sopenharmony_ci if (unlikely(radeon_bo_reserve(new_rbo, false) != 0)) { 6078c2ecf20Sopenharmony_ci DRM_ERROR("failed to reserve new rbo in error path\n"); 6088c2ecf20Sopenharmony_ci goto cleanup; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci if (unlikely(radeon_bo_unpin(new_rbo) != 0)) { 6118c2ecf20Sopenharmony_ci DRM_ERROR("failed to unpin new rbo in error path\n"); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci radeon_bo_unreserve(new_rbo); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cicleanup: 6168c2ecf20Sopenharmony_ci drm_gem_object_put(&work->old_rbo->tbo.base); 6178c2ecf20Sopenharmony_ci dma_fence_put(work->fence); 6188c2ecf20Sopenharmony_ci kfree(work); 6198c2ecf20Sopenharmony_ci return r; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int 6238c2ecf20Sopenharmony_ciradeon_crtc_set_config(struct drm_mode_set *set, 6248c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct drm_device *dev; 6278c2ecf20Sopenharmony_ci struct radeon_device *rdev; 6288c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 6298c2ecf20Sopenharmony_ci bool active = false; 6308c2ecf20Sopenharmony_ci int ret; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (!set || !set->crtc) 6338c2ecf20Sopenharmony_ci return -EINVAL; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci dev = set->crtc->dev; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev->dev); 6388c2ecf20Sopenharmony_ci if (ret < 0) { 6398c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 6408c2ecf20Sopenharmony_ci return ret; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci ret = drm_crtc_helper_set_config(set, ctx); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 6468c2ecf20Sopenharmony_ci if (crtc->enabled) 6478c2ecf20Sopenharmony_ci active = true; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci rdev = dev->dev_private; 6528c2ecf20Sopenharmony_ci /* if we have active crtcs and we don't have a power ref, 6538c2ecf20Sopenharmony_ci take the current one */ 6548c2ecf20Sopenharmony_ci if (active && !rdev->have_disp_power_ref) { 6558c2ecf20Sopenharmony_ci rdev->have_disp_power_ref = true; 6568c2ecf20Sopenharmony_ci return ret; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci /* if we have no active crtcs, then drop the power ref 6598c2ecf20Sopenharmony_ci we got before */ 6608c2ecf20Sopenharmony_ci if (!active && rdev->have_disp_power_ref) { 6618c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 6628c2ecf20Sopenharmony_ci rdev->have_disp_power_ref = false; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* drop the power reference we got coming in here */ 6668c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 6678c2ecf20Sopenharmony_ci return ret; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs radeon_crtc_funcs = { 6718c2ecf20Sopenharmony_ci .cursor_set2 = radeon_crtc_cursor_set2, 6728c2ecf20Sopenharmony_ci .cursor_move = radeon_crtc_cursor_move, 6738c2ecf20Sopenharmony_ci .gamma_set = radeon_crtc_gamma_set, 6748c2ecf20Sopenharmony_ci .set_config = radeon_crtc_set_config, 6758c2ecf20Sopenharmony_ci .destroy = radeon_crtc_destroy, 6768c2ecf20Sopenharmony_ci .page_flip_target = radeon_crtc_page_flip_target, 6778c2ecf20Sopenharmony_ci .get_vblank_counter = radeon_get_vblank_counter_kms, 6788c2ecf20Sopenharmony_ci .enable_vblank = radeon_enable_vblank_kms, 6798c2ecf20Sopenharmony_ci .disable_vblank = radeon_disable_vblank_kms, 6808c2ecf20Sopenharmony_ci .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, 6818c2ecf20Sopenharmony_ci}; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic void radeon_crtc_init(struct drm_device *dev, int index) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 6868c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci radeon_crtc = kzalloc(sizeof(struct radeon_crtc) + (RADEONFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); 6898c2ecf20Sopenharmony_ci if (radeon_crtc == NULL) 6908c2ecf20Sopenharmony_ci return; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci radeon_crtc->flip_queue = alloc_workqueue("radeon-crtc", WQ_HIGHPRI, 0); 6938c2ecf20Sopenharmony_ci if (!radeon_crtc->flip_queue) { 6948c2ecf20Sopenharmony_ci kfree(radeon_crtc); 6958c2ecf20Sopenharmony_ci return; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci drm_crtc_init(dev, &radeon_crtc->base, &radeon_crtc_funcs); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256); 7018c2ecf20Sopenharmony_ci radeon_crtc->crtc_id = index; 7028c2ecf20Sopenharmony_ci rdev->mode_info.crtcs[index] = radeon_crtc; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (rdev->family >= CHIP_BONAIRE) { 7058c2ecf20Sopenharmony_ci radeon_crtc->max_cursor_width = CIK_CURSOR_WIDTH; 7068c2ecf20Sopenharmony_ci radeon_crtc->max_cursor_height = CIK_CURSOR_HEIGHT; 7078c2ecf20Sopenharmony_ci } else { 7088c2ecf20Sopenharmony_ci radeon_crtc->max_cursor_width = CURSOR_WIDTH; 7098c2ecf20Sopenharmony_ci radeon_crtc->max_cursor_height = CURSOR_HEIGHT; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci dev->mode_config.cursor_width = radeon_crtc->max_cursor_width; 7128c2ecf20Sopenharmony_ci dev->mode_config.cursor_height = radeon_crtc->max_cursor_height; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci#if 0 7158c2ecf20Sopenharmony_ci radeon_crtc->mode_set.crtc = &radeon_crtc->base; 7168c2ecf20Sopenharmony_ci radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1); 7178c2ecf20Sopenharmony_ci radeon_crtc->mode_set.num_connectors = 0; 7188c2ecf20Sopenharmony_ci#endif 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (rdev->is_atom_bios && (ASIC_IS_AVIVO(rdev) || radeon_r4xx_atom)) 7218c2ecf20Sopenharmony_ci radeon_atombios_init_crtc(dev, radeon_crtc); 7228c2ecf20Sopenharmony_ci else 7238c2ecf20Sopenharmony_ci radeon_legacy_init_crtc(dev, radeon_crtc); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic const char *encoder_names[38] = { 7278c2ecf20Sopenharmony_ci "NONE", 7288c2ecf20Sopenharmony_ci "INTERNAL_LVDS", 7298c2ecf20Sopenharmony_ci "INTERNAL_TMDS1", 7308c2ecf20Sopenharmony_ci "INTERNAL_TMDS2", 7318c2ecf20Sopenharmony_ci "INTERNAL_DAC1", 7328c2ecf20Sopenharmony_ci "INTERNAL_DAC2", 7338c2ecf20Sopenharmony_ci "INTERNAL_SDVOA", 7348c2ecf20Sopenharmony_ci "INTERNAL_SDVOB", 7358c2ecf20Sopenharmony_ci "SI170B", 7368c2ecf20Sopenharmony_ci "CH7303", 7378c2ecf20Sopenharmony_ci "CH7301", 7388c2ecf20Sopenharmony_ci "INTERNAL_DVO1", 7398c2ecf20Sopenharmony_ci "EXTERNAL_SDVOA", 7408c2ecf20Sopenharmony_ci "EXTERNAL_SDVOB", 7418c2ecf20Sopenharmony_ci "TITFP513", 7428c2ecf20Sopenharmony_ci "INTERNAL_LVTM1", 7438c2ecf20Sopenharmony_ci "VT1623", 7448c2ecf20Sopenharmony_ci "HDMI_SI1930", 7458c2ecf20Sopenharmony_ci "HDMI_INTERNAL", 7468c2ecf20Sopenharmony_ci "INTERNAL_KLDSCP_TMDS1", 7478c2ecf20Sopenharmony_ci "INTERNAL_KLDSCP_DVO1", 7488c2ecf20Sopenharmony_ci "INTERNAL_KLDSCP_DAC1", 7498c2ecf20Sopenharmony_ci "INTERNAL_KLDSCP_DAC2", 7508c2ecf20Sopenharmony_ci "SI178", 7518c2ecf20Sopenharmony_ci "MVPU_FPGA", 7528c2ecf20Sopenharmony_ci "INTERNAL_DDI", 7538c2ecf20Sopenharmony_ci "VT1625", 7548c2ecf20Sopenharmony_ci "HDMI_SI1932", 7558c2ecf20Sopenharmony_ci "DP_AN9801", 7568c2ecf20Sopenharmony_ci "DP_DP501", 7578c2ecf20Sopenharmony_ci "INTERNAL_UNIPHY", 7588c2ecf20Sopenharmony_ci "INTERNAL_KLDSCP_LVTMA", 7598c2ecf20Sopenharmony_ci "INTERNAL_UNIPHY1", 7608c2ecf20Sopenharmony_ci "INTERNAL_UNIPHY2", 7618c2ecf20Sopenharmony_ci "NUTMEG", 7628c2ecf20Sopenharmony_ci "TRAVIS", 7638c2ecf20Sopenharmony_ci "INTERNAL_VCE", 7648c2ecf20Sopenharmony_ci "INTERNAL_UNIPHY3", 7658c2ecf20Sopenharmony_ci}; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic const char *hpd_names[6] = { 7688c2ecf20Sopenharmony_ci "HPD1", 7698c2ecf20Sopenharmony_ci "HPD2", 7708c2ecf20Sopenharmony_ci "HPD3", 7718c2ecf20Sopenharmony_ci "HPD4", 7728c2ecf20Sopenharmony_ci "HPD5", 7738c2ecf20Sopenharmony_ci "HPD6", 7748c2ecf20Sopenharmony_ci}; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic void radeon_print_display_setup(struct drm_device *dev) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct drm_connector *connector; 7798c2ecf20Sopenharmony_ci struct radeon_connector *radeon_connector; 7808c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 7818c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder; 7828c2ecf20Sopenharmony_ci uint32_t devices; 7838c2ecf20Sopenharmony_ci int i = 0; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci DRM_INFO("Radeon Display Connectors\n"); 7868c2ecf20Sopenharmony_ci list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 7878c2ecf20Sopenharmony_ci radeon_connector = to_radeon_connector(connector); 7888c2ecf20Sopenharmony_ci DRM_INFO("Connector %d:\n", i); 7898c2ecf20Sopenharmony_ci DRM_INFO(" %s\n", connector->name); 7908c2ecf20Sopenharmony_ci if (radeon_connector->hpd.hpd != RADEON_HPD_NONE) 7918c2ecf20Sopenharmony_ci DRM_INFO(" %s\n", hpd_names[radeon_connector->hpd.hpd]); 7928c2ecf20Sopenharmony_ci if (radeon_connector->ddc_bus) { 7938c2ecf20Sopenharmony_ci DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", 7948c2ecf20Sopenharmony_ci radeon_connector->ddc_bus->rec.mask_clk_reg, 7958c2ecf20Sopenharmony_ci radeon_connector->ddc_bus->rec.mask_data_reg, 7968c2ecf20Sopenharmony_ci radeon_connector->ddc_bus->rec.a_clk_reg, 7978c2ecf20Sopenharmony_ci radeon_connector->ddc_bus->rec.a_data_reg, 7988c2ecf20Sopenharmony_ci radeon_connector->ddc_bus->rec.en_clk_reg, 7998c2ecf20Sopenharmony_ci radeon_connector->ddc_bus->rec.en_data_reg, 8008c2ecf20Sopenharmony_ci radeon_connector->ddc_bus->rec.y_clk_reg, 8018c2ecf20Sopenharmony_ci radeon_connector->ddc_bus->rec.y_data_reg); 8028c2ecf20Sopenharmony_ci if (radeon_connector->router.ddc_valid) 8038c2ecf20Sopenharmony_ci DRM_INFO(" DDC Router 0x%x/0x%x\n", 8048c2ecf20Sopenharmony_ci radeon_connector->router.ddc_mux_control_pin, 8058c2ecf20Sopenharmony_ci radeon_connector->router.ddc_mux_state); 8068c2ecf20Sopenharmony_ci if (radeon_connector->router.cd_valid) 8078c2ecf20Sopenharmony_ci DRM_INFO(" Clock/Data Router 0x%x/0x%x\n", 8088c2ecf20Sopenharmony_ci radeon_connector->router.cd_mux_control_pin, 8098c2ecf20Sopenharmony_ci radeon_connector->router.cd_mux_state); 8108c2ecf20Sopenharmony_ci } else { 8118c2ecf20Sopenharmony_ci if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || 8128c2ecf20Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVII || 8138c2ecf20Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVID || 8148c2ecf20Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_DVIA || 8158c2ecf20Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || 8168c2ecf20Sopenharmony_ci connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) 8178c2ecf20Sopenharmony_ci DRM_INFO(" DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n"); 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci DRM_INFO(" Encoders:\n"); 8208c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 8218c2ecf20Sopenharmony_ci radeon_encoder = to_radeon_encoder(encoder); 8228c2ecf20Sopenharmony_ci devices = radeon_encoder->devices & radeon_connector->devices; 8238c2ecf20Sopenharmony_ci if (devices) { 8248c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_CRT1_SUPPORT) 8258c2ecf20Sopenharmony_ci DRM_INFO(" CRT1: %s\n", encoder_names[radeon_encoder->encoder_id]); 8268c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_CRT2_SUPPORT) 8278c2ecf20Sopenharmony_ci DRM_INFO(" CRT2: %s\n", encoder_names[radeon_encoder->encoder_id]); 8288c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_LCD1_SUPPORT) 8298c2ecf20Sopenharmony_ci DRM_INFO(" LCD1: %s\n", encoder_names[radeon_encoder->encoder_id]); 8308c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_DFP1_SUPPORT) 8318c2ecf20Sopenharmony_ci DRM_INFO(" DFP1: %s\n", encoder_names[radeon_encoder->encoder_id]); 8328c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_DFP2_SUPPORT) 8338c2ecf20Sopenharmony_ci DRM_INFO(" DFP2: %s\n", encoder_names[radeon_encoder->encoder_id]); 8348c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_DFP3_SUPPORT) 8358c2ecf20Sopenharmony_ci DRM_INFO(" DFP3: %s\n", encoder_names[radeon_encoder->encoder_id]); 8368c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_DFP4_SUPPORT) 8378c2ecf20Sopenharmony_ci DRM_INFO(" DFP4: %s\n", encoder_names[radeon_encoder->encoder_id]); 8388c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_DFP5_SUPPORT) 8398c2ecf20Sopenharmony_ci DRM_INFO(" DFP5: %s\n", encoder_names[radeon_encoder->encoder_id]); 8408c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_DFP6_SUPPORT) 8418c2ecf20Sopenharmony_ci DRM_INFO(" DFP6: %s\n", encoder_names[radeon_encoder->encoder_id]); 8428c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_TV1_SUPPORT) 8438c2ecf20Sopenharmony_ci DRM_INFO(" TV1: %s\n", encoder_names[radeon_encoder->encoder_id]); 8448c2ecf20Sopenharmony_ci if (devices & ATOM_DEVICE_CV_SUPPORT) 8458c2ecf20Sopenharmony_ci DRM_INFO(" CV: %s\n", encoder_names[radeon_encoder->encoder_id]); 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci i++; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic bool radeon_setup_enc_conn(struct drm_device *dev) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 8558c2ecf20Sopenharmony_ci bool ret = false; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (rdev->bios) { 8588c2ecf20Sopenharmony_ci if (rdev->is_atom_bios) { 8598c2ecf20Sopenharmony_ci ret = radeon_get_atom_connector_info_from_supported_devices_table(dev); 8608c2ecf20Sopenharmony_ci if (!ret) 8618c2ecf20Sopenharmony_ci ret = radeon_get_atom_connector_info_from_object_table(dev); 8628c2ecf20Sopenharmony_ci } else { 8638c2ecf20Sopenharmony_ci ret = radeon_get_legacy_connector_info_from_bios(dev); 8648c2ecf20Sopenharmony_ci if (!ret) 8658c2ecf20Sopenharmony_ci ret = radeon_get_legacy_connector_info_from_table(dev); 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci } else { 8688c2ecf20Sopenharmony_ci if (!ASIC_IS_AVIVO(rdev)) 8698c2ecf20Sopenharmony_ci ret = radeon_get_legacy_connector_info_from_table(dev); 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci if (ret) { 8728c2ecf20Sopenharmony_ci radeon_setup_encoder_clones(dev); 8738c2ecf20Sopenharmony_ci radeon_print_display_setup(dev); 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci return ret; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci/* avivo */ 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci/** 8828c2ecf20Sopenharmony_ci * avivo_reduce_ratio - fractional number reduction 8838c2ecf20Sopenharmony_ci * 8848c2ecf20Sopenharmony_ci * @nom: nominator 8858c2ecf20Sopenharmony_ci * @den: denominator 8868c2ecf20Sopenharmony_ci * @nom_min: minimum value for nominator 8878c2ecf20Sopenharmony_ci * @den_min: minimum value for denominator 8888c2ecf20Sopenharmony_ci * 8898c2ecf20Sopenharmony_ci * Find the greatest common divisor and apply it on both nominator and 8908c2ecf20Sopenharmony_ci * denominator, but make nominator and denominator are at least as large 8918c2ecf20Sopenharmony_ci * as their minimum values. 8928c2ecf20Sopenharmony_ci */ 8938c2ecf20Sopenharmony_cistatic void avivo_reduce_ratio(unsigned *nom, unsigned *den, 8948c2ecf20Sopenharmony_ci unsigned nom_min, unsigned den_min) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci unsigned tmp; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* reduce the numbers to a simpler ratio */ 8998c2ecf20Sopenharmony_ci tmp = gcd(*nom, *den); 9008c2ecf20Sopenharmony_ci *nom /= tmp; 9018c2ecf20Sopenharmony_ci *den /= tmp; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* make sure nominator is large enough */ 9048c2ecf20Sopenharmony_ci if (*nom < nom_min) { 9058c2ecf20Sopenharmony_ci tmp = DIV_ROUND_UP(nom_min, *nom); 9068c2ecf20Sopenharmony_ci *nom *= tmp; 9078c2ecf20Sopenharmony_ci *den *= tmp; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* make sure the denominator is large enough */ 9118c2ecf20Sopenharmony_ci if (*den < den_min) { 9128c2ecf20Sopenharmony_ci tmp = DIV_ROUND_UP(den_min, *den); 9138c2ecf20Sopenharmony_ci *nom *= tmp; 9148c2ecf20Sopenharmony_ci *den *= tmp; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci/** 9198c2ecf20Sopenharmony_ci * avivo_get_fb_ref_div - feedback and ref divider calculation 9208c2ecf20Sopenharmony_ci * 9218c2ecf20Sopenharmony_ci * @nom: nominator 9228c2ecf20Sopenharmony_ci * @den: denominator 9238c2ecf20Sopenharmony_ci * @post_div: post divider 9248c2ecf20Sopenharmony_ci * @fb_div_max: feedback divider maximum 9258c2ecf20Sopenharmony_ci * @ref_div_max: reference divider maximum 9268c2ecf20Sopenharmony_ci * @fb_div: resulting feedback divider 9278c2ecf20Sopenharmony_ci * @ref_div: resulting reference divider 9288c2ecf20Sopenharmony_ci * 9298c2ecf20Sopenharmony_ci * Calculate feedback and reference divider for a given post divider. Makes 9308c2ecf20Sopenharmony_ci * sure we stay within the limits. 9318c2ecf20Sopenharmony_ci */ 9328c2ecf20Sopenharmony_cistatic void avivo_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div, 9338c2ecf20Sopenharmony_ci unsigned fb_div_max, unsigned ref_div_max, 9348c2ecf20Sopenharmony_ci unsigned *fb_div, unsigned *ref_div) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci /* limit reference * post divider to a maximum */ 9378c2ecf20Sopenharmony_ci ref_div_max = max(min(100 / post_div, ref_div_max), 1u); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* get matching reference and feedback divider */ 9408c2ecf20Sopenharmony_ci *ref_div = min(max(den/post_div, 1u), ref_div_max); 9418c2ecf20Sopenharmony_ci *fb_div = DIV_ROUND_CLOSEST(nom * *ref_div * post_div, den); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* limit fb divider to its maximum */ 9448c2ecf20Sopenharmony_ci if (*fb_div > fb_div_max) { 9458c2ecf20Sopenharmony_ci *ref_div = (*ref_div * fb_div_max)/(*fb_div); 9468c2ecf20Sopenharmony_ci *fb_div = fb_div_max; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci/** 9518c2ecf20Sopenharmony_ci * radeon_compute_pll_avivo - compute PLL paramaters 9528c2ecf20Sopenharmony_ci * 9538c2ecf20Sopenharmony_ci * @pll: information about the PLL 9548c2ecf20Sopenharmony_ci * @dot_clock_p: resulting pixel clock 9558c2ecf20Sopenharmony_ci * fb_div_p: resulting feedback divider 9568c2ecf20Sopenharmony_ci * frac_fb_div_p: fractional part of the feedback divider 9578c2ecf20Sopenharmony_ci * ref_div_p: resulting reference divider 9588c2ecf20Sopenharmony_ci * post_div_p: resulting reference divider 9598c2ecf20Sopenharmony_ci * 9608c2ecf20Sopenharmony_ci * Try to calculate the PLL parameters to generate the given frequency: 9618c2ecf20Sopenharmony_ci * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div) 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_civoid radeon_compute_pll_avivo(struct radeon_pll *pll, 9648c2ecf20Sopenharmony_ci u32 freq, 9658c2ecf20Sopenharmony_ci u32 *dot_clock_p, 9668c2ecf20Sopenharmony_ci u32 *fb_div_p, 9678c2ecf20Sopenharmony_ci u32 *frac_fb_div_p, 9688c2ecf20Sopenharmony_ci u32 *ref_div_p, 9698c2ecf20Sopenharmony_ci u32 *post_div_p) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci unsigned target_clock = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ? 9728c2ecf20Sopenharmony_ci freq : freq / 10; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci unsigned fb_div_min, fb_div_max, fb_div; 9758c2ecf20Sopenharmony_ci unsigned post_div_min, post_div_max, post_div; 9768c2ecf20Sopenharmony_ci unsigned ref_div_min, ref_div_max, ref_div; 9778c2ecf20Sopenharmony_ci unsigned post_div_best, diff_best; 9788c2ecf20Sopenharmony_ci unsigned nom, den; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* determine allowed feedback divider range */ 9818c2ecf20Sopenharmony_ci fb_div_min = pll->min_feedback_div; 9828c2ecf20Sopenharmony_ci fb_div_max = pll->max_feedback_div; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 9858c2ecf20Sopenharmony_ci fb_div_min *= 10; 9868c2ecf20Sopenharmony_ci fb_div_max *= 10; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci /* determine allowed ref divider range */ 9908c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_REF_DIV) 9918c2ecf20Sopenharmony_ci ref_div_min = pll->reference_div; 9928c2ecf20Sopenharmony_ci else 9938c2ecf20Sopenharmony_ci ref_div_min = pll->min_ref_div; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && 9968c2ecf20Sopenharmony_ci pll->flags & RADEON_PLL_USE_REF_DIV) 9978c2ecf20Sopenharmony_ci ref_div_max = pll->reference_div; 9988c2ecf20Sopenharmony_ci else if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) 9998c2ecf20Sopenharmony_ci /* fix for problems on RS880 */ 10008c2ecf20Sopenharmony_ci ref_div_max = min(pll->max_ref_div, 7u); 10018c2ecf20Sopenharmony_ci else 10028c2ecf20Sopenharmony_ci ref_div_max = pll->max_ref_div; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* determine allowed post divider range */ 10058c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_POST_DIV) { 10068c2ecf20Sopenharmony_ci post_div_min = pll->post_div; 10078c2ecf20Sopenharmony_ci post_div_max = pll->post_div; 10088c2ecf20Sopenharmony_ci } else { 10098c2ecf20Sopenharmony_ci unsigned vco_min, vco_max; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_IS_LCD) { 10128c2ecf20Sopenharmony_ci vco_min = pll->lcd_pll_out_min; 10138c2ecf20Sopenharmony_ci vco_max = pll->lcd_pll_out_max; 10148c2ecf20Sopenharmony_ci } else { 10158c2ecf20Sopenharmony_ci vco_min = pll->pll_out_min; 10168c2ecf20Sopenharmony_ci vco_max = pll->pll_out_max; 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 10208c2ecf20Sopenharmony_ci vco_min *= 10; 10218c2ecf20Sopenharmony_ci vco_max *= 10; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci post_div_min = vco_min / target_clock; 10258c2ecf20Sopenharmony_ci if ((target_clock * post_div_min) < vco_min) 10268c2ecf20Sopenharmony_ci ++post_div_min; 10278c2ecf20Sopenharmony_ci if (post_div_min < pll->min_post_div) 10288c2ecf20Sopenharmony_ci post_div_min = pll->min_post_div; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci post_div_max = vco_max / target_clock; 10318c2ecf20Sopenharmony_ci if ((target_clock * post_div_max) > vco_max) 10328c2ecf20Sopenharmony_ci --post_div_max; 10338c2ecf20Sopenharmony_ci if (post_div_max > pll->max_post_div) 10348c2ecf20Sopenharmony_ci post_div_max = pll->max_post_div; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* represent the searched ratio as fractional number */ 10388c2ecf20Sopenharmony_ci nom = target_clock; 10398c2ecf20Sopenharmony_ci den = pll->reference_freq; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci /* reduce the numbers to a simpler ratio */ 10428c2ecf20Sopenharmony_ci avivo_reduce_ratio(&nom, &den, fb_div_min, post_div_min); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci /* now search for a post divider */ 10458c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) 10468c2ecf20Sopenharmony_ci post_div_best = post_div_min; 10478c2ecf20Sopenharmony_ci else 10488c2ecf20Sopenharmony_ci post_div_best = post_div_max; 10498c2ecf20Sopenharmony_ci diff_best = ~0; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci for (post_div = post_div_min; post_div <= post_div_max; ++post_div) { 10528c2ecf20Sopenharmony_ci unsigned diff; 10538c2ecf20Sopenharmony_ci avivo_get_fb_ref_div(nom, den, post_div, fb_div_max, 10548c2ecf20Sopenharmony_ci ref_div_max, &fb_div, &ref_div); 10558c2ecf20Sopenharmony_ci diff = abs(target_clock - (pll->reference_freq * fb_div) / 10568c2ecf20Sopenharmony_ci (ref_div * post_div)); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (diff < diff_best || (diff == diff_best && 10598c2ecf20Sopenharmony_ci !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) { 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci post_div_best = post_div; 10628c2ecf20Sopenharmony_ci diff_best = diff; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci post_div = post_div_best; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci /* get the feedback and reference divider for the optimal value */ 10688c2ecf20Sopenharmony_ci avivo_get_fb_ref_div(nom, den, post_div, fb_div_max, ref_div_max, 10698c2ecf20Sopenharmony_ci &fb_div, &ref_div); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* reduce the numbers to a simpler ratio once more */ 10728c2ecf20Sopenharmony_ci /* this also makes sure that the reference divider is large enough */ 10738c2ecf20Sopenharmony_ci avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* avoid high jitter with small fractional dividers */ 10768c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) { 10778c2ecf20Sopenharmony_ci fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 50); 10788c2ecf20Sopenharmony_ci if (fb_div < fb_div_min) { 10798c2ecf20Sopenharmony_ci unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div); 10808c2ecf20Sopenharmony_ci fb_div *= tmp; 10818c2ecf20Sopenharmony_ci ref_div *= tmp; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* and finally save the result */ 10868c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 10878c2ecf20Sopenharmony_ci *fb_div_p = fb_div / 10; 10888c2ecf20Sopenharmony_ci *frac_fb_div_p = fb_div % 10; 10898c2ecf20Sopenharmony_ci } else { 10908c2ecf20Sopenharmony_ci *fb_div_p = fb_div; 10918c2ecf20Sopenharmony_ci *frac_fb_div_p = 0; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) + 10958c2ecf20Sopenharmony_ci (pll->reference_freq * *frac_fb_div_p)) / 10968c2ecf20Sopenharmony_ci (ref_div * post_div * 10); 10978c2ecf20Sopenharmony_ci *ref_div_p = ref_div; 10988c2ecf20Sopenharmony_ci *post_div_p = post_div; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n", 11018c2ecf20Sopenharmony_ci freq, *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, 11028c2ecf20Sopenharmony_ci ref_div, post_div); 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci/* pre-avivo */ 11068c2ecf20Sopenharmony_cistatic inline uint32_t radeon_div(uint64_t n, uint32_t d) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci uint64_t mod; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci n += d / 2; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci mod = do_div(n, d); 11138c2ecf20Sopenharmony_ci return n; 11148c2ecf20Sopenharmony_ci} 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_civoid radeon_compute_pll_legacy(struct radeon_pll *pll, 11178c2ecf20Sopenharmony_ci uint64_t freq, 11188c2ecf20Sopenharmony_ci uint32_t *dot_clock_p, 11198c2ecf20Sopenharmony_ci uint32_t *fb_div_p, 11208c2ecf20Sopenharmony_ci uint32_t *frac_fb_div_p, 11218c2ecf20Sopenharmony_ci uint32_t *ref_div_p, 11228c2ecf20Sopenharmony_ci uint32_t *post_div_p) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci uint32_t min_ref_div = pll->min_ref_div; 11258c2ecf20Sopenharmony_ci uint32_t max_ref_div = pll->max_ref_div; 11268c2ecf20Sopenharmony_ci uint32_t min_post_div = pll->min_post_div; 11278c2ecf20Sopenharmony_ci uint32_t max_post_div = pll->max_post_div; 11288c2ecf20Sopenharmony_ci uint32_t min_fractional_feed_div = 0; 11298c2ecf20Sopenharmony_ci uint32_t max_fractional_feed_div = 0; 11308c2ecf20Sopenharmony_ci uint32_t best_vco = pll->best_vco; 11318c2ecf20Sopenharmony_ci uint32_t best_post_div = 1; 11328c2ecf20Sopenharmony_ci uint32_t best_ref_div = 1; 11338c2ecf20Sopenharmony_ci uint32_t best_feedback_div = 1; 11348c2ecf20Sopenharmony_ci uint32_t best_frac_feedback_div = 0; 11358c2ecf20Sopenharmony_ci uint32_t best_freq = -1; 11368c2ecf20Sopenharmony_ci uint32_t best_error = 0xffffffff; 11378c2ecf20Sopenharmony_ci uint32_t best_vco_diff = 1; 11388c2ecf20Sopenharmony_ci uint32_t post_div; 11398c2ecf20Sopenharmony_ci u32 pll_out_min, pll_out_max; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("PLL freq %llu %u %u\n", freq, pll->min_ref_div, pll->max_ref_div); 11428c2ecf20Sopenharmony_ci freq = freq * 1000; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_IS_LCD) { 11458c2ecf20Sopenharmony_ci pll_out_min = pll->lcd_pll_out_min; 11468c2ecf20Sopenharmony_ci pll_out_max = pll->lcd_pll_out_max; 11478c2ecf20Sopenharmony_ci } else { 11488c2ecf20Sopenharmony_ci pll_out_min = pll->pll_out_min; 11498c2ecf20Sopenharmony_ci pll_out_max = pll->pll_out_max; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (pll_out_min > 64800) 11538c2ecf20Sopenharmony_ci pll_out_min = 64800; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_REF_DIV) 11568c2ecf20Sopenharmony_ci min_ref_div = max_ref_div = pll->reference_div; 11578c2ecf20Sopenharmony_ci else { 11588c2ecf20Sopenharmony_ci while (min_ref_div < max_ref_div-1) { 11598c2ecf20Sopenharmony_ci uint32_t mid = (min_ref_div + max_ref_div) / 2; 11608c2ecf20Sopenharmony_ci uint32_t pll_in = pll->reference_freq / mid; 11618c2ecf20Sopenharmony_ci if (pll_in < pll->pll_in_min) 11628c2ecf20Sopenharmony_ci max_ref_div = mid; 11638c2ecf20Sopenharmony_ci else if (pll_in > pll->pll_in_max) 11648c2ecf20Sopenharmony_ci min_ref_div = mid; 11658c2ecf20Sopenharmony_ci else 11668c2ecf20Sopenharmony_ci break; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_POST_DIV) 11718c2ecf20Sopenharmony_ci min_post_div = max_post_div = pll->post_div; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 11748c2ecf20Sopenharmony_ci min_fractional_feed_div = pll->min_frac_feedback_div; 11758c2ecf20Sopenharmony_ci max_fractional_feed_div = pll->max_frac_feedback_div; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci for (post_div = max_post_div; post_div >= min_post_div; --post_div) { 11798c2ecf20Sopenharmony_ci uint32_t ref_div; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) 11828c2ecf20Sopenharmony_ci continue; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci /* legacy radeons only have a few post_divs */ 11858c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_LEGACY) { 11868c2ecf20Sopenharmony_ci if ((post_div == 5) || 11878c2ecf20Sopenharmony_ci (post_div == 7) || 11888c2ecf20Sopenharmony_ci (post_div == 9) || 11898c2ecf20Sopenharmony_ci (post_div == 10) || 11908c2ecf20Sopenharmony_ci (post_div == 11) || 11918c2ecf20Sopenharmony_ci (post_div == 13) || 11928c2ecf20Sopenharmony_ci (post_div == 14) || 11938c2ecf20Sopenharmony_ci (post_div == 15)) 11948c2ecf20Sopenharmony_ci continue; 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci for (ref_div = min_ref_div; ref_div <= max_ref_div; ++ref_div) { 11988c2ecf20Sopenharmony_ci uint32_t feedback_div, current_freq = 0, error, vco_diff; 11998c2ecf20Sopenharmony_ci uint32_t pll_in = pll->reference_freq / ref_div; 12008c2ecf20Sopenharmony_ci uint32_t min_feed_div = pll->min_feedback_div; 12018c2ecf20Sopenharmony_ci uint32_t max_feed_div = pll->max_feedback_div + 1; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max) 12048c2ecf20Sopenharmony_ci continue; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci while (min_feed_div < max_feed_div) { 12078c2ecf20Sopenharmony_ci uint32_t vco; 12088c2ecf20Sopenharmony_ci uint32_t min_frac_feed_div = min_fractional_feed_div; 12098c2ecf20Sopenharmony_ci uint32_t max_frac_feed_div = max_fractional_feed_div + 1; 12108c2ecf20Sopenharmony_ci uint32_t frac_feedback_div; 12118c2ecf20Sopenharmony_ci uint64_t tmp; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci feedback_div = (min_feed_div + max_feed_div) / 2; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci tmp = (uint64_t)pll->reference_freq * feedback_div; 12168c2ecf20Sopenharmony_ci vco = radeon_div(tmp, ref_div); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (vco < pll_out_min) { 12198c2ecf20Sopenharmony_ci min_feed_div = feedback_div + 1; 12208c2ecf20Sopenharmony_ci continue; 12218c2ecf20Sopenharmony_ci } else if (vco > pll_out_max) { 12228c2ecf20Sopenharmony_ci max_feed_div = feedback_div; 12238c2ecf20Sopenharmony_ci continue; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci while (min_frac_feed_div < max_frac_feed_div) { 12278c2ecf20Sopenharmony_ci frac_feedback_div = (min_frac_feed_div + max_frac_feed_div) / 2; 12288c2ecf20Sopenharmony_ci tmp = (uint64_t)pll->reference_freq * 10000 * feedback_div; 12298c2ecf20Sopenharmony_ci tmp += (uint64_t)pll->reference_freq * 1000 * frac_feedback_div; 12308c2ecf20Sopenharmony_ci current_freq = radeon_div(tmp, ref_div * post_div); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci if (pll->flags & RADEON_PLL_PREFER_CLOSEST_LOWER) { 12338c2ecf20Sopenharmony_ci if (freq < current_freq) 12348c2ecf20Sopenharmony_ci error = 0xffffffff; 12358c2ecf20Sopenharmony_ci else 12368c2ecf20Sopenharmony_ci error = freq - current_freq; 12378c2ecf20Sopenharmony_ci } else 12388c2ecf20Sopenharmony_ci error = abs(current_freq - freq); 12398c2ecf20Sopenharmony_ci vco_diff = abs(vco - best_vco); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if ((best_vco == 0 && error < best_error) || 12428c2ecf20Sopenharmony_ci (best_vco != 0 && 12438c2ecf20Sopenharmony_ci ((best_error > 100 && error < best_error - 100) || 12448c2ecf20Sopenharmony_ci (abs(error - best_error) < 100 && vco_diff < best_vco_diff)))) { 12458c2ecf20Sopenharmony_ci best_post_div = post_div; 12468c2ecf20Sopenharmony_ci best_ref_div = ref_div; 12478c2ecf20Sopenharmony_ci best_feedback_div = feedback_div; 12488c2ecf20Sopenharmony_ci best_frac_feedback_div = frac_feedback_div; 12498c2ecf20Sopenharmony_ci best_freq = current_freq; 12508c2ecf20Sopenharmony_ci best_error = error; 12518c2ecf20Sopenharmony_ci best_vco_diff = vco_diff; 12528c2ecf20Sopenharmony_ci } else if (current_freq == freq) { 12538c2ecf20Sopenharmony_ci if (best_freq == -1) { 12548c2ecf20Sopenharmony_ci best_post_div = post_div; 12558c2ecf20Sopenharmony_ci best_ref_div = ref_div; 12568c2ecf20Sopenharmony_ci best_feedback_div = feedback_div; 12578c2ecf20Sopenharmony_ci best_frac_feedback_div = frac_feedback_div; 12588c2ecf20Sopenharmony_ci best_freq = current_freq; 12598c2ecf20Sopenharmony_ci best_error = error; 12608c2ecf20Sopenharmony_ci best_vco_diff = vco_diff; 12618c2ecf20Sopenharmony_ci } else if (((pll->flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) || 12628c2ecf20Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) || 12638c2ecf20Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) || 12648c2ecf20Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) || 12658c2ecf20Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) || 12668c2ecf20Sopenharmony_ci ((pll->flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) { 12678c2ecf20Sopenharmony_ci best_post_div = post_div; 12688c2ecf20Sopenharmony_ci best_ref_div = ref_div; 12698c2ecf20Sopenharmony_ci best_feedback_div = feedback_div; 12708c2ecf20Sopenharmony_ci best_frac_feedback_div = frac_feedback_div; 12718c2ecf20Sopenharmony_ci best_freq = current_freq; 12728c2ecf20Sopenharmony_ci best_error = error; 12738c2ecf20Sopenharmony_ci best_vco_diff = vco_diff; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci if (current_freq < freq) 12778c2ecf20Sopenharmony_ci min_frac_feed_div = frac_feedback_div + 1; 12788c2ecf20Sopenharmony_ci else 12798c2ecf20Sopenharmony_ci max_frac_feed_div = frac_feedback_div; 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci if (current_freq < freq) 12828c2ecf20Sopenharmony_ci min_feed_div = feedback_div + 1; 12838c2ecf20Sopenharmony_ci else 12848c2ecf20Sopenharmony_ci max_feed_div = feedback_div; 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci *dot_clock_p = best_freq / 10000; 12908c2ecf20Sopenharmony_ci *fb_div_p = best_feedback_div; 12918c2ecf20Sopenharmony_ci *frac_fb_div_p = best_frac_feedback_div; 12928c2ecf20Sopenharmony_ci *ref_div_p = best_ref_div; 12938c2ecf20Sopenharmony_ci *post_div_p = best_post_div; 12948c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%lld %d, pll dividers - fb: %d.%d ref: %d, post %d\n", 12958c2ecf20Sopenharmony_ci (long long)freq, 12968c2ecf20Sopenharmony_ci best_freq / 1000, best_feedback_div, best_frac_feedback_div, 12978c2ecf20Sopenharmony_ci best_ref_div, best_post_div); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci} 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_cistatic const struct drm_framebuffer_funcs radeon_fb_funcs = { 13028c2ecf20Sopenharmony_ci .destroy = drm_gem_fb_destroy, 13038c2ecf20Sopenharmony_ci .create_handle = drm_gem_fb_create_handle, 13048c2ecf20Sopenharmony_ci}; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ciint 13078c2ecf20Sopenharmony_ciradeon_framebuffer_init(struct drm_device *dev, 13088c2ecf20Sopenharmony_ci struct drm_framebuffer *fb, 13098c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd, 13108c2ecf20Sopenharmony_ci struct drm_gem_object *obj) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci int ret; 13138c2ecf20Sopenharmony_ci fb->obj[0] = obj; 13148c2ecf20Sopenharmony_ci drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); 13158c2ecf20Sopenharmony_ci ret = drm_framebuffer_init(dev, fb, &radeon_fb_funcs); 13168c2ecf20Sopenharmony_ci if (ret) { 13178c2ecf20Sopenharmony_ci fb->obj[0] = NULL; 13188c2ecf20Sopenharmony_ci return ret; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci return 0; 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_cistatic struct drm_framebuffer * 13248c2ecf20Sopenharmony_ciradeon_user_framebuffer_create(struct drm_device *dev, 13258c2ecf20Sopenharmony_ci struct drm_file *file_priv, 13268c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 13278c2ecf20Sopenharmony_ci{ 13288c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 13298c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 13308c2ecf20Sopenharmony_ci int ret; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]); 13338c2ecf20Sopenharmony_ci if (obj == NULL) { 13348c2ecf20Sopenharmony_ci dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, " 13358c2ecf20Sopenharmony_ci "can't create framebuffer\n", mode_cmd->handles[0]); 13368c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci /* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */ 13408c2ecf20Sopenharmony_ci if (obj->import_attach) { 13418c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Cannot create framebuffer from imported dma_buf\n"); 13428c2ecf20Sopenharmony_ci drm_gem_object_put(obj); 13438c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci fb = kzalloc(sizeof(*fb), GFP_KERNEL); 13478c2ecf20Sopenharmony_ci if (fb == NULL) { 13488c2ecf20Sopenharmony_ci drm_gem_object_put(obj); 13498c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci ret = radeon_framebuffer_init(dev, fb, mode_cmd, obj); 13538c2ecf20Sopenharmony_ci if (ret) { 13548c2ecf20Sopenharmony_ci kfree(fb); 13558c2ecf20Sopenharmony_ci drm_gem_object_put(obj); 13568c2ecf20Sopenharmony_ci return ERR_PTR(ret); 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci return fb; 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs radeon_mode_funcs = { 13638c2ecf20Sopenharmony_ci .fb_create = radeon_user_framebuffer_create, 13648c2ecf20Sopenharmony_ci .output_poll_changed = drm_fb_helper_output_poll_changed, 13658c2ecf20Sopenharmony_ci}; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_cistatic const struct drm_prop_enum_list radeon_tmds_pll_enum_list[] = 13688c2ecf20Sopenharmony_ci{ { 0, "driver" }, 13698c2ecf20Sopenharmony_ci { 1, "bios" }, 13708c2ecf20Sopenharmony_ci}; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cistatic const struct drm_prop_enum_list radeon_tv_std_enum_list[] = 13738c2ecf20Sopenharmony_ci{ { TV_STD_NTSC, "ntsc" }, 13748c2ecf20Sopenharmony_ci { TV_STD_PAL, "pal" }, 13758c2ecf20Sopenharmony_ci { TV_STD_PAL_M, "pal-m" }, 13768c2ecf20Sopenharmony_ci { TV_STD_PAL_60, "pal-60" }, 13778c2ecf20Sopenharmony_ci { TV_STD_NTSC_J, "ntsc-j" }, 13788c2ecf20Sopenharmony_ci { TV_STD_SCART_PAL, "scart-pal" }, 13798c2ecf20Sopenharmony_ci { TV_STD_PAL_CN, "pal-cn" }, 13808c2ecf20Sopenharmony_ci { TV_STD_SECAM, "secam" }, 13818c2ecf20Sopenharmony_ci}; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic const struct drm_prop_enum_list radeon_underscan_enum_list[] = 13848c2ecf20Sopenharmony_ci{ { UNDERSCAN_OFF, "off" }, 13858c2ecf20Sopenharmony_ci { UNDERSCAN_ON, "on" }, 13868c2ecf20Sopenharmony_ci { UNDERSCAN_AUTO, "auto" }, 13878c2ecf20Sopenharmony_ci}; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_cistatic const struct drm_prop_enum_list radeon_audio_enum_list[] = 13908c2ecf20Sopenharmony_ci{ { RADEON_AUDIO_DISABLE, "off" }, 13918c2ecf20Sopenharmony_ci { RADEON_AUDIO_ENABLE, "on" }, 13928c2ecf20Sopenharmony_ci { RADEON_AUDIO_AUTO, "auto" }, 13938c2ecf20Sopenharmony_ci}; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci/* XXX support different dither options? spatial, temporal, both, etc. */ 13968c2ecf20Sopenharmony_cistatic const struct drm_prop_enum_list radeon_dither_enum_list[] = 13978c2ecf20Sopenharmony_ci{ { RADEON_FMT_DITHER_DISABLE, "off" }, 13988c2ecf20Sopenharmony_ci { RADEON_FMT_DITHER_ENABLE, "on" }, 13998c2ecf20Sopenharmony_ci}; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic const struct drm_prop_enum_list radeon_output_csc_enum_list[] = 14028c2ecf20Sopenharmony_ci{ { RADEON_OUTPUT_CSC_BYPASS, "bypass" }, 14038c2ecf20Sopenharmony_ci { RADEON_OUTPUT_CSC_TVRGB, "tvrgb" }, 14048c2ecf20Sopenharmony_ci { RADEON_OUTPUT_CSC_YCBCR601, "ycbcr601" }, 14058c2ecf20Sopenharmony_ci { RADEON_OUTPUT_CSC_YCBCR709, "ycbcr709" }, 14068c2ecf20Sopenharmony_ci}; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_cistatic int radeon_modeset_create_props(struct radeon_device *rdev) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci int sz; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci if (rdev->is_atom_bios) { 14138c2ecf20Sopenharmony_ci rdev->mode_info.coherent_mode_property = 14148c2ecf20Sopenharmony_ci drm_property_create_range(rdev->ddev, 0 , "coherent", 0, 1); 14158c2ecf20Sopenharmony_ci if (!rdev->mode_info.coherent_mode_property) 14168c2ecf20Sopenharmony_ci return -ENOMEM; 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci if (!ASIC_IS_AVIVO(rdev)) { 14208c2ecf20Sopenharmony_ci sz = ARRAY_SIZE(radeon_tmds_pll_enum_list); 14218c2ecf20Sopenharmony_ci rdev->mode_info.tmds_pll_property = 14228c2ecf20Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 14238c2ecf20Sopenharmony_ci "tmds_pll", 14248c2ecf20Sopenharmony_ci radeon_tmds_pll_enum_list, sz); 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci rdev->mode_info.load_detect_property = 14288c2ecf20Sopenharmony_ci drm_property_create_range(rdev->ddev, 0, "load detection", 0, 1); 14298c2ecf20Sopenharmony_ci if (!rdev->mode_info.load_detect_property) 14308c2ecf20Sopenharmony_ci return -ENOMEM; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci drm_mode_create_scaling_mode_property(rdev->ddev); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci sz = ARRAY_SIZE(radeon_tv_std_enum_list); 14358c2ecf20Sopenharmony_ci rdev->mode_info.tv_std_property = 14368c2ecf20Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 14378c2ecf20Sopenharmony_ci "tv standard", 14388c2ecf20Sopenharmony_ci radeon_tv_std_enum_list, sz); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci sz = ARRAY_SIZE(radeon_underscan_enum_list); 14418c2ecf20Sopenharmony_ci rdev->mode_info.underscan_property = 14428c2ecf20Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 14438c2ecf20Sopenharmony_ci "underscan", 14448c2ecf20Sopenharmony_ci radeon_underscan_enum_list, sz); 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci rdev->mode_info.underscan_hborder_property = 14478c2ecf20Sopenharmony_ci drm_property_create_range(rdev->ddev, 0, 14488c2ecf20Sopenharmony_ci "underscan hborder", 0, 128); 14498c2ecf20Sopenharmony_ci if (!rdev->mode_info.underscan_hborder_property) 14508c2ecf20Sopenharmony_ci return -ENOMEM; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci rdev->mode_info.underscan_vborder_property = 14538c2ecf20Sopenharmony_ci drm_property_create_range(rdev->ddev, 0, 14548c2ecf20Sopenharmony_ci "underscan vborder", 0, 128); 14558c2ecf20Sopenharmony_ci if (!rdev->mode_info.underscan_vborder_property) 14568c2ecf20Sopenharmony_ci return -ENOMEM; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci sz = ARRAY_SIZE(radeon_audio_enum_list); 14598c2ecf20Sopenharmony_ci rdev->mode_info.audio_property = 14608c2ecf20Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 14618c2ecf20Sopenharmony_ci "audio", 14628c2ecf20Sopenharmony_ci radeon_audio_enum_list, sz); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci sz = ARRAY_SIZE(radeon_dither_enum_list); 14658c2ecf20Sopenharmony_ci rdev->mode_info.dither_property = 14668c2ecf20Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 14678c2ecf20Sopenharmony_ci "dither", 14688c2ecf20Sopenharmony_ci radeon_dither_enum_list, sz); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci sz = ARRAY_SIZE(radeon_output_csc_enum_list); 14718c2ecf20Sopenharmony_ci rdev->mode_info.output_csc_property = 14728c2ecf20Sopenharmony_ci drm_property_create_enum(rdev->ddev, 0, 14738c2ecf20Sopenharmony_ci "output_csc", 14748c2ecf20Sopenharmony_ci radeon_output_csc_enum_list, sz); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci return 0; 14778c2ecf20Sopenharmony_ci} 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_civoid radeon_update_display_priority(struct radeon_device *rdev) 14808c2ecf20Sopenharmony_ci{ 14818c2ecf20Sopenharmony_ci /* adjustment options for the display watermarks */ 14828c2ecf20Sopenharmony_ci if ((radeon_disp_priority == 0) || (radeon_disp_priority > 2)) { 14838c2ecf20Sopenharmony_ci /* set display priority to high for r3xx, rv515 chips 14848c2ecf20Sopenharmony_ci * this avoids flickering due to underflow to the 14858c2ecf20Sopenharmony_ci * display controllers during heavy acceleration. 14868c2ecf20Sopenharmony_ci * Don't force high on rs4xx igp chips as it seems to 14878c2ecf20Sopenharmony_ci * affect the sound card. See kernel bug 15982. 14888c2ecf20Sopenharmony_ci */ 14898c2ecf20Sopenharmony_ci if ((ASIC_IS_R300(rdev) || (rdev->family == CHIP_RV515)) && 14908c2ecf20Sopenharmony_ci !(rdev->flags & RADEON_IS_IGP)) 14918c2ecf20Sopenharmony_ci rdev->disp_priority = 2; 14928c2ecf20Sopenharmony_ci else 14938c2ecf20Sopenharmony_ci rdev->disp_priority = 0; 14948c2ecf20Sopenharmony_ci } else 14958c2ecf20Sopenharmony_ci rdev->disp_priority = radeon_disp_priority; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci} 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci/* 15008c2ecf20Sopenharmony_ci * Allocate hdmi structs and determine register offsets 15018c2ecf20Sopenharmony_ci */ 15028c2ecf20Sopenharmony_cistatic void radeon_afmt_init(struct radeon_device *rdev) 15038c2ecf20Sopenharmony_ci{ 15048c2ecf20Sopenharmony_ci int i; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) 15078c2ecf20Sopenharmony_ci rdev->mode_info.afmt[i] = NULL; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (ASIC_IS_NODCE(rdev)) { 15108c2ecf20Sopenharmony_ci /* nothing to do */ 15118c2ecf20Sopenharmony_ci } else if (ASIC_IS_DCE4(rdev)) { 15128c2ecf20Sopenharmony_ci static uint32_t eg_offsets[] = { 15138c2ecf20Sopenharmony_ci EVERGREEN_CRTC0_REGISTER_OFFSET, 15148c2ecf20Sopenharmony_ci EVERGREEN_CRTC1_REGISTER_OFFSET, 15158c2ecf20Sopenharmony_ci EVERGREEN_CRTC2_REGISTER_OFFSET, 15168c2ecf20Sopenharmony_ci EVERGREEN_CRTC3_REGISTER_OFFSET, 15178c2ecf20Sopenharmony_ci EVERGREEN_CRTC4_REGISTER_OFFSET, 15188c2ecf20Sopenharmony_ci EVERGREEN_CRTC5_REGISTER_OFFSET, 15198c2ecf20Sopenharmony_ci 0x13830 - 0x7030, 15208c2ecf20Sopenharmony_ci }; 15218c2ecf20Sopenharmony_ci int num_afmt; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci /* DCE8 has 7 audio blocks tied to DIG encoders */ 15248c2ecf20Sopenharmony_ci /* DCE6 has 6 audio blocks tied to DIG encoders */ 15258c2ecf20Sopenharmony_ci /* DCE4/5 has 6 audio blocks tied to DIG encoders */ 15268c2ecf20Sopenharmony_ci /* DCE4.1 has 2 audio blocks tied to DIG encoders */ 15278c2ecf20Sopenharmony_ci if (ASIC_IS_DCE8(rdev)) 15288c2ecf20Sopenharmony_ci num_afmt = 7; 15298c2ecf20Sopenharmony_ci else if (ASIC_IS_DCE6(rdev)) 15308c2ecf20Sopenharmony_ci num_afmt = 6; 15318c2ecf20Sopenharmony_ci else if (ASIC_IS_DCE5(rdev)) 15328c2ecf20Sopenharmony_ci num_afmt = 6; 15338c2ecf20Sopenharmony_ci else if (ASIC_IS_DCE41(rdev)) 15348c2ecf20Sopenharmony_ci num_afmt = 2; 15358c2ecf20Sopenharmony_ci else /* DCE4 */ 15368c2ecf20Sopenharmony_ci num_afmt = 6; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci BUG_ON(num_afmt > ARRAY_SIZE(eg_offsets)); 15398c2ecf20Sopenharmony_ci for (i = 0; i < num_afmt; i++) { 15408c2ecf20Sopenharmony_ci rdev->mode_info.afmt[i] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 15418c2ecf20Sopenharmony_ci if (rdev->mode_info.afmt[i]) { 15428c2ecf20Sopenharmony_ci rdev->mode_info.afmt[i]->offset = eg_offsets[i]; 15438c2ecf20Sopenharmony_ci rdev->mode_info.afmt[i]->id = i; 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci } 15468c2ecf20Sopenharmony_ci } else if (ASIC_IS_DCE3(rdev)) { 15478c2ecf20Sopenharmony_ci /* DCE3.x has 2 audio blocks tied to DIG encoders */ 15488c2ecf20Sopenharmony_ci rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 15498c2ecf20Sopenharmony_ci if (rdev->mode_info.afmt[0]) { 15508c2ecf20Sopenharmony_ci rdev->mode_info.afmt[0]->offset = DCE3_HDMI_OFFSET0; 15518c2ecf20Sopenharmony_ci rdev->mode_info.afmt[0]->id = 0; 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 15548c2ecf20Sopenharmony_ci if (rdev->mode_info.afmt[1]) { 15558c2ecf20Sopenharmony_ci rdev->mode_info.afmt[1]->offset = DCE3_HDMI_OFFSET1; 15568c2ecf20Sopenharmony_ci rdev->mode_info.afmt[1]->id = 1; 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci } else if (ASIC_IS_DCE2(rdev)) { 15598c2ecf20Sopenharmony_ci /* DCE2 has at least 1 routable audio block */ 15608c2ecf20Sopenharmony_ci rdev->mode_info.afmt[0] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 15618c2ecf20Sopenharmony_ci if (rdev->mode_info.afmt[0]) { 15628c2ecf20Sopenharmony_ci rdev->mode_info.afmt[0]->offset = DCE2_HDMI_OFFSET0; 15638c2ecf20Sopenharmony_ci rdev->mode_info.afmt[0]->id = 0; 15648c2ecf20Sopenharmony_ci } 15658c2ecf20Sopenharmony_ci /* r6xx has 2 routable audio blocks */ 15668c2ecf20Sopenharmony_ci if (rdev->family >= CHIP_R600) { 15678c2ecf20Sopenharmony_ci rdev->mode_info.afmt[1] = kzalloc(sizeof(struct radeon_afmt), GFP_KERNEL); 15688c2ecf20Sopenharmony_ci if (rdev->mode_info.afmt[1]) { 15698c2ecf20Sopenharmony_ci rdev->mode_info.afmt[1]->offset = DCE2_HDMI_OFFSET1; 15708c2ecf20Sopenharmony_ci rdev->mode_info.afmt[1]->id = 1; 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_cistatic void radeon_afmt_fini(struct radeon_device *rdev) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci int i; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_MAX_AFMT_BLOCKS; i++) { 15818c2ecf20Sopenharmony_ci kfree(rdev->mode_info.afmt[i]); 15828c2ecf20Sopenharmony_ci rdev->mode_info.afmt[i] = NULL; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci} 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ciint radeon_modeset_init(struct radeon_device *rdev) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci int i; 15898c2ecf20Sopenharmony_ci int ret; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci drm_mode_config_init(rdev->ddev); 15928c2ecf20Sopenharmony_ci rdev->mode_info.mode_config_initialized = true; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci rdev->ddev->mode_config.funcs = &radeon_mode_funcs; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci if (radeon_use_pflipirq == 2 && rdev->family >= CHIP_R600) 15978c2ecf20Sopenharmony_ci rdev->ddev->mode_config.async_page_flip = true; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci if (ASIC_IS_DCE5(rdev)) { 16008c2ecf20Sopenharmony_ci rdev->ddev->mode_config.max_width = 16384; 16018c2ecf20Sopenharmony_ci rdev->ddev->mode_config.max_height = 16384; 16028c2ecf20Sopenharmony_ci } else if (ASIC_IS_AVIVO(rdev)) { 16038c2ecf20Sopenharmony_ci rdev->ddev->mode_config.max_width = 8192; 16048c2ecf20Sopenharmony_ci rdev->ddev->mode_config.max_height = 8192; 16058c2ecf20Sopenharmony_ci } else { 16068c2ecf20Sopenharmony_ci rdev->ddev->mode_config.max_width = 4096; 16078c2ecf20Sopenharmony_ci rdev->ddev->mode_config.max_height = 4096; 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci rdev->ddev->mode_config.preferred_depth = 24; 16118c2ecf20Sopenharmony_ci rdev->ddev->mode_config.prefer_shadow = 1; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci rdev->ddev->mode_config.fb_base = rdev->mc.aper_base; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci ret = radeon_modeset_create_props(rdev); 16168c2ecf20Sopenharmony_ci if (ret) { 16178c2ecf20Sopenharmony_ci return ret; 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci /* init i2c buses */ 16218c2ecf20Sopenharmony_ci radeon_i2c_init(rdev); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci /* check combios for a valid hardcoded EDID - Sun servers */ 16248c2ecf20Sopenharmony_ci if (!rdev->is_atom_bios) { 16258c2ecf20Sopenharmony_ci /* check for hardcoded EDID in BIOS */ 16268c2ecf20Sopenharmony_ci radeon_combios_check_hardcoded_edid(rdev); 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* allocate crtcs */ 16308c2ecf20Sopenharmony_ci for (i = 0; i < rdev->num_crtc; i++) { 16318c2ecf20Sopenharmony_ci radeon_crtc_init(rdev->ddev, i); 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci /* okay we should have all the bios connectors */ 16358c2ecf20Sopenharmony_ci ret = radeon_setup_enc_conn(rdev->ddev); 16368c2ecf20Sopenharmony_ci if (!ret) { 16378c2ecf20Sopenharmony_ci return ret; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci /* init dig PHYs, disp eng pll */ 16418c2ecf20Sopenharmony_ci if (rdev->is_atom_bios) { 16428c2ecf20Sopenharmony_ci radeon_atom_encoder_init(rdev); 16438c2ecf20Sopenharmony_ci radeon_atom_disp_eng_pll_init(rdev); 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci /* initialize hpd */ 16478c2ecf20Sopenharmony_ci radeon_hpd_init(rdev); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci /* setup afmt */ 16508c2ecf20Sopenharmony_ci radeon_afmt_init(rdev); 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci radeon_fbdev_init(rdev); 16538c2ecf20Sopenharmony_ci drm_kms_helper_poll_init(rdev->ddev); 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci /* do pm late init */ 16568c2ecf20Sopenharmony_ci ret = radeon_pm_late_init(rdev); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci return 0; 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_civoid radeon_modeset_fini(struct radeon_device *rdev) 16628c2ecf20Sopenharmony_ci{ 16638c2ecf20Sopenharmony_ci if (rdev->mode_info.mode_config_initialized) { 16648c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(rdev->ddev); 16658c2ecf20Sopenharmony_ci radeon_hpd_fini(rdev); 16668c2ecf20Sopenharmony_ci drm_helper_force_disable_all(rdev->ddev); 16678c2ecf20Sopenharmony_ci radeon_fbdev_fini(rdev); 16688c2ecf20Sopenharmony_ci radeon_afmt_fini(rdev); 16698c2ecf20Sopenharmony_ci drm_mode_config_cleanup(rdev->ddev); 16708c2ecf20Sopenharmony_ci rdev->mode_info.mode_config_initialized = false; 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci kfree(rdev->mode_info.bios_hardcoded_edid); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci /* free i2c buses */ 16768c2ecf20Sopenharmony_ci radeon_i2c_fini(rdev); 16778c2ecf20Sopenharmony_ci} 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_cistatic bool is_hdtv_mode(const struct drm_display_mode *mode) 16808c2ecf20Sopenharmony_ci{ 16818c2ecf20Sopenharmony_ci /* try and guess if this is a tv or a monitor */ 16828c2ecf20Sopenharmony_ci if ((mode->vdisplay == 480 && mode->hdisplay == 720) || /* 480p */ 16838c2ecf20Sopenharmony_ci (mode->vdisplay == 576) || /* 576p */ 16848c2ecf20Sopenharmony_ci (mode->vdisplay == 720) || /* 720p */ 16858c2ecf20Sopenharmony_ci (mode->vdisplay == 1080)) /* 1080p */ 16868c2ecf20Sopenharmony_ci return true; 16878c2ecf20Sopenharmony_ci else 16888c2ecf20Sopenharmony_ci return false; 16898c2ecf20Sopenharmony_ci} 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_cibool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, 16928c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 16938c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 16948c2ecf20Sopenharmony_ci{ 16958c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 16968c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 16978c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 16988c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 16998c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder; 17008c2ecf20Sopenharmony_ci struct drm_connector *connector; 17018c2ecf20Sopenharmony_ci bool first = true; 17028c2ecf20Sopenharmony_ci u32 src_v = 1, dst_v = 1; 17038c2ecf20Sopenharmony_ci u32 src_h = 1, dst_h = 1; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci radeon_crtc->h_border = 0; 17068c2ecf20Sopenharmony_ci radeon_crtc->v_border = 0; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 17098c2ecf20Sopenharmony_ci if (encoder->crtc != crtc) 17108c2ecf20Sopenharmony_ci continue; 17118c2ecf20Sopenharmony_ci radeon_encoder = to_radeon_encoder(encoder); 17128c2ecf20Sopenharmony_ci connector = radeon_get_connector_for_encoder(encoder); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci if (first) { 17158c2ecf20Sopenharmony_ci /* set scaling */ 17168c2ecf20Sopenharmony_ci if (radeon_encoder->rmx_type == RMX_OFF) 17178c2ecf20Sopenharmony_ci radeon_crtc->rmx_type = RMX_OFF; 17188c2ecf20Sopenharmony_ci else if (mode->hdisplay < radeon_encoder->native_mode.hdisplay || 17198c2ecf20Sopenharmony_ci mode->vdisplay < radeon_encoder->native_mode.vdisplay) 17208c2ecf20Sopenharmony_ci radeon_crtc->rmx_type = radeon_encoder->rmx_type; 17218c2ecf20Sopenharmony_ci else 17228c2ecf20Sopenharmony_ci radeon_crtc->rmx_type = RMX_OFF; 17238c2ecf20Sopenharmony_ci /* copy native mode */ 17248c2ecf20Sopenharmony_ci memcpy(&radeon_crtc->native_mode, 17258c2ecf20Sopenharmony_ci &radeon_encoder->native_mode, 17268c2ecf20Sopenharmony_ci sizeof(struct drm_display_mode)); 17278c2ecf20Sopenharmony_ci src_v = crtc->mode.vdisplay; 17288c2ecf20Sopenharmony_ci dst_v = radeon_crtc->native_mode.vdisplay; 17298c2ecf20Sopenharmony_ci src_h = crtc->mode.hdisplay; 17308c2ecf20Sopenharmony_ci dst_h = radeon_crtc->native_mode.hdisplay; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci /* fix up for overscan on hdmi */ 17338c2ecf20Sopenharmony_ci if (ASIC_IS_AVIVO(rdev) && 17348c2ecf20Sopenharmony_ci (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) && 17358c2ecf20Sopenharmony_ci ((radeon_encoder->underscan_type == UNDERSCAN_ON) || 17368c2ecf20Sopenharmony_ci ((radeon_encoder->underscan_type == UNDERSCAN_AUTO) && 17378c2ecf20Sopenharmony_ci drm_detect_hdmi_monitor(radeon_connector_edid(connector)) && 17388c2ecf20Sopenharmony_ci is_hdtv_mode(mode)))) { 17398c2ecf20Sopenharmony_ci if (radeon_encoder->underscan_hborder != 0) 17408c2ecf20Sopenharmony_ci radeon_crtc->h_border = radeon_encoder->underscan_hborder; 17418c2ecf20Sopenharmony_ci else 17428c2ecf20Sopenharmony_ci radeon_crtc->h_border = (mode->hdisplay >> 5) + 16; 17438c2ecf20Sopenharmony_ci if (radeon_encoder->underscan_vborder != 0) 17448c2ecf20Sopenharmony_ci radeon_crtc->v_border = radeon_encoder->underscan_vborder; 17458c2ecf20Sopenharmony_ci else 17468c2ecf20Sopenharmony_ci radeon_crtc->v_border = (mode->vdisplay >> 5) + 16; 17478c2ecf20Sopenharmony_ci radeon_crtc->rmx_type = RMX_FULL; 17488c2ecf20Sopenharmony_ci src_v = crtc->mode.vdisplay; 17498c2ecf20Sopenharmony_ci dst_v = crtc->mode.vdisplay - (radeon_crtc->v_border * 2); 17508c2ecf20Sopenharmony_ci src_h = crtc->mode.hdisplay; 17518c2ecf20Sopenharmony_ci dst_h = crtc->mode.hdisplay - (radeon_crtc->h_border * 2); 17528c2ecf20Sopenharmony_ci } 17538c2ecf20Sopenharmony_ci first = false; 17548c2ecf20Sopenharmony_ci } else { 17558c2ecf20Sopenharmony_ci if (radeon_crtc->rmx_type != radeon_encoder->rmx_type) { 17568c2ecf20Sopenharmony_ci /* WARNING: Right now this can't happen but 17578c2ecf20Sopenharmony_ci * in the future we need to check that scaling 17588c2ecf20Sopenharmony_ci * are consistent across different encoder 17598c2ecf20Sopenharmony_ci * (ie all encoder can work with the same 17608c2ecf20Sopenharmony_ci * scaling). 17618c2ecf20Sopenharmony_ci */ 17628c2ecf20Sopenharmony_ci DRM_ERROR("Scaling not consistent across encoder.\n"); 17638c2ecf20Sopenharmony_ci return false; 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci if (radeon_crtc->rmx_type != RMX_OFF) { 17688c2ecf20Sopenharmony_ci fixed20_12 a, b; 17698c2ecf20Sopenharmony_ci a.full = dfixed_const(src_v); 17708c2ecf20Sopenharmony_ci b.full = dfixed_const(dst_v); 17718c2ecf20Sopenharmony_ci radeon_crtc->vsc.full = dfixed_div(a, b); 17728c2ecf20Sopenharmony_ci a.full = dfixed_const(src_h); 17738c2ecf20Sopenharmony_ci b.full = dfixed_const(dst_h); 17748c2ecf20Sopenharmony_ci radeon_crtc->hsc.full = dfixed_div(a, b); 17758c2ecf20Sopenharmony_ci } else { 17768c2ecf20Sopenharmony_ci radeon_crtc->vsc.full = dfixed_const(1); 17778c2ecf20Sopenharmony_ci radeon_crtc->hsc.full = dfixed_const(1); 17788c2ecf20Sopenharmony_ci } 17798c2ecf20Sopenharmony_ci return true; 17808c2ecf20Sopenharmony_ci} 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci/* 17838c2ecf20Sopenharmony_ci * Retrieve current video scanout position of crtc on a given gpu, and 17848c2ecf20Sopenharmony_ci * an optional accurate timestamp of when query happened. 17858c2ecf20Sopenharmony_ci * 17868c2ecf20Sopenharmony_ci * \param dev Device to query. 17878c2ecf20Sopenharmony_ci * \param crtc Crtc to query. 17888c2ecf20Sopenharmony_ci * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). 17898c2ecf20Sopenharmony_ci * For driver internal use only also supports these flags: 17908c2ecf20Sopenharmony_ci * 17918c2ecf20Sopenharmony_ci * USE_REAL_VBLANKSTART to use the real start of vblank instead 17928c2ecf20Sopenharmony_ci * of a fudged earlier start of vblank. 17938c2ecf20Sopenharmony_ci * 17948c2ecf20Sopenharmony_ci * GET_DISTANCE_TO_VBLANKSTART to return distance to the 17958c2ecf20Sopenharmony_ci * fudged earlier start of vblank in *vpos and the distance 17968c2ecf20Sopenharmony_ci * to true start of vblank in *hpos. 17978c2ecf20Sopenharmony_ci * 17988c2ecf20Sopenharmony_ci * \param *vpos Location where vertical scanout position should be stored. 17998c2ecf20Sopenharmony_ci * \param *hpos Location where horizontal scanout position should go. 18008c2ecf20Sopenharmony_ci * \param *stime Target location for timestamp taken immediately before 18018c2ecf20Sopenharmony_ci * scanout position query. Can be NULL to skip timestamp. 18028c2ecf20Sopenharmony_ci * \param *etime Target location for timestamp taken immediately after 18038c2ecf20Sopenharmony_ci * scanout position query. Can be NULL to skip timestamp. 18048c2ecf20Sopenharmony_ci * 18058c2ecf20Sopenharmony_ci * Returns vpos as a positive number while in active scanout area. 18068c2ecf20Sopenharmony_ci * Returns vpos as a negative number inside vblank, counting the number 18078c2ecf20Sopenharmony_ci * of scanlines to go until end of vblank, e.g., -1 means "one scanline 18088c2ecf20Sopenharmony_ci * until start of active scanout / end of vblank." 18098c2ecf20Sopenharmony_ci * 18108c2ecf20Sopenharmony_ci * \return Flags, or'ed together as follows: 18118c2ecf20Sopenharmony_ci * 18128c2ecf20Sopenharmony_ci * DRM_SCANOUTPOS_VALID = Query successful. 18138c2ecf20Sopenharmony_ci * DRM_SCANOUTPOS_INVBL = Inside vblank. 18148c2ecf20Sopenharmony_ci * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of 18158c2ecf20Sopenharmony_ci * this flag means that returned position may be offset by a constant but 18168c2ecf20Sopenharmony_ci * unknown small number of scanlines wrt. real scanout position. 18178c2ecf20Sopenharmony_ci * 18188c2ecf20Sopenharmony_ci */ 18198c2ecf20Sopenharmony_ciint radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, 18208c2ecf20Sopenharmony_ci unsigned int flags, int *vpos, int *hpos, 18218c2ecf20Sopenharmony_ci ktime_t *stime, ktime_t *etime, 18228c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 18238c2ecf20Sopenharmony_ci{ 18248c2ecf20Sopenharmony_ci u32 stat_crtc = 0, vbl = 0, position = 0; 18258c2ecf20Sopenharmony_ci int vbl_start, vbl_end, vtotal, ret = 0; 18268c2ecf20Sopenharmony_ci bool in_vbl = true; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci /* Get optional system timestamp before query. */ 18338c2ecf20Sopenharmony_ci if (stime) 18348c2ecf20Sopenharmony_ci *stime = ktime_get(); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (ASIC_IS_DCE4(rdev)) { 18378c2ecf20Sopenharmony_ci if (pipe == 0) { 18388c2ecf20Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 18398c2ecf20Sopenharmony_ci EVERGREEN_CRTC0_REGISTER_OFFSET); 18408c2ecf20Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 18418c2ecf20Sopenharmony_ci EVERGREEN_CRTC0_REGISTER_OFFSET); 18428c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 18438c2ecf20Sopenharmony_ci } 18448c2ecf20Sopenharmony_ci if (pipe == 1) { 18458c2ecf20Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 18468c2ecf20Sopenharmony_ci EVERGREEN_CRTC1_REGISTER_OFFSET); 18478c2ecf20Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 18488c2ecf20Sopenharmony_ci EVERGREEN_CRTC1_REGISTER_OFFSET); 18498c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 18508c2ecf20Sopenharmony_ci } 18518c2ecf20Sopenharmony_ci if (pipe == 2) { 18528c2ecf20Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 18538c2ecf20Sopenharmony_ci EVERGREEN_CRTC2_REGISTER_OFFSET); 18548c2ecf20Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 18558c2ecf20Sopenharmony_ci EVERGREEN_CRTC2_REGISTER_OFFSET); 18568c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci if (pipe == 3) { 18598c2ecf20Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 18608c2ecf20Sopenharmony_ci EVERGREEN_CRTC3_REGISTER_OFFSET); 18618c2ecf20Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 18628c2ecf20Sopenharmony_ci EVERGREEN_CRTC3_REGISTER_OFFSET); 18638c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci if (pipe == 4) { 18668c2ecf20Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 18678c2ecf20Sopenharmony_ci EVERGREEN_CRTC4_REGISTER_OFFSET); 18688c2ecf20Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 18698c2ecf20Sopenharmony_ci EVERGREEN_CRTC4_REGISTER_OFFSET); 18708c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci if (pipe == 5) { 18738c2ecf20Sopenharmony_ci vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + 18748c2ecf20Sopenharmony_ci EVERGREEN_CRTC5_REGISTER_OFFSET); 18758c2ecf20Sopenharmony_ci position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + 18768c2ecf20Sopenharmony_ci EVERGREEN_CRTC5_REGISTER_OFFSET); 18778c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci } else if (ASIC_IS_AVIVO(rdev)) { 18808c2ecf20Sopenharmony_ci if (pipe == 0) { 18818c2ecf20Sopenharmony_ci vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END); 18828c2ecf20Sopenharmony_ci position = RREG32(AVIVO_D1CRTC_STATUS_POSITION); 18838c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 18848c2ecf20Sopenharmony_ci } 18858c2ecf20Sopenharmony_ci if (pipe == 1) { 18868c2ecf20Sopenharmony_ci vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END); 18878c2ecf20Sopenharmony_ci position = RREG32(AVIVO_D2CRTC_STATUS_POSITION); 18888c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci } else { 18918c2ecf20Sopenharmony_ci /* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */ 18928c2ecf20Sopenharmony_ci if (pipe == 0) { 18938c2ecf20Sopenharmony_ci /* Assume vbl_end == 0, get vbl_start from 18948c2ecf20Sopenharmony_ci * upper 16 bits. 18958c2ecf20Sopenharmony_ci */ 18968c2ecf20Sopenharmony_ci vbl = (RREG32(RADEON_CRTC_V_TOTAL_DISP) & 18978c2ecf20Sopenharmony_ci RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT; 18988c2ecf20Sopenharmony_ci /* Only retrieve vpos from upper 16 bits, set hpos == 0. */ 18998c2ecf20Sopenharmony_ci position = (RREG32(RADEON_CRTC_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL; 19008c2ecf20Sopenharmony_ci stat_crtc = RREG32(RADEON_CRTC_STATUS); 19018c2ecf20Sopenharmony_ci if (!(stat_crtc & 1)) 19028c2ecf20Sopenharmony_ci in_vbl = false; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci if (pipe == 1) { 19078c2ecf20Sopenharmony_ci vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) & 19088c2ecf20Sopenharmony_ci RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT; 19098c2ecf20Sopenharmony_ci position = (RREG32(RADEON_CRTC2_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL; 19108c2ecf20Sopenharmony_ci stat_crtc = RREG32(RADEON_CRTC2_STATUS); 19118c2ecf20Sopenharmony_ci if (!(stat_crtc & 1)) 19128c2ecf20Sopenharmony_ci in_vbl = false; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_VALID; 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci /* Get optional system timestamp after query. */ 19198c2ecf20Sopenharmony_ci if (etime) 19208c2ecf20Sopenharmony_ci *etime = ktime_get(); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci /* Decode into vertical and horizontal scanout position. */ 19258c2ecf20Sopenharmony_ci *vpos = position & 0x1fff; 19268c2ecf20Sopenharmony_ci *hpos = (position >> 16) & 0x1fff; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci /* Valid vblank area boundaries from gpu retrieved? */ 19298c2ecf20Sopenharmony_ci if (vbl > 0) { 19308c2ecf20Sopenharmony_ci /* Yes: Decode. */ 19318c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_ACCURATE; 19328c2ecf20Sopenharmony_ci vbl_start = vbl & 0x1fff; 19338c2ecf20Sopenharmony_ci vbl_end = (vbl >> 16) & 0x1fff; 19348c2ecf20Sopenharmony_ci } 19358c2ecf20Sopenharmony_ci else { 19368c2ecf20Sopenharmony_ci /* No: Fake something reasonable which gives at least ok results. */ 19378c2ecf20Sopenharmony_ci vbl_start = mode->crtc_vdisplay; 19388c2ecf20Sopenharmony_ci vbl_end = 0; 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci /* Called from driver internal vblank counter query code? */ 19428c2ecf20Sopenharmony_ci if (flags & GET_DISTANCE_TO_VBLANKSTART) { 19438c2ecf20Sopenharmony_ci /* Caller wants distance from real vbl_start in *hpos */ 19448c2ecf20Sopenharmony_ci *hpos = *vpos - vbl_start; 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci /* Fudge vblank to start a few scanlines earlier to handle the 19488c2ecf20Sopenharmony_ci * problem that vblank irqs fire a few scanlines before start 19498c2ecf20Sopenharmony_ci * of vblank. Some driver internal callers need the true vblank 19508c2ecf20Sopenharmony_ci * start to be used and signal this via the USE_REAL_VBLANKSTART flag. 19518c2ecf20Sopenharmony_ci * 19528c2ecf20Sopenharmony_ci * The cause of the "early" vblank irq is that the irq is triggered 19538c2ecf20Sopenharmony_ci * by the line buffer logic when the line buffer read position enters 19548c2ecf20Sopenharmony_ci * the vblank, whereas our crtc scanout position naturally lags the 19558c2ecf20Sopenharmony_ci * line buffer read position. 19568c2ecf20Sopenharmony_ci */ 19578c2ecf20Sopenharmony_ci if (!(flags & USE_REAL_VBLANKSTART)) 19588c2ecf20Sopenharmony_ci vbl_start -= rdev->mode_info.crtcs[pipe]->lb_vblank_lead_lines; 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci /* Test scanout position against vblank region. */ 19618c2ecf20Sopenharmony_ci if ((*vpos < vbl_start) && (*vpos >= vbl_end)) 19628c2ecf20Sopenharmony_ci in_vbl = false; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci /* In vblank? */ 19658c2ecf20Sopenharmony_ci if (in_vbl) 19668c2ecf20Sopenharmony_ci ret |= DRM_SCANOUTPOS_IN_VBLANK; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci /* Called from driver internal vblank counter query code? */ 19698c2ecf20Sopenharmony_ci if (flags & GET_DISTANCE_TO_VBLANKSTART) { 19708c2ecf20Sopenharmony_ci /* Caller wants distance from fudged earlier vbl_start */ 19718c2ecf20Sopenharmony_ci *vpos -= vbl_start; 19728c2ecf20Sopenharmony_ci return ret; 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /* Check if inside vblank area and apply corrective offsets: 19768c2ecf20Sopenharmony_ci * vpos will then be >=0 in video scanout area, but negative 19778c2ecf20Sopenharmony_ci * within vblank area, counting down the number of lines until 19788c2ecf20Sopenharmony_ci * start of scanout. 19798c2ecf20Sopenharmony_ci */ 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci /* Inside "upper part" of vblank area? Apply corrective offset if so: */ 19828c2ecf20Sopenharmony_ci if (in_vbl && (*vpos >= vbl_start)) { 19838c2ecf20Sopenharmony_ci vtotal = mode->crtc_vtotal; 19848c2ecf20Sopenharmony_ci *vpos = *vpos - vtotal; 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* Correct for shifted end of vbl at vbl_end. */ 19888c2ecf20Sopenharmony_ci *vpos = *vpos - vbl_end; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci return ret; 19918c2ecf20Sopenharmony_ci} 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_cibool 19948c2ecf20Sopenharmony_ciradeon_get_crtc_scanout_position(struct drm_crtc *crtc, 19958c2ecf20Sopenharmony_ci bool in_vblank_irq, int *vpos, int *hpos, 19968c2ecf20Sopenharmony_ci ktime_t *stime, ktime_t *etime, 19978c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 19988c2ecf20Sopenharmony_ci{ 19998c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 20008c2ecf20Sopenharmony_ci unsigned int pipe = crtc->index; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci return radeon_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos, 20038c2ecf20Sopenharmony_ci stime, etime, mode); 20048c2ecf20Sopenharmony_ci} 2005