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