162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2007-8 Advanced Micro Devices, Inc. 362306a36Sopenharmony_ci * Copyright 2008 Red Hat Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 662306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 762306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 862306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 962306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1062306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1362306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1862306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1962306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2062306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2162306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Authors: Dave Airlie 2462306a36Sopenharmony_ci * Alex Deucher 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <drm/drm_fixed.h> 2862306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2962306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 3062306a36Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h> 3162306a36Sopenharmony_ci#include <drm/drm_vblank.h> 3262306a36Sopenharmony_ci#include <drm/radeon_drm.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "atom.h" 3562306a36Sopenharmony_ci#include "radeon.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void radeon_overscan_setup(struct drm_crtc *crtc, 3862306a36Sopenharmony_ci struct drm_display_mode *mode) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 4162306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 4262306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci WREG32(RADEON_OVR_CLR + radeon_crtc->crtc_offset, 0); 4562306a36Sopenharmony_ci WREG32(RADEON_OVR_WID_LEFT_RIGHT + radeon_crtc->crtc_offset, 0); 4662306a36Sopenharmony_ci WREG32(RADEON_OVR_WID_TOP_BOTTOM + radeon_crtc->crtc_offset, 0); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc, 5062306a36Sopenharmony_ci struct drm_display_mode *mode) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 5362306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 5462306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 5562306a36Sopenharmony_ci int xres = mode->hdisplay; 5662306a36Sopenharmony_ci int yres = mode->vdisplay; 5762306a36Sopenharmony_ci bool hscale = true, vscale = true; 5862306a36Sopenharmony_ci int hsync_wid; 5962306a36Sopenharmony_ci int vsync_wid; 6062306a36Sopenharmony_ci int hsync_start; 6162306a36Sopenharmony_ci int blank_width; 6262306a36Sopenharmony_ci u32 scale, inc, crtc_more_cntl; 6362306a36Sopenharmony_ci u32 fp_horz_stretch, fp_vert_stretch, fp_horz_vert_active; 6462306a36Sopenharmony_ci u32 fp_h_sync_strt_wid, fp_crtc_h_total_disp; 6562306a36Sopenharmony_ci u32 fp_v_sync_strt_wid, fp_crtc_v_total_disp; 6662306a36Sopenharmony_ci struct drm_display_mode *native_mode = &radeon_crtc->native_mode; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH) & 6962306a36Sopenharmony_ci (RADEON_VERT_STRETCH_RESERVED | 7062306a36Sopenharmony_ci RADEON_VERT_AUTO_RATIO_INC); 7162306a36Sopenharmony_ci fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH) & 7262306a36Sopenharmony_ci (RADEON_HORZ_FP_LOOP_STRETCH | 7362306a36Sopenharmony_ci RADEON_HORZ_AUTO_RATIO_INC); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci crtc_more_cntl = 0; 7662306a36Sopenharmony_ci if ((rdev->family == CHIP_RS100) || 7762306a36Sopenharmony_ci (rdev->family == CHIP_RS200)) { 7862306a36Sopenharmony_ci /* This is to workaround the asic bug for RMX, some versions 7962306a36Sopenharmony_ci of BIOS dosen't have this register initialized correctly. */ 8062306a36Sopenharmony_ci crtc_more_cntl |= RADEON_CRTC_H_CUTOFF_ACTIVE_EN; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci fp_crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) 8562306a36Sopenharmony_ci | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; 8862306a36Sopenharmony_ci if (!hsync_wid) 8962306a36Sopenharmony_ci hsync_wid = 1; 9062306a36Sopenharmony_ci hsync_start = mode->crtc_hsync_start - 8; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci fp_h_sync_strt_wid = ((hsync_start & 0x1fff) 9362306a36Sopenharmony_ci | ((hsync_wid & 0x3f) << 16) 9462306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NHSYNC) 9562306a36Sopenharmony_ci ? RADEON_CRTC_H_SYNC_POL 9662306a36Sopenharmony_ci : 0)); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci fp_crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) 9962306a36Sopenharmony_ci | ((mode->crtc_vdisplay - 1) << 16)); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; 10262306a36Sopenharmony_ci if (!vsync_wid) 10362306a36Sopenharmony_ci vsync_wid = 1; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci fp_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) 10662306a36Sopenharmony_ci | ((vsync_wid & 0x1f) << 16) 10762306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NVSYNC) 10862306a36Sopenharmony_ci ? RADEON_CRTC_V_SYNC_POL 10962306a36Sopenharmony_ci : 0)); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci fp_horz_vert_active = 0; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (native_mode->hdisplay == 0 || 11462306a36Sopenharmony_ci native_mode->vdisplay == 0) { 11562306a36Sopenharmony_ci hscale = false; 11662306a36Sopenharmony_ci vscale = false; 11762306a36Sopenharmony_ci } else { 11862306a36Sopenharmony_ci if (xres > native_mode->hdisplay) 11962306a36Sopenharmony_ci xres = native_mode->hdisplay; 12062306a36Sopenharmony_ci if (yres > native_mode->vdisplay) 12162306a36Sopenharmony_ci yres = native_mode->vdisplay; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (xres == native_mode->hdisplay) 12462306a36Sopenharmony_ci hscale = false; 12562306a36Sopenharmony_ci if (yres == native_mode->vdisplay) 12662306a36Sopenharmony_ci vscale = false; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci switch (radeon_crtc->rmx_type) { 13062306a36Sopenharmony_ci case RMX_FULL: 13162306a36Sopenharmony_ci case RMX_ASPECT: 13262306a36Sopenharmony_ci if (!hscale) 13362306a36Sopenharmony_ci fp_horz_stretch |= ((xres/8-1) << 16); 13462306a36Sopenharmony_ci else { 13562306a36Sopenharmony_ci inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0; 13662306a36Sopenharmony_ci scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX) 13762306a36Sopenharmony_ci / native_mode->hdisplay + 1; 13862306a36Sopenharmony_ci fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) | 13962306a36Sopenharmony_ci RADEON_HORZ_STRETCH_BLEND | 14062306a36Sopenharmony_ci RADEON_HORZ_STRETCH_ENABLE | 14162306a36Sopenharmony_ci ((native_mode->hdisplay/8-1) << 16)); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (!vscale) 14562306a36Sopenharmony_ci fp_vert_stretch |= ((yres-1) << 12); 14662306a36Sopenharmony_ci else { 14762306a36Sopenharmony_ci inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0; 14862306a36Sopenharmony_ci scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX) 14962306a36Sopenharmony_ci / native_mode->vdisplay + 1; 15062306a36Sopenharmony_ci fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) | 15162306a36Sopenharmony_ci RADEON_VERT_STRETCH_ENABLE | 15262306a36Sopenharmony_ci RADEON_VERT_STRETCH_BLEND | 15362306a36Sopenharmony_ci ((native_mode->vdisplay-1) << 12)); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case RMX_CENTER: 15762306a36Sopenharmony_ci fp_horz_stretch |= ((xres/8-1) << 16); 15862306a36Sopenharmony_ci fp_vert_stretch |= ((yres-1) << 12); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci crtc_more_cntl |= (RADEON_CRTC_AUTO_HORZ_CENTER_EN | 16162306a36Sopenharmony_ci RADEON_CRTC_AUTO_VERT_CENTER_EN); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci blank_width = (mode->crtc_hblank_end - mode->crtc_hblank_start) / 8; 16462306a36Sopenharmony_ci if (blank_width > 110) 16562306a36Sopenharmony_ci blank_width = 110; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci fp_crtc_h_total_disp = (((blank_width) & 0x3ff) 16862306a36Sopenharmony_ci | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; 17162306a36Sopenharmony_ci if (!hsync_wid) 17262306a36Sopenharmony_ci hsync_wid = 1; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci fp_h_sync_strt_wid = ((((mode->crtc_hsync_start - mode->crtc_hblank_start) / 8) & 0x1fff) 17562306a36Sopenharmony_ci | ((hsync_wid & 0x3f) << 16) 17662306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NHSYNC) 17762306a36Sopenharmony_ci ? RADEON_CRTC_H_SYNC_POL 17862306a36Sopenharmony_ci : 0)); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci fp_crtc_v_total_disp = (((mode->crtc_vblank_end - mode->crtc_vblank_start) & 0xffff) 18162306a36Sopenharmony_ci | ((mode->crtc_vdisplay - 1) << 16)); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; 18462306a36Sopenharmony_ci if (!vsync_wid) 18562306a36Sopenharmony_ci vsync_wid = 1; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci fp_v_sync_strt_wid = ((((mode->crtc_vsync_start - mode->crtc_vblank_start) & 0xfff) 18862306a36Sopenharmony_ci | ((vsync_wid & 0x1f) << 16) 18962306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NVSYNC) 19062306a36Sopenharmony_ci ? RADEON_CRTC_V_SYNC_POL 19162306a36Sopenharmony_ci : 0))); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci fp_horz_vert_active = (((native_mode->vdisplay) & 0xfff) | 19462306a36Sopenharmony_ci (((native_mode->hdisplay / 8) & 0x1ff) << 16)); 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci case RMX_OFF: 19762306a36Sopenharmony_ci default: 19862306a36Sopenharmony_ci fp_horz_stretch |= ((xres/8-1) << 16); 19962306a36Sopenharmony_ci fp_vert_stretch |= ((yres-1) << 12); 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci WREG32(RADEON_FP_HORZ_STRETCH, fp_horz_stretch); 20462306a36Sopenharmony_ci WREG32(RADEON_FP_VERT_STRETCH, fp_vert_stretch); 20562306a36Sopenharmony_ci WREG32(RADEON_CRTC_MORE_CNTL, crtc_more_cntl); 20662306a36Sopenharmony_ci WREG32(RADEON_FP_HORZ_VERT_ACTIVE, fp_horz_vert_active); 20762306a36Sopenharmony_ci WREG32(RADEON_FP_H_SYNC_STRT_WID, fp_h_sync_strt_wid); 20862306a36Sopenharmony_ci WREG32(RADEON_FP_V_SYNC_STRT_WID, fp_v_sync_strt_wid); 20962306a36Sopenharmony_ci WREG32(RADEON_FP_CRTC_H_TOTAL_DISP, fp_crtc_h_total_disp); 21062306a36Sopenharmony_ci WREG32(RADEON_FP_CRTC_V_TOTAL_DISP, fp_crtc_v_total_disp); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void radeon_pll_wait_for_read_update_complete(struct drm_device *dev) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 21662306a36Sopenharmony_ci int i = 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* FIXME: Certain revisions of R300 can't recover here. Not sure of 21962306a36Sopenharmony_ci the cause yet, but this workaround will mask the problem for now. 22062306a36Sopenharmony_ci Other chips usually will pass at the very first test, so the 22162306a36Sopenharmony_ci workaround shouldn't have any effect on them. */ 22262306a36Sopenharmony_ci for (i = 0; 22362306a36Sopenharmony_ci (i < 10000 && 22462306a36Sopenharmony_ci RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); 22562306a36Sopenharmony_ci i++); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void radeon_pll_write_update(struct drm_device *dev) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci while (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_REF_DIV, 23562306a36Sopenharmony_ci RADEON_PPLL_ATOMIC_UPDATE_W, 23662306a36Sopenharmony_ci ~(RADEON_PPLL_ATOMIC_UPDATE_W)); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void radeon_pll2_wait_for_read_update_complete(struct drm_device *dev) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 24262306a36Sopenharmony_ci int i = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* FIXME: Certain revisions of R300 can't recover here. Not sure of 24662306a36Sopenharmony_ci the cause yet, but this workaround will mask the problem for now. 24762306a36Sopenharmony_ci Other chips usually will pass at the very first test, so the 24862306a36Sopenharmony_ci workaround shouldn't have any effect on them. */ 24962306a36Sopenharmony_ci for (i = 0; 25062306a36Sopenharmony_ci (i < 10000 && 25162306a36Sopenharmony_ci RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); 25262306a36Sopenharmony_ci i++); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic void radeon_pll2_write_update(struct drm_device *dev) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci while (RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_REF_DIV, 26262306a36Sopenharmony_ci RADEON_P2PLL_ATOMIC_UPDATE_W, 26362306a36Sopenharmony_ci ~(RADEON_P2PLL_ATOMIC_UPDATE_W)); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic uint8_t radeon_compute_pll_gain(uint16_t ref_freq, uint16_t ref_div, 26762306a36Sopenharmony_ci uint16_t fb_div) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci unsigned int vcoFreq; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (!ref_div) 27262306a36Sopenharmony_ci return 1; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci vcoFreq = ((unsigned)ref_freq * fb_div) / ref_div; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* 27762306a36Sopenharmony_ci * This is horribly crude: the VCO frequency range is divided into 27862306a36Sopenharmony_ci * 3 parts, each part having a fixed PLL gain value. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci if (vcoFreq >= 30000) 28162306a36Sopenharmony_ci /* 28262306a36Sopenharmony_ci * [300..max] MHz : 7 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci return 7; 28562306a36Sopenharmony_ci else if (vcoFreq >= 18000) 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * [180..300) MHz : 4 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci return 4; 29062306a36Sopenharmony_ci else 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * [0..180) MHz : 1 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci return 1; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 30062306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 30162306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 30262306a36Sopenharmony_ci uint32_t crtc_ext_cntl = 0; 30362306a36Sopenharmony_ci uint32_t mask; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (radeon_crtc->crtc_id) 30662306a36Sopenharmony_ci mask = (RADEON_CRTC2_DISP_DIS | 30762306a36Sopenharmony_ci RADEON_CRTC2_VSYNC_DIS | 30862306a36Sopenharmony_ci RADEON_CRTC2_HSYNC_DIS | 30962306a36Sopenharmony_ci RADEON_CRTC2_DISP_REQ_EN_B); 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci mask = (RADEON_CRTC_DISPLAY_DIS | 31262306a36Sopenharmony_ci RADEON_CRTC_VSYNC_DIS | 31362306a36Sopenharmony_ci RADEON_CRTC_HSYNC_DIS); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * On all dual CRTC GPUs this bit controls the CRTC of the primary DAC. 31762306a36Sopenharmony_ci * Therefore it is set in the DAC DMPS function. 31862306a36Sopenharmony_ci * This is different for GPU's with a single CRTC but a primary and a 31962306a36Sopenharmony_ci * TV DAC: here it controls the single CRTC no matter where it is 32062306a36Sopenharmony_ci * routed. Therefore we set it here. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci if (rdev->flags & RADEON_SINGLE_CRTC) 32362306a36Sopenharmony_ci crtc_ext_cntl = RADEON_CRTC_CRT_ON; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci switch (mode) { 32662306a36Sopenharmony_ci case DRM_MODE_DPMS_ON: 32762306a36Sopenharmony_ci radeon_crtc->enabled = true; 32862306a36Sopenharmony_ci /* adjust pm to dpms changes BEFORE enabling crtcs */ 32962306a36Sopenharmony_ci radeon_pm_compute_clocks(rdev); 33062306a36Sopenharmony_ci if (radeon_crtc->crtc_id) 33162306a36Sopenharmony_ci WREG32_P(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_EN, ~(RADEON_CRTC2_EN | mask)); 33262306a36Sopenharmony_ci else { 33362306a36Sopenharmony_ci WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_EN, ~(RADEON_CRTC_EN | 33462306a36Sopenharmony_ci RADEON_CRTC_DISP_REQ_EN_B)); 33562306a36Sopenharmony_ci WREG32_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, ~(mask | crtc_ext_cntl)); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci if (dev->num_crtcs > radeon_crtc->crtc_id) 33862306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 33962306a36Sopenharmony_ci radeon_crtc_load_lut(crtc); 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case DRM_MODE_DPMS_STANDBY: 34262306a36Sopenharmony_ci case DRM_MODE_DPMS_SUSPEND: 34362306a36Sopenharmony_ci case DRM_MODE_DPMS_OFF: 34462306a36Sopenharmony_ci if (dev->num_crtcs > radeon_crtc->crtc_id) 34562306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 34662306a36Sopenharmony_ci if (radeon_crtc->crtc_id) 34762306a36Sopenharmony_ci WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~(RADEON_CRTC2_EN | mask)); 34862306a36Sopenharmony_ci else { 34962306a36Sopenharmony_ci WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B, ~(RADEON_CRTC_EN | 35062306a36Sopenharmony_ci RADEON_CRTC_DISP_REQ_EN_B)); 35162306a36Sopenharmony_ci WREG32_P(RADEON_CRTC_EXT_CNTL, mask, ~(mask | crtc_ext_cntl)); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci radeon_crtc->enabled = false; 35462306a36Sopenharmony_ci /* adjust pm to dpms changes AFTER disabling crtcs */ 35562306a36Sopenharmony_ci radeon_pm_compute_clocks(rdev); 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ciint radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, 36162306a36Sopenharmony_ci struct drm_framebuffer *old_fb) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ciint radeon_crtc_set_base_atomic(struct drm_crtc *crtc, 36762306a36Sopenharmony_ci struct drm_framebuffer *fb, 36862306a36Sopenharmony_ci int x, int y, enum mode_set_atomic state) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci return radeon_crtc_do_set_base(crtc, fb, x, y, 1); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ciint radeon_crtc_do_set_base(struct drm_crtc *crtc, 37462306a36Sopenharmony_ci struct drm_framebuffer *fb, 37562306a36Sopenharmony_ci int x, int y, int atomic) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 37862306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 37962306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 38062306a36Sopenharmony_ci struct drm_framebuffer *target_fb; 38162306a36Sopenharmony_ci struct drm_gem_object *obj; 38262306a36Sopenharmony_ci struct radeon_bo *rbo; 38362306a36Sopenharmony_ci uint64_t base; 38462306a36Sopenharmony_ci uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0; 38562306a36Sopenharmony_ci uint32_t crtc_pitch, pitch_pixels; 38662306a36Sopenharmony_ci uint32_t tiling_flags; 38762306a36Sopenharmony_ci int format; 38862306a36Sopenharmony_ci uint32_t gen_cntl_reg, gen_cntl_val; 38962306a36Sopenharmony_ci int r; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci DRM_DEBUG_KMS("\n"); 39262306a36Sopenharmony_ci /* no fb bound */ 39362306a36Sopenharmony_ci if (!atomic && !crtc->primary->fb) { 39462306a36Sopenharmony_ci DRM_DEBUG_KMS("No FB bound\n"); 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (atomic) 39962306a36Sopenharmony_ci target_fb = fb; 40062306a36Sopenharmony_ci else 40162306a36Sopenharmony_ci target_fb = crtc->primary->fb; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci switch (target_fb->format->cpp[0] * 8) { 40462306a36Sopenharmony_ci case 8: 40562306a36Sopenharmony_ci format = 2; 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci case 15: /* 555 */ 40862306a36Sopenharmony_ci format = 3; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case 16: /* 565 */ 41162306a36Sopenharmony_ci format = 4; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case 24: /* RGB */ 41462306a36Sopenharmony_ci format = 5; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case 32: /* xRGB */ 41762306a36Sopenharmony_ci format = 6; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci default: 42062306a36Sopenharmony_ci return false; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Pin framebuffer & get tilling informations */ 42462306a36Sopenharmony_ci obj = target_fb->obj[0]; 42562306a36Sopenharmony_ci rbo = gem_to_radeon_bo(obj); 42662306a36Sopenharmony_ciretry: 42762306a36Sopenharmony_ci r = radeon_bo_reserve(rbo, false); 42862306a36Sopenharmony_ci if (unlikely(r != 0)) 42962306a36Sopenharmony_ci return r; 43062306a36Sopenharmony_ci /* Only 27 bit offset for legacy CRTC */ 43162306a36Sopenharmony_ci r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, 1 << 27, 43262306a36Sopenharmony_ci &base); 43362306a36Sopenharmony_ci if (unlikely(r != 0)) { 43462306a36Sopenharmony_ci radeon_bo_unreserve(rbo); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* On old GPU like RN50 with little vram pining can fails because 43762306a36Sopenharmony_ci * current fb is taking all space needed. So instead of unpining 43862306a36Sopenharmony_ci * the old buffer after pining the new one, first unpin old one 43962306a36Sopenharmony_ci * and then retry pining new one. 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * As only master can set mode only master can pin and it is 44262306a36Sopenharmony_ci * unlikely the master client will race with itself especialy 44362306a36Sopenharmony_ci * on those old gpu with single crtc. 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * We don't shutdown the display controller because new buffer 44662306a36Sopenharmony_ci * will end up in same spot. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci if (!atomic && fb && fb != crtc->primary->fb) { 44962306a36Sopenharmony_ci struct radeon_bo *old_rbo; 45062306a36Sopenharmony_ci unsigned long nsize, osize; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci old_rbo = gem_to_radeon_bo(fb->obj[0]); 45362306a36Sopenharmony_ci osize = radeon_bo_size(old_rbo); 45462306a36Sopenharmony_ci nsize = radeon_bo_size(rbo); 45562306a36Sopenharmony_ci if (nsize <= osize && !radeon_bo_reserve(old_rbo, false)) { 45662306a36Sopenharmony_ci radeon_bo_unpin(old_rbo); 45762306a36Sopenharmony_ci radeon_bo_unreserve(old_rbo); 45862306a36Sopenharmony_ci fb = NULL; 45962306a36Sopenharmony_ci goto retry; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); 46562306a36Sopenharmony_ci radeon_bo_unreserve(rbo); 46662306a36Sopenharmony_ci if (tiling_flags & RADEON_TILING_MICRO) 46762306a36Sopenharmony_ci DRM_ERROR("trying to scanout microtiled buffer\n"); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* if scanout was in GTT this really wouldn't work */ 47062306a36Sopenharmony_ci /* crtc offset is from display base addr not FB location */ 47162306a36Sopenharmony_ci radeon_crtc->legacy_display_base_addr = rdev->mc.vram_start; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci base -= radeon_crtc->legacy_display_base_addr; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci crtc_offset_cntl = 0; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0]; 47862306a36Sopenharmony_ci crtc_pitch = DIV_ROUND_UP(pitch_pixels * target_fb->format->cpp[0] * 8, 47962306a36Sopenharmony_ci target_fb->format->cpp[0] * 8 * 8); 48062306a36Sopenharmony_ci crtc_pitch |= crtc_pitch << 16; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci crtc_offset_cntl |= RADEON_CRTC_GUI_TRIG_OFFSET_LEFT_EN; 48362306a36Sopenharmony_ci if (tiling_flags & RADEON_TILING_MACRO) { 48462306a36Sopenharmony_ci if (ASIC_IS_R300(rdev)) 48562306a36Sopenharmony_ci crtc_offset_cntl |= (R300_CRTC_X_Y_MODE_EN | 48662306a36Sopenharmony_ci R300_CRTC_MICRO_TILE_BUFFER_DIS | 48762306a36Sopenharmony_ci R300_CRTC_MACRO_TILE_EN); 48862306a36Sopenharmony_ci else 48962306a36Sopenharmony_ci crtc_offset_cntl |= RADEON_CRTC_TILE_EN; 49062306a36Sopenharmony_ci } else { 49162306a36Sopenharmony_ci if (ASIC_IS_R300(rdev)) 49262306a36Sopenharmony_ci crtc_offset_cntl &= ~(R300_CRTC_X_Y_MODE_EN | 49362306a36Sopenharmony_ci R300_CRTC_MICRO_TILE_BUFFER_DIS | 49462306a36Sopenharmony_ci R300_CRTC_MACRO_TILE_EN); 49562306a36Sopenharmony_ci else 49662306a36Sopenharmony_ci crtc_offset_cntl &= ~RADEON_CRTC_TILE_EN; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (tiling_flags & RADEON_TILING_MACRO) { 50062306a36Sopenharmony_ci if (ASIC_IS_R300(rdev)) { 50162306a36Sopenharmony_ci crtc_tile_x0_y0 = x | (y << 16); 50262306a36Sopenharmony_ci base &= ~0x7ff; 50362306a36Sopenharmony_ci } else { 50462306a36Sopenharmony_ci int byteshift = target_fb->format->cpp[0] * 8 >> 4; 50562306a36Sopenharmony_ci int tile_addr = (((y >> 3) * pitch_pixels + x) >> (8 - byteshift)) << 11; 50662306a36Sopenharmony_ci base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8); 50762306a36Sopenharmony_ci crtc_offset_cntl |= (y % 16); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } else { 51062306a36Sopenharmony_ci int offset = y * pitch_pixels + x; 51162306a36Sopenharmony_ci switch (target_fb->format->cpp[0] * 8) { 51262306a36Sopenharmony_ci case 8: 51362306a36Sopenharmony_ci offset *= 1; 51462306a36Sopenharmony_ci break; 51562306a36Sopenharmony_ci case 15: 51662306a36Sopenharmony_ci case 16: 51762306a36Sopenharmony_ci offset *= 2; 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci case 24: 52062306a36Sopenharmony_ci offset *= 3; 52162306a36Sopenharmony_ci break; 52262306a36Sopenharmony_ci case 32: 52362306a36Sopenharmony_ci offset *= 4; 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci default: 52662306a36Sopenharmony_ci return false; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci base += offset; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci base &= ~7; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (radeon_crtc->crtc_id == 1) 53462306a36Sopenharmony_ci gen_cntl_reg = RADEON_CRTC2_GEN_CNTL; 53562306a36Sopenharmony_ci else 53662306a36Sopenharmony_ci gen_cntl_reg = RADEON_CRTC_GEN_CNTL; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci gen_cntl_val = RREG32(gen_cntl_reg); 53962306a36Sopenharmony_ci gen_cntl_val &= ~(0xf << 8); 54062306a36Sopenharmony_ci gen_cntl_val |= (format << 8); 54162306a36Sopenharmony_ci gen_cntl_val &= ~RADEON_CRTC_VSTAT_MODE_MASK; 54262306a36Sopenharmony_ci WREG32(gen_cntl_reg, gen_cntl_val); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci crtc_offset = (u32)base; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci WREG32(RADEON_DISPLAY_BASE_ADDR + radeon_crtc->crtc_offset, radeon_crtc->legacy_display_base_addr); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (ASIC_IS_R300(rdev)) { 54962306a36Sopenharmony_ci if (radeon_crtc->crtc_id) 55062306a36Sopenharmony_ci WREG32(R300_CRTC2_TILE_X0_Y0, crtc_tile_x0_y0); 55162306a36Sopenharmony_ci else 55262306a36Sopenharmony_ci WREG32(R300_CRTC_TILE_X0_Y0, crtc_tile_x0_y0); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci WREG32(RADEON_CRTC_OFFSET_CNTL + radeon_crtc->crtc_offset, crtc_offset_cntl); 55562306a36Sopenharmony_ci WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, crtc_offset); 55662306a36Sopenharmony_ci WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (!atomic && fb && fb != crtc->primary->fb) { 55962306a36Sopenharmony_ci rbo = gem_to_radeon_bo(fb->obj[0]); 56062306a36Sopenharmony_ci r = radeon_bo_reserve(rbo, false); 56162306a36Sopenharmony_ci if (unlikely(r != 0)) 56262306a36Sopenharmony_ci return r; 56362306a36Sopenharmony_ci radeon_bo_unpin(rbo); 56462306a36Sopenharmony_ci radeon_bo_unreserve(rbo); 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Bytes per pixel may have changed */ 56862306a36Sopenharmony_ci radeon_bandwidth_update(rdev); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mode *mode) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 57662306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 57762306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 57862306a36Sopenharmony_ci const struct drm_framebuffer *fb = crtc->primary->fb; 57962306a36Sopenharmony_ci struct drm_encoder *encoder; 58062306a36Sopenharmony_ci int format; 58162306a36Sopenharmony_ci int hsync_start; 58262306a36Sopenharmony_ci int hsync_wid; 58362306a36Sopenharmony_ci int vsync_wid; 58462306a36Sopenharmony_ci uint32_t crtc_h_total_disp; 58562306a36Sopenharmony_ci uint32_t crtc_h_sync_strt_wid; 58662306a36Sopenharmony_ci uint32_t crtc_v_total_disp; 58762306a36Sopenharmony_ci uint32_t crtc_v_sync_strt_wid; 58862306a36Sopenharmony_ci bool is_tv = false; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci DRM_DEBUG_KMS("\n"); 59162306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 59262306a36Sopenharmony_ci if (encoder->crtc == crtc) { 59362306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 59462306a36Sopenharmony_ci if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { 59562306a36Sopenharmony_ci is_tv = true; 59662306a36Sopenharmony_ci DRM_INFO("crtc %d is connected to a TV\n", radeon_crtc->crtc_id); 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci switch (fb->format->cpp[0] * 8) { 60362306a36Sopenharmony_ci case 8: 60462306a36Sopenharmony_ci format = 2; 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci case 15: /* 555 */ 60762306a36Sopenharmony_ci format = 3; 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci case 16: /* 565 */ 61062306a36Sopenharmony_ci format = 4; 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci case 24: /* RGB */ 61362306a36Sopenharmony_ci format = 5; 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci case 32: /* xRGB */ 61662306a36Sopenharmony_ci format = 6; 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci default: 61962306a36Sopenharmony_ci return false; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) 62362306a36Sopenharmony_ci | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; 62662306a36Sopenharmony_ci if (!hsync_wid) 62762306a36Sopenharmony_ci hsync_wid = 1; 62862306a36Sopenharmony_ci hsync_start = mode->crtc_hsync_start - 8; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) 63162306a36Sopenharmony_ci | ((hsync_wid & 0x3f) << 16) 63262306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NHSYNC) 63362306a36Sopenharmony_ci ? RADEON_CRTC_H_SYNC_POL 63462306a36Sopenharmony_ci : 0)); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* This works for double scan mode. */ 63762306a36Sopenharmony_ci crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) 63862306a36Sopenharmony_ci | ((mode->crtc_vdisplay - 1) << 16)); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; 64162306a36Sopenharmony_ci if (!vsync_wid) 64262306a36Sopenharmony_ci vsync_wid = 1; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci crtc_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) 64562306a36Sopenharmony_ci | ((vsync_wid & 0x1f) << 16) 64662306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_NVSYNC) 64762306a36Sopenharmony_ci ? RADEON_CRTC_V_SYNC_POL 64862306a36Sopenharmony_ci : 0)); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (radeon_crtc->crtc_id) { 65162306a36Sopenharmony_ci uint32_t crtc2_gen_cntl; 65262306a36Sopenharmony_ci uint32_t disp2_merge_cntl; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* if TV DAC is enabled for another crtc and keep it enabled */ 65562306a36Sopenharmony_ci crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0x00718080; 65662306a36Sopenharmony_ci crtc2_gen_cntl |= ((format << 8) 65762306a36Sopenharmony_ci | RADEON_CRTC2_VSYNC_DIS 65862306a36Sopenharmony_ci | RADEON_CRTC2_HSYNC_DIS 65962306a36Sopenharmony_ci | RADEON_CRTC2_DISP_DIS 66062306a36Sopenharmony_ci | RADEON_CRTC2_DISP_REQ_EN_B 66162306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_DBLSCAN) 66262306a36Sopenharmony_ci ? RADEON_CRTC2_DBL_SCAN_EN 66362306a36Sopenharmony_ci : 0) 66462306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_CSYNC) 66562306a36Sopenharmony_ci ? RADEON_CRTC2_CSYNC_EN 66662306a36Sopenharmony_ci : 0) 66762306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_INTERLACE) 66862306a36Sopenharmony_ci ? RADEON_CRTC2_INTERLACE_EN 66962306a36Sopenharmony_ci : 0)); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* rs4xx chips seem to like to have the crtc enabled when the timing is set */ 67262306a36Sopenharmony_ci if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480)) 67362306a36Sopenharmony_ci crtc2_gen_cntl |= RADEON_CRTC2_EN; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci disp2_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL); 67662306a36Sopenharmony_ci disp2_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci WREG32(RADEON_DISP2_MERGE_CNTL, disp2_merge_cntl); 67962306a36Sopenharmony_ci WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci WREG32(RADEON_FP_H2_SYNC_STRT_WID, crtc_h_sync_strt_wid); 68262306a36Sopenharmony_ci WREG32(RADEON_FP_V2_SYNC_STRT_WID, crtc_v_sync_strt_wid); 68362306a36Sopenharmony_ci } else { 68462306a36Sopenharmony_ci uint32_t crtc_gen_cntl; 68562306a36Sopenharmony_ci uint32_t crtc_ext_cntl; 68662306a36Sopenharmony_ci uint32_t disp_merge_cntl; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0x00718000; 68962306a36Sopenharmony_ci crtc_gen_cntl |= (RADEON_CRTC_EXT_DISP_EN 69062306a36Sopenharmony_ci | (format << 8) 69162306a36Sopenharmony_ci | RADEON_CRTC_DISP_REQ_EN_B 69262306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_DBLSCAN) 69362306a36Sopenharmony_ci ? RADEON_CRTC_DBL_SCAN_EN 69462306a36Sopenharmony_ci : 0) 69562306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_CSYNC) 69662306a36Sopenharmony_ci ? RADEON_CRTC_CSYNC_EN 69762306a36Sopenharmony_ci : 0) 69862306a36Sopenharmony_ci | ((mode->flags & DRM_MODE_FLAG_INTERLACE) 69962306a36Sopenharmony_ci ? RADEON_CRTC_INTERLACE_EN 70062306a36Sopenharmony_ci : 0)); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* rs4xx chips seem to like to have the crtc enabled when the timing is set */ 70362306a36Sopenharmony_ci if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480)) 70462306a36Sopenharmony_ci crtc_gen_cntl |= RADEON_CRTC_EN; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); 70762306a36Sopenharmony_ci crtc_ext_cntl |= (RADEON_XCRT_CNT_EN | 70862306a36Sopenharmony_ci RADEON_CRTC_VSYNC_DIS | 70962306a36Sopenharmony_ci RADEON_CRTC_HSYNC_DIS | 71062306a36Sopenharmony_ci RADEON_CRTC_DISPLAY_DIS); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL); 71362306a36Sopenharmony_ci disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl); 71662306a36Sopenharmony_ci WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl); 71762306a36Sopenharmony_ci WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (is_tv) 72162306a36Sopenharmony_ci radeon_legacy_tv_adjust_crtc_reg(encoder, &crtc_h_total_disp, 72262306a36Sopenharmony_ci &crtc_h_sync_strt_wid, &crtc_v_total_disp, 72362306a36Sopenharmony_ci &crtc_v_sync_strt_wid); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci WREG32(RADEON_CRTC_H_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_h_total_disp); 72662306a36Sopenharmony_ci WREG32(RADEON_CRTC_H_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_h_sync_strt_wid); 72762306a36Sopenharmony_ci WREG32(RADEON_CRTC_V_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_v_total_disp); 72862306a36Sopenharmony_ci WREG32(RADEON_CRTC_V_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_v_sync_strt_wid); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return true; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 73662306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 73762306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 73862306a36Sopenharmony_ci struct drm_encoder *encoder; 73962306a36Sopenharmony_ci uint32_t feedback_div = 0; 74062306a36Sopenharmony_ci uint32_t frac_fb_div = 0; 74162306a36Sopenharmony_ci uint32_t reference_div = 0; 74262306a36Sopenharmony_ci uint32_t post_divider = 0; 74362306a36Sopenharmony_ci uint32_t freq = 0; 74462306a36Sopenharmony_ci uint8_t pll_gain; 74562306a36Sopenharmony_ci bool use_bios_divs = false; 74662306a36Sopenharmony_ci /* PLL registers */ 74762306a36Sopenharmony_ci uint32_t pll_ref_div = 0; 74862306a36Sopenharmony_ci uint32_t pll_fb_post_div = 0; 74962306a36Sopenharmony_ci uint32_t htotal_cntl = 0; 75062306a36Sopenharmony_ci bool is_tv = false; 75162306a36Sopenharmony_ci struct radeon_pll *pll; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci struct { 75462306a36Sopenharmony_ci int divider; 75562306a36Sopenharmony_ci int bitvalue; 75662306a36Sopenharmony_ci } *post_div, post_divs[] = { 75762306a36Sopenharmony_ci /* From RAGE 128 VR/RAGE 128 GL Register 75862306a36Sopenharmony_ci * Reference Manual (Technical Reference 75962306a36Sopenharmony_ci * Manual P/N RRG-G04100-C Rev. 0.04), page 76062306a36Sopenharmony_ci * 3-17 (PLL_DIV_[3:0]). 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci { 1, 0 }, /* VCLK_SRC */ 76362306a36Sopenharmony_ci { 2, 1 }, /* VCLK_SRC/2 */ 76462306a36Sopenharmony_ci { 4, 2 }, /* VCLK_SRC/4 */ 76562306a36Sopenharmony_ci { 8, 3 }, /* VCLK_SRC/8 */ 76662306a36Sopenharmony_ci { 3, 4 }, /* VCLK_SRC/3 */ 76762306a36Sopenharmony_ci { 16, 5 }, /* VCLK_SRC/16 */ 76862306a36Sopenharmony_ci { 6, 6 }, /* VCLK_SRC/6 */ 76962306a36Sopenharmony_ci { 12, 7 }, /* VCLK_SRC/12 */ 77062306a36Sopenharmony_ci { 0, 0 } 77162306a36Sopenharmony_ci }; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (radeon_crtc->crtc_id) 77462306a36Sopenharmony_ci pll = &rdev->clock.p2pll; 77562306a36Sopenharmony_ci else 77662306a36Sopenharmony_ci pll = &rdev->clock.p1pll; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci pll->flags = RADEON_PLL_LEGACY; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (mode->clock > 200000) /* range limits??? */ 78162306a36Sopenharmony_ci pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; 78262306a36Sopenharmony_ci else 78362306a36Sopenharmony_ci pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 78662306a36Sopenharmony_ci if (encoder->crtc == crtc) { 78762306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { 79062306a36Sopenharmony_ci is_tv = true; 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) 79562306a36Sopenharmony_ci pll->flags |= RADEON_PLL_NO_ODD_POST_DIV; 79662306a36Sopenharmony_ci if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) { 79762306a36Sopenharmony_ci if (!rdev->is_atom_bios) { 79862306a36Sopenharmony_ci struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); 79962306a36Sopenharmony_ci struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv; 80062306a36Sopenharmony_ci if (lvds) { 80162306a36Sopenharmony_ci if (lvds->use_bios_dividers) { 80262306a36Sopenharmony_ci pll_ref_div = lvds->panel_ref_divider; 80362306a36Sopenharmony_ci pll_fb_post_div = (lvds->panel_fb_divider | 80462306a36Sopenharmony_ci (lvds->panel_post_divider << 16)); 80562306a36Sopenharmony_ci htotal_cntl = 0; 80662306a36Sopenharmony_ci use_bios_divs = true; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci pll->flags |= RADEON_PLL_USE_REF_DIV; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci DRM_DEBUG_KMS("\n"); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (!use_bios_divs) { 81862306a36Sopenharmony_ci radeon_compute_pll_legacy(pll, mode->clock, 81962306a36Sopenharmony_ci &freq, &feedback_div, &frac_fb_div, 82062306a36Sopenharmony_ci &reference_div, &post_divider); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci for (post_div = &post_divs[0]; post_div->divider; ++post_div) { 82362306a36Sopenharmony_ci if (post_div->divider == post_divider) 82462306a36Sopenharmony_ci break; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (!post_div->divider) 82862306a36Sopenharmony_ci post_div = &post_divs[0]; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci DRM_DEBUG_KMS("dc=%u, fd=%d, rd=%d, pd=%d\n", 83162306a36Sopenharmony_ci (unsigned)freq, 83262306a36Sopenharmony_ci feedback_div, 83362306a36Sopenharmony_ci reference_div, 83462306a36Sopenharmony_ci post_divider); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci pll_ref_div = reference_div; 83762306a36Sopenharmony_ci#if defined(__powerpc__) && (0) /* TODO */ 83862306a36Sopenharmony_ci /* apparently programming this otherwise causes a hang??? */ 83962306a36Sopenharmony_ci if (info->MacModel == RADEON_MAC_IBOOK) 84062306a36Sopenharmony_ci pll_fb_post_div = 0x000600ad; 84162306a36Sopenharmony_ci else 84262306a36Sopenharmony_ci#endif 84362306a36Sopenharmony_ci pll_fb_post_div = (feedback_div | (post_div->bitvalue << 16)); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci htotal_cntl = mode->htotal & 0x7; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci pll_gain = radeon_compute_pll_gain(pll->reference_freq, 85062306a36Sopenharmony_ci pll_ref_div & 0x3ff, 85162306a36Sopenharmony_ci pll_fb_post_div & 0x7ff); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (radeon_crtc->crtc_id) { 85462306a36Sopenharmony_ci uint32_t pixclks_cntl = ((RREG32_PLL(RADEON_PIXCLKS_CNTL) & 85562306a36Sopenharmony_ci ~(RADEON_PIX2CLK_SRC_SEL_MASK)) | 85662306a36Sopenharmony_ci RADEON_PIX2CLK_SRC_SEL_P2PLLCLK); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (is_tv) { 85962306a36Sopenharmony_ci radeon_legacy_tv_adjust_pll2(encoder, &htotal_cntl, 86062306a36Sopenharmony_ci &pll_ref_div, &pll_fb_post_div, 86162306a36Sopenharmony_ci &pixclks_cntl); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PIXCLKS_CNTL, 86562306a36Sopenharmony_ci RADEON_PIX2CLK_SRC_SEL_CPUCLK, 86662306a36Sopenharmony_ci ~(RADEON_PIX2CLK_SRC_SEL_MASK)); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_CNTL, 86962306a36Sopenharmony_ci RADEON_P2PLL_RESET 87062306a36Sopenharmony_ci | RADEON_P2PLL_ATOMIC_UPDATE_EN 87162306a36Sopenharmony_ci | ((uint32_t)pll_gain << RADEON_P2PLL_PVG_SHIFT), 87262306a36Sopenharmony_ci ~(RADEON_P2PLL_RESET 87362306a36Sopenharmony_ci | RADEON_P2PLL_ATOMIC_UPDATE_EN 87462306a36Sopenharmony_ci | RADEON_P2PLL_PVG_MASK)); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_REF_DIV, 87762306a36Sopenharmony_ci pll_ref_div, 87862306a36Sopenharmony_ci ~RADEON_P2PLL_REF_DIV_MASK); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_DIV_0, 88162306a36Sopenharmony_ci pll_fb_post_div, 88262306a36Sopenharmony_ci ~RADEON_P2PLL_FB0_DIV_MASK); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_DIV_0, 88562306a36Sopenharmony_ci pll_fb_post_div, 88662306a36Sopenharmony_ci ~RADEON_P2PLL_POST0_DIV_MASK); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci radeon_pll2_write_update(dev); 88962306a36Sopenharmony_ci radeon_pll2_wait_for_read_update_complete(dev); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci WREG32_PLL(RADEON_HTOTAL2_CNTL, htotal_cntl); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci WREG32_PLL_P(RADEON_P2PLL_CNTL, 89462306a36Sopenharmony_ci 0, 89562306a36Sopenharmony_ci ~(RADEON_P2PLL_RESET 89662306a36Sopenharmony_ci | RADEON_P2PLL_SLEEP 89762306a36Sopenharmony_ci | RADEON_P2PLL_ATOMIC_UPDATE_EN)); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci DRM_DEBUG_KMS("Wrote2: 0x%08x 0x%08x 0x%08x (0x%08x)\n", 90062306a36Sopenharmony_ci (unsigned)pll_ref_div, 90162306a36Sopenharmony_ci (unsigned)pll_fb_post_div, 90262306a36Sopenharmony_ci (unsigned)htotal_cntl, 90362306a36Sopenharmony_ci RREG32_PLL(RADEON_P2PLL_CNTL)); 90462306a36Sopenharmony_ci DRM_DEBUG_KMS("Wrote2: rd=%u, fd=%u, pd=%u\n", 90562306a36Sopenharmony_ci (unsigned)pll_ref_div & RADEON_P2PLL_REF_DIV_MASK, 90662306a36Sopenharmony_ci (unsigned)pll_fb_post_div & RADEON_P2PLL_FB0_DIV_MASK, 90762306a36Sopenharmony_ci (unsigned)((pll_fb_post_div & 90862306a36Sopenharmony_ci RADEON_P2PLL_POST0_DIV_MASK) >> 16)); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci mdelay(50); /* Let the clock to lock */ 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PIXCLKS_CNTL, 91362306a36Sopenharmony_ci RADEON_PIX2CLK_SRC_SEL_P2PLLCLK, 91462306a36Sopenharmony_ci ~(RADEON_PIX2CLK_SRC_SEL_MASK)); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); 91762306a36Sopenharmony_ci } else { 91862306a36Sopenharmony_ci uint32_t pixclks_cntl; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (is_tv) { 92262306a36Sopenharmony_ci pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL); 92362306a36Sopenharmony_ci radeon_legacy_tv_adjust_pll1(encoder, &htotal_cntl, &pll_ref_div, 92462306a36Sopenharmony_ci &pll_fb_post_div, &pixclks_cntl); 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (rdev->flags & RADEON_IS_MOBILITY) { 92862306a36Sopenharmony_ci /* A temporal workaround for the occasional blanking on certain laptop panels. 92962306a36Sopenharmony_ci This appears to related to the PLL divider registers (fail to lock?). 93062306a36Sopenharmony_ci It occurs even when all dividers are the same with their old settings. 93162306a36Sopenharmony_ci In this case we really don't need to fiddle with PLL registers. 93262306a36Sopenharmony_ci By doing this we can avoid the blanking problem with some panels. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci if ((pll_ref_div == (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_REF_DIV_MASK)) && 93562306a36Sopenharmony_ci (pll_fb_post_div == (RREG32_PLL(RADEON_PPLL_DIV_3) & 93662306a36Sopenharmony_ci (RADEON_PPLL_POST3_DIV_MASK | RADEON_PPLL_FB3_DIV_MASK)))) { 93762306a36Sopenharmony_ci WREG32_P(RADEON_CLOCK_CNTL_INDEX, 93862306a36Sopenharmony_ci RADEON_PLL_DIV_SEL, 93962306a36Sopenharmony_ci ~(RADEON_PLL_DIV_SEL)); 94062306a36Sopenharmony_ci r100_pll_errata_after_index(rdev); 94162306a36Sopenharmony_ci return; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, 94662306a36Sopenharmony_ci RADEON_VCLK_SRC_SEL_CPUCLK, 94762306a36Sopenharmony_ci ~(RADEON_VCLK_SRC_SEL_MASK)); 94862306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_CNTL, 94962306a36Sopenharmony_ci RADEON_PPLL_RESET 95062306a36Sopenharmony_ci | RADEON_PPLL_ATOMIC_UPDATE_EN 95162306a36Sopenharmony_ci | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN 95262306a36Sopenharmony_ci | ((uint32_t)pll_gain << RADEON_PPLL_PVG_SHIFT), 95362306a36Sopenharmony_ci ~(RADEON_PPLL_RESET 95462306a36Sopenharmony_ci | RADEON_PPLL_ATOMIC_UPDATE_EN 95562306a36Sopenharmony_ci | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN 95662306a36Sopenharmony_ci | RADEON_PPLL_PVG_MASK)); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci WREG32_P(RADEON_CLOCK_CNTL_INDEX, 95962306a36Sopenharmony_ci RADEON_PLL_DIV_SEL, 96062306a36Sopenharmony_ci ~(RADEON_PLL_DIV_SEL)); 96162306a36Sopenharmony_ci r100_pll_errata_after_index(rdev); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (ASIC_IS_R300(rdev) || 96462306a36Sopenharmony_ci (rdev->family == CHIP_RS300) || 96562306a36Sopenharmony_ci (rdev->family == CHIP_RS400) || 96662306a36Sopenharmony_ci (rdev->family == CHIP_RS480)) { 96762306a36Sopenharmony_ci if (pll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { 96862306a36Sopenharmony_ci /* When restoring console mode, use saved PPLL_REF_DIV 96962306a36Sopenharmony_ci * setting. 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_REF_DIV, 97262306a36Sopenharmony_ci pll_ref_div, 97362306a36Sopenharmony_ci 0); 97462306a36Sopenharmony_ci } else { 97562306a36Sopenharmony_ci /* R300 uses ref_div_acc field as real ref divider */ 97662306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_REF_DIV, 97762306a36Sopenharmony_ci (pll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), 97862306a36Sopenharmony_ci ~R300_PPLL_REF_DIV_ACC_MASK); 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci } else 98162306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_REF_DIV, 98262306a36Sopenharmony_ci pll_ref_div, 98362306a36Sopenharmony_ci ~RADEON_PPLL_REF_DIV_MASK); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_DIV_3, 98662306a36Sopenharmony_ci pll_fb_post_div, 98762306a36Sopenharmony_ci ~RADEON_PPLL_FB3_DIV_MASK); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_DIV_3, 99062306a36Sopenharmony_ci pll_fb_post_div, 99162306a36Sopenharmony_ci ~RADEON_PPLL_POST3_DIV_MASK); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci radeon_pll_write_update(dev); 99462306a36Sopenharmony_ci radeon_pll_wait_for_read_update_complete(dev); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci WREG32_PLL(RADEON_HTOTAL_CNTL, htotal_cntl); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci WREG32_PLL_P(RADEON_PPLL_CNTL, 99962306a36Sopenharmony_ci 0, 100062306a36Sopenharmony_ci ~(RADEON_PPLL_RESET 100162306a36Sopenharmony_ci | RADEON_PPLL_SLEEP 100262306a36Sopenharmony_ci | RADEON_PPLL_ATOMIC_UPDATE_EN 100362306a36Sopenharmony_ci | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN)); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci DRM_DEBUG_KMS("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n", 100662306a36Sopenharmony_ci pll_ref_div, 100762306a36Sopenharmony_ci pll_fb_post_div, 100862306a36Sopenharmony_ci (unsigned)htotal_cntl, 100962306a36Sopenharmony_ci RREG32_PLL(RADEON_PPLL_CNTL)); 101062306a36Sopenharmony_ci DRM_DEBUG_KMS("Wrote: rd=%d, fd=%d, pd=%d\n", 101162306a36Sopenharmony_ci pll_ref_div & RADEON_PPLL_REF_DIV_MASK, 101262306a36Sopenharmony_ci pll_fb_post_div & RADEON_PPLL_FB3_DIV_MASK, 101362306a36Sopenharmony_ci (pll_fb_post_div & RADEON_PPLL_POST3_DIV_MASK) >> 16); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci mdelay(50); /* Let the clock to lock */ 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, 101862306a36Sopenharmony_ci RADEON_VCLK_SRC_SEL_PPLLCLK, 101962306a36Sopenharmony_ci ~(RADEON_VCLK_SRC_SEL_MASK)); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (is_tv) 102262306a36Sopenharmony_ci WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic bool radeon_crtc_mode_fixup(struct drm_crtc *crtc, 102762306a36Sopenharmony_ci const struct drm_display_mode *mode, 102862306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode)) 103162306a36Sopenharmony_ci return false; 103262306a36Sopenharmony_ci return true; 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic int radeon_crtc_mode_set(struct drm_crtc *crtc, 103662306a36Sopenharmony_ci struct drm_display_mode *mode, 103762306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode, 103862306a36Sopenharmony_ci int x, int y, struct drm_framebuffer *old_fb) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* TODO TV */ 104362306a36Sopenharmony_ci radeon_crtc_set_base(crtc, x, y, old_fb); 104462306a36Sopenharmony_ci radeon_set_crtc_timing(crtc, adjusted_mode); 104562306a36Sopenharmony_ci radeon_set_pll(crtc, adjusted_mode); 104662306a36Sopenharmony_ci radeon_overscan_setup(crtc, adjusted_mode); 104762306a36Sopenharmony_ci if (radeon_crtc->crtc_id == 0) { 104862306a36Sopenharmony_ci radeon_legacy_rmx_mode_set(crtc, adjusted_mode); 104962306a36Sopenharmony_ci } else { 105062306a36Sopenharmony_ci if (radeon_crtc->rmx_type != RMX_OFF) { 105162306a36Sopenharmony_ci /* FIXME: only first crtc has rmx what should we 105262306a36Sopenharmony_ci * do ? 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_ci DRM_ERROR("Mode need scaling but only first crtc can do that.\n"); 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci radeon_cursor_reset(crtc); 105862306a36Sopenharmony_ci return 0; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic void radeon_crtc_prepare(struct drm_crtc *crtc) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 106462306a36Sopenharmony_ci struct drm_crtc *crtci; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* 106762306a36Sopenharmony_ci * The hardware wedges sometimes if you reconfigure one CRTC 106862306a36Sopenharmony_ci * whilst another is running (see fdo bug #24611). 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) 107162306a36Sopenharmony_ci radeon_crtc_dpms(crtci, DRM_MODE_DPMS_OFF); 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic void radeon_crtc_commit(struct drm_crtc *crtc) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 107762306a36Sopenharmony_ci struct drm_crtc *crtci; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci /* 108062306a36Sopenharmony_ci * Reenable the CRTCs that should be running. 108162306a36Sopenharmony_ci */ 108262306a36Sopenharmony_ci list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) { 108362306a36Sopenharmony_ci if (crtci->enabled) 108462306a36Sopenharmony_ci radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON); 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic void radeon_crtc_disable(struct drm_crtc *crtc) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); 109162306a36Sopenharmony_ci if (crtc->primary->fb) { 109262306a36Sopenharmony_ci int r; 109362306a36Sopenharmony_ci struct radeon_bo *rbo; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci rbo = gem_to_radeon_bo(crtc->primary->fb->obj[0]); 109662306a36Sopenharmony_ci r = radeon_bo_reserve(rbo, false); 109762306a36Sopenharmony_ci if (unlikely(r)) 109862306a36Sopenharmony_ci DRM_ERROR("failed to reserve rbo before unpin\n"); 109962306a36Sopenharmony_ci else { 110062306a36Sopenharmony_ci radeon_bo_unpin(rbo); 110162306a36Sopenharmony_ci radeon_bo_unreserve(rbo); 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs legacy_helper_funcs = { 110762306a36Sopenharmony_ci .dpms = radeon_crtc_dpms, 110862306a36Sopenharmony_ci .mode_fixup = radeon_crtc_mode_fixup, 110962306a36Sopenharmony_ci .mode_set = radeon_crtc_mode_set, 111062306a36Sopenharmony_ci .mode_set_base = radeon_crtc_set_base, 111162306a36Sopenharmony_ci .mode_set_base_atomic = radeon_crtc_set_base_atomic, 111262306a36Sopenharmony_ci .prepare = radeon_crtc_prepare, 111362306a36Sopenharmony_ci .commit = radeon_crtc_commit, 111462306a36Sopenharmony_ci .disable = radeon_crtc_disable, 111562306a36Sopenharmony_ci .get_scanout_position = radeon_get_crtc_scanout_position, 111662306a36Sopenharmony_ci}; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_civoid radeon_legacy_init_crtc(struct drm_device *dev, 112062306a36Sopenharmony_ci struct radeon_crtc *radeon_crtc) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci if (radeon_crtc->crtc_id == 1) 112362306a36Sopenharmony_ci radeon_crtc->crtc_offset = RADEON_CRTC2_H_TOTAL_DISP - RADEON_CRTC_H_TOTAL_DISP; 112462306a36Sopenharmony_ci drm_crtc_helper_add(&radeon_crtc->base, &legacy_helper_funcs); 112562306a36Sopenharmony_ci} 1126