1926deccbSFrançois Tigeot /*
2926deccbSFrançois Tigeot  * Copyright 2007-8 Advanced Micro Devices, Inc.
3926deccbSFrançois Tigeot  * Copyright 2008 Red Hat Inc.
4926deccbSFrançois Tigeot  *
5926deccbSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
6926deccbSFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
7926deccbSFrançois Tigeot  * to deal in the Software without restriction, including without limitation
8926deccbSFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9926deccbSFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
10926deccbSFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
11926deccbSFrançois Tigeot  *
12926deccbSFrançois Tigeot  * The above copyright notice and this permission notice shall be included in
13926deccbSFrançois Tigeot  * all copies or substantial portions of the Software.
14926deccbSFrançois Tigeot  *
15926deccbSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16926deccbSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17926deccbSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18926deccbSFrançois Tigeot  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19926deccbSFrançois Tigeot  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20926deccbSFrançois Tigeot  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21926deccbSFrançois Tigeot  * OTHER DEALINGS IN THE SOFTWARE.
22926deccbSFrançois Tigeot  *
23926deccbSFrançois Tigeot  * Authors: Dave Airlie
24926deccbSFrançois Tigeot  *          Alex Deucher
25926deccbSFrançois Tigeot  */
26926deccbSFrançois Tigeot #include <drm/drmP.h>
27926deccbSFrançois Tigeot #include <drm/drm_crtc_helper.h>
28c0e85e96SFrançois Tigeot #include <drm/drm_fb_helper.h>
2983b4b9b9SFrançois Tigeot #include <drm/radeon_drm.h>
30926deccbSFrançois Tigeot #include <drm/drm_fixed.h>
31926deccbSFrançois Tigeot #include "radeon.h"
32926deccbSFrançois Tigeot #include "atom.h"
33926deccbSFrançois Tigeot 
radeon_overscan_setup(struct drm_crtc * crtc,struct drm_display_mode * mode)34926deccbSFrançois Tigeot static void radeon_overscan_setup(struct drm_crtc *crtc,
35926deccbSFrançois Tigeot 				  struct drm_display_mode *mode)
36926deccbSFrançois Tigeot {
37926deccbSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
38926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
39926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
40926deccbSFrançois Tigeot 
41926deccbSFrançois Tigeot 	WREG32(RADEON_OVR_CLR + radeon_crtc->crtc_offset, 0);
42926deccbSFrançois Tigeot 	WREG32(RADEON_OVR_WID_LEFT_RIGHT + radeon_crtc->crtc_offset, 0);
43926deccbSFrançois Tigeot 	WREG32(RADEON_OVR_WID_TOP_BOTTOM + radeon_crtc->crtc_offset, 0);
44926deccbSFrançois Tigeot }
45926deccbSFrançois Tigeot 
radeon_legacy_rmx_mode_set(struct drm_crtc * crtc,struct drm_display_mode * mode)46926deccbSFrançois Tigeot static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc,
47926deccbSFrançois Tigeot 				       struct drm_display_mode *mode)
48926deccbSFrançois Tigeot {
49926deccbSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
50926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
51926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
52926deccbSFrançois Tigeot 	int xres = mode->hdisplay;
53926deccbSFrançois Tigeot 	int yres = mode->vdisplay;
54926deccbSFrançois Tigeot 	bool hscale = true, vscale = true;
55926deccbSFrançois Tigeot 	int hsync_wid;
56926deccbSFrançois Tigeot 	int vsync_wid;
57926deccbSFrançois Tigeot 	int hsync_start;
58926deccbSFrançois Tigeot 	int blank_width;
59926deccbSFrançois Tigeot 	u32 scale, inc, crtc_more_cntl;
60926deccbSFrançois Tigeot 	u32 fp_horz_stretch, fp_vert_stretch, fp_horz_vert_active;
61926deccbSFrançois Tigeot 	u32 fp_h_sync_strt_wid, fp_crtc_h_total_disp;
62926deccbSFrançois Tigeot 	u32 fp_v_sync_strt_wid, fp_crtc_v_total_disp;
63926deccbSFrançois Tigeot 	struct drm_display_mode *native_mode = &radeon_crtc->native_mode;
64926deccbSFrançois Tigeot 
65926deccbSFrançois Tigeot 	fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH) &
66926deccbSFrançois Tigeot 		(RADEON_VERT_STRETCH_RESERVED |
67926deccbSFrançois Tigeot 		 RADEON_VERT_AUTO_RATIO_INC);
68926deccbSFrançois Tigeot 	fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH) &
69926deccbSFrançois Tigeot 		(RADEON_HORZ_FP_LOOP_STRETCH |
70926deccbSFrançois Tigeot 		 RADEON_HORZ_AUTO_RATIO_INC);
71926deccbSFrançois Tigeot 
72926deccbSFrançois Tigeot 	crtc_more_cntl = 0;
73926deccbSFrançois Tigeot 	if ((rdev->family == CHIP_RS100) ||
74926deccbSFrançois Tigeot 	    (rdev->family == CHIP_RS200)) {
75926deccbSFrançois Tigeot 		/* This is to workaround the asic bug for RMX, some versions
76926deccbSFrançois Tigeot 		   of BIOS dosen't have this register initialized correctly. */
77926deccbSFrançois Tigeot 		crtc_more_cntl |= RADEON_CRTC_H_CUTOFF_ACTIVE_EN;
78926deccbSFrançois Tigeot 	}
79926deccbSFrançois Tigeot 
80926deccbSFrançois Tigeot 
81926deccbSFrançois Tigeot 	fp_crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff)
82926deccbSFrançois Tigeot 				| ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16));
83926deccbSFrançois Tigeot 
84926deccbSFrançois Tigeot 	hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8;
85926deccbSFrançois Tigeot 	if (!hsync_wid)
86926deccbSFrançois Tigeot 		hsync_wid = 1;
87926deccbSFrançois Tigeot 	hsync_start = mode->crtc_hsync_start - 8;
88926deccbSFrançois Tigeot 
89926deccbSFrançois Tigeot 	fp_h_sync_strt_wid = ((hsync_start & 0x1fff)
90926deccbSFrançois Tigeot 			      | ((hsync_wid & 0x3f) << 16)
91926deccbSFrançois Tigeot 			      | ((mode->flags & DRM_MODE_FLAG_NHSYNC)
92926deccbSFrançois Tigeot 				 ? RADEON_CRTC_H_SYNC_POL
93926deccbSFrançois Tigeot 				 : 0));
94926deccbSFrançois Tigeot 
95926deccbSFrançois Tigeot 	fp_crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff)
96926deccbSFrançois Tigeot 				| ((mode->crtc_vdisplay - 1) << 16));
97926deccbSFrançois Tigeot 
98926deccbSFrançois Tigeot 	vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start;
99926deccbSFrançois Tigeot 	if (!vsync_wid)
100926deccbSFrançois Tigeot 		vsync_wid = 1;
101926deccbSFrançois Tigeot 
102926deccbSFrançois Tigeot 	fp_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff)
103926deccbSFrançois Tigeot 			      | ((vsync_wid & 0x1f) << 16)
104926deccbSFrançois Tigeot 			      | ((mode->flags & DRM_MODE_FLAG_NVSYNC)
105926deccbSFrançois Tigeot 				 ? RADEON_CRTC_V_SYNC_POL
106926deccbSFrançois Tigeot 				 : 0));
107926deccbSFrançois Tigeot 
108926deccbSFrançois Tigeot 	fp_horz_vert_active = 0;
109926deccbSFrançois Tigeot 
110926deccbSFrançois Tigeot 	if (native_mode->hdisplay == 0 ||
111926deccbSFrançois Tigeot 	    native_mode->vdisplay == 0) {
112926deccbSFrançois Tigeot 		hscale = false;
113926deccbSFrançois Tigeot 		vscale = false;
114926deccbSFrançois Tigeot 	} else {
115926deccbSFrançois Tigeot 		if (xres > native_mode->hdisplay)
116926deccbSFrançois Tigeot 			xres = native_mode->hdisplay;
117926deccbSFrançois Tigeot 		if (yres > native_mode->vdisplay)
118926deccbSFrançois Tigeot 			yres = native_mode->vdisplay;
119926deccbSFrançois Tigeot 
120926deccbSFrançois Tigeot 		if (xres == native_mode->hdisplay)
121926deccbSFrançois Tigeot 			hscale = false;
122926deccbSFrançois Tigeot 		if (yres == native_mode->vdisplay)
123926deccbSFrançois Tigeot 			vscale = false;
124926deccbSFrançois Tigeot 	}
125926deccbSFrançois Tigeot 
126926deccbSFrançois Tigeot 	switch (radeon_crtc->rmx_type) {
127926deccbSFrançois Tigeot 	case RMX_FULL:
128926deccbSFrançois Tigeot 	case RMX_ASPECT:
129926deccbSFrançois Tigeot 		if (!hscale)
130926deccbSFrançois Tigeot 			fp_horz_stretch |= ((xres/8-1) << 16);
131926deccbSFrançois Tigeot 		else {
132926deccbSFrançois Tigeot 			inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0;
133926deccbSFrançois Tigeot 			scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX)
134926deccbSFrançois Tigeot 				/ native_mode->hdisplay + 1;
135926deccbSFrançois Tigeot 			fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) |
136926deccbSFrançois Tigeot 					RADEON_HORZ_STRETCH_BLEND |
137926deccbSFrançois Tigeot 					RADEON_HORZ_STRETCH_ENABLE |
138926deccbSFrançois Tigeot 					((native_mode->hdisplay/8-1) << 16));
139926deccbSFrançois Tigeot 		}
140926deccbSFrançois Tigeot 
141926deccbSFrançois Tigeot 		if (!vscale)
142926deccbSFrançois Tigeot 			fp_vert_stretch |= ((yres-1) << 12);
143926deccbSFrançois Tigeot 		else {
144926deccbSFrançois Tigeot 			inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0;
145926deccbSFrançois Tigeot 			scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX)
146926deccbSFrançois Tigeot 				/ native_mode->vdisplay + 1;
147926deccbSFrançois Tigeot 			fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) |
148926deccbSFrançois Tigeot 					RADEON_VERT_STRETCH_ENABLE |
149926deccbSFrançois Tigeot 					RADEON_VERT_STRETCH_BLEND |
150926deccbSFrançois Tigeot 					((native_mode->vdisplay-1) << 12));
151926deccbSFrançois Tigeot 		}
152926deccbSFrançois Tigeot 		break;
153926deccbSFrançois Tigeot 	case RMX_CENTER:
154926deccbSFrançois Tigeot 		fp_horz_stretch |= ((xres/8-1) << 16);
155926deccbSFrançois Tigeot 		fp_vert_stretch |= ((yres-1) << 12);
156926deccbSFrançois Tigeot 
157926deccbSFrançois Tigeot 		crtc_more_cntl |= (RADEON_CRTC_AUTO_HORZ_CENTER_EN |
158926deccbSFrançois Tigeot 				RADEON_CRTC_AUTO_VERT_CENTER_EN);
159926deccbSFrançois Tigeot 
160926deccbSFrançois Tigeot 		blank_width = (mode->crtc_hblank_end - mode->crtc_hblank_start) / 8;
161926deccbSFrançois Tigeot 		if (blank_width > 110)
162926deccbSFrançois Tigeot 			blank_width = 110;
163926deccbSFrançois Tigeot 
164926deccbSFrançois Tigeot 		fp_crtc_h_total_disp = (((blank_width) & 0x3ff)
165926deccbSFrançois Tigeot 				| ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16));
166926deccbSFrançois Tigeot 
167926deccbSFrançois Tigeot 		hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8;
168926deccbSFrançois Tigeot 		if (!hsync_wid)
169926deccbSFrançois Tigeot 			hsync_wid = 1;
170926deccbSFrançois Tigeot 
171926deccbSFrançois Tigeot 		fp_h_sync_strt_wid = ((((mode->crtc_hsync_start - mode->crtc_hblank_start) / 8) & 0x1fff)
172926deccbSFrançois Tigeot 				| ((hsync_wid & 0x3f) << 16)
173926deccbSFrançois Tigeot 				| ((mode->flags & DRM_MODE_FLAG_NHSYNC)
174926deccbSFrançois Tigeot 					? RADEON_CRTC_H_SYNC_POL
175926deccbSFrançois Tigeot 					: 0));
176926deccbSFrançois Tigeot 
177926deccbSFrançois Tigeot 		fp_crtc_v_total_disp = (((mode->crtc_vblank_end - mode->crtc_vblank_start) & 0xffff)
178926deccbSFrançois Tigeot 				| ((mode->crtc_vdisplay - 1) << 16));
179926deccbSFrançois Tigeot 
180926deccbSFrançois Tigeot 		vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start;
181926deccbSFrançois Tigeot 		if (!vsync_wid)
182926deccbSFrançois Tigeot 			vsync_wid = 1;
183926deccbSFrançois Tigeot 
184926deccbSFrançois Tigeot 		fp_v_sync_strt_wid = ((((mode->crtc_vsync_start - mode->crtc_vblank_start) & 0xfff)
185926deccbSFrançois Tigeot 					| ((vsync_wid & 0x1f) << 16)
186926deccbSFrançois Tigeot 					| ((mode->flags & DRM_MODE_FLAG_NVSYNC)
187926deccbSFrançois Tigeot 						? RADEON_CRTC_V_SYNC_POL
188926deccbSFrançois Tigeot 						: 0)));
189926deccbSFrançois Tigeot 
190926deccbSFrançois Tigeot 		fp_horz_vert_active = (((native_mode->vdisplay) & 0xfff) |
191926deccbSFrançois Tigeot 				(((native_mode->hdisplay / 8) & 0x1ff) << 16));
192926deccbSFrançois Tigeot 		break;
193926deccbSFrançois Tigeot 	case RMX_OFF:
194926deccbSFrançois Tigeot 	default:
195926deccbSFrançois Tigeot 		fp_horz_stretch |= ((xres/8-1) << 16);
196926deccbSFrançois Tigeot 		fp_vert_stretch |= ((yres-1) << 12);
197926deccbSFrançois Tigeot 		break;
198926deccbSFrançois Tigeot 	}
199926deccbSFrançois Tigeot 
200926deccbSFrançois Tigeot 	WREG32(RADEON_FP_HORZ_STRETCH,      fp_horz_stretch);
201926deccbSFrançois Tigeot 	WREG32(RADEON_FP_VERT_STRETCH,      fp_vert_stretch);
202926deccbSFrançois Tigeot 	WREG32(RADEON_CRTC_MORE_CNTL,       crtc_more_cntl);
203926deccbSFrançois Tigeot 	WREG32(RADEON_FP_HORZ_VERT_ACTIVE,  fp_horz_vert_active);
204926deccbSFrançois Tigeot 	WREG32(RADEON_FP_H_SYNC_STRT_WID,   fp_h_sync_strt_wid);
205926deccbSFrançois Tigeot 	WREG32(RADEON_FP_V_SYNC_STRT_WID,   fp_v_sync_strt_wid);
206926deccbSFrançois Tigeot 	WREG32(RADEON_FP_CRTC_H_TOTAL_DISP, fp_crtc_h_total_disp);
207926deccbSFrançois Tigeot 	WREG32(RADEON_FP_CRTC_V_TOTAL_DISP, fp_crtc_v_total_disp);
208926deccbSFrançois Tigeot }
209926deccbSFrançois Tigeot 
radeon_pll_wait_for_read_update_complete(struct drm_device * dev)210926deccbSFrançois Tigeot static void radeon_pll_wait_for_read_update_complete(struct drm_device *dev)
211926deccbSFrançois Tigeot {
212926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
213926deccbSFrançois Tigeot 	int i = 0;
214926deccbSFrançois Tigeot 
215926deccbSFrançois Tigeot 	/* FIXME: Certain revisions of R300 can't recover here.  Not sure of
216926deccbSFrançois Tigeot 	   the cause yet, but this workaround will mask the problem for now.
217926deccbSFrançois Tigeot 	   Other chips usually will pass at the very first test, so the
218926deccbSFrançois Tigeot 	   workaround shouldn't have any effect on them. */
219926deccbSFrançois Tigeot 	for (i = 0;
220926deccbSFrançois Tigeot 	     (i < 10000 &&
221926deccbSFrançois Tigeot 	      RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R);
222926deccbSFrançois Tigeot 	     i++);
223926deccbSFrançois Tigeot }
224926deccbSFrançois Tigeot 
radeon_pll_write_update(struct drm_device * dev)225926deccbSFrançois Tigeot static void radeon_pll_write_update(struct drm_device *dev)
226926deccbSFrançois Tigeot {
227926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
228926deccbSFrançois Tigeot 
229926deccbSFrançois Tigeot 	while (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R);
230926deccbSFrançois Tigeot 
231926deccbSFrançois Tigeot 	WREG32_PLL_P(RADEON_PPLL_REF_DIV,
232926deccbSFrançois Tigeot 			   RADEON_PPLL_ATOMIC_UPDATE_W,
233926deccbSFrançois Tigeot 			   ~(RADEON_PPLL_ATOMIC_UPDATE_W));
234926deccbSFrançois Tigeot }
235926deccbSFrançois Tigeot 
radeon_pll2_wait_for_read_update_complete(struct drm_device * dev)236926deccbSFrançois Tigeot static void radeon_pll2_wait_for_read_update_complete(struct drm_device *dev)
237926deccbSFrançois Tigeot {
238926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
239926deccbSFrançois Tigeot 	int i = 0;
240926deccbSFrançois Tigeot 
241926deccbSFrançois Tigeot 
242926deccbSFrançois Tigeot 	/* FIXME: Certain revisions of R300 can't recover here.  Not sure of
243926deccbSFrançois Tigeot 	   the cause yet, but this workaround will mask the problem for now.
244926deccbSFrançois Tigeot 	   Other chips usually will pass at the very first test, so the
245926deccbSFrançois Tigeot 	   workaround shouldn't have any effect on them. */
246926deccbSFrançois Tigeot 	for (i = 0;
247926deccbSFrançois Tigeot 	     (i < 10000 &&
248926deccbSFrançois Tigeot 	      RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R);
249926deccbSFrançois Tigeot 	     i++);
250926deccbSFrançois Tigeot }
251926deccbSFrançois Tigeot 
radeon_pll2_write_update(struct drm_device * dev)252926deccbSFrançois Tigeot static void radeon_pll2_write_update(struct drm_device *dev)
253926deccbSFrançois Tigeot {
254926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
255926deccbSFrançois Tigeot 
256926deccbSFrançois Tigeot 	while (RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R);
257926deccbSFrançois Tigeot 
258926deccbSFrançois Tigeot 	WREG32_PLL_P(RADEON_P2PLL_REF_DIV,
259926deccbSFrançois Tigeot 			   RADEON_P2PLL_ATOMIC_UPDATE_W,
260926deccbSFrançois Tigeot 			   ~(RADEON_P2PLL_ATOMIC_UPDATE_W));
261926deccbSFrançois Tigeot }
262926deccbSFrançois Tigeot 
radeon_compute_pll_gain(uint16_t ref_freq,uint16_t ref_div,uint16_t fb_div)263926deccbSFrançois Tigeot static uint8_t radeon_compute_pll_gain(uint16_t ref_freq, uint16_t ref_div,
264926deccbSFrançois Tigeot 				       uint16_t fb_div)
265926deccbSFrançois Tigeot {
266926deccbSFrançois Tigeot 	unsigned int vcoFreq;
267926deccbSFrançois Tigeot 
268926deccbSFrançois Tigeot 	if (!ref_div)
269926deccbSFrançois Tigeot 		return 1;
270926deccbSFrançois Tigeot 
271926deccbSFrançois Tigeot 	vcoFreq = ((unsigned)ref_freq * fb_div) / ref_div;
272926deccbSFrançois Tigeot 
273926deccbSFrançois Tigeot 	/*
274926deccbSFrançois Tigeot 	 * This is horribly crude: the VCO frequency range is divided into
275926deccbSFrançois Tigeot 	 * 3 parts, each part having a fixed PLL gain value.
276926deccbSFrançois Tigeot 	 */
277926deccbSFrançois Tigeot 	if (vcoFreq >= 30000)
278926deccbSFrançois Tigeot 		/*
279926deccbSFrançois Tigeot 		 * [300..max] MHz : 7
280926deccbSFrançois Tigeot 		 */
281926deccbSFrançois Tigeot 		return 7;
282926deccbSFrançois Tigeot 	else if (vcoFreq >= 18000)
283926deccbSFrançois Tigeot 		/*
284926deccbSFrançois Tigeot 		 * [180..300) MHz : 4
285926deccbSFrançois Tigeot 		 */
286926deccbSFrançois Tigeot 		return 4;
287926deccbSFrançois Tigeot 	else
288926deccbSFrançois Tigeot 		/*
289926deccbSFrançois Tigeot 		 * [0..180) MHz : 1
290926deccbSFrançois Tigeot 		 */
291926deccbSFrançois Tigeot 		return 1;
292926deccbSFrançois Tigeot }
293926deccbSFrançois Tigeot 
radeon_crtc_dpms(struct drm_crtc * crtc,int mode)294926deccbSFrançois Tigeot static void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
295926deccbSFrançois Tigeot {
296926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
297926deccbSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
298926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
299926deccbSFrançois Tigeot 	uint32_t crtc_ext_cntl = 0;
300926deccbSFrançois Tigeot 	uint32_t mask;
301926deccbSFrançois Tigeot 
302926deccbSFrançois Tigeot 	if (radeon_crtc->crtc_id)
303926deccbSFrançois Tigeot 		mask = (RADEON_CRTC2_DISP_DIS |
304926deccbSFrançois Tigeot 			RADEON_CRTC2_VSYNC_DIS |
305926deccbSFrançois Tigeot 			RADEON_CRTC2_HSYNC_DIS |
306926deccbSFrançois Tigeot 			RADEON_CRTC2_DISP_REQ_EN_B);
307926deccbSFrançois Tigeot 	else
308926deccbSFrançois Tigeot 		mask = (RADEON_CRTC_DISPLAY_DIS |
309926deccbSFrançois Tigeot 			RADEON_CRTC_VSYNC_DIS |
310926deccbSFrançois Tigeot 			RADEON_CRTC_HSYNC_DIS);
311926deccbSFrançois Tigeot 
312926deccbSFrançois Tigeot 	/*
313926deccbSFrançois Tigeot 	 * On all dual CRTC GPUs this bit controls the CRTC of the primary DAC.
314926deccbSFrançois Tigeot 	 * Therefore it is set in the DAC DMPS function.
315926deccbSFrançois Tigeot 	 * This is different for GPU's with a single CRTC but a primary and a
316926deccbSFrançois Tigeot 	 * TV DAC: here it controls the single CRTC no matter where it is
317926deccbSFrançois Tigeot 	 * routed. Therefore we set it here.
318926deccbSFrançois Tigeot 	 */
319926deccbSFrançois Tigeot 	if (rdev->flags & RADEON_SINGLE_CRTC)
320926deccbSFrançois Tigeot 		crtc_ext_cntl = RADEON_CRTC_CRT_ON;
321926deccbSFrançois Tigeot 
322926deccbSFrançois Tigeot 	switch (mode) {
323926deccbSFrançois Tigeot 	case DRM_MODE_DPMS_ON:
324926deccbSFrançois Tigeot 		radeon_crtc->enabled = true;
325926deccbSFrançois Tigeot 		/* adjust pm to dpms changes BEFORE enabling crtcs */
326926deccbSFrançois Tigeot 		radeon_pm_compute_clocks(rdev);
327926deccbSFrançois Tigeot 		if (radeon_crtc->crtc_id)
328926deccbSFrançois Tigeot 			WREG32_P(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_EN, ~(RADEON_CRTC2_EN | mask));
329926deccbSFrançois Tigeot 		else {
330926deccbSFrançois Tigeot 			WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_EN, ~(RADEON_CRTC_EN |
331926deccbSFrançois Tigeot 									 RADEON_CRTC_DISP_REQ_EN_B));
332926deccbSFrançois Tigeot 			WREG32_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, ~(mask | crtc_ext_cntl));
333926deccbSFrançois Tigeot 		}
334d78d3a22SFrançois Tigeot 		if (dev->num_crtcs > radeon_crtc->crtc_id)
3351dedbd3bSFrançois Tigeot 			drm_crtc_vblank_on(crtc);
336926deccbSFrançois Tigeot 		radeon_crtc_load_lut(crtc);
337926deccbSFrançois Tigeot 		break;
338926deccbSFrançois Tigeot 	case DRM_MODE_DPMS_STANDBY:
339926deccbSFrançois Tigeot 	case DRM_MODE_DPMS_SUSPEND:
340926deccbSFrançois Tigeot 	case DRM_MODE_DPMS_OFF:
341d78d3a22SFrançois Tigeot 		if (dev->num_crtcs > radeon_crtc->crtc_id)
3421dedbd3bSFrançois Tigeot 			drm_crtc_vblank_off(crtc);
343926deccbSFrançois Tigeot 		if (radeon_crtc->crtc_id)
344926deccbSFrançois Tigeot 			WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~(RADEON_CRTC2_EN | mask));
345926deccbSFrançois Tigeot 		else {
346926deccbSFrançois Tigeot 			WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B, ~(RADEON_CRTC_EN |
347926deccbSFrançois Tigeot 										    RADEON_CRTC_DISP_REQ_EN_B));
348926deccbSFrançois Tigeot 			WREG32_P(RADEON_CRTC_EXT_CNTL, mask, ~(mask | crtc_ext_cntl));
349926deccbSFrançois Tigeot 		}
350926deccbSFrançois Tigeot 		radeon_crtc->enabled = false;
351926deccbSFrançois Tigeot 		/* adjust pm to dpms changes AFTER disabling crtcs */
352926deccbSFrançois Tigeot 		radeon_pm_compute_clocks(rdev);
353926deccbSFrançois Tigeot 		break;
354926deccbSFrançois Tigeot 	}
355926deccbSFrançois Tigeot }
356926deccbSFrançois Tigeot 
radeon_crtc_set_base(struct drm_crtc * crtc,int x,int y,struct drm_framebuffer * old_fb)357926deccbSFrançois Tigeot int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
358926deccbSFrançois Tigeot 			 struct drm_framebuffer *old_fb)
359926deccbSFrançois Tigeot {
360926deccbSFrançois Tigeot 	return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0);
361926deccbSFrançois Tigeot }
362926deccbSFrançois Tigeot 
radeon_crtc_set_base_atomic(struct drm_crtc * crtc,struct drm_framebuffer * fb,int x,int y,enum mode_set_atomic state)363926deccbSFrançois Tigeot int radeon_crtc_set_base_atomic(struct drm_crtc *crtc,
364926deccbSFrançois Tigeot 				struct drm_framebuffer *fb,
365926deccbSFrançois Tigeot 				int x, int y, enum mode_set_atomic state)
366926deccbSFrançois Tigeot {
367926deccbSFrançois Tigeot 	return radeon_crtc_do_set_base(crtc, fb, x, y, 1);
368926deccbSFrançois Tigeot }
369926deccbSFrançois Tigeot 
radeon_crtc_do_set_base(struct drm_crtc * crtc,struct drm_framebuffer * fb,int x,int y,int atomic)370926deccbSFrançois Tigeot int radeon_crtc_do_set_base(struct drm_crtc *crtc,
371926deccbSFrançois Tigeot 			 struct drm_framebuffer *fb,
372926deccbSFrançois Tigeot 			 int x, int y, int atomic)
373926deccbSFrançois Tigeot {
374926deccbSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
375926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
376926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
377926deccbSFrançois Tigeot 	struct radeon_framebuffer *radeon_fb;
378926deccbSFrançois Tigeot 	struct drm_framebuffer *target_fb;
379926deccbSFrançois Tigeot 	struct drm_gem_object *obj;
380926deccbSFrançois Tigeot 	struct radeon_bo *rbo;
381926deccbSFrançois Tigeot 	uint64_t base;
382926deccbSFrançois Tigeot 	uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0;
383926deccbSFrançois Tigeot 	uint32_t crtc_pitch, pitch_pixels;
384926deccbSFrançois Tigeot 	uint32_t tiling_flags;
385926deccbSFrançois Tigeot 	int format;
386926deccbSFrançois Tigeot 	uint32_t gen_cntl_reg, gen_cntl_val;
387926deccbSFrançois Tigeot 	int r;
388926deccbSFrançois Tigeot 
389926deccbSFrançois Tigeot 	DRM_DEBUG_KMS("\n");
390926deccbSFrançois Tigeot 	/* no fb bound */
391ba55f2f5SFrançois Tigeot 	if (!atomic && !crtc->primary->fb) {
392926deccbSFrançois Tigeot 		DRM_DEBUG_KMS("No FB bound\n");
393926deccbSFrançois Tigeot 		return 0;
394926deccbSFrançois Tigeot 	}
395926deccbSFrançois Tigeot 
396926deccbSFrançois Tigeot 	if (atomic) {
397926deccbSFrançois Tigeot 		radeon_fb = to_radeon_framebuffer(fb);
398926deccbSFrançois Tigeot 		target_fb = fb;
399926deccbSFrançois Tigeot 	}
400926deccbSFrançois Tigeot 	else {
401ba55f2f5SFrançois Tigeot 		radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
402ba55f2f5SFrançois Tigeot 		target_fb = crtc->primary->fb;
403926deccbSFrançois Tigeot 	}
404926deccbSFrançois Tigeot 
405*a85cb24fSFrançois Tigeot 	switch (target_fb->format->cpp[0] * 8) {
406926deccbSFrançois Tigeot 	case 8:
407926deccbSFrançois Tigeot 		format = 2;
408926deccbSFrançois Tigeot 		break;
409926deccbSFrançois Tigeot 	case 15:      /*  555 */
410926deccbSFrançois Tigeot 		format = 3;
411926deccbSFrançois Tigeot 		break;
412926deccbSFrançois Tigeot 	case 16:      /*  565 */
413926deccbSFrançois Tigeot 		format = 4;
414926deccbSFrançois Tigeot 		break;
415926deccbSFrançois Tigeot 	case 24:      /*  RGB */
416926deccbSFrançois Tigeot 		format = 5;
417926deccbSFrançois Tigeot 		break;
418926deccbSFrançois Tigeot 	case 32:      /* xRGB */
419926deccbSFrançois Tigeot 		format = 6;
420926deccbSFrançois Tigeot 		break;
421926deccbSFrançois Tigeot 	default:
422926deccbSFrançois Tigeot 		return false;
423926deccbSFrançois Tigeot 	}
424926deccbSFrançois Tigeot 
425926deccbSFrançois Tigeot 	/* Pin framebuffer & get tilling informations */
426926deccbSFrançois Tigeot 	obj = radeon_fb->obj;
427926deccbSFrançois Tigeot 	rbo = gem_to_radeon_bo(obj);
428c6f73aabSFrançois Tigeot retry:
429926deccbSFrançois Tigeot 	r = radeon_bo_reserve(rbo, false);
430926deccbSFrançois Tigeot 	if (unlikely(r != 0))
431926deccbSFrançois Tigeot 		return r;
432926deccbSFrançois Tigeot 	/* Only 27 bit offset for legacy CRTC */
433926deccbSFrançois Tigeot 	r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, 1 << 27,
434f77dbd6cSFrançois Tigeot 				     (u64 *)&base);
435926deccbSFrançois Tigeot 	if (unlikely(r != 0)) {
436926deccbSFrançois Tigeot 		radeon_bo_unreserve(rbo);
437c6f73aabSFrançois Tigeot 
438c6f73aabSFrançois Tigeot 		/* On old GPU like RN50 with little vram pining can fails because
439c6f73aabSFrançois Tigeot 		 * current fb is taking all space needed. So instead of unpining
440c6f73aabSFrançois Tigeot 		 * the old buffer after pining the new one, first unpin old one
441c6f73aabSFrançois Tigeot 		 * and then retry pining new one.
442c6f73aabSFrançois Tigeot 		 *
443c6f73aabSFrançois Tigeot 		 * As only master can set mode only master can pin and it is
444c6f73aabSFrançois Tigeot 		 * unlikely the master client will race with itself especialy
445c6f73aabSFrançois Tigeot 		 * on those old gpu with single crtc.
446c6f73aabSFrançois Tigeot 		 *
447c6f73aabSFrançois Tigeot 		 * We don't shutdown the display controller because new buffer
448c6f73aabSFrançois Tigeot 		 * will end up in same spot.
449c6f73aabSFrançois Tigeot 		 */
450c6f73aabSFrançois Tigeot 		if (!atomic && fb && fb != crtc->primary->fb) {
451c6f73aabSFrançois Tigeot 			struct radeon_bo *old_rbo;
452c6f73aabSFrançois Tigeot 			unsigned long nsize, osize;
453c6f73aabSFrançois Tigeot 
454c6f73aabSFrançois Tigeot 			old_rbo = gem_to_radeon_bo(to_radeon_framebuffer(fb)->obj);
455c6f73aabSFrançois Tigeot 			osize = radeon_bo_size(old_rbo);
456c6f73aabSFrançois Tigeot 			nsize = radeon_bo_size(rbo);
457c6f73aabSFrançois Tigeot 			if (nsize <= osize && !radeon_bo_reserve(old_rbo, false)) {
458c6f73aabSFrançois Tigeot 				radeon_bo_unpin(old_rbo);
459c6f73aabSFrançois Tigeot 				radeon_bo_unreserve(old_rbo);
460c6f73aabSFrançois Tigeot 				fb = NULL;
461c6f73aabSFrançois Tigeot 				goto retry;
462c6f73aabSFrançois Tigeot 			}
463c6f73aabSFrançois Tigeot 		}
464926deccbSFrançois Tigeot 		return -EINVAL;
465926deccbSFrançois Tigeot 	}
466926deccbSFrançois Tigeot 	radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
467926deccbSFrançois Tigeot 	radeon_bo_unreserve(rbo);
468926deccbSFrançois Tigeot 	if (tiling_flags & RADEON_TILING_MICRO)
469926deccbSFrançois Tigeot 		DRM_ERROR("trying to scanout microtiled buffer\n");
470926deccbSFrançois Tigeot 
471926deccbSFrançois Tigeot 	/* if scanout was in GTT this really wouldn't work */
472926deccbSFrançois Tigeot 	/* crtc offset is from display base addr not FB location */
473926deccbSFrançois Tigeot 	radeon_crtc->legacy_display_base_addr = rdev->mc.vram_start;
474926deccbSFrançois Tigeot 
475926deccbSFrançois Tigeot 	base -= radeon_crtc->legacy_display_base_addr;
476926deccbSFrançois Tigeot 
477926deccbSFrançois Tigeot 	crtc_offset_cntl = 0;
478926deccbSFrançois Tigeot 
479*a85cb24fSFrançois Tigeot 	pitch_pixels = target_fb->pitches[0] / target_fb->format->cpp[0];
480*a85cb24fSFrançois Tigeot 	crtc_pitch = DIV_ROUND_UP(pitch_pixels * target_fb->format->cpp[0] * 8,
481*a85cb24fSFrançois Tigeot 				  target_fb->format->cpp[0] * 8 * 8);
482926deccbSFrançois Tigeot 	crtc_pitch |= crtc_pitch << 16;
483926deccbSFrançois Tigeot 
484926deccbSFrançois Tigeot 	crtc_offset_cntl |= RADEON_CRTC_GUI_TRIG_OFFSET_LEFT_EN;
485926deccbSFrançois Tigeot 	if (tiling_flags & RADEON_TILING_MACRO) {
486926deccbSFrançois Tigeot 		if (ASIC_IS_R300(rdev))
487926deccbSFrançois Tigeot 			crtc_offset_cntl |= (R300_CRTC_X_Y_MODE_EN |
488926deccbSFrançois Tigeot 					     R300_CRTC_MICRO_TILE_BUFFER_DIS |
489926deccbSFrançois Tigeot 					     R300_CRTC_MACRO_TILE_EN);
490926deccbSFrançois Tigeot 		else
491926deccbSFrançois Tigeot 			crtc_offset_cntl |= RADEON_CRTC_TILE_EN;
492926deccbSFrançois Tigeot 	} else {
493926deccbSFrançois Tigeot 		if (ASIC_IS_R300(rdev))
494926deccbSFrançois Tigeot 			crtc_offset_cntl &= ~(R300_CRTC_X_Y_MODE_EN |
495926deccbSFrançois Tigeot 					      R300_CRTC_MICRO_TILE_BUFFER_DIS |
496926deccbSFrançois Tigeot 					      R300_CRTC_MACRO_TILE_EN);
497926deccbSFrançois Tigeot 		else
498926deccbSFrançois Tigeot 			crtc_offset_cntl &= ~RADEON_CRTC_TILE_EN;
499926deccbSFrançois Tigeot 	}
500926deccbSFrançois Tigeot 
501926deccbSFrançois Tigeot 	if (tiling_flags & RADEON_TILING_MACRO) {
502926deccbSFrançois Tigeot 		if (ASIC_IS_R300(rdev)) {
503926deccbSFrançois Tigeot 			crtc_tile_x0_y0 = x | (y << 16);
504926deccbSFrançois Tigeot 			base &= ~0x7ff;
505926deccbSFrançois Tigeot 		} else {
506*a85cb24fSFrançois Tigeot 			int byteshift = target_fb->format->cpp[0] * 8 >> 4;
507926deccbSFrançois Tigeot 			int tile_addr = (((y >> 3) * pitch_pixels +  x) >> (8 - byteshift)) << 11;
508926deccbSFrançois Tigeot 			base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8);
509926deccbSFrançois Tigeot 			crtc_offset_cntl |= (y % 16);
510926deccbSFrançois Tigeot 		}
511926deccbSFrançois Tigeot 	} else {
512926deccbSFrançois Tigeot 		int offset = y * pitch_pixels + x;
513*a85cb24fSFrançois Tigeot 		switch (target_fb->format->cpp[0] * 8) {
514926deccbSFrançois Tigeot 		case 8:
515926deccbSFrançois Tigeot 			offset *= 1;
516926deccbSFrançois Tigeot 			break;
517926deccbSFrançois Tigeot 		case 15:
518926deccbSFrançois Tigeot 		case 16:
519926deccbSFrançois Tigeot 			offset *= 2;
520926deccbSFrançois Tigeot 			break;
521926deccbSFrançois Tigeot 		case 24:
522926deccbSFrançois Tigeot 			offset *= 3;
523926deccbSFrançois Tigeot 			break;
524926deccbSFrançois Tigeot 		case 32:
525926deccbSFrançois Tigeot 			offset *= 4;
526926deccbSFrançois Tigeot 			break;
527926deccbSFrançois Tigeot 		default:
528926deccbSFrançois Tigeot 			return false;
529926deccbSFrançois Tigeot 		}
530926deccbSFrançois Tigeot 		base += offset;
531926deccbSFrançois Tigeot 	}
532926deccbSFrançois Tigeot 
533926deccbSFrançois Tigeot 	base &= ~7;
534926deccbSFrançois Tigeot 
535926deccbSFrançois Tigeot 	if (radeon_crtc->crtc_id == 1)
536926deccbSFrançois Tigeot 		gen_cntl_reg = RADEON_CRTC2_GEN_CNTL;
537926deccbSFrançois Tigeot 	else
538926deccbSFrançois Tigeot 		gen_cntl_reg = RADEON_CRTC_GEN_CNTL;
539926deccbSFrançois Tigeot 
540926deccbSFrançois Tigeot 	gen_cntl_val = RREG32(gen_cntl_reg);
541926deccbSFrançois Tigeot 	gen_cntl_val &= ~(0xf << 8);
542926deccbSFrançois Tigeot 	gen_cntl_val |= (format << 8);
543926deccbSFrançois Tigeot 	gen_cntl_val &= ~RADEON_CRTC_VSTAT_MODE_MASK;
544926deccbSFrançois Tigeot 	WREG32(gen_cntl_reg, gen_cntl_val);
545926deccbSFrançois Tigeot 
546926deccbSFrançois Tigeot 	crtc_offset = (u32)base;
547926deccbSFrançois Tigeot 
548926deccbSFrançois Tigeot 	WREG32(RADEON_DISPLAY_BASE_ADDR + radeon_crtc->crtc_offset, radeon_crtc->legacy_display_base_addr);
549926deccbSFrançois Tigeot 
550926deccbSFrançois Tigeot 	if (ASIC_IS_R300(rdev)) {
551926deccbSFrançois Tigeot 		if (radeon_crtc->crtc_id)
552926deccbSFrançois Tigeot 			WREG32(R300_CRTC2_TILE_X0_Y0, crtc_tile_x0_y0);
553926deccbSFrançois Tigeot 		else
554926deccbSFrançois Tigeot 			WREG32(R300_CRTC_TILE_X0_Y0, crtc_tile_x0_y0);
555926deccbSFrançois Tigeot 	}
556926deccbSFrançois Tigeot 	WREG32(RADEON_CRTC_OFFSET_CNTL + radeon_crtc->crtc_offset, crtc_offset_cntl);
557926deccbSFrançois Tigeot 	WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, crtc_offset);
558926deccbSFrançois Tigeot 	WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch);
559926deccbSFrançois Tigeot 
560ba55f2f5SFrançois Tigeot 	if (!atomic && fb && fb != crtc->primary->fb) {
561926deccbSFrançois Tigeot 		radeon_fb = to_radeon_framebuffer(fb);
562926deccbSFrançois Tigeot 		rbo = gem_to_radeon_bo(radeon_fb->obj);
563926deccbSFrançois Tigeot 		r = radeon_bo_reserve(rbo, false);
564926deccbSFrançois Tigeot 		if (unlikely(r != 0))
565926deccbSFrançois Tigeot 			return r;
566926deccbSFrançois Tigeot 		radeon_bo_unpin(rbo);
567926deccbSFrançois Tigeot 		radeon_bo_unreserve(rbo);
568926deccbSFrançois Tigeot 	}
569926deccbSFrançois Tigeot 
570926deccbSFrançois Tigeot 	/* Bytes per pixel may have changed */
571926deccbSFrançois Tigeot 	radeon_bandwidth_update(rdev);
572926deccbSFrançois Tigeot 
573926deccbSFrançois Tigeot 	return 0;
574926deccbSFrançois Tigeot }
575926deccbSFrançois Tigeot 
radeon_set_crtc_timing(struct drm_crtc * crtc,struct drm_display_mode * mode)576926deccbSFrançois Tigeot static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mode *mode)
577926deccbSFrançois Tigeot {
578926deccbSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
579926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
580926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
581*a85cb24fSFrançois Tigeot 	const struct drm_framebuffer *fb = crtc->primary->fb;
582926deccbSFrançois Tigeot 	struct drm_encoder *encoder;
583926deccbSFrançois Tigeot 	int format;
584926deccbSFrançois Tigeot 	int hsync_start;
585926deccbSFrançois Tigeot 	int hsync_wid;
586926deccbSFrançois Tigeot 	int vsync_wid;
587926deccbSFrançois Tigeot 	uint32_t crtc_h_total_disp;
588926deccbSFrançois Tigeot 	uint32_t crtc_h_sync_strt_wid;
589926deccbSFrançois Tigeot 	uint32_t crtc_v_total_disp;
590926deccbSFrançois Tigeot 	uint32_t crtc_v_sync_strt_wid;
591926deccbSFrançois Tigeot 	bool is_tv = false;
592926deccbSFrançois Tigeot 
593926deccbSFrançois Tigeot 	DRM_DEBUG_KMS("\n");
594926deccbSFrançois Tigeot 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
595926deccbSFrançois Tigeot 		if (encoder->crtc == crtc) {
596926deccbSFrançois Tigeot 			struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
597926deccbSFrançois Tigeot 			if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) {
598926deccbSFrançois Tigeot 				is_tv = true;
599926deccbSFrançois Tigeot 				DRM_INFO("crtc %d is connected to a TV\n", radeon_crtc->crtc_id);
600926deccbSFrançois Tigeot 				break;
601926deccbSFrançois Tigeot 			}
602926deccbSFrançois Tigeot 		}
603926deccbSFrançois Tigeot 	}
604926deccbSFrançois Tigeot 
605*a85cb24fSFrançois Tigeot 	switch (fb->format->cpp[0] * 8) {
606926deccbSFrançois Tigeot 	case 8:
607926deccbSFrançois Tigeot 		format = 2;
608926deccbSFrançois Tigeot 		break;
609926deccbSFrançois Tigeot 	case 15:      /*  555 */
610926deccbSFrançois Tigeot 		format = 3;
611926deccbSFrançois Tigeot 		break;
612926deccbSFrançois Tigeot 	case 16:      /*  565 */
613926deccbSFrançois Tigeot 		format = 4;
614926deccbSFrançois Tigeot 		break;
615926deccbSFrançois Tigeot 	case 24:      /*  RGB */
616926deccbSFrançois Tigeot 		format = 5;
617926deccbSFrançois Tigeot 		break;
618926deccbSFrançois Tigeot 	case 32:      /* xRGB */
619926deccbSFrançois Tigeot 		format = 6;
620926deccbSFrançois Tigeot 		break;
621926deccbSFrançois Tigeot 	default:
622926deccbSFrançois Tigeot 		return false;
623926deccbSFrançois Tigeot 	}
624926deccbSFrançois Tigeot 
625926deccbSFrançois Tigeot 	crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff)
626926deccbSFrançois Tigeot 			     | ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16));
627926deccbSFrançois Tigeot 
628926deccbSFrançois Tigeot 	hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8;
629926deccbSFrançois Tigeot 	if (!hsync_wid)
630926deccbSFrançois Tigeot 		hsync_wid = 1;
631926deccbSFrançois Tigeot 	hsync_start = mode->crtc_hsync_start - 8;
632926deccbSFrançois Tigeot 
633926deccbSFrançois Tigeot 	crtc_h_sync_strt_wid = ((hsync_start & 0x1fff)
634926deccbSFrançois Tigeot 				| ((hsync_wid & 0x3f) << 16)
635926deccbSFrançois Tigeot 				| ((mode->flags & DRM_MODE_FLAG_NHSYNC)
636926deccbSFrançois Tigeot 				   ? RADEON_CRTC_H_SYNC_POL
637926deccbSFrançois Tigeot 				   : 0));
638926deccbSFrançois Tigeot 
639926deccbSFrançois Tigeot 	/* This works for double scan mode. */
640926deccbSFrançois Tigeot 	crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff)
641926deccbSFrançois Tigeot 			     | ((mode->crtc_vdisplay - 1) << 16));
642926deccbSFrançois Tigeot 
643926deccbSFrançois Tigeot 	vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start;
644926deccbSFrançois Tigeot 	if (!vsync_wid)
645926deccbSFrançois Tigeot 		vsync_wid = 1;
646926deccbSFrançois Tigeot 
647926deccbSFrançois Tigeot 	crtc_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff)
648926deccbSFrançois Tigeot 				| ((vsync_wid & 0x1f) << 16)
649926deccbSFrançois Tigeot 				| ((mode->flags & DRM_MODE_FLAG_NVSYNC)
650926deccbSFrançois Tigeot 				   ? RADEON_CRTC_V_SYNC_POL
651926deccbSFrançois Tigeot 				   : 0));
652926deccbSFrançois Tigeot 
653926deccbSFrançois Tigeot 	if (radeon_crtc->crtc_id) {
654926deccbSFrançois Tigeot 		uint32_t crtc2_gen_cntl;
655926deccbSFrançois Tigeot 		uint32_t disp2_merge_cntl;
656926deccbSFrançois Tigeot 
657926deccbSFrançois Tigeot 		/* if TV DAC is enabled for another crtc and keep it enabled */
658926deccbSFrançois Tigeot 		crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0x00718080;
659926deccbSFrançois Tigeot 		crtc2_gen_cntl |= ((format << 8)
660926deccbSFrançois Tigeot 				   | RADEON_CRTC2_VSYNC_DIS
661926deccbSFrançois Tigeot 				   | RADEON_CRTC2_HSYNC_DIS
662926deccbSFrançois Tigeot 				   | RADEON_CRTC2_DISP_DIS
663926deccbSFrançois Tigeot 				   | RADEON_CRTC2_DISP_REQ_EN_B
664926deccbSFrançois Tigeot 				   | ((mode->flags & DRM_MODE_FLAG_DBLSCAN)
665926deccbSFrançois Tigeot 				      ? RADEON_CRTC2_DBL_SCAN_EN
666926deccbSFrançois Tigeot 				      : 0)
667926deccbSFrançois Tigeot 				   | ((mode->flags & DRM_MODE_FLAG_CSYNC)
668926deccbSFrançois Tigeot 				      ? RADEON_CRTC2_CSYNC_EN
669926deccbSFrançois Tigeot 				      : 0)
670926deccbSFrançois Tigeot 				   | ((mode->flags & DRM_MODE_FLAG_INTERLACE)
671926deccbSFrançois Tigeot 				      ? RADEON_CRTC2_INTERLACE_EN
672926deccbSFrançois Tigeot 				      : 0));
673926deccbSFrançois Tigeot 
674926deccbSFrançois Tigeot 		/* rs4xx chips seem to like to have the crtc enabled when the timing is set */
675926deccbSFrançois Tigeot 		if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480))
676926deccbSFrançois Tigeot 			crtc2_gen_cntl |= RADEON_CRTC2_EN;
677926deccbSFrançois Tigeot 
678926deccbSFrançois Tigeot 		disp2_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL);
679926deccbSFrançois Tigeot 		disp2_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN;
680926deccbSFrançois Tigeot 
681926deccbSFrançois Tigeot 		WREG32(RADEON_DISP2_MERGE_CNTL, disp2_merge_cntl);
682926deccbSFrançois Tigeot 		WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
683926deccbSFrançois Tigeot 
684926deccbSFrançois Tigeot 		WREG32(RADEON_FP_H2_SYNC_STRT_WID, crtc_h_sync_strt_wid);
685926deccbSFrançois Tigeot 		WREG32(RADEON_FP_V2_SYNC_STRT_WID, crtc_v_sync_strt_wid);
686926deccbSFrançois Tigeot 	} else {
687926deccbSFrançois Tigeot 		uint32_t crtc_gen_cntl;
688926deccbSFrançois Tigeot 		uint32_t crtc_ext_cntl;
689926deccbSFrançois Tigeot 		uint32_t disp_merge_cntl;
690926deccbSFrançois Tigeot 
691926deccbSFrançois Tigeot 		crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0x00718000;
692926deccbSFrançois Tigeot 		crtc_gen_cntl |= (RADEON_CRTC_EXT_DISP_EN
693926deccbSFrançois Tigeot 				 | (format << 8)
694926deccbSFrançois Tigeot 				 | RADEON_CRTC_DISP_REQ_EN_B
695926deccbSFrançois Tigeot 				 | ((mode->flags & DRM_MODE_FLAG_DBLSCAN)
696926deccbSFrançois Tigeot 				    ? RADEON_CRTC_DBL_SCAN_EN
697926deccbSFrançois Tigeot 				    : 0)
698926deccbSFrançois Tigeot 				 | ((mode->flags & DRM_MODE_FLAG_CSYNC)
699926deccbSFrançois Tigeot 				    ? RADEON_CRTC_CSYNC_EN
700926deccbSFrançois Tigeot 				    : 0)
701926deccbSFrançois Tigeot 				 | ((mode->flags & DRM_MODE_FLAG_INTERLACE)
702926deccbSFrançois Tigeot 				    ? RADEON_CRTC_INTERLACE_EN
703926deccbSFrançois Tigeot 				    : 0));
704926deccbSFrançois Tigeot 
705926deccbSFrançois Tigeot 		/* rs4xx chips seem to like to have the crtc enabled when the timing is set */
706926deccbSFrançois Tigeot 		if ((rdev->family == CHIP_RS400) || (rdev->family == CHIP_RS480))
707926deccbSFrançois Tigeot 			crtc_gen_cntl |= RADEON_CRTC_EN;
708926deccbSFrançois Tigeot 
709926deccbSFrançois Tigeot 		crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL);
710926deccbSFrançois Tigeot 		crtc_ext_cntl |= (RADEON_XCRT_CNT_EN |
711926deccbSFrançois Tigeot 				  RADEON_CRTC_VSYNC_DIS |
712926deccbSFrançois Tigeot 				  RADEON_CRTC_HSYNC_DIS |
713926deccbSFrançois Tigeot 				  RADEON_CRTC_DISPLAY_DIS);
714926deccbSFrançois Tigeot 
715926deccbSFrançois Tigeot 		disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL);
716926deccbSFrançois Tigeot 		disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN;
717926deccbSFrançois Tigeot 
718926deccbSFrançois Tigeot 		WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl);
719926deccbSFrançois Tigeot 		WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl);
720926deccbSFrançois Tigeot 		WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl);
721926deccbSFrançois Tigeot 	}
722926deccbSFrançois Tigeot 
723926deccbSFrançois Tigeot 	if (is_tv)
724926deccbSFrançois Tigeot 		radeon_legacy_tv_adjust_crtc_reg(encoder, &crtc_h_total_disp,
725926deccbSFrançois Tigeot 						 &crtc_h_sync_strt_wid, &crtc_v_total_disp,
726926deccbSFrançois Tigeot 						 &crtc_v_sync_strt_wid);
727926deccbSFrançois Tigeot 
728926deccbSFrançois Tigeot 	WREG32(RADEON_CRTC_H_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_h_total_disp);
729926deccbSFrançois Tigeot 	WREG32(RADEON_CRTC_H_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_h_sync_strt_wid);
730926deccbSFrançois Tigeot 	WREG32(RADEON_CRTC_V_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_v_total_disp);
731926deccbSFrançois Tigeot 	WREG32(RADEON_CRTC_V_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_v_sync_strt_wid);
732926deccbSFrançois Tigeot 
733926deccbSFrançois Tigeot 	return true;
734926deccbSFrançois Tigeot }
735926deccbSFrançois Tigeot 
radeon_set_pll(struct drm_crtc * crtc,struct drm_display_mode * mode)736926deccbSFrançois Tigeot static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
737926deccbSFrançois Tigeot {
738926deccbSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
739926deccbSFrançois Tigeot 	struct radeon_device *rdev = dev->dev_private;
740926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
741926deccbSFrançois Tigeot 	struct drm_encoder *encoder;
742926deccbSFrançois Tigeot 	uint32_t feedback_div = 0;
743926deccbSFrançois Tigeot 	uint32_t frac_fb_div = 0;
744926deccbSFrançois Tigeot 	uint32_t reference_div = 0;
745926deccbSFrançois Tigeot 	uint32_t post_divider = 0;
746926deccbSFrançois Tigeot 	uint32_t freq = 0;
747926deccbSFrançois Tigeot 	uint8_t pll_gain;
748926deccbSFrançois Tigeot 	bool use_bios_divs = false;
749926deccbSFrançois Tigeot 	/* PLL registers */
750926deccbSFrançois Tigeot 	uint32_t pll_ref_div = 0;
751926deccbSFrançois Tigeot 	uint32_t pll_fb_post_div = 0;
752926deccbSFrançois Tigeot 	uint32_t htotal_cntl = 0;
753926deccbSFrançois Tigeot 	bool is_tv = false;
754926deccbSFrançois Tigeot 	struct radeon_pll *pll;
755926deccbSFrançois Tigeot 
756926deccbSFrançois Tigeot 	struct {
757926deccbSFrançois Tigeot 		int divider;
758926deccbSFrançois Tigeot 		int bitvalue;
759926deccbSFrançois Tigeot 	} *post_div, post_divs[]   = {
760926deccbSFrançois Tigeot 		/* From RAGE 128 VR/RAGE 128 GL Register
761926deccbSFrançois Tigeot 		 * Reference Manual (Technical Reference
762926deccbSFrançois Tigeot 		 * Manual P/N RRG-G04100-C Rev. 0.04), page
763926deccbSFrançois Tigeot 		 * 3-17 (PLL_DIV_[3:0]).
764926deccbSFrançois Tigeot 		 */
765926deccbSFrançois Tigeot 		{  1, 0 },              /* VCLK_SRC                 */
766926deccbSFrançois Tigeot 		{  2, 1 },              /* VCLK_SRC/2               */
767926deccbSFrançois Tigeot 		{  4, 2 },              /* VCLK_SRC/4               */
768926deccbSFrançois Tigeot 		{  8, 3 },              /* VCLK_SRC/8               */
769926deccbSFrançois Tigeot 		{  3, 4 },              /* VCLK_SRC/3               */
770926deccbSFrançois Tigeot 		{ 16, 5 },              /* VCLK_SRC/16              */
771926deccbSFrançois Tigeot 		{  6, 6 },              /* VCLK_SRC/6               */
772926deccbSFrançois Tigeot 		{ 12, 7 },              /* VCLK_SRC/12              */
773926deccbSFrançois Tigeot 		{  0, 0 }
774926deccbSFrançois Tigeot 	};
775926deccbSFrançois Tigeot 
776926deccbSFrançois Tigeot 	if (radeon_crtc->crtc_id)
777926deccbSFrançois Tigeot 		pll = &rdev->clock.p2pll;
778926deccbSFrançois Tigeot 	else
779926deccbSFrançois Tigeot 		pll = &rdev->clock.p1pll;
780926deccbSFrançois Tigeot 
781926deccbSFrançois Tigeot 	pll->flags = RADEON_PLL_LEGACY;
782926deccbSFrançois Tigeot 
783926deccbSFrançois Tigeot 	if (mode->clock > 200000) /* range limits??? */
784926deccbSFrançois Tigeot 		pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
785926deccbSFrançois Tigeot 	else
786926deccbSFrançois Tigeot 		pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
787926deccbSFrançois Tigeot 
788926deccbSFrançois Tigeot 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
789926deccbSFrançois Tigeot 		if (encoder->crtc == crtc) {
790926deccbSFrançois Tigeot 			struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
791926deccbSFrançois Tigeot 
792926deccbSFrançois Tigeot 			if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) {
793926deccbSFrançois Tigeot 				is_tv = true;
794926deccbSFrançois Tigeot 				break;
795926deccbSFrançois Tigeot 			}
796926deccbSFrançois Tigeot 
797926deccbSFrançois Tigeot 			if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
798926deccbSFrançois Tigeot 				pll->flags |= RADEON_PLL_NO_ODD_POST_DIV;
799926deccbSFrançois Tigeot 			if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) {
800926deccbSFrançois Tigeot 				if (!rdev->is_atom_bios) {
801926deccbSFrançois Tigeot 					struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
802926deccbSFrançois Tigeot 					struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
803926deccbSFrançois Tigeot 					if (lvds) {
804926deccbSFrançois Tigeot 						if (lvds->use_bios_dividers) {
805926deccbSFrançois Tigeot 							pll_ref_div = lvds->panel_ref_divider;
806926deccbSFrançois Tigeot 							pll_fb_post_div   = (lvds->panel_fb_divider |
807926deccbSFrançois Tigeot 									     (lvds->panel_post_divider << 16));
808926deccbSFrançois Tigeot 							htotal_cntl  = 0;
809926deccbSFrançois Tigeot 							use_bios_divs = true;
810926deccbSFrançois Tigeot 						}
811926deccbSFrançois Tigeot 					}
812926deccbSFrançois Tigeot 				}
813926deccbSFrançois Tigeot 				pll->flags |= RADEON_PLL_USE_REF_DIV;
814926deccbSFrançois Tigeot 			}
815926deccbSFrançois Tigeot 		}
816926deccbSFrançois Tigeot 	}
817926deccbSFrançois Tigeot 
818926deccbSFrançois Tigeot 	DRM_DEBUG_KMS("\n");
819926deccbSFrançois Tigeot 
820926deccbSFrançois Tigeot 	if (!use_bios_divs) {
821926deccbSFrançois Tigeot 		radeon_compute_pll_legacy(pll, mode->clock,
822926deccbSFrançois Tigeot 					  &freq, &feedback_div, &frac_fb_div,
823926deccbSFrançois Tigeot 					  &reference_div, &post_divider);
824926deccbSFrançois Tigeot 
825926deccbSFrançois Tigeot 		for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
826926deccbSFrançois Tigeot 			if (post_div->divider == post_divider)
827926deccbSFrançois Tigeot 				break;
828926deccbSFrançois Tigeot 		}
829926deccbSFrançois Tigeot 
830926deccbSFrançois Tigeot 		if (!post_div->divider)
831926deccbSFrançois Tigeot 			post_div = &post_divs[0];
832926deccbSFrançois Tigeot 
833926deccbSFrançois Tigeot 		DRM_DEBUG_KMS("dc=%u, fd=%d, rd=%d, pd=%d\n",
834926deccbSFrançois Tigeot 			  (unsigned)freq,
835926deccbSFrançois Tigeot 			  feedback_div,
836926deccbSFrançois Tigeot 			  reference_div,
837926deccbSFrançois Tigeot 			  post_divider);
838926deccbSFrançois Tigeot 
839926deccbSFrançois Tigeot 		pll_ref_div   = reference_div;
840926deccbSFrançois Tigeot #if defined(__powerpc__) && (0) /* TODO */
841926deccbSFrançois Tigeot 		/* apparently programming this otherwise causes a hang??? */
842926deccbSFrançois Tigeot 		if (info->MacModel == RADEON_MAC_IBOOK)
843926deccbSFrançois Tigeot 			pll_fb_post_div = 0x000600ad;
844926deccbSFrançois Tigeot 		else
845926deccbSFrançois Tigeot #endif
846926deccbSFrançois Tigeot 			pll_fb_post_div     = (feedback_div | (post_div->bitvalue << 16));
847926deccbSFrançois Tigeot 
848926deccbSFrançois Tigeot 		htotal_cntl    = mode->htotal & 0x7;
849926deccbSFrançois Tigeot 
850926deccbSFrançois Tigeot 	}
851926deccbSFrançois Tigeot 
852926deccbSFrançois Tigeot 	pll_gain = radeon_compute_pll_gain(pll->reference_freq,
853926deccbSFrançois Tigeot 					   pll_ref_div & 0x3ff,
854926deccbSFrançois Tigeot 					   pll_fb_post_div & 0x7ff);
855926deccbSFrançois Tigeot 
856926deccbSFrançois Tigeot 	if (radeon_crtc->crtc_id) {
857926deccbSFrançois Tigeot 		uint32_t pixclks_cntl = ((RREG32_PLL(RADEON_PIXCLKS_CNTL) &
858926deccbSFrançois Tigeot 					  ~(RADEON_PIX2CLK_SRC_SEL_MASK)) |
859926deccbSFrançois Tigeot 					 RADEON_PIX2CLK_SRC_SEL_P2PLLCLK);
860926deccbSFrançois Tigeot 
861926deccbSFrançois Tigeot 		if (is_tv) {
862926deccbSFrançois Tigeot 			radeon_legacy_tv_adjust_pll2(encoder, &htotal_cntl,
863926deccbSFrançois Tigeot 						     &pll_ref_div, &pll_fb_post_div,
864926deccbSFrançois Tigeot 						     &pixclks_cntl);
865926deccbSFrançois Tigeot 		}
866926deccbSFrançois Tigeot 
867926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_PIXCLKS_CNTL,
868926deccbSFrançois Tigeot 			     RADEON_PIX2CLK_SRC_SEL_CPUCLK,
869926deccbSFrançois Tigeot 			     ~(RADEON_PIX2CLK_SRC_SEL_MASK));
870926deccbSFrançois Tigeot 
871926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_P2PLL_CNTL,
872926deccbSFrançois Tigeot 			     RADEON_P2PLL_RESET
873926deccbSFrançois Tigeot 			     | RADEON_P2PLL_ATOMIC_UPDATE_EN
874926deccbSFrançois Tigeot 			     | ((uint32_t)pll_gain << RADEON_P2PLL_PVG_SHIFT),
875926deccbSFrançois Tigeot 			     ~(RADEON_P2PLL_RESET
876926deccbSFrançois Tigeot 			       | RADEON_P2PLL_ATOMIC_UPDATE_EN
877926deccbSFrançois Tigeot 			       | RADEON_P2PLL_PVG_MASK));
878926deccbSFrançois Tigeot 
879926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_P2PLL_REF_DIV,
880926deccbSFrançois Tigeot 			     pll_ref_div,
881926deccbSFrançois Tigeot 			     ~RADEON_P2PLL_REF_DIV_MASK);
882926deccbSFrançois Tigeot 
883926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_P2PLL_DIV_0,
884926deccbSFrançois Tigeot 			     pll_fb_post_div,
885926deccbSFrançois Tigeot 			     ~RADEON_P2PLL_FB0_DIV_MASK);
886926deccbSFrançois Tigeot 
887926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_P2PLL_DIV_0,
888926deccbSFrançois Tigeot 			     pll_fb_post_div,
889926deccbSFrançois Tigeot 			     ~RADEON_P2PLL_POST0_DIV_MASK);
890926deccbSFrançois Tigeot 
891926deccbSFrançois Tigeot 		radeon_pll2_write_update(dev);
892926deccbSFrançois Tigeot 		radeon_pll2_wait_for_read_update_complete(dev);
893926deccbSFrançois Tigeot 
894926deccbSFrançois Tigeot 		WREG32_PLL(RADEON_HTOTAL2_CNTL, htotal_cntl);
895926deccbSFrançois Tigeot 
896926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_P2PLL_CNTL,
897926deccbSFrançois Tigeot 			     0,
898926deccbSFrançois Tigeot 			     ~(RADEON_P2PLL_RESET
899926deccbSFrançois Tigeot 			       | RADEON_P2PLL_SLEEP
900926deccbSFrançois Tigeot 			       | RADEON_P2PLL_ATOMIC_UPDATE_EN));
901926deccbSFrançois Tigeot 
902926deccbSFrançois Tigeot 		DRM_DEBUG_KMS("Wrote2: 0x%08x 0x%08x 0x%08x (0x%08x)\n",
903926deccbSFrançois Tigeot 			  (unsigned)pll_ref_div,
904926deccbSFrançois Tigeot 			  (unsigned)pll_fb_post_div,
905926deccbSFrançois Tigeot 			  (unsigned)htotal_cntl,
906926deccbSFrançois Tigeot 			  RREG32_PLL(RADEON_P2PLL_CNTL));
907926deccbSFrançois Tigeot 		DRM_DEBUG_KMS("Wrote2: rd=%u, fd=%u, pd=%u\n",
908926deccbSFrançois Tigeot 			  (unsigned)pll_ref_div & RADEON_P2PLL_REF_DIV_MASK,
909926deccbSFrançois Tigeot 			  (unsigned)pll_fb_post_div & RADEON_P2PLL_FB0_DIV_MASK,
910926deccbSFrançois Tigeot 			  (unsigned)((pll_fb_post_div &
911926deccbSFrançois Tigeot 				      RADEON_P2PLL_POST0_DIV_MASK) >> 16));
912926deccbSFrançois Tigeot 
913c4ef309bSzrj 		mdelay(50); /* Let the clock to lock */
914926deccbSFrançois Tigeot 
915926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_PIXCLKS_CNTL,
916926deccbSFrançois Tigeot 			     RADEON_PIX2CLK_SRC_SEL_P2PLLCLK,
917926deccbSFrançois Tigeot 			     ~(RADEON_PIX2CLK_SRC_SEL_MASK));
918926deccbSFrançois Tigeot 
919926deccbSFrançois Tigeot 		WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
920926deccbSFrançois Tigeot 	} else {
921926deccbSFrançois Tigeot 		uint32_t pixclks_cntl;
922926deccbSFrançois Tigeot 
923926deccbSFrançois Tigeot 
924926deccbSFrançois Tigeot 		if (is_tv) {
925926deccbSFrançois Tigeot 			pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL);
926926deccbSFrançois Tigeot 			radeon_legacy_tv_adjust_pll1(encoder, &htotal_cntl, &pll_ref_div,
927926deccbSFrançois Tigeot 						     &pll_fb_post_div, &pixclks_cntl);
928926deccbSFrançois Tigeot 		}
929926deccbSFrançois Tigeot 
930926deccbSFrançois Tigeot 		if (rdev->flags & RADEON_IS_MOBILITY) {
931926deccbSFrançois Tigeot 			/* A temporal workaround for the occasional blanking on certain laptop panels.
932926deccbSFrançois Tigeot 			   This appears to related to the PLL divider registers (fail to lock?).
933926deccbSFrançois Tigeot 			   It occurs even when all dividers are the same with their old settings.
934926deccbSFrançois Tigeot 			   In this case we really don't need to fiddle with PLL registers.
935926deccbSFrançois Tigeot 			   By doing this we can avoid the blanking problem with some panels.
936926deccbSFrançois Tigeot 			*/
937926deccbSFrançois Tigeot 			if ((pll_ref_div == (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_REF_DIV_MASK)) &&
938926deccbSFrançois Tigeot 			    (pll_fb_post_div == (RREG32_PLL(RADEON_PPLL_DIV_3) &
939926deccbSFrançois Tigeot 						 (RADEON_PPLL_POST3_DIV_MASK | RADEON_PPLL_FB3_DIV_MASK)))) {
940926deccbSFrançois Tigeot 				WREG32_P(RADEON_CLOCK_CNTL_INDEX,
941926deccbSFrançois Tigeot 					 RADEON_PLL_DIV_SEL,
942926deccbSFrançois Tigeot 					 ~(RADEON_PLL_DIV_SEL));
943926deccbSFrançois Tigeot 				r100_pll_errata_after_index(rdev);
944926deccbSFrançois Tigeot 				return;
945926deccbSFrançois Tigeot 			}
946926deccbSFrançois Tigeot 		}
947926deccbSFrançois Tigeot 
948926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_VCLK_ECP_CNTL,
949926deccbSFrançois Tigeot 			     RADEON_VCLK_SRC_SEL_CPUCLK,
950926deccbSFrançois Tigeot 			     ~(RADEON_VCLK_SRC_SEL_MASK));
951926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_PPLL_CNTL,
952926deccbSFrançois Tigeot 			     RADEON_PPLL_RESET
953926deccbSFrançois Tigeot 			     | RADEON_PPLL_ATOMIC_UPDATE_EN
954926deccbSFrançois Tigeot 			     | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN
955926deccbSFrançois Tigeot 			     | ((uint32_t)pll_gain << RADEON_PPLL_PVG_SHIFT),
956926deccbSFrançois Tigeot 			     ~(RADEON_PPLL_RESET
957926deccbSFrançois Tigeot 			       | RADEON_PPLL_ATOMIC_UPDATE_EN
958926deccbSFrançois Tigeot 			       | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN
959926deccbSFrançois Tigeot 			       | RADEON_PPLL_PVG_MASK));
960926deccbSFrançois Tigeot 
961926deccbSFrançois Tigeot 		WREG32_P(RADEON_CLOCK_CNTL_INDEX,
962926deccbSFrançois Tigeot 			 RADEON_PLL_DIV_SEL,
963926deccbSFrançois Tigeot 			 ~(RADEON_PLL_DIV_SEL));
964926deccbSFrançois Tigeot 		r100_pll_errata_after_index(rdev);
965926deccbSFrançois Tigeot 
966926deccbSFrançois Tigeot 		if (ASIC_IS_R300(rdev) ||
967926deccbSFrançois Tigeot 		    (rdev->family == CHIP_RS300) ||
968926deccbSFrançois Tigeot 		    (rdev->family == CHIP_RS400) ||
969926deccbSFrançois Tigeot 		    (rdev->family == CHIP_RS480)) {
970926deccbSFrançois Tigeot 			if (pll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) {
971926deccbSFrançois Tigeot 				/* When restoring console mode, use saved PPLL_REF_DIV
972926deccbSFrançois Tigeot 				 * setting.
973926deccbSFrançois Tigeot 				 */
974926deccbSFrançois Tigeot 				WREG32_PLL_P(RADEON_PPLL_REF_DIV,
975926deccbSFrançois Tigeot 					     pll_ref_div,
976926deccbSFrançois Tigeot 					     0);
977926deccbSFrançois Tigeot 			} else {
978926deccbSFrançois Tigeot 				/* R300 uses ref_div_acc field as real ref divider */
979926deccbSFrançois Tigeot 				WREG32_PLL_P(RADEON_PPLL_REF_DIV,
980926deccbSFrançois Tigeot 					     (pll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT),
981926deccbSFrançois Tigeot 					     ~R300_PPLL_REF_DIV_ACC_MASK);
982926deccbSFrançois Tigeot 			}
983926deccbSFrançois Tigeot 		} else
984926deccbSFrançois Tigeot 			WREG32_PLL_P(RADEON_PPLL_REF_DIV,
985926deccbSFrançois Tigeot 				     pll_ref_div,
986926deccbSFrançois Tigeot 				     ~RADEON_PPLL_REF_DIV_MASK);
987926deccbSFrançois Tigeot 
988926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_PPLL_DIV_3,
989926deccbSFrançois Tigeot 			     pll_fb_post_div,
990926deccbSFrançois Tigeot 			     ~RADEON_PPLL_FB3_DIV_MASK);
991926deccbSFrançois Tigeot 
992926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_PPLL_DIV_3,
993926deccbSFrançois Tigeot 			     pll_fb_post_div,
994926deccbSFrançois Tigeot 			     ~RADEON_PPLL_POST3_DIV_MASK);
995926deccbSFrançois Tigeot 
996926deccbSFrançois Tigeot 		radeon_pll_write_update(dev);
997926deccbSFrançois Tigeot 		radeon_pll_wait_for_read_update_complete(dev);
998926deccbSFrançois Tigeot 
999926deccbSFrançois Tigeot 		WREG32_PLL(RADEON_HTOTAL_CNTL, htotal_cntl);
1000926deccbSFrançois Tigeot 
1001926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_PPLL_CNTL,
1002926deccbSFrançois Tigeot 			     0,
1003926deccbSFrançois Tigeot 			     ~(RADEON_PPLL_RESET
1004926deccbSFrançois Tigeot 			       | RADEON_PPLL_SLEEP
1005926deccbSFrançois Tigeot 			       | RADEON_PPLL_ATOMIC_UPDATE_EN
1006926deccbSFrançois Tigeot 			       | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN));
1007926deccbSFrançois Tigeot 
1008926deccbSFrançois Tigeot 		DRM_DEBUG_KMS("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n",
1009926deccbSFrançois Tigeot 			  pll_ref_div,
1010926deccbSFrançois Tigeot 			  pll_fb_post_div,
1011926deccbSFrançois Tigeot 			  (unsigned)htotal_cntl,
1012926deccbSFrançois Tigeot 			  RREG32_PLL(RADEON_PPLL_CNTL));
1013926deccbSFrançois Tigeot 		DRM_DEBUG_KMS("Wrote: rd=%d, fd=%d, pd=%d\n",
1014926deccbSFrançois Tigeot 			  pll_ref_div & RADEON_PPLL_REF_DIV_MASK,
1015926deccbSFrançois Tigeot 			  pll_fb_post_div & RADEON_PPLL_FB3_DIV_MASK,
1016926deccbSFrançois Tigeot 			  (pll_fb_post_div & RADEON_PPLL_POST3_DIV_MASK) >> 16);
1017926deccbSFrançois Tigeot 
1018c4ef309bSzrj 		mdelay(50); /* Let the clock to lock */
1019926deccbSFrançois Tigeot 
1020926deccbSFrançois Tigeot 		WREG32_PLL_P(RADEON_VCLK_ECP_CNTL,
1021926deccbSFrançois Tigeot 			     RADEON_VCLK_SRC_SEL_PPLLCLK,
1022926deccbSFrançois Tigeot 			     ~(RADEON_VCLK_SRC_SEL_MASK));
1023926deccbSFrançois Tigeot 
1024926deccbSFrançois Tigeot 		if (is_tv)
1025926deccbSFrançois Tigeot 			WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
1026926deccbSFrançois Tigeot 	}
1027926deccbSFrançois Tigeot }
1028926deccbSFrançois Tigeot 
radeon_crtc_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)1029926deccbSFrançois Tigeot static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc,
1030926deccbSFrançois Tigeot 				   const struct drm_display_mode *mode,
1031926deccbSFrançois Tigeot 				   struct drm_display_mode *adjusted_mode)
1032926deccbSFrançois Tigeot {
1033926deccbSFrançois Tigeot 	if (!radeon_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode))
1034926deccbSFrançois Tigeot 		return false;
1035926deccbSFrançois Tigeot 	return true;
1036926deccbSFrançois Tigeot }
1037926deccbSFrançois Tigeot 
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)1038926deccbSFrançois Tigeot static int radeon_crtc_mode_set(struct drm_crtc *crtc,
1039926deccbSFrançois Tigeot 				 struct drm_display_mode *mode,
1040926deccbSFrançois Tigeot 				 struct drm_display_mode *adjusted_mode,
1041926deccbSFrançois Tigeot 				 int x, int y, struct drm_framebuffer *old_fb)
1042926deccbSFrançois Tigeot {
1043926deccbSFrançois Tigeot 	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
1044926deccbSFrançois Tigeot 
1045926deccbSFrançois Tigeot 	/* TODO TV */
1046926deccbSFrançois Tigeot 	radeon_crtc_set_base(crtc, x, y, old_fb);
1047926deccbSFrançois Tigeot 	radeon_set_crtc_timing(crtc, adjusted_mode);
1048926deccbSFrançois Tigeot 	radeon_set_pll(crtc, adjusted_mode);
1049926deccbSFrançois Tigeot 	radeon_overscan_setup(crtc, adjusted_mode);
1050926deccbSFrançois Tigeot 	if (radeon_crtc->crtc_id == 0) {
1051926deccbSFrançois Tigeot 		radeon_legacy_rmx_mode_set(crtc, adjusted_mode);
1052926deccbSFrançois Tigeot 	} else {
1053926deccbSFrançois Tigeot 		if (radeon_crtc->rmx_type != RMX_OFF) {
1054926deccbSFrançois Tigeot 			/* FIXME: only first crtc has rmx what should we
1055926deccbSFrançois Tigeot 			 * do ?
1056926deccbSFrançois Tigeot 			 */
1057926deccbSFrançois Tigeot 			DRM_ERROR("Mode need scaling but only first crtc can do that.\n");
1058926deccbSFrançois Tigeot 		}
1059926deccbSFrançois Tigeot 	}
10607dcf36dcSFrançois Tigeot 	radeon_cursor_reset(crtc);
1061926deccbSFrançois Tigeot 	return 0;
1062926deccbSFrançois Tigeot }
1063926deccbSFrançois Tigeot 
radeon_crtc_prepare(struct drm_crtc * crtc)1064926deccbSFrançois Tigeot static void radeon_crtc_prepare(struct drm_crtc *crtc)
1065926deccbSFrançois Tigeot {
1066926deccbSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
1067926deccbSFrançois Tigeot 	struct drm_crtc *crtci;
1068926deccbSFrançois Tigeot 
1069926deccbSFrançois Tigeot 	/*
1070926deccbSFrançois Tigeot 	* The hardware wedges sometimes if you reconfigure one CRTC
1071926deccbSFrançois Tigeot 	* whilst another is running (see fdo bug #24611).
1072926deccbSFrançois Tigeot 	*/
1073926deccbSFrançois Tigeot 	list_for_each_entry(crtci, &dev->mode_config.crtc_list, head)
1074926deccbSFrançois Tigeot 		radeon_crtc_dpms(crtci, DRM_MODE_DPMS_OFF);
1075926deccbSFrançois Tigeot }
1076926deccbSFrançois Tigeot 
radeon_crtc_commit(struct drm_crtc * crtc)1077926deccbSFrançois Tigeot static void radeon_crtc_commit(struct drm_crtc *crtc)
1078926deccbSFrançois Tigeot {
1079926deccbSFrançois Tigeot 	struct drm_device *dev = crtc->dev;
1080926deccbSFrançois Tigeot 	struct drm_crtc *crtci;
1081926deccbSFrançois Tigeot 
1082926deccbSFrançois Tigeot 	/*
1083926deccbSFrançois Tigeot 	* Reenable the CRTCs that should be running.
1084926deccbSFrançois Tigeot 	*/
1085926deccbSFrançois Tigeot 	list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) {
1086926deccbSFrançois Tigeot 		if (crtci->enabled)
1087926deccbSFrançois Tigeot 			radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON);
1088926deccbSFrançois Tigeot 	}
1089926deccbSFrançois Tigeot }
1090926deccbSFrançois Tigeot 
radeon_crtc_disable(struct drm_crtc * crtc)1091c6f73aabSFrançois Tigeot static void radeon_crtc_disable(struct drm_crtc *crtc)
1092c6f73aabSFrançois Tigeot {
1093c6f73aabSFrançois Tigeot 	radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
1094c6f73aabSFrançois Tigeot 	if (crtc->primary->fb) {
1095c6f73aabSFrançois Tigeot 		int r;
1096c6f73aabSFrançois Tigeot 		struct radeon_framebuffer *radeon_fb;
1097c6f73aabSFrançois Tigeot 		struct radeon_bo *rbo;
1098c6f73aabSFrançois Tigeot 
1099c6f73aabSFrançois Tigeot 		radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
1100c6f73aabSFrançois Tigeot 		rbo = gem_to_radeon_bo(radeon_fb->obj);
1101c6f73aabSFrançois Tigeot 		r = radeon_bo_reserve(rbo, false);
1102c6f73aabSFrançois Tigeot 		if (unlikely(r))
1103c6f73aabSFrançois Tigeot 			DRM_ERROR("failed to reserve rbo before unpin\n");
1104c6f73aabSFrançois Tigeot 		else {
1105c6f73aabSFrançois Tigeot 			radeon_bo_unpin(rbo);
1106c6f73aabSFrançois Tigeot 			radeon_bo_unreserve(rbo);
1107c6f73aabSFrançois Tigeot 		}
1108c6f73aabSFrançois Tigeot 	}
1109c6f73aabSFrançois Tigeot }
1110c6f73aabSFrançois Tigeot 
1111926deccbSFrançois Tigeot static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
1112926deccbSFrançois Tigeot 	.dpms = radeon_crtc_dpms,
1113926deccbSFrançois Tigeot 	.mode_fixup = radeon_crtc_mode_fixup,
1114926deccbSFrançois Tigeot 	.mode_set = radeon_crtc_mode_set,
1115926deccbSFrançois Tigeot 	.mode_set_base = radeon_crtc_set_base,
1116926deccbSFrançois Tigeot 	.mode_set_base_atomic = radeon_crtc_set_base_atomic,
1117926deccbSFrançois Tigeot 	.prepare = radeon_crtc_prepare,
1118926deccbSFrançois Tigeot 	.commit = radeon_crtc_commit,
1119c6f73aabSFrançois Tigeot 	.disable = radeon_crtc_disable
1120926deccbSFrançois Tigeot };
1121926deccbSFrançois Tigeot 
1122926deccbSFrançois Tigeot 
radeon_legacy_init_crtc(struct drm_device * dev,struct radeon_crtc * radeon_crtc)1123926deccbSFrançois Tigeot void radeon_legacy_init_crtc(struct drm_device *dev,
1124926deccbSFrançois Tigeot 			       struct radeon_crtc *radeon_crtc)
1125926deccbSFrançois Tigeot {
1126926deccbSFrançois Tigeot 	if (radeon_crtc->crtc_id == 1)
1127926deccbSFrançois Tigeot 		radeon_crtc->crtc_offset = RADEON_CRTC2_H_TOTAL_DISP - RADEON_CRTC_H_TOTAL_DISP;
1128926deccbSFrançois Tigeot 	drm_crtc_helper_add(&radeon_crtc->base, &legacy_helper_funcs);
1129926deccbSFrançois Tigeot }
1130