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 <drm/drm_crtc_helper.h> 288c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 298c2ecf20Sopenharmony_ci#include <drm/drm_fixed.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 318c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 328c2ecf20Sopenharmony_ci#include <drm/radeon_drm.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "atom.h" 358c2ecf20Sopenharmony_ci#include "radeon.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void radeon_overscan_setup(struct drm_crtc *crtc, 388c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 418c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 428c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci WREG32(RADEON_OVR_CLR + radeon_crtc->crtc_offset, 0); 458c2ecf20Sopenharmony_ci WREG32(RADEON_OVR_WID_LEFT_RIGHT + radeon_crtc->crtc_offset, 0); 468c2ecf20Sopenharmony_ci WREG32(RADEON_OVR_WID_TOP_BOTTOM + radeon_crtc->crtc_offset, 0); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc, 508c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 538c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 548c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 558c2ecf20Sopenharmony_ci int xres = mode->hdisplay; 568c2ecf20Sopenharmony_ci int yres = mode->vdisplay; 578c2ecf20Sopenharmony_ci bool hscale = true, vscale = true; 588c2ecf20Sopenharmony_ci int hsync_wid; 598c2ecf20Sopenharmony_ci int vsync_wid; 608c2ecf20Sopenharmony_ci int hsync_start; 618c2ecf20Sopenharmony_ci int blank_width; 628c2ecf20Sopenharmony_ci u32 scale, inc, crtc_more_cntl; 638c2ecf20Sopenharmony_ci u32 fp_horz_stretch, fp_vert_stretch, fp_horz_vert_active; 648c2ecf20Sopenharmony_ci u32 fp_h_sync_strt_wid, fp_crtc_h_total_disp; 658c2ecf20Sopenharmony_ci u32 fp_v_sync_strt_wid, fp_crtc_v_total_disp; 668c2ecf20Sopenharmony_ci struct drm_display_mode *native_mode = &radeon_crtc->native_mode; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH) & 698c2ecf20Sopenharmony_ci (RADEON_VERT_STRETCH_RESERVED | 708c2ecf20Sopenharmony_ci RADEON_VERT_AUTO_RATIO_INC); 718c2ecf20Sopenharmony_ci fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH) & 728c2ecf20Sopenharmony_ci (RADEON_HORZ_FP_LOOP_STRETCH | 738c2ecf20Sopenharmony_ci RADEON_HORZ_AUTO_RATIO_INC); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci crtc_more_cntl = 0; 768c2ecf20Sopenharmony_ci if ((rdev->family == CHIP_RS100) || 778c2ecf20Sopenharmony_ci (rdev->family == CHIP_RS200)) { 788c2ecf20Sopenharmony_ci /* This is to workaround the asic bug for RMX, some versions 798c2ecf20Sopenharmony_ci of BIOS dosen't have this register initialized correctly. */ 808c2ecf20Sopenharmony_ci crtc_more_cntl |= RADEON_CRTC_H_CUTOFF_ACTIVE_EN; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci fp_crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) 858c2ecf20Sopenharmony_ci | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; 888c2ecf20Sopenharmony_ci if (!hsync_wid) 898c2ecf20Sopenharmony_ci hsync_wid = 1; 908c2ecf20Sopenharmony_ci hsync_start = mode->crtc_hsync_start - 8; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci fp_h_sync_strt_wid = ((hsync_start & 0x1fff) 938c2ecf20Sopenharmony_ci | ((hsync_wid & 0x3f) << 16) 948c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NHSYNC) 958c2ecf20Sopenharmony_ci ? RADEON_CRTC_H_SYNC_POL 968c2ecf20Sopenharmony_ci : 0)); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci fp_crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) 998c2ecf20Sopenharmony_ci | ((mode->crtc_vdisplay - 1) << 16)); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; 1028c2ecf20Sopenharmony_ci if (!vsync_wid) 1038c2ecf20Sopenharmony_ci vsync_wid = 1; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci fp_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) 1068c2ecf20Sopenharmony_ci | ((vsync_wid & 0x1f) << 16) 1078c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NVSYNC) 1088c2ecf20Sopenharmony_ci ? RADEON_CRTC_V_SYNC_POL 1098c2ecf20Sopenharmony_ci : 0)); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci fp_horz_vert_active = 0; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (native_mode->hdisplay == 0 || 1148c2ecf20Sopenharmony_ci native_mode->vdisplay == 0) { 1158c2ecf20Sopenharmony_ci hscale = false; 1168c2ecf20Sopenharmony_ci vscale = false; 1178c2ecf20Sopenharmony_ci } else { 1188c2ecf20Sopenharmony_ci if (xres > native_mode->hdisplay) 1198c2ecf20Sopenharmony_ci xres = native_mode->hdisplay; 1208c2ecf20Sopenharmony_ci if (yres > native_mode->vdisplay) 1218c2ecf20Sopenharmony_ci yres = native_mode->vdisplay; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (xres == native_mode->hdisplay) 1248c2ecf20Sopenharmony_ci hscale = false; 1258c2ecf20Sopenharmony_ci if (yres == native_mode->vdisplay) 1268c2ecf20Sopenharmony_ci vscale = false; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci switch (radeon_crtc->rmx_type) { 1308c2ecf20Sopenharmony_ci case RMX_FULL: 1318c2ecf20Sopenharmony_ci case RMX_ASPECT: 1328c2ecf20Sopenharmony_ci if (!hscale) 1338c2ecf20Sopenharmony_ci fp_horz_stretch |= ((xres/8-1) << 16); 1348c2ecf20Sopenharmony_ci else { 1358c2ecf20Sopenharmony_ci inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0; 1368c2ecf20Sopenharmony_ci scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX) 1378c2ecf20Sopenharmony_ci / native_mode->hdisplay + 1; 1388c2ecf20Sopenharmony_ci fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) | 1398c2ecf20Sopenharmony_ci RADEON_HORZ_STRETCH_BLEND | 1408c2ecf20Sopenharmony_ci RADEON_HORZ_STRETCH_ENABLE | 1418c2ecf20Sopenharmony_ci ((native_mode->hdisplay/8-1) << 16)); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!vscale) 1458c2ecf20Sopenharmony_ci fp_vert_stretch |= ((yres-1) << 12); 1468c2ecf20Sopenharmony_ci else { 1478c2ecf20Sopenharmony_ci inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0; 1488c2ecf20Sopenharmony_ci scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX) 1498c2ecf20Sopenharmony_ci / native_mode->vdisplay + 1; 1508c2ecf20Sopenharmony_ci fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) | 1518c2ecf20Sopenharmony_ci RADEON_VERT_STRETCH_ENABLE | 1528c2ecf20Sopenharmony_ci RADEON_VERT_STRETCH_BLEND | 1538c2ecf20Sopenharmony_ci ((native_mode->vdisplay-1) << 12)); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci case RMX_CENTER: 1578c2ecf20Sopenharmony_ci fp_horz_stretch |= ((xres/8-1) << 16); 1588c2ecf20Sopenharmony_ci fp_vert_stretch |= ((yres-1) << 12); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci crtc_more_cntl |= (RADEON_CRTC_AUTO_HORZ_CENTER_EN | 1618c2ecf20Sopenharmony_ci RADEON_CRTC_AUTO_VERT_CENTER_EN); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci blank_width = (mode->crtc_hblank_end - mode->crtc_hblank_start) / 8; 1648c2ecf20Sopenharmony_ci if (blank_width > 110) 1658c2ecf20Sopenharmony_ci blank_width = 110; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci fp_crtc_h_total_disp = (((blank_width) & 0x3ff) 1688c2ecf20Sopenharmony_ci | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; 1718c2ecf20Sopenharmony_ci if (!hsync_wid) 1728c2ecf20Sopenharmony_ci hsync_wid = 1; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci fp_h_sync_strt_wid = ((((mode->crtc_hsync_start - mode->crtc_hblank_start) / 8) & 0x1fff) 1758c2ecf20Sopenharmony_ci | ((hsync_wid & 0x3f) << 16) 1768c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NHSYNC) 1778c2ecf20Sopenharmony_ci ? RADEON_CRTC_H_SYNC_POL 1788c2ecf20Sopenharmony_ci : 0)); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci fp_crtc_v_total_disp = (((mode->crtc_vblank_end - mode->crtc_vblank_start) & 0xffff) 1818c2ecf20Sopenharmony_ci | ((mode->crtc_vdisplay - 1) << 16)); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; 1848c2ecf20Sopenharmony_ci if (!vsync_wid) 1858c2ecf20Sopenharmony_ci vsync_wid = 1; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci fp_v_sync_strt_wid = ((((mode->crtc_vsync_start - mode->crtc_vblank_start) & 0xfff) 1888c2ecf20Sopenharmony_ci | ((vsync_wid & 0x1f) << 16) 1898c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NVSYNC) 1908c2ecf20Sopenharmony_ci ? RADEON_CRTC_V_SYNC_POL 1918c2ecf20Sopenharmony_ci : 0))); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci fp_horz_vert_active = (((native_mode->vdisplay) & 0xfff) | 1948c2ecf20Sopenharmony_ci (((native_mode->hdisplay / 8) & 0x1ff) << 16)); 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case RMX_OFF: 1978c2ecf20Sopenharmony_ci default: 1988c2ecf20Sopenharmony_ci fp_horz_stretch |= ((xres/8-1) << 16); 1998c2ecf20Sopenharmony_ci fp_vert_stretch |= ((yres-1) << 12); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci WREG32(RADEON_FP_HORZ_STRETCH, fp_horz_stretch); 2048c2ecf20Sopenharmony_ci WREG32(RADEON_FP_VERT_STRETCH, fp_vert_stretch); 2058c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_MORE_CNTL, crtc_more_cntl); 2068c2ecf20Sopenharmony_ci WREG32(RADEON_FP_HORZ_VERT_ACTIVE, fp_horz_vert_active); 2078c2ecf20Sopenharmony_ci WREG32(RADEON_FP_H_SYNC_STRT_WID, fp_h_sync_strt_wid); 2088c2ecf20Sopenharmony_ci WREG32(RADEON_FP_V_SYNC_STRT_WID, fp_v_sync_strt_wid); 2098c2ecf20Sopenharmony_ci WREG32(RADEON_FP_CRTC_H_TOTAL_DISP, fp_crtc_h_total_disp); 2108c2ecf20Sopenharmony_ci WREG32(RADEON_FP_CRTC_V_TOTAL_DISP, fp_crtc_v_total_disp); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void radeon_pll_wait_for_read_update_complete(struct drm_device *dev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 2168c2ecf20Sopenharmony_ci int i = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* FIXME: Certain revisions of R300 can't recover here. Not sure of 2198c2ecf20Sopenharmony_ci the cause yet, but this workaround will mask the problem for now. 2208c2ecf20Sopenharmony_ci Other chips usually will pass at the very first test, so the 2218c2ecf20Sopenharmony_ci workaround shouldn't have any effect on them. */ 2228c2ecf20Sopenharmony_ci for (i = 0; 2238c2ecf20Sopenharmony_ci (i < 10000 && 2248c2ecf20Sopenharmony_ci RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); 2258c2ecf20Sopenharmony_ci i++); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void radeon_pll_write_update(struct drm_device *dev) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci while (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_REF_DIV, 2358c2ecf20Sopenharmony_ci RADEON_PPLL_ATOMIC_UPDATE_W, 2368c2ecf20Sopenharmony_ci ~(RADEON_PPLL_ATOMIC_UPDATE_W)); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void radeon_pll2_wait_for_read_update_complete(struct drm_device *dev) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 2428c2ecf20Sopenharmony_ci int i = 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* FIXME: Certain revisions of R300 can't recover here. Not sure of 2468c2ecf20Sopenharmony_ci the cause yet, but this workaround will mask the problem for now. 2478c2ecf20Sopenharmony_ci Other chips usually will pass at the very first test, so the 2488c2ecf20Sopenharmony_ci workaround shouldn't have any effect on them. */ 2498c2ecf20Sopenharmony_ci for (i = 0; 2508c2ecf20Sopenharmony_ci (i < 10000 && 2518c2ecf20Sopenharmony_ci RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); 2528c2ecf20Sopenharmony_ci i++); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void radeon_pll2_write_update(struct drm_device *dev) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci while (RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_REF_DIV, 2628c2ecf20Sopenharmony_ci RADEON_P2PLL_ATOMIC_UPDATE_W, 2638c2ecf20Sopenharmony_ci ~(RADEON_P2PLL_ATOMIC_UPDATE_W)); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic uint8_t radeon_compute_pll_gain(uint16_t ref_freq, uint16_t ref_div, 2678c2ecf20Sopenharmony_ci uint16_t fb_div) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci unsigned int vcoFreq; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (!ref_div) 2728c2ecf20Sopenharmony_ci return 1; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci vcoFreq = ((unsigned)ref_freq * fb_div) / ref_div; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* 2778c2ecf20Sopenharmony_ci * This is horribly crude: the VCO frequency range is divided into 2788c2ecf20Sopenharmony_ci * 3 parts, each part having a fixed PLL gain value. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci if (vcoFreq >= 30000) 2818c2ecf20Sopenharmony_ci /* 2828c2ecf20Sopenharmony_ci * [300..max] MHz : 7 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_ci return 7; 2858c2ecf20Sopenharmony_ci else if (vcoFreq >= 18000) 2868c2ecf20Sopenharmony_ci /* 2878c2ecf20Sopenharmony_ci * [180..300) MHz : 4 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci return 4; 2908c2ecf20Sopenharmony_ci else 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * [0..180) MHz : 1 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci return 1; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 3008c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 3018c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 3028c2ecf20Sopenharmony_ci uint32_t crtc_ext_cntl = 0; 3038c2ecf20Sopenharmony_ci uint32_t mask; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id) 3068c2ecf20Sopenharmony_ci mask = (RADEON_CRTC2_DISP_DIS | 3078c2ecf20Sopenharmony_ci RADEON_CRTC2_VSYNC_DIS | 3088c2ecf20Sopenharmony_ci RADEON_CRTC2_HSYNC_DIS | 3098c2ecf20Sopenharmony_ci RADEON_CRTC2_DISP_REQ_EN_B); 3108c2ecf20Sopenharmony_ci else 3118c2ecf20Sopenharmony_ci mask = (RADEON_CRTC_DISPLAY_DIS | 3128c2ecf20Sopenharmony_ci RADEON_CRTC_VSYNC_DIS | 3138c2ecf20Sopenharmony_ci RADEON_CRTC_HSYNC_DIS); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* 3168c2ecf20Sopenharmony_ci * On all dual CRTC GPUs this bit controls the CRTC of the primary DAC. 3178c2ecf20Sopenharmony_ci * Therefore it is set in the DAC DMPS function. 3188c2ecf20Sopenharmony_ci * This is different for GPU's with a single CRTC but a primary and a 3198c2ecf20Sopenharmony_ci * TV DAC: here it controls the single CRTC no matter where it is 3208c2ecf20Sopenharmony_ci * routed. Therefore we set it here. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ci if (rdev->flags & RADEON_SINGLE_CRTC) 3238c2ecf20Sopenharmony_ci crtc_ext_cntl = RADEON_CRTC_CRT_ON; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci switch (mode) { 3268c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_ON: 3278c2ecf20Sopenharmony_ci radeon_crtc->enabled = true; 3288c2ecf20Sopenharmony_ci /* adjust pm to dpms changes BEFORE enabling crtcs */ 3298c2ecf20Sopenharmony_ci radeon_pm_compute_clocks(rdev); 3308c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id) 3318c2ecf20Sopenharmony_ci WREG32_P(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_EN, ~(RADEON_CRTC2_EN | mask)); 3328c2ecf20Sopenharmony_ci else { 3338c2ecf20Sopenharmony_ci WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_EN, ~(RADEON_CRTC_EN | 3348c2ecf20Sopenharmony_ci RADEON_CRTC_DISP_REQ_EN_B)); 3358c2ecf20Sopenharmony_ci WREG32_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, ~(mask | crtc_ext_cntl)); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci if (dev->num_crtcs > radeon_crtc->crtc_id) 3388c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 3398c2ecf20Sopenharmony_ci radeon_crtc_load_lut(crtc); 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_STANDBY: 3428c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_SUSPEND: 3438c2ecf20Sopenharmony_ci case DRM_MODE_DPMS_OFF: 3448c2ecf20Sopenharmony_ci if (dev->num_crtcs > radeon_crtc->crtc_id) 3458c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 3468c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id) 3478c2ecf20Sopenharmony_ci WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~(RADEON_CRTC2_EN | mask)); 3488c2ecf20Sopenharmony_ci else { 3498c2ecf20Sopenharmony_ci WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B, ~(RADEON_CRTC_EN | 3508c2ecf20Sopenharmony_ci RADEON_CRTC_DISP_REQ_EN_B)); 3518c2ecf20Sopenharmony_ci WREG32_P(RADEON_CRTC_EXT_CNTL, mask, ~(mask | crtc_ext_cntl)); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci radeon_crtc->enabled = false; 3548c2ecf20Sopenharmony_ci /* adjust pm to dpms changes AFTER disabling crtcs */ 3558c2ecf20Sopenharmony_ci radeon_pm_compute_clocks(rdev); 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ciint radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, 3618c2ecf20Sopenharmony_ci struct drm_framebuffer *old_fb) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciint radeon_crtc_set_base_atomic(struct drm_crtc *crtc, 3678c2ecf20Sopenharmony_ci struct drm_framebuffer *fb, 3688c2ecf20Sopenharmony_ci int x, int y, enum mode_set_atomic state) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci return radeon_crtc_do_set_base(crtc, fb, x, y, 1); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ciint radeon_crtc_do_set_base(struct drm_crtc *crtc, 3748c2ecf20Sopenharmony_ci struct drm_framebuffer *fb, 3758c2ecf20Sopenharmony_ci int x, int y, int atomic) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 3788c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 3798c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 3808c2ecf20Sopenharmony_ci struct drm_framebuffer *target_fb; 3818c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 3828c2ecf20Sopenharmony_ci struct radeon_bo *rbo; 3838c2ecf20Sopenharmony_ci uint64_t base; 3848c2ecf20Sopenharmony_ci uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0; 3858c2ecf20Sopenharmony_ci uint32_t crtc_pitch, pitch_pixels; 3868c2ecf20Sopenharmony_ci uint32_t tiling_flags; 3878c2ecf20Sopenharmony_ci int format; 3888c2ecf20Sopenharmony_ci uint32_t gen_cntl_reg, gen_cntl_val; 3898c2ecf20Sopenharmony_ci int r; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 3928c2ecf20Sopenharmony_ci /* no fb bound */ 3938c2ecf20Sopenharmony_ci if (!atomic && !crtc->primary->fb) { 3948c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("No FB bound\n"); 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (atomic) 3998c2ecf20Sopenharmony_ci target_fb = fb; 4008c2ecf20Sopenharmony_ci else 4018c2ecf20Sopenharmony_ci target_fb = crtc->primary->fb; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci switch (target_fb->format->cpp[0] * 8) { 4048c2ecf20Sopenharmony_ci case 8: 4058c2ecf20Sopenharmony_ci format = 2; 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci case 15: /* 555 */ 4088c2ecf20Sopenharmony_ci format = 3; 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci case 16: /* 565 */ 4118c2ecf20Sopenharmony_ci format = 4; 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci case 24: /* RGB */ 4148c2ecf20Sopenharmony_ci format = 5; 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci case 32: /* xRGB */ 4178c2ecf20Sopenharmony_ci format = 6; 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci default: 4208c2ecf20Sopenharmony_ci return false; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Pin framebuffer & get tilling informations */ 4248c2ecf20Sopenharmony_ci obj = target_fb->obj[0]; 4258c2ecf20Sopenharmony_ci rbo = gem_to_radeon_bo(obj); 4268c2ecf20Sopenharmony_ciretry: 4278c2ecf20Sopenharmony_ci r = radeon_bo_reserve(rbo, false); 4288c2ecf20Sopenharmony_ci if (unlikely(r != 0)) 4298c2ecf20Sopenharmony_ci return r; 4308c2ecf20Sopenharmony_ci /* Only 27 bit offset for legacy CRTC */ 4318c2ecf20Sopenharmony_ci r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, 1 << 27, 4328c2ecf20Sopenharmony_ci &base); 4338c2ecf20Sopenharmony_ci if (unlikely(r != 0)) { 4348c2ecf20Sopenharmony_ci radeon_bo_unreserve(rbo); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* On old GPU like RN50 with little vram pining can fails because 4378c2ecf20Sopenharmony_ci * current fb is taking all space needed. So instead of unpining 4388c2ecf20Sopenharmony_ci * the old buffer after pining the new one, first unpin old one 4398c2ecf20Sopenharmony_ci * and then retry pining new one. 4408c2ecf20Sopenharmony_ci * 4418c2ecf20Sopenharmony_ci * As only master can set mode only master can pin and it is 4428c2ecf20Sopenharmony_ci * unlikely the master client will race with itself especialy 4438c2ecf20Sopenharmony_ci * on those old gpu with single crtc. 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * We don't shutdown the display controller because new buffer 4468c2ecf20Sopenharmony_ci * will end up in same spot. 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_ci if (!atomic && fb && fb != crtc->primary->fb) { 4498c2ecf20Sopenharmony_ci struct radeon_bo *old_rbo; 4508c2ecf20Sopenharmony_ci unsigned long nsize, osize; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci old_rbo = gem_to_radeon_bo(fb->obj[0]); 4538c2ecf20Sopenharmony_ci osize = radeon_bo_size(old_rbo); 4548c2ecf20Sopenharmony_ci nsize = radeon_bo_size(rbo); 4558c2ecf20Sopenharmony_ci if (nsize <= osize && !radeon_bo_reserve(old_rbo, false)) { 4568c2ecf20Sopenharmony_ci radeon_bo_unpin(old_rbo); 4578c2ecf20Sopenharmony_ci radeon_bo_unreserve(old_rbo); 4588c2ecf20Sopenharmony_ci fb = NULL; 4598c2ecf20Sopenharmony_ci goto retry; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci return -EINVAL; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); 4658c2ecf20Sopenharmony_ci radeon_bo_unreserve(rbo); 4668c2ecf20Sopenharmony_ci if (tiling_flags & RADEON_TILING_MICRO) 4678c2ecf20Sopenharmony_ci DRM_ERROR("trying to scanout microtiled buffer\n"); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* if scanout was in GTT this really wouldn't work */ 4708c2ecf20Sopenharmony_ci /* crtc offset is from display base addr not FB location */ 4718c2ecf20Sopenharmony_ci radeon_crtc->legacy_display_base_addr = rdev->mc.vram_start; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci base -= radeon_crtc->legacy_display_base_addr; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci crtc_offset_cntl = 0; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0]; 4788c2ecf20Sopenharmony_ci crtc_pitch = DIV_ROUND_UP(pitch_pixels * target_fb->format->cpp[0] * 8, 4798c2ecf20Sopenharmony_ci target_fb->format->cpp[0] * 8 * 8); 4808c2ecf20Sopenharmony_ci crtc_pitch |= crtc_pitch << 16; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci crtc_offset_cntl |= RADEON_CRTC_GUI_TRIG_OFFSET_LEFT_EN; 4838c2ecf20Sopenharmony_ci if (tiling_flags & RADEON_TILING_MACRO) { 4848c2ecf20Sopenharmony_ci if (ASIC_IS_R300(rdev)) 4858c2ecf20Sopenharmony_ci crtc_offset_cntl |= (R300_CRTC_X_Y_MODE_EN | 4868c2ecf20Sopenharmony_ci R300_CRTC_MICRO_TILE_BUFFER_DIS | 4878c2ecf20Sopenharmony_ci R300_CRTC_MACRO_TILE_EN); 4888c2ecf20Sopenharmony_ci else 4898c2ecf20Sopenharmony_ci crtc_offset_cntl |= RADEON_CRTC_TILE_EN; 4908c2ecf20Sopenharmony_ci } else { 4918c2ecf20Sopenharmony_ci if (ASIC_IS_R300(rdev)) 4928c2ecf20Sopenharmony_ci crtc_offset_cntl &= ~(R300_CRTC_X_Y_MODE_EN | 4938c2ecf20Sopenharmony_ci R300_CRTC_MICRO_TILE_BUFFER_DIS | 4948c2ecf20Sopenharmony_ci R300_CRTC_MACRO_TILE_EN); 4958c2ecf20Sopenharmony_ci else 4968c2ecf20Sopenharmony_ci crtc_offset_cntl &= ~RADEON_CRTC_TILE_EN; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (tiling_flags & RADEON_TILING_MACRO) { 5008c2ecf20Sopenharmony_ci if (ASIC_IS_R300(rdev)) { 5018c2ecf20Sopenharmony_ci crtc_tile_x0_y0 = x | (y << 16); 5028c2ecf20Sopenharmony_ci base &= ~0x7ff; 5038c2ecf20Sopenharmony_ci } else { 5048c2ecf20Sopenharmony_ci int byteshift = target_fb->format->cpp[0] * 8 >> 4; 5058c2ecf20Sopenharmony_ci int tile_addr = (((y >> 3) * pitch_pixels + x) >> (8 - byteshift)) << 11; 5068c2ecf20Sopenharmony_ci base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8); 5078c2ecf20Sopenharmony_ci crtc_offset_cntl |= (y % 16); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci } else { 5108c2ecf20Sopenharmony_ci int offset = y * pitch_pixels + x; 5118c2ecf20Sopenharmony_ci switch (target_fb->format->cpp[0] * 8) { 5128c2ecf20Sopenharmony_ci case 8: 5138c2ecf20Sopenharmony_ci offset *= 1; 5148c2ecf20Sopenharmony_ci break; 5158c2ecf20Sopenharmony_ci case 15: 5168c2ecf20Sopenharmony_ci case 16: 5178c2ecf20Sopenharmony_ci offset *= 2; 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci case 24: 5208c2ecf20Sopenharmony_ci offset *= 3; 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci case 32: 5238c2ecf20Sopenharmony_ci offset *= 4; 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci default: 5268c2ecf20Sopenharmony_ci return false; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci base += offset; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci base &= ~7; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id == 1) 5348c2ecf20Sopenharmony_ci gen_cntl_reg = RADEON_CRTC2_GEN_CNTL; 5358c2ecf20Sopenharmony_ci else 5368c2ecf20Sopenharmony_ci gen_cntl_reg = RADEON_CRTC_GEN_CNTL; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci gen_cntl_val = RREG32(gen_cntl_reg); 5398c2ecf20Sopenharmony_ci gen_cntl_val &= ~(0xf << 8); 5408c2ecf20Sopenharmony_ci gen_cntl_val |= (format << 8); 5418c2ecf20Sopenharmony_ci gen_cntl_val &= ~RADEON_CRTC_VSTAT_MODE_MASK; 5428c2ecf20Sopenharmony_ci WREG32(gen_cntl_reg, gen_cntl_val); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci crtc_offset = (u32)base; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci WREG32(RADEON_DISPLAY_BASE_ADDR + radeon_crtc->crtc_offset, radeon_crtc->legacy_display_base_addr); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (ASIC_IS_R300(rdev)) { 5498c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id) 5508c2ecf20Sopenharmony_ci WREG32(R300_CRTC2_TILE_X0_Y0, crtc_tile_x0_y0); 5518c2ecf20Sopenharmony_ci else 5528c2ecf20Sopenharmony_ci WREG32(R300_CRTC_TILE_X0_Y0, crtc_tile_x0_y0); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_OFFSET_CNTL + radeon_crtc->crtc_offset, crtc_offset_cntl); 5558c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, crtc_offset); 5568c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!atomic && fb && fb != crtc->primary->fb) { 5598c2ecf20Sopenharmony_ci rbo = gem_to_radeon_bo(fb->obj[0]); 5608c2ecf20Sopenharmony_ci r = radeon_bo_reserve(rbo, false); 5618c2ecf20Sopenharmony_ci if (unlikely(r != 0)) 5628c2ecf20Sopenharmony_ci return r; 5638c2ecf20Sopenharmony_ci radeon_bo_unpin(rbo); 5648c2ecf20Sopenharmony_ci radeon_bo_unreserve(rbo); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Bytes per pixel may have changed */ 5688c2ecf20Sopenharmony_ci radeon_bandwidth_update(rdev); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mode *mode) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 5768c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 5778c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 5788c2ecf20Sopenharmony_ci const struct drm_framebuffer *fb = crtc->primary->fb; 5798c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 5808c2ecf20Sopenharmony_ci int format; 5818c2ecf20Sopenharmony_ci int hsync_start; 5828c2ecf20Sopenharmony_ci int hsync_wid; 5838c2ecf20Sopenharmony_ci int vsync_wid; 5848c2ecf20Sopenharmony_ci uint32_t crtc_h_total_disp; 5858c2ecf20Sopenharmony_ci uint32_t crtc_h_sync_strt_wid; 5868c2ecf20Sopenharmony_ci uint32_t crtc_v_total_disp; 5878c2ecf20Sopenharmony_ci uint32_t crtc_v_sync_strt_wid; 5888c2ecf20Sopenharmony_ci bool is_tv = false; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 5918c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 5928c2ecf20Sopenharmony_ci if (encoder->crtc == crtc) { 5938c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 5948c2ecf20Sopenharmony_ci if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { 5958c2ecf20Sopenharmony_ci is_tv = true; 5968c2ecf20Sopenharmony_ci DRM_INFO("crtc %d is connected to a TV\n", radeon_crtc->crtc_id); 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci switch (fb->format->cpp[0] * 8) { 6038c2ecf20Sopenharmony_ci case 8: 6048c2ecf20Sopenharmony_ci format = 2; 6058c2ecf20Sopenharmony_ci break; 6068c2ecf20Sopenharmony_ci case 15: /* 555 */ 6078c2ecf20Sopenharmony_ci format = 3; 6088c2ecf20Sopenharmony_ci break; 6098c2ecf20Sopenharmony_ci case 16: /* 565 */ 6108c2ecf20Sopenharmony_ci format = 4; 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci case 24: /* RGB */ 6138c2ecf20Sopenharmony_ci format = 5; 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci case 32: /* xRGB */ 6168c2ecf20Sopenharmony_ci format = 6; 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci default: 6198c2ecf20Sopenharmony_ci return false; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) 6238c2ecf20Sopenharmony_ci | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; 6268c2ecf20Sopenharmony_ci if (!hsync_wid) 6278c2ecf20Sopenharmony_ci hsync_wid = 1; 6288c2ecf20Sopenharmony_ci hsync_start = mode->crtc_hsync_start - 8; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) 6318c2ecf20Sopenharmony_ci | ((hsync_wid & 0x3f) << 16) 6328c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NHSYNC) 6338c2ecf20Sopenharmony_ci ? RADEON_CRTC_H_SYNC_POL 6348c2ecf20Sopenharmony_ci : 0)); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* This works for double scan mode. */ 6378c2ecf20Sopenharmony_ci crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) 6388c2ecf20Sopenharmony_ci | ((mode->crtc_vdisplay - 1) << 16)); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; 6418c2ecf20Sopenharmony_ci if (!vsync_wid) 6428c2ecf20Sopenharmony_ci vsync_wid = 1; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci crtc_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) 6458c2ecf20Sopenharmony_ci | ((vsync_wid & 0x1f) << 16) 6468c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NVSYNC) 6478c2ecf20Sopenharmony_ci ? RADEON_CRTC_V_SYNC_POL 6488c2ecf20Sopenharmony_ci : 0)); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id) { 6518c2ecf20Sopenharmony_ci uint32_t crtc2_gen_cntl; 6528c2ecf20Sopenharmony_ci uint32_t disp2_merge_cntl; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* if TV DAC is enabled for another crtc and keep it enabled */ 6558c2ecf20Sopenharmony_ci crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0x00718080; 6568c2ecf20Sopenharmony_ci crtc2_gen_cntl |= ((format << 8) 6578c2ecf20Sopenharmony_ci | RADEON_CRTC2_VSYNC_DIS 6588c2ecf20Sopenharmony_ci | RADEON_CRTC2_HSYNC_DIS 6598c2ecf20Sopenharmony_ci | RADEON_CRTC2_DISP_DIS 6608c2ecf20Sopenharmony_ci | RADEON_CRTC2_DISP_REQ_EN_B 6618c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_DBLSCAN) 6628c2ecf20Sopenharmony_ci ? RADEON_CRTC2_DBL_SCAN_EN 6638c2ecf20Sopenharmony_ci : 0) 6648c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_CSYNC) 6658c2ecf20Sopenharmony_ci ? RADEON_CRTC2_CSYNC_EN 6668c2ecf20Sopenharmony_ci : 0) 6678c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_INTERLACE) 6688c2ecf20Sopenharmony_ci ? RADEON_CRTC2_INTERLACE_EN 6698c2ecf20Sopenharmony_ci : 0)); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* rs4xx chips seem to like to have the crtc enabled when the timing is set */ 6728c2ecf20Sopenharmony_ci if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480)) 6738c2ecf20Sopenharmony_ci crtc2_gen_cntl |= RADEON_CRTC2_EN; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci disp2_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL); 6768c2ecf20Sopenharmony_ci disp2_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci WREG32(RADEON_DISP2_MERGE_CNTL, disp2_merge_cntl); 6798c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci WREG32(RADEON_FP_H2_SYNC_STRT_WID, crtc_h_sync_strt_wid); 6828c2ecf20Sopenharmony_ci WREG32(RADEON_FP_V2_SYNC_STRT_WID, crtc_v_sync_strt_wid); 6838c2ecf20Sopenharmony_ci } else { 6848c2ecf20Sopenharmony_ci uint32_t crtc_gen_cntl; 6858c2ecf20Sopenharmony_ci uint32_t crtc_ext_cntl; 6868c2ecf20Sopenharmony_ci uint32_t disp_merge_cntl; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0x00718000; 6898c2ecf20Sopenharmony_ci crtc_gen_cntl |= (RADEON_CRTC_EXT_DISP_EN 6908c2ecf20Sopenharmony_ci | (format << 8) 6918c2ecf20Sopenharmony_ci | RADEON_CRTC_DISP_REQ_EN_B 6928c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_DBLSCAN) 6938c2ecf20Sopenharmony_ci ? RADEON_CRTC_DBL_SCAN_EN 6948c2ecf20Sopenharmony_ci : 0) 6958c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_CSYNC) 6968c2ecf20Sopenharmony_ci ? RADEON_CRTC_CSYNC_EN 6978c2ecf20Sopenharmony_ci : 0) 6988c2ecf20Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_INTERLACE) 6998c2ecf20Sopenharmony_ci ? RADEON_CRTC_INTERLACE_EN 7008c2ecf20Sopenharmony_ci : 0)); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* rs4xx chips seem to like to have the crtc enabled when the timing is set */ 7038c2ecf20Sopenharmony_ci if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480)) 7048c2ecf20Sopenharmony_ci crtc_gen_cntl |= RADEON_CRTC_EN; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); 7078c2ecf20Sopenharmony_ci crtc_ext_cntl |= (RADEON_XCRT_CNT_EN | 7088c2ecf20Sopenharmony_ci RADEON_CRTC_VSYNC_DIS | 7098c2ecf20Sopenharmony_ci RADEON_CRTC_HSYNC_DIS | 7108c2ecf20Sopenharmony_ci RADEON_CRTC_DISPLAY_DIS); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL); 7138c2ecf20Sopenharmony_ci disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl); 7168c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl); 7178c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (is_tv) 7218c2ecf20Sopenharmony_ci radeon_legacy_tv_adjust_crtc_reg(encoder, &crtc_h_total_disp, 7228c2ecf20Sopenharmony_ci &crtc_h_sync_strt_wid, &crtc_v_total_disp, 7238c2ecf20Sopenharmony_ci &crtc_v_sync_strt_wid); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_H_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_h_total_disp); 7268c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_H_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_h_sync_strt_wid); 7278c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_V_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_v_total_disp); 7288c2ecf20Sopenharmony_ci WREG32(RADEON_CRTC_V_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_v_sync_strt_wid); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return true; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 7368c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 7378c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 7388c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 7398c2ecf20Sopenharmony_ci uint32_t feedback_div = 0; 7408c2ecf20Sopenharmony_ci uint32_t frac_fb_div = 0; 7418c2ecf20Sopenharmony_ci uint32_t reference_div = 0; 7428c2ecf20Sopenharmony_ci uint32_t post_divider = 0; 7438c2ecf20Sopenharmony_ci uint32_t freq = 0; 7448c2ecf20Sopenharmony_ci uint8_t pll_gain; 7458c2ecf20Sopenharmony_ci bool use_bios_divs = false; 7468c2ecf20Sopenharmony_ci /* PLL registers */ 7478c2ecf20Sopenharmony_ci uint32_t pll_ref_div = 0; 7488c2ecf20Sopenharmony_ci uint32_t pll_fb_post_div = 0; 7498c2ecf20Sopenharmony_ci uint32_t htotal_cntl = 0; 7508c2ecf20Sopenharmony_ci bool is_tv = false; 7518c2ecf20Sopenharmony_ci struct radeon_pll *pll; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci struct { 7548c2ecf20Sopenharmony_ci int divider; 7558c2ecf20Sopenharmony_ci int bitvalue; 7568c2ecf20Sopenharmony_ci } *post_div, post_divs[] = { 7578c2ecf20Sopenharmony_ci /* From RAGE 128 VR/RAGE 128 GL Register 7588c2ecf20Sopenharmony_ci * Reference Manual (Technical Reference 7598c2ecf20Sopenharmony_ci * Manual P/N RRG-G04100-C Rev. 0.04), page 7608c2ecf20Sopenharmony_ci * 3-17 (PLL_DIV_[3:0]). 7618c2ecf20Sopenharmony_ci */ 7628c2ecf20Sopenharmony_ci { 1, 0 }, /* VCLK_SRC */ 7638c2ecf20Sopenharmony_ci { 2, 1 }, /* VCLK_SRC/2 */ 7648c2ecf20Sopenharmony_ci { 4, 2 }, /* VCLK_SRC/4 */ 7658c2ecf20Sopenharmony_ci { 8, 3 }, /* VCLK_SRC/8 */ 7668c2ecf20Sopenharmony_ci { 3, 4 }, /* VCLK_SRC/3 */ 7678c2ecf20Sopenharmony_ci { 16, 5 }, /* VCLK_SRC/16 */ 7688c2ecf20Sopenharmony_ci { 6, 6 }, /* VCLK_SRC/6 */ 7698c2ecf20Sopenharmony_ci { 12, 7 }, /* VCLK_SRC/12 */ 7708c2ecf20Sopenharmony_ci { 0, 0 } 7718c2ecf20Sopenharmony_ci }; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id) 7748c2ecf20Sopenharmony_ci pll = &rdev->clock.p2pll; 7758c2ecf20Sopenharmony_ci else 7768c2ecf20Sopenharmony_ci pll = &rdev->clock.p1pll; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci pll->flags = RADEON_PLL_LEGACY; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (mode->clock > 200000) /* range limits??? */ 7818c2ecf20Sopenharmony_ci pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; 7828c2ecf20Sopenharmony_ci else 7838c2ecf20Sopenharmony_ci pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 7868c2ecf20Sopenharmony_ci if (encoder->crtc == crtc) { 7878c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { 7908c2ecf20Sopenharmony_ci is_tv = true; 7918c2ecf20Sopenharmony_ci break; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) 7958c2ecf20Sopenharmony_ci pll->flags |= RADEON_PLL_NO_ODD_POST_DIV; 7968c2ecf20Sopenharmony_ci if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) { 7978c2ecf20Sopenharmony_ci if (!rdev->is_atom_bios) { 7988c2ecf20Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 7998c2ecf20Sopenharmony_ci struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv; 8008c2ecf20Sopenharmony_ci if (lvds) { 8018c2ecf20Sopenharmony_ci if (lvds->use_bios_dividers) { 8028c2ecf20Sopenharmony_ci pll_ref_div = lvds->panel_ref_divider; 8038c2ecf20Sopenharmony_ci pll_fb_post_div = (lvds->panel_fb_divider | 8048c2ecf20Sopenharmony_ci (lvds->panel_post_divider << 16)); 8058c2ecf20Sopenharmony_ci htotal_cntl = 0; 8068c2ecf20Sopenharmony_ci use_bios_divs = true; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci pll->flags |= RADEON_PLL_USE_REF_DIV; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("\n"); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (!use_bios_divs) { 8188c2ecf20Sopenharmony_ci radeon_compute_pll_legacy(pll, mode->clock, 8198c2ecf20Sopenharmony_ci &freq, &feedback_div, &frac_fb_div, 8208c2ecf20Sopenharmony_ci &reference_div, &post_divider); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci for (post_div = &post_divs[0]; post_div->divider; ++post_div) { 8238c2ecf20Sopenharmony_ci if (post_div->divider == post_divider) 8248c2ecf20Sopenharmony_ci break; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (!post_div->divider) 8288c2ecf20Sopenharmony_ci post_div = &post_divs[0]; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("dc=%u, fd=%d, rd=%d, pd=%d\n", 8318c2ecf20Sopenharmony_ci (unsigned)freq, 8328c2ecf20Sopenharmony_ci feedback_div, 8338c2ecf20Sopenharmony_ci reference_div, 8348c2ecf20Sopenharmony_ci post_divider); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci pll_ref_div = reference_div; 8378c2ecf20Sopenharmony_ci#if defined(__powerpc__) && (0) /* TODO */ 8388c2ecf20Sopenharmony_ci /* apparently programming this otherwise causes a hang??? */ 8398c2ecf20Sopenharmony_ci if (info->MacModel == RADEON_MAC_IBOOK) 8408c2ecf20Sopenharmony_ci pll_fb_post_div = 0x000600ad; 8418c2ecf20Sopenharmony_ci else 8428c2ecf20Sopenharmony_ci#endif 8438c2ecf20Sopenharmony_ci pll_fb_post_div = (feedback_div | (post_div->bitvalue << 16)); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci htotal_cntl = mode->htotal & 0x7; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci pll_gain = radeon_compute_pll_gain(pll->reference_freq, 8508c2ecf20Sopenharmony_ci pll_ref_div & 0x3ff, 8518c2ecf20Sopenharmony_ci pll_fb_post_div & 0x7ff); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id) { 8548c2ecf20Sopenharmony_ci uint32_t pixclks_cntl = ((RREG32_PLL(RADEON_PIXCLKS_CNTL) & 8558c2ecf20Sopenharmony_ci ~(RADEON_PIX2CLK_SRC_SEL_MASK)) | 8568c2ecf20Sopenharmony_ci RADEON_PIX2CLK_SRC_SEL_P2PLLCLK); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (is_tv) { 8598c2ecf20Sopenharmony_ci radeon_legacy_tv_adjust_pll2(encoder, &htotal_cntl, 8608c2ecf20Sopenharmony_ci &pll_ref_div, &pll_fb_post_div, 8618c2ecf20Sopenharmony_ci &pixclks_cntl); 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PIXCLKS_CNTL, 8658c2ecf20Sopenharmony_ci RADEON_PIX2CLK_SRC_SEL_CPUCLK, 8668c2ecf20Sopenharmony_ci ~(RADEON_PIX2CLK_SRC_SEL_MASK)); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_CNTL, 8698c2ecf20Sopenharmony_ci RADEON_P2PLL_RESET 8708c2ecf20Sopenharmony_ci | RADEON_P2PLL_ATOMIC_UPDATE_EN 8718c2ecf20Sopenharmony_ci | ((uint32_t)pll_gain << RADEON_P2PLL_PVG_SHIFT), 8728c2ecf20Sopenharmony_ci ~(RADEON_P2PLL_RESET 8738c2ecf20Sopenharmony_ci | RADEON_P2PLL_ATOMIC_UPDATE_EN 8748c2ecf20Sopenharmony_ci | RADEON_P2PLL_PVG_MASK)); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_REF_DIV, 8778c2ecf20Sopenharmony_ci pll_ref_div, 8788c2ecf20Sopenharmony_ci ~RADEON_P2PLL_REF_DIV_MASK); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_DIV_0, 8818c2ecf20Sopenharmony_ci pll_fb_post_div, 8828c2ecf20Sopenharmony_ci ~RADEON_P2PLL_FB0_DIV_MASK); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_DIV_0, 8858c2ecf20Sopenharmony_ci pll_fb_post_div, 8868c2ecf20Sopenharmony_ci ~RADEON_P2PLL_POST0_DIV_MASK); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci radeon_pll2_write_update(dev); 8898c2ecf20Sopenharmony_ci radeon_pll2_wait_for_read_update_complete(dev); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci WREG32_PLL(RADEON_HTOTAL2_CNTL, htotal_cntl); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_CNTL, 8948c2ecf20Sopenharmony_ci 0, 8958c2ecf20Sopenharmony_ci ~(RADEON_P2PLL_RESET 8968c2ecf20Sopenharmony_ci | RADEON_P2PLL_SLEEP 8978c2ecf20Sopenharmony_ci | RADEON_P2PLL_ATOMIC_UPDATE_EN)); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Wrote2: 0x%08x 0x%08x 0x%08x (0x%08x)\n", 9008c2ecf20Sopenharmony_ci (unsigned)pll_ref_div, 9018c2ecf20Sopenharmony_ci (unsigned)pll_fb_post_div, 9028c2ecf20Sopenharmony_ci (unsigned)htotal_cntl, 9038c2ecf20Sopenharmony_ci RREG32_PLL(RADEON_P2PLL_CNTL)); 9048c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Wrote2: rd=%u, fd=%u, pd=%u\n", 9058c2ecf20Sopenharmony_ci (unsigned)pll_ref_div & RADEON_P2PLL_REF_DIV_MASK, 9068c2ecf20Sopenharmony_ci (unsigned)pll_fb_post_div & RADEON_P2PLL_FB0_DIV_MASK, 9078c2ecf20Sopenharmony_ci (unsigned)((pll_fb_post_div & 9088c2ecf20Sopenharmony_ci RADEON_P2PLL_POST0_DIV_MASK) >> 16)); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci mdelay(50); /* Let the clock to lock */ 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PIXCLKS_CNTL, 9138c2ecf20Sopenharmony_ci RADEON_PIX2CLK_SRC_SEL_P2PLLCLK, 9148c2ecf20Sopenharmony_ci ~(RADEON_PIX2CLK_SRC_SEL_MASK)); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); 9178c2ecf20Sopenharmony_ci } else { 9188c2ecf20Sopenharmony_ci uint32_t pixclks_cntl; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (is_tv) { 9228c2ecf20Sopenharmony_ci pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL); 9238c2ecf20Sopenharmony_ci radeon_legacy_tv_adjust_pll1(encoder, &htotal_cntl, &pll_ref_div, 9248c2ecf20Sopenharmony_ci &pll_fb_post_div, &pixclks_cntl); 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (rdev->flags & RADEON_IS_MOBILITY) { 9288c2ecf20Sopenharmony_ci /* A temporal workaround for the occasional blanking on certain laptop panels. 9298c2ecf20Sopenharmony_ci This appears to related to the PLL divider registers (fail to lock?). 9308c2ecf20Sopenharmony_ci It occurs even when all dividers are the same with their old settings. 9318c2ecf20Sopenharmony_ci In this case we really don't need to fiddle with PLL registers. 9328c2ecf20Sopenharmony_ci By doing this we can avoid the blanking problem with some panels. 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci if ((pll_ref_div == (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_REF_DIV_MASK)) && 9358c2ecf20Sopenharmony_ci (pll_fb_post_div == (RREG32_PLL(RADEON_PPLL_DIV_3) & 9368c2ecf20Sopenharmony_ci (RADEON_PPLL_POST3_DIV_MASK | RADEON_PPLL_FB3_DIV_MASK)))) { 9378c2ecf20Sopenharmony_ci WREG32_P(RADEON_CLOCK_CNTL_INDEX, 9388c2ecf20Sopenharmony_ci RADEON_PLL_DIV_SEL, 9398c2ecf20Sopenharmony_ci ~(RADEON_PLL_DIV_SEL)); 9408c2ecf20Sopenharmony_ci r100_pll_errata_after_index(rdev); 9418c2ecf20Sopenharmony_ci return; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, 9468c2ecf20Sopenharmony_ci RADEON_VCLK_SRC_SEL_CPUCLK, 9478c2ecf20Sopenharmony_ci ~(RADEON_VCLK_SRC_SEL_MASK)); 9488c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_CNTL, 9498c2ecf20Sopenharmony_ci RADEON_PPLL_RESET 9508c2ecf20Sopenharmony_ci | RADEON_PPLL_ATOMIC_UPDATE_EN 9518c2ecf20Sopenharmony_ci | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN 9528c2ecf20Sopenharmony_ci | ((uint32_t)pll_gain << RADEON_PPLL_PVG_SHIFT), 9538c2ecf20Sopenharmony_ci ~(RADEON_PPLL_RESET 9548c2ecf20Sopenharmony_ci | RADEON_PPLL_ATOMIC_UPDATE_EN 9558c2ecf20Sopenharmony_ci | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN 9568c2ecf20Sopenharmony_ci | RADEON_PPLL_PVG_MASK)); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci WREG32_P(RADEON_CLOCK_CNTL_INDEX, 9598c2ecf20Sopenharmony_ci RADEON_PLL_DIV_SEL, 9608c2ecf20Sopenharmony_ci ~(RADEON_PLL_DIV_SEL)); 9618c2ecf20Sopenharmony_ci r100_pll_errata_after_index(rdev); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (ASIC_IS_R300(rdev) || 9648c2ecf20Sopenharmony_ci (rdev->family == CHIP_RS300) || 9658c2ecf20Sopenharmony_ci (rdev->family == CHIP_RS400) || 9668c2ecf20Sopenharmony_ci (rdev->family == CHIP_RS480)) { 9678c2ecf20Sopenharmony_ci if (pll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { 9688c2ecf20Sopenharmony_ci /* When restoring console mode, use saved PPLL_REF_DIV 9698c2ecf20Sopenharmony_ci * setting. 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_REF_DIV, 9728c2ecf20Sopenharmony_ci pll_ref_div, 9738c2ecf20Sopenharmony_ci 0); 9748c2ecf20Sopenharmony_ci } else { 9758c2ecf20Sopenharmony_ci /* R300 uses ref_div_acc field as real ref divider */ 9768c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_REF_DIV, 9778c2ecf20Sopenharmony_ci (pll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), 9788c2ecf20Sopenharmony_ci ~R300_PPLL_REF_DIV_ACC_MASK); 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci } else 9818c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_REF_DIV, 9828c2ecf20Sopenharmony_ci pll_ref_div, 9838c2ecf20Sopenharmony_ci ~RADEON_PPLL_REF_DIV_MASK); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_DIV_3, 9868c2ecf20Sopenharmony_ci pll_fb_post_div, 9878c2ecf20Sopenharmony_ci ~RADEON_PPLL_FB3_DIV_MASK); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_DIV_3, 9908c2ecf20Sopenharmony_ci pll_fb_post_div, 9918c2ecf20Sopenharmony_ci ~RADEON_PPLL_POST3_DIV_MASK); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci radeon_pll_write_update(dev); 9948c2ecf20Sopenharmony_ci radeon_pll_wait_for_read_update_complete(dev); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci WREG32_PLL(RADEON_HTOTAL_CNTL, htotal_cntl); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_CNTL, 9998c2ecf20Sopenharmony_ci 0, 10008c2ecf20Sopenharmony_ci ~(RADEON_PPLL_RESET 10018c2ecf20Sopenharmony_ci | RADEON_PPLL_SLEEP 10028c2ecf20Sopenharmony_ci | RADEON_PPLL_ATOMIC_UPDATE_EN 10038c2ecf20Sopenharmony_ci | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN)); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n", 10068c2ecf20Sopenharmony_ci pll_ref_div, 10078c2ecf20Sopenharmony_ci pll_fb_post_div, 10088c2ecf20Sopenharmony_ci (unsigned)htotal_cntl, 10098c2ecf20Sopenharmony_ci RREG32_PLL(RADEON_PPLL_CNTL)); 10108c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Wrote: rd=%d, fd=%d, pd=%d\n", 10118c2ecf20Sopenharmony_ci pll_ref_div & RADEON_PPLL_REF_DIV_MASK, 10128c2ecf20Sopenharmony_ci pll_fb_post_div & RADEON_PPLL_FB3_DIV_MASK, 10138c2ecf20Sopenharmony_ci (pll_fb_post_div & RADEON_PPLL_POST3_DIV_MASK) >> 16); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci mdelay(50); /* Let the clock to lock */ 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, 10188c2ecf20Sopenharmony_ci RADEON_VCLK_SRC_SEL_PPLLCLK, 10198c2ecf20Sopenharmony_ci ~(RADEON_VCLK_SRC_SEL_MASK)); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (is_tv) 10228c2ecf20Sopenharmony_ci WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic bool radeon_crtc_mode_fixup(struct drm_crtc *crtc, 10278c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 10288c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode)) 10318c2ecf20Sopenharmony_ci return false; 10328c2ecf20Sopenharmony_ci return true; 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic int radeon_crtc_mode_set(struct drm_crtc *crtc, 10368c2ecf20Sopenharmony_ci struct drm_display_mode *mode, 10378c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode, 10388c2ecf20Sopenharmony_ci int x, int y, struct drm_framebuffer *old_fb) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* TODO TV */ 10438c2ecf20Sopenharmony_ci radeon_crtc_set_base(crtc, x, y, old_fb); 10448c2ecf20Sopenharmony_ci radeon_set_crtc_timing(crtc, adjusted_mode); 10458c2ecf20Sopenharmony_ci radeon_set_pll(crtc, adjusted_mode); 10468c2ecf20Sopenharmony_ci radeon_overscan_setup(crtc, adjusted_mode); 10478c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id == 0) { 10488c2ecf20Sopenharmony_ci radeon_legacy_rmx_mode_set(crtc, adjusted_mode); 10498c2ecf20Sopenharmony_ci } else { 10508c2ecf20Sopenharmony_ci if (radeon_crtc->rmx_type != RMX_OFF) { 10518c2ecf20Sopenharmony_ci /* FIXME: only first crtc has rmx what should we 10528c2ecf20Sopenharmony_ci * do ? 10538c2ecf20Sopenharmony_ci */ 10548c2ecf20Sopenharmony_ci DRM_ERROR("Mode need scaling but only first crtc can do that.\n"); 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci radeon_cursor_reset(crtc); 10588c2ecf20Sopenharmony_ci return 0; 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cistatic void radeon_crtc_prepare(struct drm_crtc *crtc) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 10648c2ecf20Sopenharmony_ci struct drm_crtc *crtci; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* 10678c2ecf20Sopenharmony_ci * The hardware wedges sometimes if you reconfigure one CRTC 10688c2ecf20Sopenharmony_ci * whilst another is running (see fdo bug #24611). 10698c2ecf20Sopenharmony_ci */ 10708c2ecf20Sopenharmony_ci list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) 10718c2ecf20Sopenharmony_ci radeon_crtc_dpms(crtci, DRM_MODE_DPMS_OFF); 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_cistatic void radeon_crtc_commit(struct drm_crtc *crtc) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 10778c2ecf20Sopenharmony_ci struct drm_crtc *crtci; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* 10808c2ecf20Sopenharmony_ci * Reenable the CRTCs that should be running. 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_ci list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) { 10838c2ecf20Sopenharmony_ci if (crtci->enabled) 10848c2ecf20Sopenharmony_ci radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON); 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_cistatic void radeon_crtc_disable(struct drm_crtc *crtc) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); 10918c2ecf20Sopenharmony_ci if (crtc->primary->fb) { 10928c2ecf20Sopenharmony_ci int r; 10938c2ecf20Sopenharmony_ci struct radeon_bo *rbo; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci rbo = gem_to_radeon_bo(crtc->primary->fb->obj[0]); 10968c2ecf20Sopenharmony_ci r = radeon_bo_reserve(rbo, false); 10978c2ecf20Sopenharmony_ci if (unlikely(r)) 10988c2ecf20Sopenharmony_ci DRM_ERROR("failed to reserve rbo before unpin\n"); 10998c2ecf20Sopenharmony_ci else { 11008c2ecf20Sopenharmony_ci radeon_bo_unpin(rbo); 11018c2ecf20Sopenharmony_ci radeon_bo_unreserve(rbo); 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs legacy_helper_funcs = { 11078c2ecf20Sopenharmony_ci .dpms = radeon_crtc_dpms, 11088c2ecf20Sopenharmony_ci .mode_fixup = radeon_crtc_mode_fixup, 11098c2ecf20Sopenharmony_ci .mode_set = radeon_crtc_mode_set, 11108c2ecf20Sopenharmony_ci .mode_set_base = radeon_crtc_set_base, 11118c2ecf20Sopenharmony_ci .mode_set_base_atomic = radeon_crtc_set_base_atomic, 11128c2ecf20Sopenharmony_ci .prepare = radeon_crtc_prepare, 11138c2ecf20Sopenharmony_ci .commit = radeon_crtc_commit, 11148c2ecf20Sopenharmony_ci .disable = radeon_crtc_disable, 11158c2ecf20Sopenharmony_ci .get_scanout_position = radeon_get_crtc_scanout_position, 11168c2ecf20Sopenharmony_ci}; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_civoid radeon_legacy_init_crtc(struct drm_device *dev, 11208c2ecf20Sopenharmony_ci struct radeon_crtc *radeon_crtc) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci if (radeon_crtc->crtc_id == 1) 11238c2ecf20Sopenharmony_ci radeon_crtc->crtc_offset = RADEON_CRTC2_H_TOTAL_DISP - RADEON_CRTC_H_TOTAL_DISP; 11248c2ecf20Sopenharmony_ci drm_crtc_helper_add(&radeon_crtc->base, &legacy_helper_funcs); 11258c2ecf20Sopenharmony_ci} 1126