xref: /openbmc/linux/drivers/gpu/drm/radeon/radeon_legacy_crtc.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1771fe6b9SJerome Glisse /*
2771fe6b9SJerome Glisse  * Copyright 2007-8 Advanced Micro Devices, Inc.
3771fe6b9SJerome Glisse  * Copyright 2008 Red Hat Inc.
4771fe6b9SJerome Glisse  *
5771fe6b9SJerome Glisse  * Permission is hereby granted, free of charge, to any person obtaining a
6771fe6b9SJerome Glisse  * copy of this software and associated documentation files (the "Software"),
7771fe6b9SJerome Glisse  * to deal in the Software without restriction, including without limitation
8771fe6b9SJerome Glisse  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9771fe6b9SJerome Glisse  * and/or sell copies of the Software, and to permit persons to whom the
10771fe6b9SJerome Glisse  * Software is furnished to do so, subject to the following conditions:
11771fe6b9SJerome Glisse  *
12771fe6b9SJerome Glisse  * The above copyright notice and this permission notice shall be included in
13771fe6b9SJerome Glisse  * all copies or substantial portions of the Software.
14771fe6b9SJerome Glisse  *
15771fe6b9SJerome Glisse  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16771fe6b9SJerome Glisse  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17771fe6b9SJerome Glisse  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18771fe6b9SJerome Glisse  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19771fe6b9SJerome Glisse  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20771fe6b9SJerome Glisse  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21771fe6b9SJerome Glisse  * OTHER DEALINGS IN THE SOFTWARE.
22771fe6b9SJerome Glisse  *
23771fe6b9SJerome Glisse  * Authors: Dave Airlie
24771fe6b9SJerome Glisse  *          Alex Deucher
25771fe6b9SJerome Glisse  */
26f9183127SSam Ravnborg 
2768adac5eSBen Skeggs #include <drm/drm_fixed.h>
28f9183127SSam Ravnborg #include <drm/drm_fourcc.h>
29720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
30*f7d17cd4SThomas Zimmermann #include <drm/drm_modeset_helper_vtables.h>
31f9183127SSam Ravnborg #include <drm/drm_vblank.h>
32f9183127SSam Ravnborg #include <drm/radeon_drm.h>
33f9183127SSam Ravnborg 
344ce001abSDave Airlie #include "atom.h"
35f9183127SSam Ravnborg #include "radeon.h"
36771fe6b9SJerome Glisse 
radeon_overscan_setup(struct drm_crtc * crtc,struct drm_display_mode * mode)376b02af1cSAlex Deucher static void radeon_overscan_setup(struct drm_crtc *crtc,
386b02af1cSAlex Deucher 				  struct drm_display_mode *mode)
396b02af1cSAlex Deucher {
406b02af1cSAlex Deucher 	struct drm_device *dev = crtc->dev;
416b02af1cSAlex Deucher 	struct radeon_device *rdev = dev->dev_private;
426b02af1cSAlex Deucher 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
436b02af1cSAlex Deucher 
446b02af1cSAlex Deucher 	WREG32(RADEON_OVR_CLR + radeon_crtc->crtc_offset, 0);
456b02af1cSAlex Deucher 	WREG32(RADEON_OVR_WID_LEFT_RIGHT + radeon_crtc->crtc_offset, 0);
466b02af1cSAlex Deucher 	WREG32(RADEON_OVR_WID_TOP_BOTTOM + radeon_crtc->crtc_offset, 0);
476b02af1cSAlex Deucher }
486b02af1cSAlex Deucher 
radeon_legacy_rmx_mode_set(struct drm_crtc * crtc,struct drm_display_mode * mode)49c93bb85bSJerome Glisse static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc,
50310a82c8SAlex Deucher 				       struct drm_display_mode *mode)
51c93bb85bSJerome Glisse {
52c93bb85bSJerome Glisse 	struct drm_device *dev = crtc->dev;
53c93bb85bSJerome Glisse 	struct radeon_device *rdev = dev->dev_private;
54c93bb85bSJerome Glisse 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
55c93bb85bSJerome Glisse 	int xres = mode->hdisplay;
56c93bb85bSJerome Glisse 	int yres = mode->vdisplay;
57c93bb85bSJerome Glisse 	bool hscale = true, vscale = true;
58c93bb85bSJerome Glisse 	int hsync_wid;
59c93bb85bSJerome Glisse 	int vsync_wid;
60c93bb85bSJerome Glisse 	int hsync_start;
61c93bb85bSJerome Glisse 	int blank_width;
62c93bb85bSJerome Glisse 	u32 scale, inc, crtc_more_cntl;
63c93bb85bSJerome Glisse 	u32 fp_horz_stretch, fp_vert_stretch, fp_horz_vert_active;
64c93bb85bSJerome Glisse 	u32 fp_h_sync_strt_wid, fp_crtc_h_total_disp;
65c93bb85bSJerome Glisse 	u32 fp_v_sync_strt_wid, fp_crtc_v_total_disp;
66de2103e4SAlex Deucher 	struct drm_display_mode *native_mode = &radeon_crtc->native_mode;
67c93bb85bSJerome Glisse 
68c93bb85bSJerome Glisse 	fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH) &
69c93bb85bSJerome Glisse 		(RADEON_VERT_STRETCH_RESERVED |
70c93bb85bSJerome Glisse 		 RADEON_VERT_AUTO_RATIO_INC);
71c93bb85bSJerome Glisse 	fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH) &
72c93bb85bSJerome Glisse 		(RADEON_HORZ_FP_LOOP_STRETCH |
73c93bb85bSJerome Glisse 		 RADEON_HORZ_AUTO_RATIO_INC);
74c93bb85bSJerome Glisse 
75c93bb85bSJerome Glisse 	crtc_more_cntl = 0;
76c93bb85bSJerome Glisse 	if ((rdev->family == CHIP_RS100) ||
77c93bb85bSJerome Glisse 	    (rdev->family == CHIP_RS200)) {
78c93bb85bSJerome Glisse 		/* This is to workaround the asic bug for RMX, some versions
79c93bb85bSJerome Glisse 		   of BIOS dosen't have this register initialized correctly. */
80c93bb85bSJerome Glisse 		crtc_more_cntl |= RADEON_CRTC_H_CUTOFF_ACTIVE_EN;
81c93bb85bSJerome Glisse 	}
82c93bb85bSJerome Glisse 
83c93bb85bSJerome Glisse 
84c93bb85bSJerome Glisse 	fp_crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff)
85c93bb85bSJerome Glisse 				| ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16));
86c93bb85bSJerome Glisse 
87c93bb85bSJerome Glisse 	hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8;
88c93bb85bSJerome Glisse 	if (!hsync_wid)
89c93bb85bSJerome Glisse 		hsync_wid = 1;
90c93bb85bSJerome Glisse 	hsync_start = mode->crtc_hsync_start - 8;
91c93bb85bSJerome Glisse 
92c93bb85bSJerome Glisse 	fp_h_sync_strt_wid = ((hsync_start & 0x1fff)
93c93bb85bSJerome Glisse 			      | ((hsync_wid & 0x3f) << 16)
94c93bb85bSJerome Glisse 			      | ((mode->flags & DRM_MODE_FLAG_NHSYNC)
95c93bb85bSJerome Glisse 				 ? RADEON_CRTC_H_SYNC_POL
96c93bb85bSJerome Glisse 				 : 0));
97c93bb85bSJerome Glisse 
98c93bb85bSJerome Glisse 	fp_crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff)
99c93bb85bSJerome Glisse 				| ((mode->crtc_vdisplay - 1) << 16));
100c93bb85bSJerome Glisse 
101c93bb85bSJerome Glisse 	vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start;
102c93bb85bSJerome Glisse 	if (!vsync_wid)
103c93bb85bSJerome Glisse 		vsync_wid = 1;
104c93bb85bSJerome Glisse 
105c93bb85bSJerome Glisse 	fp_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff)
106c93bb85bSJerome Glisse 			      | ((vsync_wid & 0x1f) << 16)
107c93bb85bSJerome Glisse 			      | ((mode->flags & DRM_MODE_FLAG_NVSYNC)
108c93bb85bSJerome Glisse 				 ? RADEON_CRTC_V_SYNC_POL
109c93bb85bSJerome Glisse 				 : 0));
110c93bb85bSJerome Glisse 
111c93bb85bSJerome Glisse 	fp_horz_vert_active = 0;
112c93bb85bSJerome Glisse 
113de2103e4SAlex Deucher 	if (native_mode->hdisplay == 0 ||
114de2103e4SAlex Deucher 	    native_mode->vdisplay == 0) {
115c93bb85bSJerome Glisse 		hscale = false;
116c93bb85bSJerome Glisse 		vscale = false;
117c93bb85bSJerome Glisse 	} else {
118de2103e4SAlex Deucher 		if (xres > native_mode->hdisplay)
119de2103e4SAlex Deucher 			xres = native_mode->hdisplay;
120de2103e4SAlex Deucher 		if (yres > native_mode->vdisplay)
121de2103e4SAlex Deucher 			yres = native_mode->vdisplay;
122c93bb85bSJerome Glisse 
123de2103e4SAlex Deucher 		if (xres == native_mode->hdisplay)
124c93bb85bSJerome Glisse 			hscale = false;
125de2103e4SAlex Deucher 		if (yres == native_mode->vdisplay)
126c93bb85bSJerome Glisse 			vscale = false;
127c93bb85bSJerome Glisse 	}
128c93bb85bSJerome Glisse 
129c93bb85bSJerome Glisse 	switch (radeon_crtc->rmx_type) {
130c93bb85bSJerome Glisse 	case RMX_FULL:
131c93bb85bSJerome Glisse 	case RMX_ASPECT:
132c93bb85bSJerome Glisse 		if (!hscale)
133c93bb85bSJerome Glisse 			fp_horz_stretch |= ((xres/8-1) << 16);
134c93bb85bSJerome Glisse 		else {
135c93bb85bSJerome Glisse 			inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0;
136c93bb85bSJerome Glisse 			scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX)
137de2103e4SAlex Deucher 				/ native_mode->hdisplay + 1;
138c93bb85bSJerome Glisse 			fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) |
139c93bb85bSJerome Glisse 					RADEON_HORZ_STRETCH_BLEND |
140c93bb85bSJerome Glisse 					RADEON_HORZ_STRETCH_ENABLE |
141de2103e4SAlex Deucher 					((native_mode->hdisplay/8-1) << 16));
142c93bb85bSJerome Glisse 		}
143c93bb85bSJerome Glisse 
144c93bb85bSJerome Glisse 		if (!vscale)
145c93bb85bSJerome Glisse 			fp_vert_stretch |= ((yres-1) << 12);
146c93bb85bSJerome Glisse 		else {
147c93bb85bSJerome Glisse 			inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0;
148c93bb85bSJerome Glisse 			scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX)
149de2103e4SAlex Deucher 				/ native_mode->vdisplay + 1;
150c93bb85bSJerome Glisse 			fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) |
151c93bb85bSJerome Glisse 					RADEON_VERT_STRETCH_ENABLE |
152c93bb85bSJerome Glisse 					RADEON_VERT_STRETCH_BLEND |
153de2103e4SAlex Deucher 					((native_mode->vdisplay-1) << 12));
154c93bb85bSJerome Glisse 		}
155c93bb85bSJerome Glisse 		break;
156c93bb85bSJerome Glisse 	case RMX_CENTER:
157c93bb85bSJerome Glisse 		fp_horz_stretch |= ((xres/8-1) << 16);
158c93bb85bSJerome Glisse 		fp_vert_stretch |= ((yres-1) << 12);
159c93bb85bSJerome Glisse 
160c93bb85bSJerome Glisse 		crtc_more_cntl |= (RADEON_CRTC_AUTO_HORZ_CENTER_EN |
161c93bb85bSJerome Glisse 				RADEON_CRTC_AUTO_VERT_CENTER_EN);
162c93bb85bSJerome Glisse 
163c93bb85bSJerome Glisse 		blank_width = (mode->crtc_hblank_end - mode->crtc_hblank_start) / 8;
164c93bb85bSJerome Glisse 		if (blank_width > 110)
165c93bb85bSJerome Glisse 			blank_width = 110;
166c93bb85bSJerome Glisse 
167c93bb85bSJerome Glisse 		fp_crtc_h_total_disp = (((blank_width) & 0x3ff)
168c93bb85bSJerome Glisse 				| ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16));
169c93bb85bSJerome Glisse 
170c93bb85bSJerome Glisse 		hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8;
171c93bb85bSJerome Glisse 		if (!hsync_wid)
172c93bb85bSJerome Glisse 			hsync_wid = 1;
173c93bb85bSJerome Glisse 
174c93bb85bSJerome Glisse 		fp_h_sync_strt_wid = ((((mode->crtc_hsync_start - mode->crtc_hblank_start) / 8) & 0x1fff)
175c93bb85bSJerome Glisse 				| ((hsync_wid & 0x3f) << 16)
176c93bb85bSJerome Glisse 				| ((mode->flags & DRM_MODE_FLAG_NHSYNC)
177c93bb85bSJerome Glisse 					? RADEON_CRTC_H_SYNC_POL
178c93bb85bSJerome Glisse 					: 0));
179c93bb85bSJerome Glisse 
180c93bb85bSJerome Glisse 		fp_crtc_v_total_disp = (((mode->crtc_vblank_end - mode->crtc_vblank_start) & 0xffff)
181c93bb85bSJerome Glisse 				| ((mode->crtc_vdisplay - 1) << 16));
182c93bb85bSJerome Glisse 
183c93bb85bSJerome Glisse 		vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start;
184c93bb85bSJerome Glisse 		if (!vsync_wid)
185c93bb85bSJerome Glisse 			vsync_wid = 1;
186c93bb85bSJerome Glisse 
187c93bb85bSJerome Glisse 		fp_v_sync_strt_wid = ((((mode->crtc_vsync_start - mode->crtc_vblank_start) & 0xfff)
188c93bb85bSJerome Glisse 					| ((vsync_wid & 0x1f) << 16)
189c93bb85bSJerome Glisse 					| ((mode->flags & DRM_MODE_FLAG_NVSYNC)
190c93bb85bSJerome Glisse 						? RADEON_CRTC_V_SYNC_POL
191c93bb85bSJerome Glisse 						: 0)));
192c93bb85bSJerome Glisse 
193de2103e4SAlex Deucher 		fp_horz_vert_active = (((native_mode->vdisplay) & 0xfff) |
194de2103e4SAlex Deucher 				(((native_mode->hdisplay / 8) & 0x1ff) << 16));
195c93bb85bSJerome Glisse 		break;
196c93bb85bSJerome Glisse 	case RMX_OFF:
197c93bb85bSJerome Glisse 	default:
198c93bb85bSJerome Glisse 		fp_horz_stretch |= ((xres/8-1) << 16);
199c93bb85bSJerome Glisse 		fp_vert_stretch |= ((yres-1) << 12);
200c93bb85bSJerome Glisse 		break;
201c93bb85bSJerome Glisse 	}
202c93bb85bSJerome Glisse 
203c93bb85bSJerome Glisse 	WREG32(RADEON_FP_HORZ_STRETCH,      fp_horz_stretch);
204c93bb85bSJerome Glisse 	WREG32(RADEON_FP_VERT_STRETCH,      fp_vert_stretch);
205c93bb85bSJerome Glisse 	WREG32(RADEON_CRTC_MORE_CNTL,       crtc_more_cntl);
206c93bb85bSJerome Glisse 	WREG32(RADEON_FP_HORZ_VERT_ACTIVE,  fp_horz_vert_active);
207c93bb85bSJerome Glisse 	WREG32(RADEON_FP_H_SYNC_STRT_WID,   fp_h_sync_strt_wid);
208c93bb85bSJerome Glisse 	WREG32(RADEON_FP_V_SYNC_STRT_WID,   fp_v_sync_strt_wid);
209c93bb85bSJerome Glisse 	WREG32(RADEON_FP_CRTC_H_TOTAL_DISP, fp_crtc_h_total_disp);
210c93bb85bSJerome Glisse 	WREG32(RADEON_FP_CRTC_V_TOTAL_DISP, fp_crtc_v_total_disp);
211c93bb85bSJerome Glisse }
212c93bb85bSJerome Glisse 
radeon_pll_wait_for_read_update_complete(struct drm_device * dev)213771fe6b9SJerome Glisse static void radeon_pll_wait_for_read_update_complete(struct drm_device *dev)
214771fe6b9SJerome Glisse {
215771fe6b9SJerome Glisse 	struct radeon_device *rdev = dev->dev_private;
216771fe6b9SJerome Glisse 	int i = 0;
217771fe6b9SJerome Glisse 
218771fe6b9SJerome Glisse 	/* FIXME: Certain revisions of R300 can't recover here.  Not sure of
219771fe6b9SJerome Glisse 	   the cause yet, but this workaround will mask the problem for now.
220771fe6b9SJerome Glisse 	   Other chips usually will pass at the very first test, so the
221771fe6b9SJerome Glisse 	   workaround shouldn't have any effect on them. */
222771fe6b9SJerome Glisse 	for (i = 0;
223771fe6b9SJerome Glisse 	     (i < 10000 &&
224771fe6b9SJerome Glisse 	      RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R);
225771fe6b9SJerome Glisse 	     i++);
226771fe6b9SJerome Glisse }
227771fe6b9SJerome Glisse 
radeon_pll_write_update(struct drm_device * dev)228771fe6b9SJerome Glisse static void radeon_pll_write_update(struct drm_device *dev)
229771fe6b9SJerome Glisse {
230771fe6b9SJerome Glisse 	struct radeon_device *rdev = dev->dev_private;
231771fe6b9SJerome Glisse 
232771fe6b9SJerome Glisse 	while (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R);
233771fe6b9SJerome Glisse 
234771fe6b9SJerome Glisse 	WREG32_PLL_P(RADEON_PPLL_REF_DIV,
235771fe6b9SJerome Glisse 			   RADEON_PPLL_ATOMIC_UPDATE_W,
236771fe6b9SJerome Glisse 			   ~(RADEON_PPLL_ATOMIC_UPDATE_W));
237771fe6b9SJerome Glisse }
238771fe6b9SJerome Glisse 
radeon_pll2_wait_for_read_update_complete(struct drm_device * dev)239771fe6b9SJerome Glisse static void radeon_pll2_wait_for_read_update_complete(struct drm_device *dev)
240771fe6b9SJerome Glisse {
241771fe6b9SJerome Glisse 	struct radeon_device *rdev = dev->dev_private;
242771fe6b9SJerome Glisse 	int i = 0;
243771fe6b9SJerome Glisse 
244771fe6b9SJerome Glisse 
245771fe6b9SJerome Glisse 	/* FIXME: Certain revisions of R300 can't recover here.  Not sure of
246771fe6b9SJerome Glisse 	   the cause yet, but this workaround will mask the problem for now.
247771fe6b9SJerome Glisse 	   Other chips usually will pass at the very first test, so the
248771fe6b9SJerome Glisse 	   workaround shouldn't have any effect on them. */
249771fe6b9SJerome Glisse 	for (i = 0;
250771fe6b9SJerome Glisse 	     (i < 10000 &&
251771fe6b9SJerome Glisse 	      RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R);
252771fe6b9SJerome Glisse 	     i++);
253771fe6b9SJerome Glisse }
254771fe6b9SJerome Glisse 
radeon_pll2_write_update(struct drm_device * dev)255771fe6b9SJerome Glisse static void radeon_pll2_write_update(struct drm_device *dev)
256771fe6b9SJerome Glisse {
257771fe6b9SJerome Glisse 	struct radeon_device *rdev = dev->dev_private;
258771fe6b9SJerome Glisse 
259771fe6b9SJerome Glisse 	while (RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R);
260771fe6b9SJerome Glisse 
261771fe6b9SJerome Glisse 	WREG32_PLL_P(RADEON_P2PLL_REF_DIV,
262771fe6b9SJerome Glisse 			   RADEON_P2PLL_ATOMIC_UPDATE_W,
263771fe6b9SJerome Glisse 			   ~(RADEON_P2PLL_ATOMIC_UPDATE_W));
264771fe6b9SJerome Glisse }
265771fe6b9SJerome Glisse 
radeon_compute_pll_gain(uint16_t ref_freq,uint16_t ref_div,uint16_t fb_div)266771fe6b9SJerome Glisse static uint8_t radeon_compute_pll_gain(uint16_t ref_freq, uint16_t ref_div,
267771fe6b9SJerome Glisse 				       uint16_t fb_div)
268771fe6b9SJerome Glisse {
269771fe6b9SJerome Glisse 	unsigned int vcoFreq;
270771fe6b9SJerome Glisse 
271771fe6b9SJerome Glisse 	if (!ref_div)
272771fe6b9SJerome Glisse 		return 1;
273771fe6b9SJerome Glisse 
2740537398bSAlex Deucher 	vcoFreq = ((unsigned)ref_freq * fb_div) / ref_div;
275771fe6b9SJerome Glisse 
276771fe6b9SJerome Glisse 	/*
277771fe6b9SJerome Glisse 	 * This is horribly crude: the VCO frequency range is divided into
278771fe6b9SJerome Glisse 	 * 3 parts, each part having a fixed PLL gain value.
279771fe6b9SJerome Glisse 	 */
280771fe6b9SJerome Glisse 	if (vcoFreq >= 30000)
281771fe6b9SJerome Glisse 		/*
282771fe6b9SJerome Glisse 		 * [300..max] MHz : 7
283771fe6b9SJerome Glisse 		 */
284771fe6b9SJerome Glisse 		return 7;
285771fe6b9SJerome Glisse 	else if (vcoFreq >= 18000)
286771fe6b9SJerome Glisse 		/*
287771fe6b9SJerome Glisse 		 * [180..300) MHz : 4
288771fe6b9SJerome Glisse 		 */
289771fe6b9SJerome Glisse 		return 4;
290771fe6b9SJerome Glisse 	else
291771fe6b9SJerome Glisse 		/*
292771fe6b9SJerome Glisse 		 * [0..180) MHz : 1
293771fe6b9SJerome Glisse 		 */
294771fe6b9SJerome Glisse 		return 1;
295771fe6b9SJerome Glisse }
296771fe6b9SJerome Glisse 
radeon_crtc_dpms(struct drm_crtc * crtc,int mode)2971109ca09SLauri Kasanen static void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
298771fe6b9SJerome Glisse {
299771fe6b9SJerome Glisse 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
300771fe6b9SJerome Glisse 	struct drm_device *dev = crtc->dev;
301771fe6b9SJerome Glisse 	struct radeon_device *rdev = dev->dev_private;
302f8c4d701SEgbert Eich 	uint32_t crtc_ext_cntl = 0;
303771fe6b9SJerome Glisse 	uint32_t mask;
304771fe6b9SJerome Glisse 
305771fe6b9SJerome Glisse 	if (radeon_crtc->crtc_id)
3068de21525SAlex Deucher 		mask = (RADEON_CRTC2_DISP_DIS |
307771fe6b9SJerome Glisse 			RADEON_CRTC2_VSYNC_DIS |
308771fe6b9SJerome Glisse 			RADEON_CRTC2_HSYNC_DIS |
309771fe6b9SJerome Glisse 			RADEON_CRTC2_DISP_REQ_EN_B);
310771fe6b9SJerome Glisse 	else
311771fe6b9SJerome Glisse 		mask = (RADEON_CRTC_DISPLAY_DIS |
312771fe6b9SJerome Glisse 			RADEON_CRTC_VSYNC_DIS |
313771fe6b9SJerome Glisse 			RADEON_CRTC_HSYNC_DIS);
314771fe6b9SJerome Glisse 
315f8c4d701SEgbert Eich 	/*
316f8c4d701SEgbert Eich 	 * On all dual CRTC GPUs this bit controls the CRTC of the primary DAC.
317f8c4d701SEgbert Eich 	 * Therefore it is set in the DAC DMPS function.
318f8c4d701SEgbert Eich 	 * This is different for GPU's with a single CRTC but a primary and a
319f8c4d701SEgbert Eich 	 * TV DAC: here it controls the single CRTC no matter where it is
320f8c4d701SEgbert Eich 	 * routed. Therefore we set it here.
321f8c4d701SEgbert Eich 	 */
322f8c4d701SEgbert Eich 	if (rdev->flags & RADEON_SINGLE_CRTC)
323f8c4d701SEgbert Eich 		crtc_ext_cntl = RADEON_CRTC_CRT_ON;
324f8c4d701SEgbert Eich 
325771fe6b9SJerome Glisse 	switch (mode) {
326771fe6b9SJerome Glisse 	case DRM_MODE_DPMS_ON:
327d7311171SAlex Deucher 		radeon_crtc->enabled = true;
328d7311171SAlex Deucher 		/* adjust pm to dpms changes BEFORE enabling crtcs */
329d7311171SAlex Deucher 		radeon_pm_compute_clocks(rdev);
330771fe6b9SJerome Glisse 		if (radeon_crtc->crtc_id)
3318de21525SAlex Deucher 			WREG32_P(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_EN, ~(RADEON_CRTC2_EN | mask));
332771fe6b9SJerome Glisse 		else {
333771fe6b9SJerome Glisse 			WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_EN, ~(RADEON_CRTC_EN |
334771fe6b9SJerome Glisse 									 RADEON_CRTC_DISP_REQ_EN_B));
335f8c4d701SEgbert Eich 			WREG32_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, ~(mask | crtc_ext_cntl));
336771fe6b9SJerome Glisse 		}
3375e916a3aSMichel Dänzer 		if (dev->num_crtcs > radeon_crtc->crtc_id)
3385c9ac115SGustavo Padovan 			drm_crtc_vblank_on(crtc);
3397ed220d7SMichel Dänzer 		radeon_crtc_load_lut(crtc);
340771fe6b9SJerome Glisse 		break;
341771fe6b9SJerome Glisse 	case DRM_MODE_DPMS_STANDBY:
342771fe6b9SJerome Glisse 	case DRM_MODE_DPMS_SUSPEND:
343771fe6b9SJerome Glisse 	case DRM_MODE_DPMS_OFF:
3445e916a3aSMichel Dänzer 		if (dev->num_crtcs > radeon_crtc->crtc_id)
3455c9ac115SGustavo Padovan 			drm_crtc_vblank_off(crtc);
346771fe6b9SJerome Glisse 		if (radeon_crtc->crtc_id)
3478de21525SAlex Deucher 			WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~(RADEON_CRTC2_EN | mask));
348771fe6b9SJerome Glisse 		else {
349771fe6b9SJerome Glisse 			WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B, ~(RADEON_CRTC_EN |
350771fe6b9SJerome Glisse 										    RADEON_CRTC_DISP_REQ_EN_B));
351f8c4d701SEgbert Eich 			WREG32_P(RADEON_CRTC_EXT_CNTL, mask, ~(mask | crtc_ext_cntl));
352771fe6b9SJerome Glisse 		}
353a48b9b4eSAlex Deucher 		radeon_crtc->enabled = false;
354d7311171SAlex Deucher 		/* adjust pm to dpms changes AFTER disabling crtcs */
355d7311171SAlex Deucher 		radeon_pm_compute_clocks(rdev);
356771fe6b9SJerome Glisse 		break;
357771fe6b9SJerome Glisse 	}
358771fe6b9SJerome Glisse }
359771fe6b9SJerome Glisse 
radeon_crtc_set_base(struct drm_crtc * crtc,int x,int y,struct drm_framebuffer * old_fb)360771fe6b9SJerome Glisse int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
361771fe6b9SJerome Glisse 			 struct drm_framebuffer *old_fb)
362771fe6b9SJerome Glisse {
3634dd19b0dSChris Ball 	return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0);
3644dd19b0dSChris Ball }
3654dd19b0dSChris Ball 
radeon_crtc_set_base_atomic(struct drm_crtc * crtc,struct drm_framebuffer * fb,int x,int y,enum mode_set_atomic state)3664dd19b0dSChris Ball int radeon_crtc_set_base_atomic(struct drm_crtc *crtc,
3674dd19b0dSChris Ball 				struct drm_framebuffer *fb,
36821c74a8eSJason Wessel 				int x, int y, enum mode_set_atomic state)
3694dd19b0dSChris Ball {
3704dd19b0dSChris Ball 	return radeon_crtc_do_set_base(crtc, fb, x, y, 1);
3714dd19b0dSChris Ball }
3724dd19b0dSChris Ball 
radeon_crtc_do_set_base(struct drm_crtc * crtc,struct drm_framebuffer * fb,int x,int y,int atomic)3734dd19b0dSChris Ball int radeon_crtc_do_set_base(struct drm_crtc *crtc,
3744dd19b0dSChris Ball 			 struct drm_framebuffer *fb,
3754dd19b0dSChris Ball 			 int x, int y, int atomic)
3764dd19b0dSChris Ball {
377771fe6b9SJerome Glisse 	struct drm_device *dev = crtc->dev;
378771fe6b9SJerome Glisse 	struct radeon_device *rdev = dev->dev_private;
379771fe6b9SJerome Glisse 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
3804dd19b0dSChris Ball 	struct drm_framebuffer *target_fb;
381771fe6b9SJerome Glisse 	struct drm_gem_object *obj;
3824c788679SJerome Glisse 	struct radeon_bo *rbo;
383771fe6b9SJerome Glisse 	uint64_t base;
384771fe6b9SJerome Glisse 	uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0;
385771fe6b9SJerome Glisse 	uint32_t crtc_pitch, pitch_pixels;
386e024e110SDave Airlie 	uint32_t tiling_flags;
38741456df2SDave Airlie 	int format;
38841456df2SDave Airlie 	uint32_t gen_cntl_reg, gen_cntl_val;
3894c788679SJerome Glisse 	int r;
390771fe6b9SJerome Glisse 
391d9fdaafbSDave Airlie 	DRM_DEBUG_KMS("\n");
3922de3b484SJerome Glisse 	/* no fb bound */
393f4510a27SMatt Roper 	if (!atomic && !crtc->primary->fb) {
394d9fdaafbSDave Airlie 		DRM_DEBUG_KMS("No FB bound\n");
3952de3b484SJerome Glisse 		return 0;
3962de3b484SJerome Glisse 	}
397771fe6b9SJerome Glisse 
3989a0f0c9dSDaniel Stone 	if (atomic)
3994dd19b0dSChris Ball 		target_fb = fb;
4009a0f0c9dSDaniel Stone 	else
401f4510a27SMatt Roper 		target_fb = crtc->primary->fb;
402771fe6b9SJerome Glisse 
403272725c7SVille Syrjälä 	switch (target_fb->format->cpp[0] * 8) {
40441456df2SDave Airlie 	case 8:
40541456df2SDave Airlie 		format = 2;
40641456df2SDave Airlie 		break;
40741456df2SDave Airlie 	case 15:      /*  555 */
40841456df2SDave Airlie 		format = 3;
40941456df2SDave Airlie 		break;
41041456df2SDave Airlie 	case 16:      /*  565 */
41141456df2SDave Airlie 		format = 4;
41241456df2SDave Airlie 		break;
41341456df2SDave Airlie 	case 24:      /*  RGB */
41441456df2SDave Airlie 		format = 5;
41541456df2SDave Airlie 		break;
41641456df2SDave Airlie 	case 32:      /* xRGB */
41741456df2SDave Airlie 		format = 6;
41841456df2SDave Airlie 		break;
41941456df2SDave Airlie 	default:
42041456df2SDave Airlie 		return false;
42141456df2SDave Airlie 	}
42241456df2SDave Airlie 
4234c788679SJerome Glisse 	/* Pin framebuffer & get tilling informations */
4249a0f0c9dSDaniel Stone 	obj = target_fb->obj[0];
4257e4d15d9SDaniel Vetter 	rbo = gem_to_radeon_bo(obj);
42697b6ff6bSJerome Glisse retry:
4274c788679SJerome Glisse 	r = radeon_bo_reserve(rbo, false);
4284c788679SJerome Glisse 	if (unlikely(r != 0))
4294c788679SJerome Glisse 		return r;
4300349af70SMichel Dänzer 	/* Only 27 bit offset for legacy CRTC */
4310349af70SMichel Dänzer 	r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, 1 << 27,
4320349af70SMichel Dänzer 				     &base);
4334c788679SJerome Glisse 	if (unlikely(r != 0)) {
4344c788679SJerome Glisse 		radeon_bo_unreserve(rbo);
43597b6ff6bSJerome Glisse 
43697b6ff6bSJerome Glisse 		/* On old GPU like RN50 with little vram pining can fails because
43797b6ff6bSJerome Glisse 		 * current fb is taking all space needed. So instead of unpining
43897b6ff6bSJerome Glisse 		 * the old buffer after pining the new one, first unpin old one
43997b6ff6bSJerome Glisse 		 * and then retry pining new one.
44097b6ff6bSJerome Glisse 		 *
44197b6ff6bSJerome Glisse 		 * As only master can set mode only master can pin and it is
44297b6ff6bSJerome Glisse 		 * unlikely the master client will race with itself especialy
44397b6ff6bSJerome Glisse 		 * on those old gpu with single crtc.
44497b6ff6bSJerome Glisse 		 *
44597b6ff6bSJerome Glisse 		 * We don't shutdown the display controller because new buffer
44697b6ff6bSJerome Glisse 		 * will end up in same spot.
44797b6ff6bSJerome Glisse 		 */
448f4510a27SMatt Roper 		if (!atomic && fb && fb != crtc->primary->fb) {
44997b6ff6bSJerome Glisse 			struct radeon_bo *old_rbo;
45097b6ff6bSJerome Glisse 			unsigned long nsize, osize;
45197b6ff6bSJerome Glisse 
452a110dfe3SDaniel Stone 			old_rbo = gem_to_radeon_bo(fb->obj[0]);
45397b6ff6bSJerome Glisse 			osize = radeon_bo_size(old_rbo);
45497b6ff6bSJerome Glisse 			nsize = radeon_bo_size(rbo);
45597b6ff6bSJerome Glisse 			if (nsize <= osize && !radeon_bo_reserve(old_rbo, false)) {
45697b6ff6bSJerome Glisse 				radeon_bo_unpin(old_rbo);
45797b6ff6bSJerome Glisse 				radeon_bo_unreserve(old_rbo);
45897b6ff6bSJerome Glisse 				fb = NULL;
45997b6ff6bSJerome Glisse 				goto retry;
46097b6ff6bSJerome Glisse 			}
46197b6ff6bSJerome Glisse 		}
462771fe6b9SJerome Glisse 		return -EINVAL;
463771fe6b9SJerome Glisse 	}
4644c788679SJerome Glisse 	radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
4654c788679SJerome Glisse 	radeon_bo_unreserve(rbo);
4664c788679SJerome Glisse 	if (tiling_flags & RADEON_TILING_MICRO)
4674c788679SJerome Glisse 		DRM_ERROR("trying to scanout microtiled buffer\n");
4684c788679SJerome Glisse 
4694162338aSDave Airlie 	/* if scanout was in GTT this really wouldn't work */
4704162338aSDave Airlie 	/* crtc offset is from display base addr not FB location */
471d594e46aSJerome Glisse 	radeon_crtc->legacy_display_base_addr = rdev->mc.vram_start;
4724162338aSDave Airlie 
4734162338aSDave Airlie 	base -= radeon_crtc->legacy_display_base_addr;
4744162338aSDave Airlie 
475771fe6b9SJerome Glisse 	crtc_offset_cntl = 0;
476771fe6b9SJerome Glisse 
477272725c7SVille Syrjälä 	pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0];
478272725c7SVille Syrjälä 	crtc_pitch = DIV_ROUND_UP(pitch_pixels * target_fb->format->cpp[0] * 8,
479272725c7SVille Syrjälä 				  target_fb->format->cpp[0] * 8 * 8);
480771fe6b9SJerome Glisse 	crtc_pitch |= crtc_pitch << 16;
481771fe6b9SJerome Glisse 
482c640e8caSDave Airlie 	crtc_offset_cntl |= RADEON_CRTC_GUI_TRIG_OFFSET_LEFT_EN;
483e024e110SDave Airlie 	if (tiling_flags & RADEON_TILING_MACRO) {
484771fe6b9SJerome Glisse 		if (ASIC_IS_R300(rdev))
485771fe6b9SJerome Glisse 			crtc_offset_cntl |= (R300_CRTC_X_Y_MODE_EN |
486771fe6b9SJerome Glisse 					     R300_CRTC_MICRO_TILE_BUFFER_DIS |
487771fe6b9SJerome Glisse 					     R300_CRTC_MACRO_TILE_EN);
488771fe6b9SJerome Glisse 		else
489771fe6b9SJerome Glisse 			crtc_offset_cntl |= RADEON_CRTC_TILE_EN;
490771fe6b9SJerome Glisse 	} else {
491771fe6b9SJerome Glisse 		if (ASIC_IS_R300(rdev))
492771fe6b9SJerome Glisse 			crtc_offset_cntl &= ~(R300_CRTC_X_Y_MODE_EN |
493771fe6b9SJerome Glisse 					      R300_CRTC_MICRO_TILE_BUFFER_DIS |
494771fe6b9SJerome Glisse 					      R300_CRTC_MACRO_TILE_EN);
495771fe6b9SJerome Glisse 		else
496771fe6b9SJerome Glisse 			crtc_offset_cntl &= ~RADEON_CRTC_TILE_EN;
497771fe6b9SJerome Glisse 	}
498771fe6b9SJerome Glisse 
499e024e110SDave Airlie 	if (tiling_flags & RADEON_TILING_MACRO) {
500771fe6b9SJerome Glisse 		if (ASIC_IS_R300(rdev)) {
501771fe6b9SJerome Glisse 			crtc_tile_x0_y0 = x | (y << 16);
502771fe6b9SJerome Glisse 			base &= ~0x7ff;
503771fe6b9SJerome Glisse 		} else {
504272725c7SVille Syrjälä 			int byteshift = target_fb->format->cpp[0] * 8 >> 4;
505e024e110SDave Airlie 			int tile_addr = (((y >> 3) * pitch_pixels +  x) >> (8 - byteshift)) << 11;
506771fe6b9SJerome Glisse 			base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8);
507771fe6b9SJerome Glisse 			crtc_offset_cntl |= (y % 16);
508771fe6b9SJerome Glisse 		}
509771fe6b9SJerome Glisse 	} else {
510771fe6b9SJerome Glisse 		int offset = y * pitch_pixels + x;
511272725c7SVille Syrjälä 		switch (target_fb->format->cpp[0] * 8) {
51241456df2SDave Airlie 		case 8:
51341456df2SDave Airlie 			offset *= 1;
51441456df2SDave Airlie 			break;
515771fe6b9SJerome Glisse 		case 15:
516771fe6b9SJerome Glisse 		case 16:
517771fe6b9SJerome Glisse 			offset *= 2;
518771fe6b9SJerome Glisse 			break;
519771fe6b9SJerome Glisse 		case 24:
520771fe6b9SJerome Glisse 			offset *= 3;
521771fe6b9SJerome Glisse 			break;
522771fe6b9SJerome Glisse 		case 32:
523771fe6b9SJerome Glisse 			offset *= 4;
524771fe6b9SJerome Glisse 			break;
525771fe6b9SJerome Glisse 		default:
526771fe6b9SJerome Glisse 			return false;
527771fe6b9SJerome Glisse 		}
528771fe6b9SJerome Glisse 		base += offset;
529771fe6b9SJerome Glisse 	}
530771fe6b9SJerome Glisse 
531771fe6b9SJerome Glisse 	base &= ~7;
532771fe6b9SJerome Glisse 
53341456df2SDave Airlie 	if (radeon_crtc->crtc_id == 1)
53441456df2SDave Airlie 		gen_cntl_reg = RADEON_CRTC2_GEN_CNTL;
53541456df2SDave Airlie 	else
53641456df2SDave Airlie 		gen_cntl_reg = RADEON_CRTC_GEN_CNTL;
53741456df2SDave Airlie 
53841456df2SDave Airlie 	gen_cntl_val = RREG32(gen_cntl_reg);
53941456df2SDave Airlie 	gen_cntl_val &= ~(0xf << 8);
54041456df2SDave Airlie 	gen_cntl_val |= (format << 8);
541c640e8caSDave Airlie 	gen_cntl_val &= ~RADEON_CRTC_VSTAT_MODE_MASK;
54241456df2SDave Airlie 	WREG32(gen_cntl_reg, gen_cntl_val);
54341456df2SDave Airlie 
544771fe6b9SJerome Glisse 	crtc_offset = (u32)base;
545771fe6b9SJerome Glisse 
5464162338aSDave Airlie 	WREG32(RADEON_DISPLAY_BASE_ADDR + radeon_crtc->crtc_offset, radeon_crtc->legacy_display_base_addr);
547771fe6b9SJerome Glisse 
548771fe6b9SJerome Glisse 	if (ASIC_IS_R300(rdev)) {
549771fe6b9SJerome Glisse 		if (radeon_crtc->crtc_id)
550771fe6b9SJerome Glisse 			WREG32(R300_CRTC2_TILE_X0_Y0, crtc_tile_x0_y0);
551771fe6b9SJerome Glisse 		else
552771fe6b9SJerome Glisse 			WREG32(R300_CRTC_TILE_X0_Y0, crtc_tile_x0_y0);
553771fe6b9SJerome Glisse 	}
554771fe6b9SJerome Glisse 	WREG32(RADEON_CRTC_OFFSET_CNTL + radeon_crtc->crtc_offset, crtc_offset_cntl);
555771fe6b9SJerome Glisse 	WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, crtc_offset);
556771fe6b9SJerome Glisse 	WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch);
557771fe6b9SJerome Glisse 
558f4510a27SMatt Roper 	if (!atomic && fb && fb != crtc->primary->fb) {
559a110dfe3SDaniel Stone 		rbo = gem_to_radeon_bo(fb->obj[0]);
5604c788679SJerome Glisse 		r = radeon_bo_reserve(rbo, false);
5614c788679SJerome Glisse 		if (unlikely(r != 0))
5624c788679SJerome Glisse 			return r;
5634c788679SJerome Glisse 		radeon_bo_unpin(rbo);
5644c788679SJerome Glisse 		radeon_bo_unreserve(rbo);
565771fe6b9SJerome Glisse 	}
566f30f37deSMichel Dänzer 
567f30f37deSMichel Dänzer 	/* Bytes per pixel may have changed */
568f30f37deSMichel Dänzer 	radeon_bandwidth_update(rdev);
569f30f37deSMichel Dänzer 
570771fe6b9SJerome Glisse 	return 0;
571771fe6b9SJerome Glisse }
572771fe6b9SJerome Glisse 
radeon_set_crtc_timing(struct drm_crtc * crtc,struct drm_display_mode * mode)573771fe6b9SJerome Glisse static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mode *mode)
574771fe6b9SJerome Glisse {
575771fe6b9SJerome Glisse 	struct drm_device *dev = crtc->dev;
576771fe6b9SJerome Glisse 	struct radeon_device *rdev = dev->dev_private;
577771fe6b9SJerome Glisse 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
578489f3267SVille Syrjälä 	const struct drm_framebuffer *fb = crtc->primary->fb;
5794ce001abSDave Airlie 	struct drm_encoder *encoder;
580771fe6b9SJerome Glisse 	int format;
581771fe6b9SJerome Glisse 	int hsync_start;
582771fe6b9SJerome Glisse 	int hsync_wid;
583771fe6b9SJerome Glisse 	int vsync_wid;
584771fe6b9SJerome Glisse 	uint32_t crtc_h_total_disp;
585771fe6b9SJerome Glisse 	uint32_t crtc_h_sync_strt_wid;
586771fe6b9SJerome Glisse 	uint32_t crtc_v_total_disp;
587771fe6b9SJerome Glisse 	uint32_t crtc_v_sync_strt_wid;
5884ce001abSDave Airlie 	bool is_tv = false;
589771fe6b9SJerome Glisse 
590d9fdaafbSDave Airlie 	DRM_DEBUG_KMS("\n");
5914ce001abSDave Airlie 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
5924ce001abSDave Airlie 		if (encoder->crtc == crtc) {
5934ce001abSDave Airlie 			struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
5944ce001abSDave Airlie 			if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) {
5954ce001abSDave Airlie 				is_tv = true;
5964ce001abSDave Airlie 				DRM_INFO("crtc %d is connected to a TV\n", radeon_crtc->crtc_id);
5974ce001abSDave Airlie 				break;
5984ce001abSDave Airlie 			}
5994ce001abSDave Airlie 		}
6004ce001abSDave Airlie 	}
601771fe6b9SJerome Glisse 
602272725c7SVille Syrjälä 	switch (fb->format->cpp[0] * 8) {
60341456df2SDave Airlie 	case 8:
60441456df2SDave Airlie 		format = 2;
60541456df2SDave Airlie 		break;
606771fe6b9SJerome Glisse 	case 15:      /*  555 */
607771fe6b9SJerome Glisse 		format = 3;
608771fe6b9SJerome Glisse 		break;
609771fe6b9SJerome Glisse 	case 16:      /*  565 */
610771fe6b9SJerome Glisse 		format = 4;
611771fe6b9SJerome Glisse 		break;
612771fe6b9SJerome Glisse 	case 24:      /*  RGB */
613771fe6b9SJerome Glisse 		format = 5;
614771fe6b9SJerome Glisse 		break;
615771fe6b9SJerome Glisse 	case 32:      /* xRGB */
616771fe6b9SJerome Glisse 		format = 6;
617771fe6b9SJerome Glisse 		break;
618771fe6b9SJerome Glisse 	default:
619771fe6b9SJerome Glisse 		return false;
620771fe6b9SJerome Glisse 	}
621771fe6b9SJerome Glisse 
622771fe6b9SJerome Glisse 	crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff)
623771fe6b9SJerome Glisse 			     | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16));
624771fe6b9SJerome Glisse 
625771fe6b9SJerome Glisse 	hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8;
626771fe6b9SJerome Glisse 	if (!hsync_wid)
627771fe6b9SJerome Glisse 		hsync_wid = 1;
628771fe6b9SJerome Glisse 	hsync_start = mode->crtc_hsync_start - 8;
629771fe6b9SJerome Glisse 
630771fe6b9SJerome Glisse 	crtc_h_sync_strt_wid = ((hsync_start & 0x1fff)
631771fe6b9SJerome Glisse 				| ((hsync_wid & 0x3f) << 16)
632771fe6b9SJerome Glisse 				| ((mode->flags & DRM_MODE_FLAG_NHSYNC)
633771fe6b9SJerome Glisse 				   ? RADEON_CRTC_H_SYNC_POL
634771fe6b9SJerome Glisse 				   : 0));
635771fe6b9SJerome Glisse 
636771fe6b9SJerome Glisse 	/* This works for double scan mode. */
637771fe6b9SJerome Glisse 	crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff)
638771fe6b9SJerome Glisse 			     | ((mode->crtc_vdisplay - 1) << 16));
639771fe6b9SJerome Glisse 
640771fe6b9SJerome Glisse 	vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start;
641771fe6b9SJerome Glisse 	if (!vsync_wid)
642771fe6b9SJerome Glisse 		vsync_wid = 1;
643771fe6b9SJerome Glisse 
644771fe6b9SJerome Glisse 	crtc_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff)
645771fe6b9SJerome Glisse 				| ((vsync_wid & 0x1f) << 16)
646771fe6b9SJerome Glisse 				| ((mode->flags & DRM_MODE_FLAG_NVSYNC)
647771fe6b9SJerome Glisse 				   ? RADEON_CRTC_V_SYNC_POL
648771fe6b9SJerome Glisse 				   : 0));
649771fe6b9SJerome Glisse 
650771fe6b9SJerome Glisse 	if (radeon_crtc->crtc_id) {
651771fe6b9SJerome Glisse 		uint32_t crtc2_gen_cntl;
652771fe6b9SJerome Glisse 		uint32_t disp2_merge_cntl;
653771fe6b9SJerome Glisse 
654ee2215f0SJerome Glisse 		/* if TV DAC is enabled for another crtc and keep it enabled */
655ee2215f0SJerome Glisse 		crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0x00718080;
656771fe6b9SJerome Glisse 		crtc2_gen_cntl |= ((format << 8)
657771fe6b9SJerome Glisse 				   | RADEON_CRTC2_VSYNC_DIS
658771fe6b9SJerome Glisse 				   | RADEON_CRTC2_HSYNC_DIS
659771fe6b9SJerome Glisse 				   | RADEON_CRTC2_DISP_DIS
660771fe6b9SJerome Glisse 				   | RADEON_CRTC2_DISP_REQ_EN_B
661771fe6b9SJerome Glisse 				   | ((mode->flags & DRM_MODE_FLAG_DBLSCAN)
662771fe6b9SJerome Glisse 				      ? RADEON_CRTC2_DBL_SCAN_EN
663771fe6b9SJerome Glisse 				      : 0)
664771fe6b9SJerome Glisse 				   | ((mode->flags & DRM_MODE_FLAG_CSYNC)
665771fe6b9SJerome Glisse 				      ? RADEON_CRTC2_CSYNC_EN
666771fe6b9SJerome Glisse 				      : 0)
667771fe6b9SJerome Glisse 				   | ((mode->flags & DRM_MODE_FLAG_INTERLACE)
668771fe6b9SJerome Glisse 				      ? RADEON_CRTC2_INTERLACE_EN
669771fe6b9SJerome Glisse 				      : 0));
670771fe6b9SJerome Glisse 
671d805f50aSAlex Deucher 		/* rs4xx chips seem to like to have the crtc enabled when the timing is set */
672d805f50aSAlex Deucher 		if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480))
673d805f50aSAlex Deucher 			crtc2_gen_cntl |= RADEON_CRTC2_EN;
674d805f50aSAlex Deucher 
675771fe6b9SJerome Glisse 		disp2_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL);
676771fe6b9SJerome Glisse 		disp2_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN;
677771fe6b9SJerome Glisse 
678771fe6b9SJerome Glisse 		WREG32(RADEON_DISP2_MERGE_CNTL, disp2_merge_cntl);
679771fe6b9SJerome Glisse 		WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
6801b4d7d75SAlex Deucher 
6811b4d7d75SAlex Deucher 		WREG32(RADEON_FP_H2_SYNC_STRT_WID, crtc_h_sync_strt_wid);
6821b4d7d75SAlex Deucher 		WREG32(RADEON_FP_V2_SYNC_STRT_WID, crtc_v_sync_strt_wid);
683771fe6b9SJerome Glisse 	} else {
684771fe6b9SJerome Glisse 		uint32_t crtc_gen_cntl;
685771fe6b9SJerome Glisse 		uint32_t crtc_ext_cntl;
686771fe6b9SJerome Glisse 		uint32_t disp_merge_cntl;
687771fe6b9SJerome Glisse 
688ee2215f0SJerome Glisse 		crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0x00718000;
689ee2215f0SJerome Glisse 		crtc_gen_cntl |= (RADEON_CRTC_EXT_DISP_EN
690771fe6b9SJerome Glisse 				 | (format << 8)
691771fe6b9SJerome Glisse 				 | RADEON_CRTC_DISP_REQ_EN_B
692771fe6b9SJerome Glisse 				 | ((mode->flags & DRM_MODE_FLAG_DBLSCAN)
693771fe6b9SJerome Glisse 				    ? RADEON_CRTC_DBL_SCAN_EN
694771fe6b9SJerome Glisse 				    : 0)
695771fe6b9SJerome Glisse 				 | ((mode->flags & DRM_MODE_FLAG_CSYNC)
696771fe6b9SJerome Glisse 				    ? RADEON_CRTC_CSYNC_EN
697771fe6b9SJerome Glisse 				    : 0)
698771fe6b9SJerome Glisse 				 | ((mode->flags & DRM_MODE_FLAG_INTERLACE)
699771fe6b9SJerome Glisse 				    ? RADEON_CRTC_INTERLACE_EN
700771fe6b9SJerome Glisse 				    : 0));
701771fe6b9SJerome Glisse 
702d805f50aSAlex Deucher 		/* rs4xx chips seem to like to have the crtc enabled when the timing is set */
703d805f50aSAlex Deucher 		if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480))
704d805f50aSAlex Deucher 			crtc_gen_cntl |= RADEON_CRTC_EN;
705d805f50aSAlex Deucher 
706771fe6b9SJerome Glisse 		crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL);
707771fe6b9SJerome Glisse 		crtc_ext_cntl |= (RADEON_XCRT_CNT_EN |
708771fe6b9SJerome Glisse 				  RADEON_CRTC_VSYNC_DIS |
709771fe6b9SJerome Glisse 				  RADEON_CRTC_HSYNC_DIS |
710771fe6b9SJerome Glisse 				  RADEON_CRTC_DISPLAY_DIS);
711771fe6b9SJerome Glisse 
712771fe6b9SJerome Glisse 		disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL);
713771fe6b9SJerome Glisse 		disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN;
714771fe6b9SJerome Glisse 
715771fe6b9SJerome Glisse 		WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl);
716771fe6b9SJerome Glisse 		WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl);
717771fe6b9SJerome Glisse 		WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl);
718771fe6b9SJerome Glisse 	}
719771fe6b9SJerome Glisse 
7204ce001abSDave Airlie 	if (is_tv)
7214ce001abSDave Airlie 		radeon_legacy_tv_adjust_crtc_reg(encoder, &crtc_h_total_disp,
7224ce001abSDave Airlie 						 &crtc_h_sync_strt_wid, &crtc_v_total_disp,
7234ce001abSDave Airlie 						 &crtc_v_sync_strt_wid);
7244ce001abSDave Airlie 
725771fe6b9SJerome Glisse 	WREG32(RADEON_CRTC_H_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_h_total_disp);
726771fe6b9SJerome Glisse 	WREG32(RADEON_CRTC_H_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_h_sync_strt_wid);
727771fe6b9SJerome Glisse 	WREG32(RADEON_CRTC_V_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_v_total_disp);
728771fe6b9SJerome Glisse 	WREG32(RADEON_CRTC_V_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_v_sync_strt_wid);
729771fe6b9SJerome Glisse 
730771fe6b9SJerome Glisse 	return true;
731771fe6b9SJerome Glisse }
732771fe6b9SJerome Glisse 
radeon_set_pll(struct drm_crtc * crtc,struct drm_display_mode * mode)733771fe6b9SJerome Glisse static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
734771fe6b9SJerome Glisse {
735771fe6b9SJerome Glisse 	struct drm_device *dev = crtc->dev;
736771fe6b9SJerome Glisse 	struct radeon_device *rdev = dev->dev_private;
737771fe6b9SJerome Glisse 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
738771fe6b9SJerome Glisse 	struct drm_encoder *encoder;
739771fe6b9SJerome Glisse 	uint32_t feedback_div = 0;
740771fe6b9SJerome Glisse 	uint32_t frac_fb_div = 0;
741771fe6b9SJerome Glisse 	uint32_t reference_div = 0;
742771fe6b9SJerome Glisse 	uint32_t post_divider = 0;
743771fe6b9SJerome Glisse 	uint32_t freq = 0;
744771fe6b9SJerome Glisse 	uint8_t pll_gain;
745771fe6b9SJerome Glisse 	bool use_bios_divs = false;
746771fe6b9SJerome Glisse 	/* PLL registers */
747771fe6b9SJerome Glisse 	uint32_t pll_ref_div = 0;
748771fe6b9SJerome Glisse 	uint32_t pll_fb_post_div = 0;
749771fe6b9SJerome Glisse 	uint32_t htotal_cntl = 0;
7504ce001abSDave Airlie 	bool is_tv = false;
751771fe6b9SJerome Glisse 	struct radeon_pll *pll;
752771fe6b9SJerome Glisse 
753771fe6b9SJerome Glisse 	struct {
754771fe6b9SJerome Glisse 		int divider;
755771fe6b9SJerome Glisse 		int bitvalue;
756771fe6b9SJerome Glisse 	} *post_div, post_divs[]   = {
757771fe6b9SJerome Glisse 		/* From RAGE 128 VR/RAGE 128 GL Register
758771fe6b9SJerome Glisse 		 * Reference Manual (Technical Reference
759771fe6b9SJerome Glisse 		 * Manual P/N RRG-G04100-C Rev. 0.04), page
760771fe6b9SJerome Glisse 		 * 3-17 (PLL_DIV_[3:0]).
761771fe6b9SJerome Glisse 		 */
762771fe6b9SJerome Glisse 		{  1, 0 },              /* VCLK_SRC                 */
763771fe6b9SJerome Glisse 		{  2, 1 },              /* VCLK_SRC/2               */
764771fe6b9SJerome Glisse 		{  4, 2 },              /* VCLK_SRC/4               */
765771fe6b9SJerome Glisse 		{  8, 3 },              /* VCLK_SRC/8               */
766771fe6b9SJerome Glisse 		{  3, 4 },              /* VCLK_SRC/3               */
767771fe6b9SJerome Glisse 		{ 16, 5 },              /* VCLK_SRC/16              */
768771fe6b9SJerome Glisse 		{  6, 6 },              /* VCLK_SRC/6               */
769771fe6b9SJerome Glisse 		{ 12, 7 },              /* VCLK_SRC/12              */
770771fe6b9SJerome Glisse 		{  0, 0 }
771771fe6b9SJerome Glisse 	};
772771fe6b9SJerome Glisse 
773771fe6b9SJerome Glisse 	if (radeon_crtc->crtc_id)
774771fe6b9SJerome Glisse 		pll = &rdev->clock.p2pll;
775771fe6b9SJerome Glisse 	else
776771fe6b9SJerome Glisse 		pll = &rdev->clock.p1pll;
777771fe6b9SJerome Glisse 
778fc10332bSAlex Deucher 	pll->flags = RADEON_PLL_LEGACY;
779771fe6b9SJerome Glisse 
7805480f727SDave Airlie 	if (mode->clock > 200000) /* range limits??? */
7815480f727SDave Airlie 		pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
7825480f727SDave Airlie 	else
7835480f727SDave Airlie 		pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
7845480f727SDave Airlie 
785771fe6b9SJerome Glisse 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
786771fe6b9SJerome Glisse 		if (encoder->crtc == crtc) {
7874ce001abSDave Airlie 			struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
7884ce001abSDave Airlie 
7894ce001abSDave Airlie 			if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) {
7904ce001abSDave Airlie 				is_tv = true;
7914ce001abSDave Airlie 				break;
7924ce001abSDave Airlie 			}
7934ce001abSDave Airlie 
794771fe6b9SJerome Glisse 			if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
795fc10332bSAlex Deucher 				pll->flags |= RADEON_PLL_NO_ODD_POST_DIV;
796771fe6b9SJerome Glisse 			if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) {
7974c4f5413SAlex Deucher 				if (!rdev->is_atom_bios) {
798771fe6b9SJerome Glisse 					struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
799771fe6b9SJerome Glisse 					struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
800771fe6b9SJerome Glisse 					if (lvds) {
801771fe6b9SJerome Glisse 						if (lvds->use_bios_dividers) {
802771fe6b9SJerome Glisse 							pll_ref_div = lvds->panel_ref_divider;
803771fe6b9SJerome Glisse 							pll_fb_post_div   = (lvds->panel_fb_divider |
804771fe6b9SJerome Glisse 									     (lvds->panel_post_divider << 16));
805771fe6b9SJerome Glisse 							htotal_cntl  = 0;
806771fe6b9SJerome Glisse 							use_bios_divs = true;
807771fe6b9SJerome Glisse 						}
808771fe6b9SJerome Glisse 					}
809771fe6b9SJerome Glisse 				}
810fc10332bSAlex Deucher 				pll->flags |= RADEON_PLL_USE_REF_DIV;
811771fe6b9SJerome Glisse 			}
812771fe6b9SJerome Glisse 		}
8134c4f5413SAlex Deucher 	}
814771fe6b9SJerome Glisse 
815d9fdaafbSDave Airlie 	DRM_DEBUG_KMS("\n");
816771fe6b9SJerome Glisse 
817771fe6b9SJerome Glisse 	if (!use_bios_divs) {
818f523f74eSAlex Deucher 		radeon_compute_pll_legacy(pll, mode->clock,
819771fe6b9SJerome Glisse 					  &freq, &feedback_div, &frac_fb_div,
820fc10332bSAlex Deucher 					  &reference_div, &post_divider);
821771fe6b9SJerome Glisse 
822771fe6b9SJerome Glisse 		for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
823771fe6b9SJerome Glisse 			if (post_div->divider == post_divider)
824771fe6b9SJerome Glisse 				break;
825771fe6b9SJerome Glisse 		}
826771fe6b9SJerome Glisse 
827771fe6b9SJerome Glisse 		if (!post_div->divider)
828771fe6b9SJerome Glisse 			post_div = &post_divs[0];
829771fe6b9SJerome Glisse 
830d9fdaafbSDave Airlie 		DRM_DEBUG_KMS("dc=%u, fd=%d, rd=%d, pd=%d\n",
831771fe6b9SJerome Glisse 			  (unsigned)freq,
832771fe6b9SJerome Glisse 			  feedback_div,
833771fe6b9SJerome Glisse 			  reference_div,
834771fe6b9SJerome Glisse 			  post_divider);
835771fe6b9SJerome Glisse 
836771fe6b9SJerome Glisse 		pll_ref_div   = reference_div;
837771fe6b9SJerome Glisse #if defined(__powerpc__) && (0) /* TODO */
838771fe6b9SJerome Glisse 		/* apparently programming this otherwise causes a hang??? */
839771fe6b9SJerome Glisse 		if (info->MacModel == RADEON_MAC_IBOOK)
840771fe6b9SJerome Glisse 			pll_fb_post_div = 0x000600ad;
841771fe6b9SJerome Glisse 		else
842771fe6b9SJerome Glisse #endif
843771fe6b9SJerome Glisse 			pll_fb_post_div     = (feedback_div | (post_div->bitvalue << 16));
844771fe6b9SJerome Glisse 
845771fe6b9SJerome Glisse 		htotal_cntl    = mode->htotal & 0x7;
846771fe6b9SJerome Glisse 
847771fe6b9SJerome Glisse 	}
848771fe6b9SJerome Glisse 
849771fe6b9SJerome Glisse 	pll_gain = radeon_compute_pll_gain(pll->reference_freq,
850771fe6b9SJerome Glisse 					   pll_ref_div & 0x3ff,
851771fe6b9SJerome Glisse 					   pll_fb_post_div & 0x7ff);
852771fe6b9SJerome Glisse 
853771fe6b9SJerome Glisse 	if (radeon_crtc->crtc_id) {
854771fe6b9SJerome Glisse 		uint32_t pixclks_cntl = ((RREG32_PLL(RADEON_PIXCLKS_CNTL) &
855771fe6b9SJerome Glisse 					  ~(RADEON_PIX2CLK_SRC_SEL_MASK)) |
856771fe6b9SJerome Glisse 					 RADEON_PIX2CLK_SRC_SEL_P2PLLCLK);
857771fe6b9SJerome Glisse 
8584ce001abSDave Airlie 		if (is_tv) {
8594ce001abSDave Airlie 			radeon_legacy_tv_adjust_pll2(encoder, &htotal_cntl,
8604ce001abSDave Airlie 						     &pll_ref_div, &pll_fb_post_div,
8614ce001abSDave Airlie 						     &pixclks_cntl);
8624ce001abSDave Airlie 		}
8634ce001abSDave Airlie 
864771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_PIXCLKS_CNTL,
865771fe6b9SJerome Glisse 			     RADEON_PIX2CLK_SRC_SEL_CPUCLK,
866771fe6b9SJerome Glisse 			     ~(RADEON_PIX2CLK_SRC_SEL_MASK));
867771fe6b9SJerome Glisse 
868771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_P2PLL_CNTL,
869771fe6b9SJerome Glisse 			     RADEON_P2PLL_RESET
870771fe6b9SJerome Glisse 			     | RADEON_P2PLL_ATOMIC_UPDATE_EN
871771fe6b9SJerome Glisse 			     | ((uint32_t)pll_gain << RADEON_P2PLL_PVG_SHIFT),
872771fe6b9SJerome Glisse 			     ~(RADEON_P2PLL_RESET
873771fe6b9SJerome Glisse 			       | RADEON_P2PLL_ATOMIC_UPDATE_EN
874771fe6b9SJerome Glisse 			       | RADEON_P2PLL_PVG_MASK));
875771fe6b9SJerome Glisse 
876771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_P2PLL_REF_DIV,
877771fe6b9SJerome Glisse 			     pll_ref_div,
878771fe6b9SJerome Glisse 			     ~RADEON_P2PLL_REF_DIV_MASK);
879771fe6b9SJerome Glisse 
880771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_P2PLL_DIV_0,
881771fe6b9SJerome Glisse 			     pll_fb_post_div,
882771fe6b9SJerome Glisse 			     ~RADEON_P2PLL_FB0_DIV_MASK);
883771fe6b9SJerome Glisse 
884771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_P2PLL_DIV_0,
885771fe6b9SJerome Glisse 			     pll_fb_post_div,
886771fe6b9SJerome Glisse 			     ~RADEON_P2PLL_POST0_DIV_MASK);
887771fe6b9SJerome Glisse 
888771fe6b9SJerome Glisse 		radeon_pll2_write_update(dev);
889771fe6b9SJerome Glisse 		radeon_pll2_wait_for_read_update_complete(dev);
890771fe6b9SJerome Glisse 
891771fe6b9SJerome Glisse 		WREG32_PLL(RADEON_HTOTAL2_CNTL, htotal_cntl);
892771fe6b9SJerome Glisse 
893771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_P2PLL_CNTL,
894771fe6b9SJerome Glisse 			     0,
895771fe6b9SJerome Glisse 			     ~(RADEON_P2PLL_RESET
896771fe6b9SJerome Glisse 			       | RADEON_P2PLL_SLEEP
897771fe6b9SJerome Glisse 			       | RADEON_P2PLL_ATOMIC_UPDATE_EN));
898771fe6b9SJerome Glisse 
899d9fdaafbSDave Airlie 		DRM_DEBUG_KMS("Wrote2: 0x%08x 0x%08x 0x%08x (0x%08x)\n",
900771fe6b9SJerome Glisse 			  (unsigned)pll_ref_div,
901771fe6b9SJerome Glisse 			  (unsigned)pll_fb_post_div,
902771fe6b9SJerome Glisse 			  (unsigned)htotal_cntl,
903771fe6b9SJerome Glisse 			  RREG32_PLL(RADEON_P2PLL_CNTL));
904d9fdaafbSDave Airlie 		DRM_DEBUG_KMS("Wrote2: rd=%u, fd=%u, pd=%u\n",
905771fe6b9SJerome Glisse 			  (unsigned)pll_ref_div & RADEON_P2PLL_REF_DIV_MASK,
906771fe6b9SJerome Glisse 			  (unsigned)pll_fb_post_div & RADEON_P2PLL_FB0_DIV_MASK,
907771fe6b9SJerome Glisse 			  (unsigned)((pll_fb_post_div &
908771fe6b9SJerome Glisse 				      RADEON_P2PLL_POST0_DIV_MASK) >> 16));
909771fe6b9SJerome Glisse 
910771fe6b9SJerome Glisse 		mdelay(50); /* Let the clock to lock */
911771fe6b9SJerome Glisse 
912771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_PIXCLKS_CNTL,
913771fe6b9SJerome Glisse 			     RADEON_PIX2CLK_SRC_SEL_P2PLLCLK,
914771fe6b9SJerome Glisse 			     ~(RADEON_PIX2CLK_SRC_SEL_MASK));
915771fe6b9SJerome Glisse 
916771fe6b9SJerome Glisse 		WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
917771fe6b9SJerome Glisse 	} else {
9184ce001abSDave Airlie 		uint32_t pixclks_cntl;
9194ce001abSDave Airlie 
9204ce001abSDave Airlie 
9214ce001abSDave Airlie 		if (is_tv) {
9224ce001abSDave Airlie 			pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL);
9234ce001abSDave Airlie 			radeon_legacy_tv_adjust_pll1(encoder, &htotal_cntl, &pll_ref_div,
9244ce001abSDave Airlie 						     &pll_fb_post_div, &pixclks_cntl);
9254ce001abSDave Airlie 		}
9264ce001abSDave Airlie 
927771fe6b9SJerome Glisse 		if (rdev->flags & RADEON_IS_MOBILITY) {
92883e61f71SJustin P. Mattock 			/* A temporal workaround for the occasional blanking on certain laptop panels.
929771fe6b9SJerome Glisse 			   This appears to related to the PLL divider registers (fail to lock?).
930771fe6b9SJerome Glisse 			   It occurs even when all dividers are the same with their old settings.
931771fe6b9SJerome Glisse 			   In this case we really don't need to fiddle with PLL registers.
932771fe6b9SJerome Glisse 			   By doing this we can avoid the blanking problem with some panels.
933771fe6b9SJerome Glisse 			*/
934771fe6b9SJerome Glisse 			if ((pll_ref_div == (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_REF_DIV_MASK)) &&
935771fe6b9SJerome Glisse 			    (pll_fb_post_div == (RREG32_PLL(RADEON_PPLL_DIV_3) &
936771fe6b9SJerome Glisse 						 (RADEON_PPLL_POST3_DIV_MASK | RADEON_PPLL_FB3_DIV_MASK)))) {
937771fe6b9SJerome Glisse 				WREG32_P(RADEON_CLOCK_CNTL_INDEX,
938771fe6b9SJerome Glisse 					 RADEON_PLL_DIV_SEL,
939771fe6b9SJerome Glisse 					 ~(RADEON_PLL_DIV_SEL));
940771fe6b9SJerome Glisse 				r100_pll_errata_after_index(rdev);
941771fe6b9SJerome Glisse 				return;
942771fe6b9SJerome Glisse 			}
943771fe6b9SJerome Glisse 		}
944771fe6b9SJerome Glisse 
945771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_VCLK_ECP_CNTL,
946771fe6b9SJerome Glisse 			     RADEON_VCLK_SRC_SEL_CPUCLK,
947771fe6b9SJerome Glisse 			     ~(RADEON_VCLK_SRC_SEL_MASK));
948771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_PPLL_CNTL,
949771fe6b9SJerome Glisse 			     RADEON_PPLL_RESET
950771fe6b9SJerome Glisse 			     | RADEON_PPLL_ATOMIC_UPDATE_EN
951771fe6b9SJerome Glisse 			     | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN
952771fe6b9SJerome Glisse 			     | ((uint32_t)pll_gain << RADEON_PPLL_PVG_SHIFT),
953771fe6b9SJerome Glisse 			     ~(RADEON_PPLL_RESET
954771fe6b9SJerome Glisse 			       | RADEON_PPLL_ATOMIC_UPDATE_EN
955771fe6b9SJerome Glisse 			       | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN
956771fe6b9SJerome Glisse 			       | RADEON_PPLL_PVG_MASK));
957771fe6b9SJerome Glisse 
958771fe6b9SJerome Glisse 		WREG32_P(RADEON_CLOCK_CNTL_INDEX,
959771fe6b9SJerome Glisse 			 RADEON_PLL_DIV_SEL,
960771fe6b9SJerome Glisse 			 ~(RADEON_PLL_DIV_SEL));
961771fe6b9SJerome Glisse 		r100_pll_errata_after_index(rdev);
962771fe6b9SJerome Glisse 
963771fe6b9SJerome Glisse 		if (ASIC_IS_R300(rdev) ||
964771fe6b9SJerome Glisse 		    (rdev->family == CHIP_RS300) ||
965771fe6b9SJerome Glisse 		    (rdev->family == CHIP_RS400) ||
966771fe6b9SJerome Glisse 		    (rdev->family == CHIP_RS480)) {
967771fe6b9SJerome Glisse 			if (pll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) {
968771fe6b9SJerome Glisse 				/* When restoring console mode, use saved PPLL_REF_DIV
969771fe6b9SJerome Glisse 				 * setting.
970771fe6b9SJerome Glisse 				 */
971771fe6b9SJerome Glisse 				WREG32_PLL_P(RADEON_PPLL_REF_DIV,
972771fe6b9SJerome Glisse 					     pll_ref_div,
973771fe6b9SJerome Glisse 					     0);
974771fe6b9SJerome Glisse 			} else {
975771fe6b9SJerome Glisse 				/* R300 uses ref_div_acc field as real ref divider */
976771fe6b9SJerome Glisse 				WREG32_PLL_P(RADEON_PPLL_REF_DIV,
977771fe6b9SJerome Glisse 					     (pll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT),
978771fe6b9SJerome Glisse 					     ~R300_PPLL_REF_DIV_ACC_MASK);
979771fe6b9SJerome Glisse 			}
980771fe6b9SJerome Glisse 		} else
981771fe6b9SJerome Glisse 			WREG32_PLL_P(RADEON_PPLL_REF_DIV,
982771fe6b9SJerome Glisse 				     pll_ref_div,
983771fe6b9SJerome Glisse 				     ~RADEON_PPLL_REF_DIV_MASK);
984771fe6b9SJerome Glisse 
985771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_PPLL_DIV_3,
986771fe6b9SJerome Glisse 			     pll_fb_post_div,
987771fe6b9SJerome Glisse 			     ~RADEON_PPLL_FB3_DIV_MASK);
988771fe6b9SJerome Glisse 
989771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_PPLL_DIV_3,
990771fe6b9SJerome Glisse 			     pll_fb_post_div,
991771fe6b9SJerome Glisse 			     ~RADEON_PPLL_POST3_DIV_MASK);
992771fe6b9SJerome Glisse 
993771fe6b9SJerome Glisse 		radeon_pll_write_update(dev);
994771fe6b9SJerome Glisse 		radeon_pll_wait_for_read_update_complete(dev);
995771fe6b9SJerome Glisse 
996771fe6b9SJerome Glisse 		WREG32_PLL(RADEON_HTOTAL_CNTL, htotal_cntl);
997771fe6b9SJerome Glisse 
998771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_PPLL_CNTL,
999771fe6b9SJerome Glisse 			     0,
1000771fe6b9SJerome Glisse 			     ~(RADEON_PPLL_RESET
1001771fe6b9SJerome Glisse 			       | RADEON_PPLL_SLEEP
1002771fe6b9SJerome Glisse 			       | RADEON_PPLL_ATOMIC_UPDATE_EN
1003771fe6b9SJerome Glisse 			       | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN));
1004771fe6b9SJerome Glisse 
1005d9fdaafbSDave Airlie 		DRM_DEBUG_KMS("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n",
1006771fe6b9SJerome Glisse 			  pll_ref_div,
1007771fe6b9SJerome Glisse 			  pll_fb_post_div,
1008771fe6b9SJerome Glisse 			  (unsigned)htotal_cntl,
1009771fe6b9SJerome Glisse 			  RREG32_PLL(RADEON_PPLL_CNTL));
1010d9fdaafbSDave Airlie 		DRM_DEBUG_KMS("Wrote: rd=%d, fd=%d, pd=%d\n",
1011771fe6b9SJerome Glisse 			  pll_ref_div & RADEON_PPLL_REF_DIV_MASK,
1012771fe6b9SJerome Glisse 			  pll_fb_post_div & RADEON_PPLL_FB3_DIV_MASK,
1013771fe6b9SJerome Glisse 			  (pll_fb_post_div & RADEON_PPLL_POST3_DIV_MASK) >> 16);
1014771fe6b9SJerome Glisse 
1015771fe6b9SJerome Glisse 		mdelay(50); /* Let the clock to lock */
1016771fe6b9SJerome Glisse 
1017771fe6b9SJerome Glisse 		WREG32_PLL_P(RADEON_VCLK_ECP_CNTL,
1018771fe6b9SJerome Glisse 			     RADEON_VCLK_SRC_SEL_PPLLCLK,
1019771fe6b9SJerome Glisse 			     ~(RADEON_VCLK_SRC_SEL_MASK));
1020771fe6b9SJerome Glisse 
10214ce001abSDave Airlie 		if (is_tv)
10224ce001abSDave Airlie 			WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
1023771fe6b9SJerome Glisse 	}
1024771fe6b9SJerome Glisse }
1025771fe6b9SJerome Glisse 
radeon_crtc_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)1026771fe6b9SJerome Glisse static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc,
1027e811f5aeSLaurent Pinchart 				   const struct drm_display_mode *mode,
1028771fe6b9SJerome Glisse 				   struct drm_display_mode *adjusted_mode)
1029771fe6b9SJerome Glisse {
1030c93bb85bSJerome Glisse 	if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode))
1031c93bb85bSJerome Glisse 		return false;
1032771fe6b9SJerome Glisse 	return true;
1033771fe6b9SJerome Glisse }
1034771fe6b9SJerome Glisse 
radeon_crtc_mode_set(struct drm_crtc * crtc,struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode,int x,int y,struct drm_framebuffer * old_fb)1035771fe6b9SJerome Glisse static int radeon_crtc_mode_set(struct drm_crtc *crtc,
1036771fe6b9SJerome Glisse 				 struct drm_display_mode *mode,
1037771fe6b9SJerome Glisse 				 struct drm_display_mode *adjusted_mode,
1038771fe6b9SJerome Glisse 				 int x, int y, struct drm_framebuffer *old_fb)
1039771fe6b9SJerome Glisse {
1040c93bb85bSJerome Glisse 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
1041771fe6b9SJerome Glisse 
1042771fe6b9SJerome Glisse 	/* TODO TV */
1043771fe6b9SJerome Glisse 	radeon_crtc_set_base(crtc, x, y, old_fb);
1044771fe6b9SJerome Glisse 	radeon_set_crtc_timing(crtc, adjusted_mode);
1045771fe6b9SJerome Glisse 	radeon_set_pll(crtc, adjusted_mode);
10466b02af1cSAlex Deucher 	radeon_overscan_setup(crtc, adjusted_mode);
1047c93bb85bSJerome Glisse 	if (radeon_crtc->crtc_id == 0) {
1048310a82c8SAlex Deucher 		radeon_legacy_rmx_mode_set(crtc, adjusted_mode);
1049c93bb85bSJerome Glisse 	} else {
1050c93bb85bSJerome Glisse 		if (radeon_crtc->rmx_type != RMX_OFF) {
1051c93bb85bSJerome Glisse 			/* FIXME: only first crtc has rmx what should we
1052c93bb85bSJerome Glisse 			 * do ?
1053c93bb85bSJerome Glisse 			 */
1054c93bb85bSJerome Glisse 			DRM_ERROR("Mode need scaling but only first crtc can do that.\n");
1055c93bb85bSJerome Glisse 		}
1056c93bb85bSJerome Glisse 	}
10576d3759faSMichel Dänzer 	radeon_cursor_reset(crtc);
1058771fe6b9SJerome Glisse 	return 0;
1059771fe6b9SJerome Glisse }
1060771fe6b9SJerome Glisse 
radeon_crtc_prepare(struct drm_crtc * crtc)1061771fe6b9SJerome Glisse static void radeon_crtc_prepare(struct drm_crtc *crtc)
1062771fe6b9SJerome Glisse {
1063ec51efa9SPierre Ossman 	struct drm_device *dev = crtc->dev;
1064ec51efa9SPierre Ossman 	struct drm_crtc *crtci;
1065ec51efa9SPierre Ossman 
1066ec51efa9SPierre Ossman 	/*
1067ec51efa9SPierre Ossman 	* The hardware wedges sometimes if you reconfigure one CRTC
1068ec51efa9SPierre Ossman 	* whilst another is running (see fdo bug #24611).
1069ec51efa9SPierre Ossman 	*/
1070ec51efa9SPierre Ossman 	list_for_each_entry(crtci, &dev->mode_config.crtc_list, head)
1071ec51efa9SPierre Ossman 		radeon_crtc_dpms(crtci, DRM_MODE_DPMS_OFF);
1072771fe6b9SJerome Glisse }
1073771fe6b9SJerome Glisse 
radeon_crtc_commit(struct drm_crtc * crtc)1074771fe6b9SJerome Glisse static void radeon_crtc_commit(struct drm_crtc *crtc)
1075771fe6b9SJerome Glisse {
1076ec51efa9SPierre Ossman 	struct drm_device *dev = crtc->dev;
1077ec51efa9SPierre Ossman 	struct drm_crtc *crtci;
1078ec51efa9SPierre Ossman 
1079ec51efa9SPierre Ossman 	/*
1080ec51efa9SPierre Ossman 	* Reenable the CRTCs that should be running.
1081ec51efa9SPierre Ossman 	*/
1082ec51efa9SPierre Ossman 	list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) {
1083ec51efa9SPierre Ossman 		if (crtci->enabled)
1084ec51efa9SPierre Ossman 			radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON);
1085ec51efa9SPierre Ossman 	}
1086771fe6b9SJerome Glisse }
1087771fe6b9SJerome Glisse 
radeon_crtc_disable(struct drm_crtc * crtc)1088520a8718SIlija Hadzic static void radeon_crtc_disable(struct drm_crtc *crtc)
1089520a8718SIlija Hadzic {
1090520a8718SIlija Hadzic 	radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
1091f4510a27SMatt Roper 	if (crtc->primary->fb) {
1092520a8718SIlija Hadzic 		int r;
1093520a8718SIlija Hadzic 		struct radeon_bo *rbo;
1094520a8718SIlija Hadzic 
1095a110dfe3SDaniel Stone 		rbo = gem_to_radeon_bo(crtc->primary->fb->obj[0]);
1096520a8718SIlija Hadzic 		r = radeon_bo_reserve(rbo, false);
1097520a8718SIlija Hadzic 		if (unlikely(r))
1098520a8718SIlija Hadzic 			DRM_ERROR("failed to reserve rbo before unpin\n");
1099520a8718SIlija Hadzic 		else {
1100520a8718SIlija Hadzic 			radeon_bo_unpin(rbo);
1101520a8718SIlija Hadzic 			radeon_bo_unreserve(rbo);
1102520a8718SIlija Hadzic 		}
1103520a8718SIlija Hadzic 	}
1104520a8718SIlija Hadzic }
1105520a8718SIlija Hadzic 
1106771fe6b9SJerome Glisse static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
1107771fe6b9SJerome Glisse 	.dpms = radeon_crtc_dpms,
1108771fe6b9SJerome Glisse 	.mode_fixup = radeon_crtc_mode_fixup,
1109771fe6b9SJerome Glisse 	.mode_set = radeon_crtc_mode_set,
1110771fe6b9SJerome Glisse 	.mode_set_base = radeon_crtc_set_base,
11114dd19b0dSChris Ball 	.mode_set_base_atomic = radeon_crtc_set_base_atomic,
1112771fe6b9SJerome Glisse 	.prepare = radeon_crtc_prepare,
1113771fe6b9SJerome Glisse 	.commit = radeon_crtc_commit,
111427b4118dSThomas Zimmermann 	.disable = radeon_crtc_disable,
111527b4118dSThomas Zimmermann 	.get_scanout_position = radeon_get_crtc_scanout_position,
1116771fe6b9SJerome Glisse };
1117771fe6b9SJerome Glisse 
1118771fe6b9SJerome Glisse 
radeon_legacy_init_crtc(struct drm_device * dev,struct radeon_crtc * radeon_crtc)1119771fe6b9SJerome Glisse void radeon_legacy_init_crtc(struct drm_device *dev,
1120771fe6b9SJerome Glisse 			       struct radeon_crtc *radeon_crtc)
1121771fe6b9SJerome Glisse {
1122771fe6b9SJerome Glisse 	if (radeon_crtc->crtc_id == 1)
1123771fe6b9SJerome Glisse 		radeon_crtc->crtc_offset = RADEON_CRTC2_H_TOTAL_DISP - RADEON_CRTC_H_TOTAL_DISP;
1124771fe6b9SJerome Glisse 	drm_crtc_helper_add(&radeon_crtc->base, &legacy_helper_funcs);
1125771fe6b9SJerome Glisse }
1126