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