1254885Sdumbbell/*
2254885Sdumbbell * Copyright 2007-8 Advanced Micro Devices, Inc.
3254885Sdumbbell * Copyright 2008 Red Hat Inc.
4254885Sdumbbell *
5254885Sdumbbell * Permission is hereby granted, free of charge, to any person obtaining a
6254885Sdumbbell * copy of this software and associated documentation files (the "Software"),
7254885Sdumbbell * to deal in the Software without restriction, including without limitation
8254885Sdumbbell * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9254885Sdumbbell * and/or sell copies of the Software, and to permit persons to whom the
10254885Sdumbbell * Software is furnished to do so, subject to the following conditions:
11254885Sdumbbell *
12254885Sdumbbell * The above copyright notice and this permission notice shall be included in
13254885Sdumbbell * all copies or substantial portions of the Software.
14254885Sdumbbell *
15254885Sdumbbell * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16254885Sdumbbell * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17254885Sdumbbell * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18254885Sdumbbell * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19254885Sdumbbell * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20254885Sdumbbell * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21254885Sdumbbell * OTHER DEALINGS IN THE SOFTWARE.
22254885Sdumbbell *
23254885Sdumbbell * Authors: Dave Airlie
24254885Sdumbbell *          Alex Deucher
25254885Sdumbbell */
26254885Sdumbbell
27254885Sdumbbell#include <sys/cdefs.h>
28254885Sdumbbell__FBSDID("$FreeBSD: releng/10.3/sys/dev/drm2/radeon/radeon_cursor.c 254885 2013-08-25 19:37:15Z dumbbell $");
29254885Sdumbbell
30254885Sdumbbell#include <dev/drm2/drmP.h>
31254885Sdumbbell#include <dev/drm2/radeon/radeon_drm.h>
32254885Sdumbbell#include "radeon.h"
33254885Sdumbbell
34254885Sdumbbell#define CURSOR_WIDTH 64
35254885Sdumbbell#define CURSOR_HEIGHT 64
36254885Sdumbbell
37254885Sdumbbellstatic void radeon_lock_cursor(struct drm_crtc *crtc, bool lock)
38254885Sdumbbell{
39254885Sdumbbell	struct radeon_device *rdev = crtc->dev->dev_private;
40254885Sdumbbell	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
41254885Sdumbbell	uint32_t cur_lock;
42254885Sdumbbell
43254885Sdumbbell	if (ASIC_IS_DCE4(rdev)) {
44254885Sdumbbell		cur_lock = RREG32(EVERGREEN_CUR_UPDATE + radeon_crtc->crtc_offset);
45254885Sdumbbell		if (lock)
46254885Sdumbbell			cur_lock |= EVERGREEN_CURSOR_UPDATE_LOCK;
47254885Sdumbbell		else
48254885Sdumbbell			cur_lock &= ~EVERGREEN_CURSOR_UPDATE_LOCK;
49254885Sdumbbell		WREG32(EVERGREEN_CUR_UPDATE + radeon_crtc->crtc_offset, cur_lock);
50254885Sdumbbell	} else if (ASIC_IS_AVIVO(rdev)) {
51254885Sdumbbell		cur_lock = RREG32(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset);
52254885Sdumbbell		if (lock)
53254885Sdumbbell			cur_lock |= AVIVO_D1CURSOR_UPDATE_LOCK;
54254885Sdumbbell		else
55254885Sdumbbell			cur_lock &= ~AVIVO_D1CURSOR_UPDATE_LOCK;
56254885Sdumbbell		WREG32(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset, cur_lock);
57254885Sdumbbell	} else {
58254885Sdumbbell		cur_lock = RREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset);
59254885Sdumbbell		if (lock)
60254885Sdumbbell			cur_lock |= RADEON_CUR_LOCK;
61254885Sdumbbell		else
62254885Sdumbbell			cur_lock &= ~RADEON_CUR_LOCK;
63254885Sdumbbell		WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, cur_lock);
64254885Sdumbbell	}
65254885Sdumbbell}
66254885Sdumbbell
67254885Sdumbbellstatic void radeon_hide_cursor(struct drm_crtc *crtc)
68254885Sdumbbell{
69254885Sdumbbell	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
70254885Sdumbbell	struct radeon_device *rdev = crtc->dev->dev_private;
71254885Sdumbbell
72254885Sdumbbell	if (ASIC_IS_DCE4(rdev)) {
73254885Sdumbbell		WREG32_IDX(EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset,
74254885Sdumbbell			   EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) |
75254885Sdumbbell			   EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2));
76254885Sdumbbell	} else if (ASIC_IS_AVIVO(rdev)) {
77254885Sdumbbell		WREG32_IDX(AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset,
78254885Sdumbbell			   (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT));
79254885Sdumbbell	} else {
80254885Sdumbbell		u32 reg;
81254885Sdumbbell		switch (radeon_crtc->crtc_id) {
82254885Sdumbbell		case 0:
83254885Sdumbbell			reg = RADEON_CRTC_GEN_CNTL;
84254885Sdumbbell			break;
85254885Sdumbbell		case 1:
86254885Sdumbbell			reg = RADEON_CRTC2_GEN_CNTL;
87254885Sdumbbell			break;
88254885Sdumbbell		default:
89254885Sdumbbell			return;
90254885Sdumbbell		}
91254885Sdumbbell		WREG32_IDX(reg, RREG32_IDX(reg) & ~RADEON_CRTC_CUR_EN);
92254885Sdumbbell	}
93254885Sdumbbell}
94254885Sdumbbell
95254885Sdumbbellstatic void radeon_show_cursor(struct drm_crtc *crtc)
96254885Sdumbbell{
97254885Sdumbbell	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
98254885Sdumbbell	struct radeon_device *rdev = crtc->dev->dev_private;
99254885Sdumbbell
100254885Sdumbbell	if (ASIC_IS_DCE4(rdev)) {
101254885Sdumbbell		WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset);
102254885Sdumbbell		WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_EN |
103254885Sdumbbell		       EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) |
104254885Sdumbbell		       EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2));
105254885Sdumbbell	} else if (ASIC_IS_AVIVO(rdev)) {
106254885Sdumbbell		WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset);
107254885Sdumbbell		WREG32(RADEON_MM_DATA, AVIVO_D1CURSOR_EN |
108254885Sdumbbell		       (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT));
109254885Sdumbbell	} else {
110254885Sdumbbell		switch (radeon_crtc->crtc_id) {
111254885Sdumbbell		case 0:
112254885Sdumbbell			WREG32(RADEON_MM_INDEX, RADEON_CRTC_GEN_CNTL);
113254885Sdumbbell			break;
114254885Sdumbbell		case 1:
115254885Sdumbbell			WREG32(RADEON_MM_INDEX, RADEON_CRTC2_GEN_CNTL);
116254885Sdumbbell			break;
117254885Sdumbbell		default:
118254885Sdumbbell			return;
119254885Sdumbbell		}
120254885Sdumbbell
121254885Sdumbbell		WREG32_P(RADEON_MM_DATA, (RADEON_CRTC_CUR_EN |
122254885Sdumbbell					  (RADEON_CRTC_CUR_MODE_24BPP << RADEON_CRTC_CUR_MODE_SHIFT)),
123254885Sdumbbell			 ~(RADEON_CRTC_CUR_EN | RADEON_CRTC_CUR_MODE_MASK));
124254885Sdumbbell	}
125254885Sdumbbell}
126254885Sdumbbell
127254885Sdumbbellstatic void radeon_set_cursor(struct drm_crtc *crtc, struct drm_gem_object *obj,
128254885Sdumbbell			      uint64_t gpu_addr)
129254885Sdumbbell{
130254885Sdumbbell	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
131254885Sdumbbell	struct radeon_device *rdev = crtc->dev->dev_private;
132254885Sdumbbell
133254885Sdumbbell	if (ASIC_IS_DCE4(rdev)) {
134254885Sdumbbell		WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset,
135254885Sdumbbell		       upper_32_bits(gpu_addr));
136254885Sdumbbell		WREG32(EVERGREEN_CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset,
137254885Sdumbbell		       gpu_addr & 0xffffffff);
138254885Sdumbbell	} else if (ASIC_IS_AVIVO(rdev)) {
139254885Sdumbbell		if (rdev->family >= CHIP_RV770) {
140254885Sdumbbell			if (radeon_crtc->crtc_id)
141254885Sdumbbell				WREG32(R700_D2CUR_SURFACE_ADDRESS_HIGH, upper_32_bits(gpu_addr));
142254885Sdumbbell			else
143254885Sdumbbell				WREG32(R700_D1CUR_SURFACE_ADDRESS_HIGH, upper_32_bits(gpu_addr));
144254885Sdumbbell		}
145254885Sdumbbell		WREG32(AVIVO_D1CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset,
146254885Sdumbbell		       gpu_addr & 0xffffffff);
147254885Sdumbbell	} else {
148254885Sdumbbell		radeon_crtc->legacy_cursor_offset = gpu_addr - radeon_crtc->legacy_display_base_addr;
149254885Sdumbbell		/* offset is from DISP(2)_BASE_ADDRESS */
150254885Sdumbbell		WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, radeon_crtc->legacy_cursor_offset);
151254885Sdumbbell	}
152254885Sdumbbell}
153254885Sdumbbell
154254885Sdumbbellint radeon_crtc_cursor_set(struct drm_crtc *crtc,
155254885Sdumbbell			   struct drm_file *file_priv,
156254885Sdumbbell			   uint32_t handle,
157254885Sdumbbell			   uint32_t width,
158254885Sdumbbell			   uint32_t height)
159254885Sdumbbell{
160254885Sdumbbell	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
161254885Sdumbbell	struct radeon_device *rdev = crtc->dev->dev_private;
162254885Sdumbbell	struct drm_gem_object *obj;
163254885Sdumbbell	struct radeon_bo *robj;
164254885Sdumbbell	uint64_t gpu_addr;
165254885Sdumbbell	int ret;
166254885Sdumbbell
167254885Sdumbbell	if (!handle) {
168254885Sdumbbell		/* turn off cursor */
169254885Sdumbbell		radeon_hide_cursor(crtc);
170254885Sdumbbell		obj = NULL;
171254885Sdumbbell		goto unpin;
172254885Sdumbbell	}
173254885Sdumbbell
174254885Sdumbbell	if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) {
175254885Sdumbbell		DRM_ERROR("bad cursor width or height %d x %d\n", width, height);
176254885Sdumbbell		return -EINVAL;
177254885Sdumbbell	}
178254885Sdumbbell
179254885Sdumbbell	obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
180254885Sdumbbell	if (!obj) {
181254885Sdumbbell		DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id);
182254885Sdumbbell		return -ENOENT;
183254885Sdumbbell	}
184254885Sdumbbell
185254885Sdumbbell	robj = gem_to_radeon_bo(obj);
186254885Sdumbbell	ret = radeon_bo_reserve(robj, false);
187254885Sdumbbell	if (unlikely(ret != 0))
188254885Sdumbbell		goto fail;
189254885Sdumbbell	/* Only 27 bit offset for legacy cursor */
190254885Sdumbbell	ret = radeon_bo_pin_restricted(robj, RADEON_GEM_DOMAIN_VRAM,
191254885Sdumbbell				       ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27,
192254885Sdumbbell				       &gpu_addr);
193254885Sdumbbell	radeon_bo_unreserve(robj);
194254885Sdumbbell	if (ret)
195254885Sdumbbell		goto fail;
196254885Sdumbbell
197254885Sdumbbell	radeon_crtc->cursor_width = width;
198254885Sdumbbell	radeon_crtc->cursor_height = height;
199254885Sdumbbell
200254885Sdumbbell	radeon_lock_cursor(crtc, true);
201254885Sdumbbell	radeon_set_cursor(crtc, obj, gpu_addr);
202254885Sdumbbell	radeon_show_cursor(crtc);
203254885Sdumbbell	radeon_lock_cursor(crtc, false);
204254885Sdumbbell
205254885Sdumbbellunpin:
206254885Sdumbbell	if (radeon_crtc->cursor_bo) {
207254885Sdumbbell		robj = gem_to_radeon_bo(radeon_crtc->cursor_bo);
208254885Sdumbbell		ret = radeon_bo_reserve(robj, false);
209254885Sdumbbell		if (likely(ret == 0)) {
210254885Sdumbbell			radeon_bo_unpin(robj);
211254885Sdumbbell			radeon_bo_unreserve(robj);
212254885Sdumbbell		}
213254885Sdumbbell		drm_gem_object_unreference_unlocked(radeon_crtc->cursor_bo);
214254885Sdumbbell	}
215254885Sdumbbell
216254885Sdumbbell	radeon_crtc->cursor_bo = obj;
217254885Sdumbbell	return 0;
218254885Sdumbbellfail:
219254885Sdumbbell	drm_gem_object_unreference_unlocked(obj);
220254885Sdumbbell
221254885Sdumbbell	return ret;
222254885Sdumbbell}
223254885Sdumbbell
224254885Sdumbbellint radeon_crtc_cursor_move(struct drm_crtc *crtc,
225254885Sdumbbell			    int x, int y)
226254885Sdumbbell{
227254885Sdumbbell	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
228254885Sdumbbell	struct radeon_device *rdev = crtc->dev->dev_private;
229254885Sdumbbell	int xorigin = 0, yorigin = 0;
230254885Sdumbbell	int w = radeon_crtc->cursor_width;
231254885Sdumbbell
232254885Sdumbbell	if (ASIC_IS_AVIVO(rdev)) {
233254885Sdumbbell		/* avivo cursor are offset into the total surface */
234254885Sdumbbell		x += crtc->x;
235254885Sdumbbell		y += crtc->y;
236254885Sdumbbell	}
237254885Sdumbbell	DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
238254885Sdumbbell
239254885Sdumbbell	if (x < 0) {
240254885Sdumbbell		xorigin = min(-x, CURSOR_WIDTH - 1);
241254885Sdumbbell		x = 0;
242254885Sdumbbell	}
243254885Sdumbbell	if (y < 0) {
244254885Sdumbbell		yorigin = min(-y, CURSOR_HEIGHT - 1);
245254885Sdumbbell		y = 0;
246254885Sdumbbell	}
247254885Sdumbbell
248254885Sdumbbell	/* fixed on DCE6 and newer */
249254885Sdumbbell	if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE6(rdev)) {
250254885Sdumbbell		int i = 0;
251254885Sdumbbell		struct drm_crtc *crtc_p;
252254885Sdumbbell
253254885Sdumbbell		/* avivo cursor image can't end on 128 pixel boundary or
254254885Sdumbbell		 * go past the end of the frame if both crtcs are enabled
255254885Sdumbbell		 */
256254885Sdumbbell		list_for_each_entry(crtc_p, &crtc->dev->mode_config.crtc_list, head) {
257254885Sdumbbell			if (crtc_p->enabled)
258254885Sdumbbell				i++;
259254885Sdumbbell		}
260254885Sdumbbell		if (i > 1) {
261254885Sdumbbell			int cursor_end, frame_end;
262254885Sdumbbell
263254885Sdumbbell			cursor_end = x - xorigin + w;
264254885Sdumbbell			frame_end = crtc->x + crtc->mode.crtc_hdisplay;
265254885Sdumbbell			if (cursor_end >= frame_end) {
266254885Sdumbbell				w = w - (cursor_end - frame_end);
267254885Sdumbbell				if (!(frame_end & 0x7f))
268254885Sdumbbell					w--;
269254885Sdumbbell			} else {
270254885Sdumbbell				if (!(cursor_end & 0x7f))
271254885Sdumbbell					w--;
272254885Sdumbbell			}
273254885Sdumbbell			if (w <= 0) {
274254885Sdumbbell				w = 1;
275254885Sdumbbell				cursor_end = x - xorigin + w;
276254885Sdumbbell				if (!(cursor_end & 0x7f)) {
277254885Sdumbbell					x--;
278254885Sdumbbell					if (x < 0) {
279254885Sdumbbell						DRM_ERROR("%s: x(%d) < 0", __func__, x);
280254885Sdumbbell					}
281254885Sdumbbell				}
282254885Sdumbbell			}
283254885Sdumbbell		}
284254885Sdumbbell	}
285254885Sdumbbell
286254885Sdumbbell	radeon_lock_cursor(crtc, true);
287254885Sdumbbell	if (ASIC_IS_DCE4(rdev)) {
288254885Sdumbbell		WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y);
289254885Sdumbbell		WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin);
290254885Sdumbbell		WREG32(EVERGREEN_CUR_SIZE + radeon_crtc->crtc_offset,
291254885Sdumbbell		       ((w - 1) << 16) | (radeon_crtc->cursor_height - 1));
292254885Sdumbbell	} else if (ASIC_IS_AVIVO(rdev)) {
293254885Sdumbbell		WREG32(AVIVO_D1CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y);
294254885Sdumbbell		WREG32(AVIVO_D1CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin);
295254885Sdumbbell		WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset,
296254885Sdumbbell		       ((w - 1) << 16) | (radeon_crtc->cursor_height - 1));
297254885Sdumbbell	} else {
298254885Sdumbbell		if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)
299254885Sdumbbell			y *= 2;
300254885Sdumbbell
301254885Sdumbbell		WREG32(RADEON_CUR_HORZ_VERT_OFF + radeon_crtc->crtc_offset,
302254885Sdumbbell		       (RADEON_CUR_LOCK
303254885Sdumbbell			| (xorigin << 16)
304254885Sdumbbell			| yorigin));
305254885Sdumbbell		WREG32(RADEON_CUR_HORZ_VERT_POSN + radeon_crtc->crtc_offset,
306254885Sdumbbell		       (RADEON_CUR_LOCK
307254885Sdumbbell			| (x << 16)
308254885Sdumbbell			| y));
309254885Sdumbbell		/* offset is from DISP(2)_BASE_ADDRESS */
310254885Sdumbbell		WREG32(RADEON_CUR_OFFSET + radeon_crtc->crtc_offset, (radeon_crtc->legacy_cursor_offset +
311254885Sdumbbell								      (yorigin * 256)));
312254885Sdumbbell	}
313254885Sdumbbell	radeon_lock_cursor(crtc, false);
314254885Sdumbbell
315254885Sdumbbell	return 0;
316254885Sdumbbell}
317