drmmode_display.c revision 35d5b7c7
1d6c0b56eSmrg/*
2d6c0b56eSmrg * Copyright © 2007 Red Hat, Inc.
3d6c0b56eSmrg *
4d6c0b56eSmrg * Permission is hereby granted, free of charge, to any person obtaining a
5d6c0b56eSmrg * copy of this software and associated documentation files (the "Software"),
6d6c0b56eSmrg * to deal in the Software without restriction, including without limitation
7d6c0b56eSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8d6c0b56eSmrg * and/or sell copies of the Software, and to permit persons to whom the
9d6c0b56eSmrg * Software is furnished to do so, subject to the following conditions:
10d6c0b56eSmrg *
11d6c0b56eSmrg * The above copyright notice and this permission notice (including the next
12d6c0b56eSmrg * paragraph) shall be included in all copies or substantial portions of the
13d6c0b56eSmrg * Software.
14d6c0b56eSmrg *
15d6c0b56eSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16d6c0b56eSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17d6c0b56eSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18d6c0b56eSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19d6c0b56eSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20d6c0b56eSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21d6c0b56eSmrg * SOFTWARE.
22d6c0b56eSmrg *
23d6c0b56eSmrg * Authors:
24d6c0b56eSmrg *    Dave Airlie <airlied@redhat.com>
25d6c0b56eSmrg *
26d6c0b56eSmrg */
27d6c0b56eSmrg
28d6c0b56eSmrg#ifdef HAVE_CONFIG_H
29d6c0b56eSmrg#include "config.h"
30d6c0b56eSmrg#endif
31d6c0b56eSmrg
32d6c0b56eSmrg#include <errno.h>
33d6c0b56eSmrg#include <sys/ioctl.h>
34d6c0b56eSmrg#include <time.h>
35d6c0b56eSmrg#include "cursorstr.h"
36d6c0b56eSmrg#include "damagestr.h"
3724b90cf4Smrg#include "inputstr.h"
3824b90cf4Smrg#include "list.h"
39d6c0b56eSmrg#include "micmap.h"
4035d5b7c7Smrg#include "mipointrst.h"
41d6c0b56eSmrg#include "xf86cmap.h"
42504d986fSmrg#include "xf86Priv.h"
43d6c0b56eSmrg#include "sarea.h"
44d6c0b56eSmrg
45d6c0b56eSmrg#include "drmmode_display.h"
46d6c0b56eSmrg#include "amdgpu_bo_helper.h"
47d6c0b56eSmrg#include "amdgpu_glamor.h"
48d6c0b56eSmrg#include "amdgpu_pixmap.h"
49d6c0b56eSmrg
50d6c0b56eSmrg#include <dri.h>
51d6c0b56eSmrg
52d6c0b56eSmrg/* DPMS */
53d6c0b56eSmrg#ifdef HAVE_XEXTPROTO_71
54d6c0b56eSmrg#include <X11/extensions/dpmsconst.h>
55d6c0b56eSmrg#else
56d6c0b56eSmrg#define DPMS_SERVER
57d6c0b56eSmrg#include <X11/extensions/dpms.h>
58d6c0b56eSmrg#endif
59d6c0b56eSmrg
60d6c0b56eSmrg#include <gbm.h>
61d6c0b56eSmrg
62d6c0b56eSmrg#define DEFAULT_NOMINAL_FRAME_RATE 60
63d6c0b56eSmrg
64d6c0b56eSmrgstatic Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height);
65d6c0b56eSmrg
66d6c0b56eSmrgstatic Bool
67d6c0b56eSmrgAMDGPUZaphodStringMatches(ScrnInfoPtr pScrn, const char *s, char *output_name)
68d6c0b56eSmrg{
69d6c0b56eSmrg	int i = 0;
70d6c0b56eSmrg	char s1[20];
71d6c0b56eSmrg
72d6c0b56eSmrg	do {
73d6c0b56eSmrg		switch (*s) {
74d6c0b56eSmrg		case ',':
75d6c0b56eSmrg			s1[i] = '\0';
76d6c0b56eSmrg			i = 0;
77d6c0b56eSmrg			if (strcmp(s1, output_name) == 0)
78d6c0b56eSmrg				return TRUE;
79d6c0b56eSmrg			break;
80d6c0b56eSmrg		case ' ':
81d6c0b56eSmrg		case '\t':
82d6c0b56eSmrg		case '\n':
83d6c0b56eSmrg		case '\r':
84d6c0b56eSmrg			break;
85d6c0b56eSmrg		default:
86d6c0b56eSmrg			s1[i] = *s;
87d6c0b56eSmrg			i++;
88d6c0b56eSmrg			break;
89d6c0b56eSmrg		}
90d6c0b56eSmrg	} while (*s++);
91d6c0b56eSmrg
92d6c0b56eSmrg	s1[i] = '\0';
93d6c0b56eSmrg	if (strcmp(s1, output_name) == 0)
94d6c0b56eSmrg		return TRUE;
95d6c0b56eSmrg
96d6c0b56eSmrg	return FALSE;
97d6c0b56eSmrg}
98d6c0b56eSmrg
9924b90cf4Smrg
100d6c0b56eSmrgstatic PixmapPtr drmmode_create_bo_pixmap(ScrnInfoPtr pScrn,
101d6c0b56eSmrg					  int width, int height,
102d6c0b56eSmrg					  int depth, int bpp,
103d6c0b56eSmrg					  int pitch,
104d6c0b56eSmrg					  struct amdgpu_buffer *bo)
105d6c0b56eSmrg{
106d6c0b56eSmrg	ScreenPtr pScreen = pScrn->pScreen;
107d6c0b56eSmrg	PixmapPtr pixmap;
108d6c0b56eSmrg
109d6c0b56eSmrg	pixmap = (*pScreen->CreatePixmap)(pScreen, 0, 0, depth,
110d6c0b56eSmrg					  AMDGPU_CREATE_PIXMAP_SCANOUT);
111d6c0b56eSmrg	if (!pixmap)
112d6c0b56eSmrg		return NULL;
113d6c0b56eSmrg
114d6c0b56eSmrg	if (!(*pScreen->ModifyPixmapHeader) (pixmap, width, height,
115504d986fSmrg					     depth, bpp, pitch, NULL))
116504d986fSmrg		goto fail;
117d6c0b56eSmrg
118504d986fSmrg	if (!amdgpu_glamor_create_textured_pixmap(pixmap, bo))
119504d986fSmrg		goto fail;
120d6c0b56eSmrg
121504d986fSmrg	if (amdgpu_set_pixmap_bo(pixmap, bo))
122504d986fSmrg		return pixmap;
123d6c0b56eSmrg
124504d986fSmrgfail:
125504d986fSmrg	pScreen->DestroyPixmap(pixmap);
126504d986fSmrg	return NULL;
127d6c0b56eSmrg}
128d6c0b56eSmrg
129d6c0b56eSmrgstatic void drmmode_destroy_bo_pixmap(PixmapPtr pixmap)
130d6c0b56eSmrg{
131d6c0b56eSmrg	ScreenPtr pScreen = pixmap->drawable.pScreen;
132d6c0b56eSmrg
133d6c0b56eSmrg	(*pScreen->DestroyPixmap) (pixmap);
134d6c0b56eSmrg}
135d6c0b56eSmrg
136d6c0b56eSmrgstatic void
137d6c0b56eSmrgdrmmode_ConvertFromKMode(ScrnInfoPtr scrn,
138d6c0b56eSmrg			 drmModeModeInfo * kmode, DisplayModePtr mode)
139d6c0b56eSmrg{
140d6c0b56eSmrg	memset(mode, 0, sizeof(DisplayModeRec));
141d6c0b56eSmrg	mode->status = MODE_OK;
142d6c0b56eSmrg
143d6c0b56eSmrg	mode->Clock = kmode->clock;
144d6c0b56eSmrg
145d6c0b56eSmrg	mode->HDisplay = kmode->hdisplay;
146d6c0b56eSmrg	mode->HSyncStart = kmode->hsync_start;
147d6c0b56eSmrg	mode->HSyncEnd = kmode->hsync_end;
148d6c0b56eSmrg	mode->HTotal = kmode->htotal;
149d6c0b56eSmrg	mode->HSkew = kmode->hskew;
150d6c0b56eSmrg
151d6c0b56eSmrg	mode->VDisplay = kmode->vdisplay;
152d6c0b56eSmrg	mode->VSyncStart = kmode->vsync_start;
153d6c0b56eSmrg	mode->VSyncEnd = kmode->vsync_end;
154d6c0b56eSmrg	mode->VTotal = kmode->vtotal;
155d6c0b56eSmrg	mode->VScan = kmode->vscan;
156d6c0b56eSmrg
157d6c0b56eSmrg	mode->Flags = kmode->flags;	//& FLAG_BITS;
158d6c0b56eSmrg	mode->name = strdup(kmode->name);
159d6c0b56eSmrg
160d6c0b56eSmrg	if (kmode->type & DRM_MODE_TYPE_DRIVER)
161d6c0b56eSmrg		mode->type = M_T_DRIVER;
162d6c0b56eSmrg	if (kmode->type & DRM_MODE_TYPE_PREFERRED)
163d6c0b56eSmrg		mode->type |= M_T_PREFERRED;
164d6c0b56eSmrg	xf86SetModeCrtc(mode, scrn->adjustFlags);
165d6c0b56eSmrg}
166d6c0b56eSmrg
167d6c0b56eSmrgstatic void
168d6c0b56eSmrgdrmmode_ConvertToKMode(ScrnInfoPtr scrn,
169d6c0b56eSmrg		       drmModeModeInfo * kmode, DisplayModePtr mode)
170d6c0b56eSmrg{
171d6c0b56eSmrg	memset(kmode, 0, sizeof(*kmode));
172d6c0b56eSmrg
173d6c0b56eSmrg	kmode->clock = mode->Clock;
174d6c0b56eSmrg	kmode->hdisplay = mode->HDisplay;
175d6c0b56eSmrg	kmode->hsync_start = mode->HSyncStart;
176d6c0b56eSmrg	kmode->hsync_end = mode->HSyncEnd;
177d6c0b56eSmrg	kmode->htotal = mode->HTotal;
178d6c0b56eSmrg	kmode->hskew = mode->HSkew;
179d6c0b56eSmrg
180d6c0b56eSmrg	kmode->vdisplay = mode->VDisplay;
181d6c0b56eSmrg	kmode->vsync_start = mode->VSyncStart;
182d6c0b56eSmrg	kmode->vsync_end = mode->VSyncEnd;
183d6c0b56eSmrg	kmode->vtotal = mode->VTotal;
184d6c0b56eSmrg	kmode->vscan = mode->VScan;
185d6c0b56eSmrg
186d6c0b56eSmrg	kmode->flags = mode->Flags;	//& FLAG_BITS;
187d6c0b56eSmrg	if (mode->name)
188d6c0b56eSmrg		strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
189d6c0b56eSmrg	kmode->name[DRM_DISPLAY_MODE_LEN - 1] = 0;
190d6c0b56eSmrg
191d6c0b56eSmrg}
192d6c0b56eSmrg
19324b90cf4Smrg/*
19424b90cf4Smrg * Utility helper for drmWaitVBlank
19524b90cf4Smrg */
19624b90cf4SmrgBool
19724b90cf4Smrgdrmmode_wait_vblank(xf86CrtcPtr crtc, drmVBlankSeqType type,
19824b90cf4Smrg		    uint32_t target_seq, unsigned long signal, uint64_t *ust,
19924b90cf4Smrg		    uint32_t *result_seq)
20024b90cf4Smrg{
20124b90cf4Smrg	int crtc_id = drmmode_get_crtc_id(crtc);
20224b90cf4Smrg	ScrnInfoPtr scrn = crtc->scrn;
20324b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
20424b90cf4Smrg	drmVBlank vbl;
20524b90cf4Smrg
20624b90cf4Smrg	if (crtc_id == 1)
20724b90cf4Smrg		type |= DRM_VBLANK_SECONDARY;
20824b90cf4Smrg	else if (crtc_id > 1)
20924b90cf4Smrg		type |= (crtc_id << DRM_VBLANK_HIGH_CRTC_SHIFT) &
21024b90cf4Smrg			DRM_VBLANK_HIGH_CRTC_MASK;
21124b90cf4Smrg
21224b90cf4Smrg	vbl.request.type = type;
21324b90cf4Smrg	vbl.request.sequence = target_seq;
21424b90cf4Smrg	vbl.request.signal = signal;
21524b90cf4Smrg
21624b90cf4Smrg	if (drmWaitVBlank(pAMDGPUEnt->fd, &vbl) != 0)
21724b90cf4Smrg		return FALSE;
21824b90cf4Smrg
21924b90cf4Smrg	if (ust)
22024b90cf4Smrg		*ust = (uint64_t)vbl.reply.tval_sec * 1000000 +
22124b90cf4Smrg			vbl.reply.tval_usec;
22224b90cf4Smrg	if (result_seq)
22324b90cf4Smrg		*result_seq = vbl.reply.sequence;
22424b90cf4Smrg
22524b90cf4Smrg	return TRUE;
22624b90cf4Smrg}
22724b90cf4Smrg
228d6c0b56eSmrg/*
229d6c0b56eSmrg * Retrieves present time in microseconds that is compatible
230d6c0b56eSmrg * with units used by vblank timestamps. Depending on the kernel
231d6c0b56eSmrg * version and DRM kernel module configuration, the vblank
232d6c0b56eSmrg * timestamp can either be in real time or monotonic time
233d6c0b56eSmrg */
234d6c0b56eSmrgint drmmode_get_current_ust(int drm_fd, CARD64 * ust)
235d6c0b56eSmrg{
236d6c0b56eSmrg	uint64_t cap_value;
237d6c0b56eSmrg	int ret;
238d6c0b56eSmrg	struct timespec now;
239d6c0b56eSmrg
240d6c0b56eSmrg	ret = drmGetCap(drm_fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap_value);
241d6c0b56eSmrg	if (ret || !cap_value)
242d6c0b56eSmrg		/* old kernel or drm_timestamp_monotonic turned off */
243d6c0b56eSmrg		ret = clock_gettime(CLOCK_REALTIME, &now);
244d6c0b56eSmrg	else
245d6c0b56eSmrg		ret = clock_gettime(CLOCK_MONOTONIC, &now);
246d6c0b56eSmrg	if (ret)
247d6c0b56eSmrg		return ret;
248d6c0b56eSmrg	*ust = ((CARD64) now.tv_sec * 1000000) + ((CARD64) now.tv_nsec / 1000);
249d6c0b56eSmrg	return 0;
250d6c0b56eSmrg}
251d6c0b56eSmrg
252d6c0b56eSmrg/*
253d6c0b56eSmrg * Get current frame count and frame count timestamp of the crtc.
254d6c0b56eSmrg */
255d6c0b56eSmrgint drmmode_crtc_get_ust_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc)
256d6c0b56eSmrg{
257d6c0b56eSmrg	ScrnInfoPtr scrn = crtc->scrn;
25824b90cf4Smrg	uint32_t seq;
259d6c0b56eSmrg
26024b90cf4Smrg	if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, ust, &seq)) {
261d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
262d6c0b56eSmrg			   "get vblank counter failed: %s\n", strerror(errno));
26324b90cf4Smrg		return -1;
264d6c0b56eSmrg	}
265d6c0b56eSmrg
26624b90cf4Smrg	*msc = seq;
267d6c0b56eSmrg
268d6c0b56eSmrg	return Success;
269d6c0b56eSmrg}
270d6c0b56eSmrg
271d6c0b56eSmrgstatic void
272d6c0b56eSmrgdrmmode_do_crtc_dpms(xf86CrtcPtr crtc, int mode)
273d6c0b56eSmrg{
274d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
275d6c0b56eSmrg	ScrnInfoPtr scrn = crtc->scrn;
276d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
277d6c0b56eSmrg	CARD64 ust;
278d6c0b56eSmrg	int ret;
279d6c0b56eSmrg
280d6c0b56eSmrg	if (drmmode_crtc->dpms_mode == DPMSModeOn && mode != DPMSModeOn) {
28124b90cf4Smrg		uint32_t seq;
282d6c0b56eSmrg
28335d5b7c7Smrg		amdgpu_drm_wait_pending_flip(crtc);
284504d986fSmrg
285d6c0b56eSmrg		/*
286d6c0b56eSmrg		 * On->Off transition: record the last vblank time,
287d6c0b56eSmrg		 * sequence number and frame period.
288d6c0b56eSmrg		 */
28924b90cf4Smrg		if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, &ust,
29024b90cf4Smrg					 &seq))
291d6c0b56eSmrg			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
292d6c0b56eSmrg				   "%s cannot get last vblank counter\n",
293d6c0b56eSmrg				   __func__);
294d6c0b56eSmrg		else {
295d6c0b56eSmrg			CARD64 nominal_frame_rate, pix_in_frame;
296d6c0b56eSmrg
297d6c0b56eSmrg			drmmode_crtc->dpms_last_ust = ust;
298d6c0b56eSmrg			drmmode_crtc->dpms_last_seq = seq;
299d6c0b56eSmrg			nominal_frame_rate = crtc->mode.Clock;
300d6c0b56eSmrg			nominal_frame_rate *= 1000;
301d6c0b56eSmrg			pix_in_frame = crtc->mode.HTotal * crtc->mode.VTotal;
302d6c0b56eSmrg			if (nominal_frame_rate == 0 || pix_in_frame == 0)
303d6c0b56eSmrg				nominal_frame_rate = DEFAULT_NOMINAL_FRAME_RATE;
304d6c0b56eSmrg			else
305d6c0b56eSmrg				nominal_frame_rate /= pix_in_frame;
306d6c0b56eSmrg			drmmode_crtc->dpms_last_fps = nominal_frame_rate;
307d6c0b56eSmrg		}
30835d5b7c7Smrg
30935d5b7c7Smrg		drmmode_crtc->dpms_mode = mode;
31035d5b7c7Smrg		amdgpu_drm_queue_handle_deferred(crtc);
311d6c0b56eSmrg	} else if (drmmode_crtc->dpms_mode != DPMSModeOn && mode == DPMSModeOn) {
312d6c0b56eSmrg		/*
313d6c0b56eSmrg		 * Off->On transition: calculate and accumulate the
314d6c0b56eSmrg		 * number of interpolated vblanks while we were in Off state
315d6c0b56eSmrg		 */
316d6c0b56eSmrg		ret = drmmode_get_current_ust(pAMDGPUEnt->fd, &ust);
317d6c0b56eSmrg		if (ret)
318d6c0b56eSmrg			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
319d6c0b56eSmrg				   "%s cannot get current time\n", __func__);
320d6c0b56eSmrg		else if (drmmode_crtc->dpms_last_ust) {
321d6c0b56eSmrg			CARD64 time_elapsed, delta_seq;
322d6c0b56eSmrg			time_elapsed = ust - drmmode_crtc->dpms_last_ust;
323d6c0b56eSmrg			delta_seq = time_elapsed * drmmode_crtc->dpms_last_fps;
324d6c0b56eSmrg			delta_seq /= 1000000;
325d6c0b56eSmrg			drmmode_crtc->interpolated_vblanks += delta_seq;
326d6c0b56eSmrg
327d6c0b56eSmrg		}
32835d5b7c7Smrg
32935d5b7c7Smrg		drmmode_crtc->dpms_mode = DPMSModeOn;
330d6c0b56eSmrg	}
331d6c0b56eSmrg}
332d6c0b56eSmrg
333d6c0b56eSmrgstatic void
334d6c0b56eSmrgdrmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
335d6c0b56eSmrg{
336d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
337d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
338d6c0b56eSmrg
339d6c0b56eSmrg	/* Disable unused CRTCs and enable/disable active CRTCs */
340504d986fSmrg	if (!crtc->enabled || mode != DPMSModeOn) {
34135d5b7c7Smrg		drmmode_do_crtc_dpms(crtc, DPMSModeOff);
342d6c0b56eSmrg		drmModeSetCrtc(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id,
343d6c0b56eSmrg			       0, 0, 0, NULL, 0, NULL);
34424b90cf4Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, NULL);
345504d986fSmrg	} else if (drmmode_crtc->dpms_mode != DPMSModeOn)
346d6c0b56eSmrg		crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation,
347d6c0b56eSmrg					    crtc->x, crtc->y);
348d6c0b56eSmrg}
349d6c0b56eSmrg
350d6c0b56eSmrgstatic PixmapPtr
351d6c0b56eSmrgcreate_pixmap_for_fbcon(drmmode_ptr drmmode,
352d6c0b56eSmrg			ScrnInfoPtr pScrn, int fbcon_id)
353d6c0b56eSmrg{
35435d5b7c7Smrg	ScreenPtr pScreen = pScrn->pScreen;
355d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
35635d5b7c7Smrg	PixmapPtr pixmap = NULL;
357d6c0b56eSmrg	drmModeFBPtr fbcon;
358d6c0b56eSmrg
359d6c0b56eSmrg	fbcon = drmModeGetFB(pAMDGPUEnt->fd, fbcon_id);
36035d5b7c7Smrg	if (!fbcon)
361d6c0b56eSmrg		return NULL;
362d6c0b56eSmrg
363d6c0b56eSmrg	if (fbcon->depth != pScrn->depth ||
364d6c0b56eSmrg	    fbcon->width != pScrn->virtualX ||
365d6c0b56eSmrg	    fbcon->height != pScrn->virtualY)
366d6c0b56eSmrg		goto out_free_fb;
367d6c0b56eSmrg
36835d5b7c7Smrg	pixmap = fbCreatePixmap(pScreen, 0, 0, fbcon->depth, 0);
36935d5b7c7Smrg	if (!pixmap)
370d6c0b56eSmrg		goto out_free_fb;
371d6c0b56eSmrg
37235d5b7c7Smrg	pScreen->ModifyPixmapHeader(pixmap, fbcon->width, fbcon->height, 0, 0,
37335d5b7c7Smrg				    fbcon->pitch, NULL);
37435d5b7c7Smrg	pixmap->devPrivate.ptr = NULL;
375d6c0b56eSmrg
37635d5b7c7Smrg	if (!glamor_egl_create_textured_pixmap(pixmap, fbcon->handle,
37735d5b7c7Smrg					       pixmap->devKind)) {
37835d5b7c7Smrg		pScreen->DestroyPixmap(pixmap);
37935d5b7c7Smrg		pixmap = NULL;
380d6c0b56eSmrg	}
381d6c0b56eSmrg
382d6c0b56eSmrgout_free_fb:
383d6c0b56eSmrg	drmModeFreeFB(fbcon);
384d6c0b56eSmrg	return pixmap;
385d6c0b56eSmrg}
386d6c0b56eSmrg
387d6c0b56eSmrgvoid drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
388d6c0b56eSmrg{
389d6c0b56eSmrg	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
390d6c0b56eSmrg	ScreenPtr pScreen = pScrn->pScreen;
39124b90cf4Smrg	PixmapPtr src, dst = pScreen->GetScreenPixmap(pScreen);
39224b90cf4Smrg	struct drmmode_fb *fb = amdgpu_pixmap_get_fb(dst);
393d6c0b56eSmrg	int fbcon_id = 0;
394d6c0b56eSmrg	GCPtr gc;
395d6c0b56eSmrg	int i;
396d6c0b56eSmrg
397d6c0b56eSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
398d6c0b56eSmrg		drmmode_crtc_private_ptr drmmode_crtc = xf86_config->crtc[i]->driver_private;
399d6c0b56eSmrg
400d6c0b56eSmrg		if (drmmode_crtc->mode_crtc->buffer_id)
401d6c0b56eSmrg			fbcon_id = drmmode_crtc->mode_crtc->buffer_id;
402d6c0b56eSmrg	}
403d6c0b56eSmrg
404d6c0b56eSmrg	if (!fbcon_id)
405d6c0b56eSmrg		return;
406d6c0b56eSmrg
40724b90cf4Smrg	if (fbcon_id == fb->handle) {
408d6c0b56eSmrg		/* in some rare case there might be no fbcon and we might already
409d6c0b56eSmrg		 * be the one with the current fb to avoid a false deadlck in
410d6c0b56eSmrg		 * kernel ttm code just do nothing as anyway there is nothing
411d6c0b56eSmrg		 * to do
412d6c0b56eSmrg		 */
413d6c0b56eSmrg		return;
414d6c0b56eSmrg	}
415d6c0b56eSmrg
416d6c0b56eSmrg	src = create_pixmap_for_fbcon(drmmode, pScrn, fbcon_id);
417d6c0b56eSmrg	if (!src)
418d6c0b56eSmrg		return;
419d6c0b56eSmrg
420d6c0b56eSmrg	gc = GetScratchGC(pScrn->depth, pScreen);
421d6c0b56eSmrg	ValidateGC(&dst->drawable, gc);
422d6c0b56eSmrg
423d6c0b56eSmrg	(*gc->ops->CopyArea)(&src->drawable, &dst->drawable, gc, 0, 0,
424d6c0b56eSmrg			     pScrn->virtualX, pScrn->virtualY, 0, 0);
425d6c0b56eSmrg
426d6c0b56eSmrg	FreeScratchGC(gc);
427d6c0b56eSmrg
428d6c0b56eSmrg	pScreen->canDoBGNoneRoot = TRUE;
42935d5b7c7Smrg	pScreen->DestroyPixmap(src);
430d6c0b56eSmrg
431d6c0b56eSmrg	return;
432d6c0b56eSmrg}
433d6c0b56eSmrg
43424b90cf4Smrgvoid
435d6c0b56eSmrgdrmmode_crtc_scanout_destroy(drmmode_ptr drmmode,
436d6c0b56eSmrg			     struct drmmode_scanout *scanout)
437d6c0b56eSmrg{
438d6c0b56eSmrg
439d6c0b56eSmrg	if (scanout->pixmap) {
440d6c0b56eSmrg		drmmode_destroy_bo_pixmap(scanout->pixmap);
441d6c0b56eSmrg		scanout->pixmap = NULL;
442d6c0b56eSmrg	}
443d6c0b56eSmrg
444d6c0b56eSmrg	if (scanout->bo) {
445d6c0b56eSmrg		amdgpu_bo_unref(&scanout->bo);
446d6c0b56eSmrg		scanout->bo = NULL;
447d6c0b56eSmrg	}
448504d986fSmrg}
449504d986fSmrg
45024b90cf4Smrgvoid
451504d986fSmrgdrmmode_crtc_scanout_free(drmmode_crtc_private_ptr drmmode_crtc)
452504d986fSmrg{
45324b90cf4Smrg	drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
45424b90cf4Smrg				     &drmmode_crtc->scanout[0]);
45524b90cf4Smrg	drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
45624b90cf4Smrg				     &drmmode_crtc->scanout[1]);
457d6c0b56eSmrg
45824b90cf4Smrg	if (drmmode_crtc->scanout_damage)
459504d986fSmrg		DamageDestroy(drmmode_crtc->scanout_damage);
460d6c0b56eSmrg}
461d6c0b56eSmrg
46224b90cf4SmrgPixmapPtr
46311bf0794Smrgdrmmode_crtc_scanout_create(xf86CrtcPtr crtc, struct drmmode_scanout *scanout,
46411bf0794Smrg			    int width, int height)
465d6c0b56eSmrg{
466d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
467d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
468d6c0b56eSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
46911bf0794Smrg	int pitch;
470d6c0b56eSmrg
47111bf0794Smrg	if (scanout->pixmap) {
472d6c0b56eSmrg		if (scanout->width == width && scanout->height == height)
47311bf0794Smrg			return scanout->pixmap;
474d6c0b56eSmrg
475d6c0b56eSmrg		drmmode_crtc_scanout_destroy(drmmode, scanout);
476d6c0b56eSmrg	}
477d6c0b56eSmrg
478d6c0b56eSmrg	scanout->bo = amdgpu_alloc_pixmap_bo(pScrn, width, height,
47911bf0794Smrg					     pScrn->depth, 0,
48011bf0794Smrg					     pScrn->bitsPerPixel, &pitch);
481d6c0b56eSmrg	if (!scanout->bo) {
482d6c0b56eSmrg		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
48311bf0794Smrg			   "Failed to allocate scanout buffer memory\n");
48424b90cf4Smrg		return NULL;
485d6c0b56eSmrg	}
486d6c0b56eSmrg
487d6c0b56eSmrg	scanout->pixmap = drmmode_create_bo_pixmap(pScrn,
488d6c0b56eSmrg						 width, height,
489d6c0b56eSmrg						 pScrn->depth,
490d6c0b56eSmrg						 pScrn->bitsPerPixel,
49111bf0794Smrg						 pitch, scanout->bo);
49224b90cf4Smrg	if (!scanout->pixmap) {
49324b90cf4Smrg		ErrorF("failed to create CRTC scanout pixmap\n");
49424b90cf4Smrg		goto error;
49524b90cf4Smrg	}
49624b90cf4Smrg
49724b90cf4Smrg	if (amdgpu_pixmap_get_fb(scanout->pixmap)) {
49811bf0794Smrg		scanout->width = width;
49911bf0794Smrg		scanout->height = height;
50011bf0794Smrg	} else {
50124b90cf4Smrg		ErrorF("failed to create CRTC scanout FB\n");
50224b90cf4Smrgerror:
50311bf0794Smrg		drmmode_crtc_scanout_destroy(drmmode, scanout);
504d6c0b56eSmrg	}
505d6c0b56eSmrg
50611bf0794Smrg	return scanout->pixmap;
507d6c0b56eSmrg}
508d6c0b56eSmrg
509d6c0b56eSmrgstatic void
510d6c0b56eSmrgamdgpu_screen_damage_report(DamagePtr damage, RegionPtr region, void *closure)
511d6c0b56eSmrg{
51224b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = closure;
51324b90cf4Smrg
51424b90cf4Smrg	if (drmmode_crtc->ignore_damage) {
51524b90cf4Smrg		RegionEmpty(&damage->damage);
51624b90cf4Smrg		drmmode_crtc->ignore_damage = FALSE;
51724b90cf4Smrg		return;
51824b90cf4Smrg	}
51924b90cf4Smrg
520d6c0b56eSmrg	/* Only keep track of the extents */
521d6c0b56eSmrg	RegionUninit(&damage->damage);
522d6c0b56eSmrg	damage->damage.data = NULL;
523d6c0b56eSmrg}
524d6c0b56eSmrg
52524b90cf4Smrgstatic void
52624b90cf4Smrgdrmmode_screen_damage_destroy(DamagePtr damage, void *closure)
52724b90cf4Smrg{
52824b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = closure;
52924b90cf4Smrg
53024b90cf4Smrg	drmmode_crtc->scanout_damage = NULL;
53124b90cf4Smrg	RegionUninit(&drmmode_crtc->scanout_last_region);
53224b90cf4Smrg}
53324b90cf4Smrg
534d6c0b56eSmrgstatic Bool
535d6c0b56eSmrgdrmmode_can_use_hw_cursor(xf86CrtcPtr crtc)
536d6c0b56eSmrg{
537d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(crtc->scrn);
538d6c0b56eSmrg
539d6c0b56eSmrg	/* Check for Option "SWcursor" */
540d6c0b56eSmrg	if (xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE))
541d6c0b56eSmrg		return FALSE;
542d6c0b56eSmrg
543d6c0b56eSmrg	/* Fall back to SW cursor if the CRTC is transformed */
544d6c0b56eSmrg	if (crtc->transformPresent)
545d6c0b56eSmrg		return FALSE;
546d6c0b56eSmrg
54724b90cf4Smrg#if XF86_CRTC_VERSION < 7
548d6c0b56eSmrg	/* Xorg doesn't correctly handle cursor position transform in the
549d6c0b56eSmrg	 * rotation case
550d6c0b56eSmrg	 */
551d6c0b56eSmrg	if (crtc->driverIsPerformingTransform &&
552d6c0b56eSmrg	    (crtc->rotation & 0xf) != RR_Rotate_0)
553d6c0b56eSmrg		return FALSE;
554d6c0b56eSmrg#endif
555d6c0b56eSmrg
556504d986fSmrg	/* HW cursor not supported with RandR 1.4 multihead up to 1.18.99.901 */
557504d986fSmrg	if (xorgGetVersion() <= XORG_VERSION_NUMERIC(1,18,99,901,0) &&
558504d986fSmrg	    !xorg_list_is_empty(&crtc->scrn->pScreen->pixmap_dirty_list))
559d6c0b56eSmrg		return FALSE;
560d6c0b56eSmrg
561d6c0b56eSmrg	return TRUE;
562d6c0b56eSmrg}
563d6c0b56eSmrg
56411bf0794Smrgstatic void
56511bf0794Smrgdrmmode_crtc_update_tear_free(xf86CrtcPtr crtc)
56611bf0794Smrg{
56711bf0794Smrg	AMDGPUInfoPtr info = AMDGPUPTR(crtc->scrn);
56811bf0794Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
56911bf0794Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
57011bf0794Smrg	int i;
57111bf0794Smrg
57211bf0794Smrg	drmmode_crtc->tear_free = FALSE;
57311bf0794Smrg
57411bf0794Smrg	for (i = 0; i < xf86_config->num_output; i++) {
57511bf0794Smrg		xf86OutputPtr output = xf86_config->output[i];
57611bf0794Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
57711bf0794Smrg
57811bf0794Smrg		if (output->crtc != crtc)
57911bf0794Smrg			continue;
58011bf0794Smrg
58111bf0794Smrg		if (drmmode_output->tear_free == 1 ||
58211bf0794Smrg		    (drmmode_output->tear_free == 2 &&
58324b90cf4Smrg		     (crtc->scrn->pScreen->isGPU ||
58411bf0794Smrg		      info->shadow_primary ||
58511bf0794Smrg		      crtc->transformPresent || crtc->rotation != RR_Rotate_0))) {
58611bf0794Smrg			drmmode_crtc->tear_free = TRUE;
58711bf0794Smrg			return;
58811bf0794Smrg		}
58911bf0794Smrg	}
59011bf0794Smrg}
59111bf0794Smrg
59211bf0794Smrg#if XF86_CRTC_VERSION < 7
59311bf0794Smrg#define XF86DriverTransformOutput TRUE
59411bf0794Smrg#define XF86DriverTransformNone FALSE
59511bf0794Smrg#endif
59611bf0794Smrg
597d6c0b56eSmrgstatic Bool
598d6c0b56eSmrgdrmmode_handle_transform(xf86CrtcPtr crtc)
599d6c0b56eSmrg{
600d6c0b56eSmrg	Bool ret;
601d6c0b56eSmrg
60224b90cf4Smrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,903,0)
60335d5b7c7Smrg	crtc->driverIsPerformingTransform = XF86DriverTransformOutput;
60424b90cf4Smrg#else
60524b90cf4Smrg	crtc->driverIsPerformingTransform = !crtc->transformPresent &&
60624b90cf4Smrg		(crtc->rotation & 0xf) == RR_Rotate_0;
60724b90cf4Smrg#endif
608d6c0b56eSmrg
609d6c0b56eSmrg	ret = xf86CrtcRotate(crtc);
610d6c0b56eSmrg
611d6c0b56eSmrg	crtc->driverIsPerformingTransform &= ret && crtc->transform_in_use;
612d6c0b56eSmrg
613d6c0b56eSmrg	return ret;
614d6c0b56eSmrg}
615d6c0b56eSmrg
61611bf0794Smrg
61711bf0794Smrgstatic void
61811bf0794Smrgdrmmode_crtc_prime_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
61924b90cf4Smrg				  unsigned scanout_id, struct drmmode_fb **fb,
62024b90cf4Smrg				  int *x, int *y)
62111bf0794Smrg{
62211bf0794Smrg	ScrnInfoPtr scrn = crtc->scrn;
62311bf0794Smrg	ScreenPtr screen = scrn->pScreen;
62411bf0794Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
62511bf0794Smrg
62611bf0794Smrg	if (drmmode_crtc->tear_free &&
62711bf0794Smrg	    !drmmode_crtc->scanout[1].pixmap) {
62811bf0794Smrg		RegionPtr region;
62911bf0794Smrg		BoxPtr box;
63011bf0794Smrg
63111bf0794Smrg		drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1],
63211bf0794Smrg					    mode->HDisplay,
63311bf0794Smrg					    mode->VDisplay);
63411bf0794Smrg		region = &drmmode_crtc->scanout_last_region;
63511bf0794Smrg		RegionUninit(region);
63611bf0794Smrg		region->data = NULL;
63711bf0794Smrg		box = RegionExtents(region);
63811bf0794Smrg		box->x1 = crtc->x;
63911bf0794Smrg		box->y1 = crtc->y;
64011bf0794Smrg		box->x2 = crtc->x + mode->HDisplay;
64111bf0794Smrg		box->y2 = crtc->y + mode->VDisplay;
64211bf0794Smrg	}
64311bf0794Smrg
64411bf0794Smrg	if (scanout_id != drmmode_crtc->scanout_id) {
64511bf0794Smrg		PixmapDirtyUpdatePtr dirty = NULL;
64611bf0794Smrg
64711bf0794Smrg		xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list,
64811bf0794Smrg					 ent) {
64924b90cf4Smrg			if (amdgpu_dirty_src_equals(dirty, drmmode_crtc->prime_scanout_pixmap)) {
65011bf0794Smrg				dirty->slave_dst =
65111bf0794Smrg					drmmode_crtc->scanout[scanout_id].pixmap;
65211bf0794Smrg				break;
65311bf0794Smrg			}
65411bf0794Smrg		}
65511bf0794Smrg
65611bf0794Smrg		if (!drmmode_crtc->tear_free) {
65711bf0794Smrg			GCPtr gc = GetScratchGC(scrn->depth, screen);
65811bf0794Smrg
65911bf0794Smrg			ValidateGC(&drmmode_crtc->scanout[0].pixmap->drawable, gc);
66011bf0794Smrg			gc->ops->CopyArea(&drmmode_crtc->scanout[1].pixmap->drawable,
66111bf0794Smrg					  &drmmode_crtc->scanout[0].pixmap->drawable,
66211bf0794Smrg					  gc, 0, 0, mode->HDisplay, mode->VDisplay,
66311bf0794Smrg					  0, 0);
66411bf0794Smrg			FreeScratchGC(gc);
66511bf0794Smrg			amdgpu_glamor_finish(scrn);
66611bf0794Smrg		}
66711bf0794Smrg	}
66811bf0794Smrg
66924b90cf4Smrg	*fb = amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap);
67011bf0794Smrg	*x = *y = 0;
67111bf0794Smrg	drmmode_crtc->scanout_id = scanout_id;
67211bf0794Smrg}
67311bf0794Smrg
67411bf0794Smrg
67511bf0794Smrgstatic void
67611bf0794Smrgdrmmode_crtc_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
67724b90cf4Smrg			    unsigned scanout_id, struct drmmode_fb **fb, int *x,
67824b90cf4Smrg			    int *y)
67911bf0794Smrg{
68011bf0794Smrg	ScrnInfoPtr scrn = crtc->scrn;
68111bf0794Smrg	ScreenPtr screen = scrn->pScreen;
68211bf0794Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
68311bf0794Smrg
68424b90cf4Smrg	drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[scanout_id],
68511bf0794Smrg				    mode->HDisplay, mode->VDisplay);
68611bf0794Smrg	if (drmmode_crtc->tear_free) {
68724b90cf4Smrg		drmmode_crtc_scanout_create(crtc,
68824b90cf4Smrg					    &drmmode_crtc->scanout[scanout_id ^ 1],
68911bf0794Smrg					    mode->HDisplay, mode->VDisplay);
69011bf0794Smrg	}
69111bf0794Smrg
69224b90cf4Smrg	if (drmmode_crtc->scanout[scanout_id].pixmap &&
69324b90cf4Smrg	    (!drmmode_crtc->tear_free ||
69424b90cf4Smrg	     drmmode_crtc->scanout[scanout_id ^ 1].pixmap)) {
69535d5b7c7Smrg		BoxRec extents = { .x1 = 0, .y1 = 0,
69635d5b7c7Smrg				   .x2 = scrn->virtualX, .y2 = scrn->virtualY };
69711bf0794Smrg
69811bf0794Smrg		if (!drmmode_crtc->scanout_damage) {
69911bf0794Smrg			drmmode_crtc->scanout_damage =
70011bf0794Smrg				DamageCreate(amdgpu_screen_damage_report,
70124b90cf4Smrg					     drmmode_screen_damage_destroy,
70224b90cf4Smrg					     DamageReportRawRegion,
70324b90cf4Smrg					     TRUE, screen, drmmode_crtc);
70424b90cf4Smrg			DamageRegister(&screen->root->drawable,
70511bf0794Smrg				       drmmode_crtc->scanout_damage);
70611bf0794Smrg		}
70711bf0794Smrg
70824b90cf4Smrg		*fb = amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap);
70911bf0794Smrg		*x = *y = 0;
71011bf0794Smrg
71124b90cf4Smrg		amdgpu_scanout_do_update(crtc, scanout_id,
71224b90cf4Smrg					 screen->GetWindowPixmap(screen->root),
71335d5b7c7Smrg					 extents);
71435d5b7c7Smrg		RegionEmpty(DamageRegion(drmmode_crtc->scanout_damage));
71511bf0794Smrg		amdgpu_glamor_finish(scrn);
71611bf0794Smrg	}
71711bf0794Smrg}
71811bf0794Smrg
71935d5b7c7Smrgstatic char *cm_prop_names[] = {
72035d5b7c7Smrg	"DEGAMMA_LUT",
72135d5b7c7Smrg	"CTM",
72235d5b7c7Smrg	"GAMMA_LUT",
72335d5b7c7Smrg	"DEGAMMA_LUT_SIZE",
72435d5b7c7Smrg	"GAMMA_LUT_SIZE",
72535d5b7c7Smrg};
72635d5b7c7Smrg
72735d5b7c7Smrg/**
72835d5b7c7Smrg * Return the enum of the color management property with the given name.
72935d5b7c7Smrg */
73035d5b7c7Smrgstatic enum drmmode_cm_prop get_cm_enum_from_str(const char *prop_name)
73135d5b7c7Smrg{
73235d5b7c7Smrg	enum drmmode_cm_prop ret;
73335d5b7c7Smrg
73435d5b7c7Smrg	for (ret = 0; ret < CM_NUM_PROPS; ret++) {
73535d5b7c7Smrg		if (!strcmp(prop_name, cm_prop_names[ret]))
73635d5b7c7Smrg			return ret;
73735d5b7c7Smrg	}
73835d5b7c7Smrg	return CM_INVALID_PROP;
73935d5b7c7Smrg}
74035d5b7c7Smrg
74135d5b7c7Smrg/**
74235d5b7c7Smrg * Return TRUE if kernel supports non-legacy color management.
74335d5b7c7Smrg */
74435d5b7c7Smrgstatic Bool drmmode_cm_enabled(drmmode_ptr drmmode)
74535d5b7c7Smrg{
74635d5b7c7Smrg	return drmmode->cm_prop_ids[CM_GAMMA_LUT_SIZE] != 0;
74735d5b7c7Smrg}
74835d5b7c7Smrg
74935d5b7c7Smrg/**
75035d5b7c7Smrg * If legacy LUT is a, and non-legacy LUT is b, then the result of b(a(x)) is
75135d5b7c7Smrg * returned in out_lut. out_lut's length is expected to be the same as the
75235d5b7c7Smrg * non-legacy LUT b.
75335d5b7c7Smrg *
75435d5b7c7Smrg * @a_(red|green|blue): The red, green, and blue components of the legacy LUT.
75535d5b7c7Smrg * @b_lut: The non-legacy LUT, in DRM's color LUT format.
75635d5b7c7Smrg * @out_lut: The composed LUT, in DRM's color LUT format.
75735d5b7c7Smrg * @len_a: Length of legacy lut.
75835d5b7c7Smrg * @len_b: Length of non-legacy lut.
75935d5b7c7Smrg */
76035d5b7c7Smrgstatic void drmmode_lut_compose(uint16_t *a_red,
76135d5b7c7Smrg				uint16_t *a_green,
76235d5b7c7Smrg				uint16_t *a_blue,
76335d5b7c7Smrg				struct drm_color_lut *b_lut,
76435d5b7c7Smrg				struct drm_color_lut *out_lut,
76535d5b7c7Smrg				uint32_t len_a, uint32_t len_b)
76635d5b7c7Smrg{
76735d5b7c7Smrg	uint32_t i_l, i_r, i;
76835d5b7c7Smrg	uint32_t i_amax, i_bmax;
76935d5b7c7Smrg	uint32_t coeff_ibmax;
77035d5b7c7Smrg	uint32_t j;
77135d5b7c7Smrg	uint64_t a_out_ibmax;
77235d5b7c7Smrg	int color;
77335d5b7c7Smrg	size_t struct_size = sizeof(struct drm_color_lut);
77435d5b7c7Smrg
77535d5b7c7Smrg	uint32_t max_lut = (1 << 16) - 1;
77635d5b7c7Smrg
77735d5b7c7Smrg	i_amax = len_a - 1;
77835d5b7c7Smrg	i_bmax = len_b - 1;
77935d5b7c7Smrg
78035d5b7c7Smrg	/* A linear interpolation is done on the legacy LUT before it is
78135d5b7c7Smrg	 * composed, to bring it up-to-size with the non-legacy LUT. The
78235d5b7c7Smrg	 * interpolation uses integers by keeping things multiplied until the
78335d5b7c7Smrg	 * last moment.
78435d5b7c7Smrg	 */
78535d5b7c7Smrg	for (color = 0; color < 3; color++) {
78635d5b7c7Smrg		uint16_t *a, *b, *out;
78735d5b7c7Smrg
78835d5b7c7Smrg		/* Set the initial pointers to the right color components. The
78935d5b7c7Smrg		 * inner for-loop will then maintain the correct offset from
79035d5b7c7Smrg		 * the initial element.
79135d5b7c7Smrg		 */
79235d5b7c7Smrg		if (color == 0) {
79335d5b7c7Smrg			a = a_red;
79435d5b7c7Smrg			b = &b_lut[0].red;
79535d5b7c7Smrg			out = &out_lut[0].red;
79635d5b7c7Smrg		} else if (color == 1) {
79735d5b7c7Smrg			a = a_green;
79835d5b7c7Smrg			b = &b_lut[0].green;
79935d5b7c7Smrg			out = &out_lut[0].green;
80035d5b7c7Smrg		} else {
80135d5b7c7Smrg			a = a_blue;
80235d5b7c7Smrg			b = &b_lut[0].blue;
80335d5b7c7Smrg			out = &out_lut[0].blue;
80435d5b7c7Smrg		}
80535d5b7c7Smrg
80635d5b7c7Smrg		for (i = 0; i < len_b; i++) {
80735d5b7c7Smrg			/* i_l and i_r tracks the left and right elements in
80835d5b7c7Smrg			 * a_lut, to the sample point i. Also handle last
80935d5b7c7Smrg			 * element edge case, when i_l = i_amax.
81035d5b7c7Smrg			 */
81135d5b7c7Smrg			i_l = i * i_amax / i_bmax;
81235d5b7c7Smrg			i_r = i_l + !!(i_amax - i_l);
81335d5b7c7Smrg
81435d5b7c7Smrg			/* coeff is intended to be in [0, 1), depending on
81535d5b7c7Smrg			 * where sample i is between i_l and i_r. We keep it
81635d5b7c7Smrg			 * multiplied with i_bmax throughout to maintain
81735d5b7c7Smrg			 * precision */
81835d5b7c7Smrg			coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
81935d5b7c7Smrg			a_out_ibmax = i_bmax * a[i_l] +
82035d5b7c7Smrg				      coeff_ibmax * (a[i_r] - a[i_l]);
82135d5b7c7Smrg
82235d5b7c7Smrg			/* j = floor((a_out/max_lut)*i_bmax).
82335d5b7c7Smrg			 * i.e. the element in LUT b that a_out maps to. We
82435d5b7c7Smrg			 * have to divide by max_lut to normalize a_out, since
82535d5b7c7Smrg			 * values in the LUTs are [0, 1<<16)
82635d5b7c7Smrg			 */
82735d5b7c7Smrg			j = a_out_ibmax / max_lut;
82835d5b7c7Smrg			*(uint16_t*)((void*)out + (i*struct_size)) =
82935d5b7c7Smrg				*(uint16_t*)((void*)b + (j*struct_size));
83035d5b7c7Smrg		}
83135d5b7c7Smrg	}
83235d5b7c7Smrg
83335d5b7c7Smrg	for (i = 0; i < len_b; i++)
83435d5b7c7Smrg		out_lut[i].reserved = 0;
83535d5b7c7Smrg}
83635d5b7c7Smrg
83735d5b7c7Smrg/**
83835d5b7c7Smrg * Resize a LUT, using linear interpolation.
83935d5b7c7Smrg *
84035d5b7c7Smrg * @in_(red|green|blue): Legacy LUT components
84135d5b7c7Smrg * @out_lut: The resized LUT is returned here, in DRM color LUT format.
84235d5b7c7Smrg * @len_in: Length of legacy LUT.
84335d5b7c7Smrg * @len_out: Length of out_lut, i.e. the target size.
84435d5b7c7Smrg */
84535d5b7c7Smrgstatic void drmmode_lut_interpolate(uint16_t *in_red,
84635d5b7c7Smrg				    uint16_t *in_green,
84735d5b7c7Smrg				    uint16_t *in_blue,
84835d5b7c7Smrg				    struct drm_color_lut *out_lut,
84935d5b7c7Smrg				    uint32_t len_in, uint32_t len_out)
85035d5b7c7Smrg{
85135d5b7c7Smrg	uint32_t i_l, i_r, i;
85235d5b7c7Smrg	uint32_t i_amax, i_bmax;
85335d5b7c7Smrg	uint32_t coeff_ibmax;
85435d5b7c7Smrg	uint64_t out_ibmax;
85535d5b7c7Smrg	int color;
85635d5b7c7Smrg	size_t struct_size = sizeof(struct drm_color_lut);
85735d5b7c7Smrg
85835d5b7c7Smrg	i_amax = len_in - 1;
85935d5b7c7Smrg	i_bmax = len_out - 1;
86035d5b7c7Smrg
86135d5b7c7Smrg	/* See @drmmode_lut_compose for details */
86235d5b7c7Smrg	for (color = 0; color < 3; color++) {
86335d5b7c7Smrg		uint16_t *in, *out;
86435d5b7c7Smrg
86535d5b7c7Smrg		if (color == 0) {
86635d5b7c7Smrg			in = in_red;
86735d5b7c7Smrg			out = &out_lut[0].red;
86835d5b7c7Smrg		} else if (color == 1) {
86935d5b7c7Smrg			in = in_green;
87035d5b7c7Smrg			out = &out_lut[0].green;
87135d5b7c7Smrg		} else {
87235d5b7c7Smrg			in = in_blue;
87335d5b7c7Smrg			out = &out_lut[0].blue;
87435d5b7c7Smrg		}
87535d5b7c7Smrg
87635d5b7c7Smrg		for (i = 0; i < len_out; i++) {
87735d5b7c7Smrg			i_l = i * i_amax / i_bmax;
87835d5b7c7Smrg			i_r = i_l + !!(i_amax - i_l);
87935d5b7c7Smrg
88035d5b7c7Smrg			coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
88135d5b7c7Smrg			out_ibmax = i_bmax * in[i_l] +
88235d5b7c7Smrg				      coeff_ibmax * (in[i_r] - in[i_l]);
88335d5b7c7Smrg
88435d5b7c7Smrg			*(uint16_t*)((void*)out + (i*struct_size)) =
88535d5b7c7Smrg				out_ibmax / i_bmax;
88635d5b7c7Smrg		}
88735d5b7c7Smrg	}
88835d5b7c7Smrg
88935d5b7c7Smrg	for (i = 0; i < len_out; i++)
89035d5b7c7Smrg		out_lut[i].reserved = 0;
89135d5b7c7Smrg}
89235d5b7c7Smrg
89335d5b7c7Smrg/**
89435d5b7c7Smrg * Configure and change a color property on a CRTC, through RandR. Only the
89535d5b7c7Smrg * specified output will be affected, even if the CRTC is attached to multiple
89635d5b7c7Smrg * outputs. Note that changes will be non-pending: the changes won't be pushed
89735d5b7c7Smrg * to kernel driver.
89835d5b7c7Smrg *
89935d5b7c7Smrg * @output: RandR output to set the property on.
90035d5b7c7Smrg * @crtc: The driver-private CRTC object containing the color properties.
90135d5b7c7Smrg *        If this is NULL, "disabled" values of 0 will be used.
90235d5b7c7Smrg * @cm_prop_index: Color management property to configure and change.
90335d5b7c7Smrg *
90435d5b7c7Smrg * Return 0 on success, X-defined error code otherwise.
90535d5b7c7Smrg */
90635d5b7c7Smrgstatic int rr_configure_and_change_cm_property(xf86OutputPtr output,
90735d5b7c7Smrg					       drmmode_crtc_private_ptr crtc,
90835d5b7c7Smrg					       enum drmmode_cm_prop cm_prop_index)
90935d5b7c7Smrg{
91035d5b7c7Smrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
91135d5b7c7Smrg	drmmode_ptr drmmode = drmmode_output->drmmode;
91235d5b7c7Smrg	Bool need_configure = TRUE;
91335d5b7c7Smrg	unsigned long length = 0;
91435d5b7c7Smrg	void *data = NULL;
91535d5b7c7Smrg	int format = 0;
91635d5b7c7Smrg	uint32_t zero = 0;
91735d5b7c7Smrg	INT32 range[2];
91835d5b7c7Smrg	Atom atom;
91935d5b7c7Smrg	int err;
92035d5b7c7Smrg
92135d5b7c7Smrg	if (cm_prop_index == CM_INVALID_PROP)
92235d5b7c7Smrg		return BadName;
92335d5b7c7Smrg
92435d5b7c7Smrg	switch(cm_prop_index) {
92535d5b7c7Smrg	case CM_GAMMA_LUT_SIZE:
92635d5b7c7Smrg		format = 32;
92735d5b7c7Smrg		length = 1;
92835d5b7c7Smrg		data = &drmmode->gamma_lut_size;
92935d5b7c7Smrg		range[0] = 0;
93035d5b7c7Smrg		range[1] = -1;
93135d5b7c7Smrg		break;
93235d5b7c7Smrg	case CM_DEGAMMA_LUT_SIZE:
93335d5b7c7Smrg		format = 32;
93435d5b7c7Smrg		length = 1;
93535d5b7c7Smrg		data = &drmmode->degamma_lut_size;
93635d5b7c7Smrg		range[0] = 0;
93735d5b7c7Smrg		range[1] = -1;
93835d5b7c7Smrg		break;
93935d5b7c7Smrg	case CM_GAMMA_LUT:
94035d5b7c7Smrg		format = 16;
94135d5b7c7Smrg		range[0] = 0;
94235d5b7c7Smrg		range[1] = (1 << 16) - 1; // Max 16 bit unsigned int.
94335d5b7c7Smrg		if (crtc && crtc->gamma_lut) {
94435d5b7c7Smrg			/* Convert from 8bit size to 16bit size */
94535d5b7c7Smrg			length = sizeof(*crtc->gamma_lut) >> 1;
94635d5b7c7Smrg			length *= drmmode->gamma_lut_size;
94735d5b7c7Smrg			data = crtc->gamma_lut;
94835d5b7c7Smrg		} else {
94935d5b7c7Smrg			length = 1;
95035d5b7c7Smrg			data = &zero;
95135d5b7c7Smrg		}
95235d5b7c7Smrg		break;
95335d5b7c7Smrg	case CM_DEGAMMA_LUT:
95435d5b7c7Smrg		format = 16;
95535d5b7c7Smrg		range[0] = 0;
95635d5b7c7Smrg		range[1] = (1 << 16) - 1;
95735d5b7c7Smrg		if (crtc && crtc->degamma_lut) {
95835d5b7c7Smrg			length = sizeof(*crtc->degamma_lut) >> 1;
95935d5b7c7Smrg			length *= drmmode->degamma_lut_size;
96035d5b7c7Smrg			data = crtc->degamma_lut;
96135d5b7c7Smrg		} else {
96235d5b7c7Smrg			length = 1;
96335d5b7c7Smrg			data = &zero;
96435d5b7c7Smrg		}
96535d5b7c7Smrg		break;
96635d5b7c7Smrg	case CM_CTM:
96735d5b7c7Smrg		/* CTM is fixed-point S31.32 format. */
96835d5b7c7Smrg		format = 32;
96935d5b7c7Smrg		need_configure = FALSE;
97035d5b7c7Smrg		if (crtc && crtc->ctm) {
97135d5b7c7Smrg			/* Convert from 8bit size to 32bit size */
97235d5b7c7Smrg			length = sizeof(*crtc->ctm) >> 2;
97335d5b7c7Smrg			data = crtc->ctm;
97435d5b7c7Smrg		} else {
97535d5b7c7Smrg			length = 1;
97635d5b7c7Smrg			data = &zero;
97735d5b7c7Smrg		}
97835d5b7c7Smrg		break;
97935d5b7c7Smrg	default:
98035d5b7c7Smrg		return BadName;
98135d5b7c7Smrg	}
98235d5b7c7Smrg
98335d5b7c7Smrg	atom = MakeAtom(cm_prop_names[cm_prop_index],
98435d5b7c7Smrg			strlen(cm_prop_names[cm_prop_index]),
98535d5b7c7Smrg			TRUE);
98635d5b7c7Smrg	if (!atom)
98735d5b7c7Smrg		return BadAlloc;
98835d5b7c7Smrg
98935d5b7c7Smrg	if (need_configure) {
99035d5b7c7Smrg		err = RRConfigureOutputProperty(output->randr_output, atom,
99135d5b7c7Smrg						FALSE, TRUE, FALSE, 2, range);
99235d5b7c7Smrg		if (err) {
99335d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
99435d5b7c7Smrg				   "Configuring color management property %s failed with %d\n",
99535d5b7c7Smrg				   cm_prop_names[cm_prop_index], err);
99635d5b7c7Smrg			return err;
99735d5b7c7Smrg		}
99835d5b7c7Smrg	}
99935d5b7c7Smrg
100035d5b7c7Smrg	/* Always issue a non-pending change. We'll push cm properties
100135d5b7c7Smrg	 * ourselves.
100235d5b7c7Smrg	 */
100335d5b7c7Smrg	err = RRChangeOutputProperty(output->randr_output, atom,
100435d5b7c7Smrg				     XA_INTEGER, format,
100535d5b7c7Smrg				     PropModeReplace,
100635d5b7c7Smrg				     length, data, FALSE, FALSE);
100735d5b7c7Smrg	if (err)
100835d5b7c7Smrg		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
100935d5b7c7Smrg			   "Changing color management property %s failed with %d\n",
101035d5b7c7Smrg			   cm_prop_names[cm_prop_index], err);
101135d5b7c7Smrg	return err;
101235d5b7c7Smrg}
101335d5b7c7Smrg
101435d5b7c7Smrg/**
101535d5b7c7Smrg* Stage a color management property. This parses the property value, according
101635d5b7c7Smrg* to the cm property type, then stores it within the driver-private CRTC
101735d5b7c7Smrg* object.
101835d5b7c7Smrg*
101935d5b7c7Smrg* @crtc: The CRTC to stage the new color management properties in
102035d5b7c7Smrg* @cm_prop_index: The color property to stage
102135d5b7c7Smrg* @value: The RandR property value to stage
102235d5b7c7Smrg*
102335d5b7c7Smrg* Return 0 on success, X-defined error code on failure.
102435d5b7c7Smrg*/
102535d5b7c7Smrgstatic int drmmode_crtc_stage_cm_prop(xf86CrtcPtr crtc,
102635d5b7c7Smrg				      enum drmmode_cm_prop cm_prop_index,
102735d5b7c7Smrg				      RRPropertyValuePtr value)
102835d5b7c7Smrg{
102935d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
103035d5b7c7Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
103135d5b7c7Smrg	size_t expected_bytes = 0;
103235d5b7c7Smrg	void **blob_data = NULL;
103335d5b7c7Smrg	Bool use_default = FALSE;
103435d5b7c7Smrg
103535d5b7c7Smrg	/* Update properties on the driver-private CRTC */
103635d5b7c7Smrg	switch (cm_prop_index) {
103735d5b7c7Smrg	case CM_GAMMA_LUT:
103835d5b7c7Smrg		/* Calculate the expected size of value in bytes */
103935d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
104035d5b7c7Smrg					drmmode->gamma_lut_size;
104135d5b7c7Smrg
104235d5b7c7Smrg		/* For gamma and degamma, we allow a default SRGB curve to be
104335d5b7c7Smrg		 * set via setting a single element
104435d5b7c7Smrg		 *
104535d5b7c7Smrg		 * Otherwise, value size is in terms of the value format.
104635d5b7c7Smrg		 * Ensure it's also in bytes (<< 1) before comparing with the
104735d5b7c7Smrg		 * expected bytes.
104835d5b7c7Smrg		 */
104935d5b7c7Smrg		if (value->size == 1)
105035d5b7c7Smrg			use_default = TRUE;
105135d5b7c7Smrg		else if (value->type != XA_INTEGER || value->format != 16 ||
105235d5b7c7Smrg			 (size_t)(value->size << 1) != expected_bytes)
105335d5b7c7Smrg			return BadLength;
105435d5b7c7Smrg
105535d5b7c7Smrg		blob_data = (void**)&drmmode_crtc->gamma_lut;
105635d5b7c7Smrg		break;
105735d5b7c7Smrg	case CM_DEGAMMA_LUT:
105835d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
105935d5b7c7Smrg					drmmode->degamma_lut_size;
106035d5b7c7Smrg
106135d5b7c7Smrg		if (value->size == 1)
106235d5b7c7Smrg			use_default = TRUE;
106335d5b7c7Smrg		else if (value->type != XA_INTEGER || value->format != 16 ||
106435d5b7c7Smrg			 (size_t)(value->size << 1) != expected_bytes)
106535d5b7c7Smrg			return BadLength;
106635d5b7c7Smrg
106735d5b7c7Smrg		blob_data = (void**)&drmmode_crtc->degamma_lut;
106835d5b7c7Smrg		break;
106935d5b7c7Smrg	case CM_CTM:
107035d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_ctm);
107135d5b7c7Smrg
107235d5b7c7Smrg		if (value->size == 1)
107335d5b7c7Smrg			use_default = TRUE;
107435d5b7c7Smrg		if (value->type != XA_INTEGER || value->format != 32 ||
107535d5b7c7Smrg		    (size_t)(value->size << 2) != expected_bytes)
107635d5b7c7Smrg			return BadLength;
107735d5b7c7Smrg
107835d5b7c7Smrg		blob_data = (void**)&drmmode_crtc->ctm;
107935d5b7c7Smrg		break;
108035d5b7c7Smrg	default:
108135d5b7c7Smrg		return BadName;
108235d5b7c7Smrg	}
108335d5b7c7Smrg
108435d5b7c7Smrg	free(*blob_data);
108535d5b7c7Smrg	if (!use_default) {
108635d5b7c7Smrg		*blob_data = malloc(expected_bytes);
108735d5b7c7Smrg		if (!*blob_data)
108835d5b7c7Smrg			return BadAlloc;
108935d5b7c7Smrg		memcpy(*blob_data, value->data, expected_bytes);
109035d5b7c7Smrg	} else
109135d5b7c7Smrg		*blob_data = NULL;
109235d5b7c7Smrg
109335d5b7c7Smrg	return Success;
109435d5b7c7Smrg}
109535d5b7c7Smrg
109635d5b7c7Smrg/**
109735d5b7c7Smrg * Push staged color management properties on the CRTC to DRM.
109835d5b7c7Smrg *
109935d5b7c7Smrg * @crtc: The CRTC containing staged properties
110035d5b7c7Smrg * @cm_prop_index: The color property to push
110135d5b7c7Smrg *
110235d5b7c7Smrg * Return 0 on success, X-defined error codes on failure.
110335d5b7c7Smrg */
110435d5b7c7Smrgstatic int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
110535d5b7c7Smrg				     enum drmmode_cm_prop cm_prop_index)
110635d5b7c7Smrg{
110735d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
110835d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
110935d5b7c7Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
111035d5b7c7Smrg	Bool free_blob_data = FALSE;
111135d5b7c7Smrg	uint32_t created_blob_id = 0;
111235d5b7c7Smrg	uint32_t drm_prop_id;
111335d5b7c7Smrg	size_t expected_bytes = 0;
111435d5b7c7Smrg	void *blob_data = NULL;
111535d5b7c7Smrg	int ret;
111635d5b7c7Smrg
111735d5b7c7Smrg	switch (cm_prop_index) {
111835d5b7c7Smrg	case CM_GAMMA_LUT:
111935d5b7c7Smrg		/* Calculate the expected size of value in bytes */
112035d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
112135d5b7c7Smrg					drmmode->gamma_lut_size;
112235d5b7c7Smrg
112335d5b7c7Smrg		/* Legacy gamma LUT is disabled on deep 30bpp color. In which
112435d5b7c7Smrg		 * case, directly use non-legacy LUT.
112535d5b7c7Smrg		 */
112635d5b7c7Smrg		if (!crtc->funcs->gamma_set) {
112735d5b7c7Smrg			blob_data = drmmode_crtc->gamma_lut;
112835d5b7c7Smrg			goto do_push;
112935d5b7c7Smrg		}
113035d5b7c7Smrg
113135d5b7c7Smrg		blob_data = malloc(expected_bytes);
113235d5b7c7Smrg		if (!blob_data)
113335d5b7c7Smrg			return BadAlloc;
113435d5b7c7Smrg
113535d5b7c7Smrg		free_blob_data = TRUE;
113635d5b7c7Smrg		/*
113735d5b7c7Smrg		 * Compose legacy and non-legacy LUT if non-legacy was set.
113835d5b7c7Smrg		 * Otherwise, interpolate legacy LUT to non-legacy size.
113935d5b7c7Smrg		 */
114035d5b7c7Smrg		if (drmmode_crtc->gamma_lut) {
114135d5b7c7Smrg			drmmode_lut_compose(crtc->gamma_red,
114235d5b7c7Smrg					    crtc->gamma_green,
114335d5b7c7Smrg					    crtc->gamma_blue,
114435d5b7c7Smrg					    drmmode_crtc->gamma_lut,
114535d5b7c7Smrg					    blob_data, crtc->gamma_size,
114635d5b7c7Smrg					    drmmode->gamma_lut_size);
114735d5b7c7Smrg		} else {
114835d5b7c7Smrg			drmmode_lut_interpolate(crtc->gamma_red,
114935d5b7c7Smrg						crtc->gamma_green,
115035d5b7c7Smrg						crtc->gamma_blue,
115135d5b7c7Smrg						blob_data,
115235d5b7c7Smrg						crtc->gamma_size,
115335d5b7c7Smrg						drmmode->gamma_lut_size);
115435d5b7c7Smrg		}
115535d5b7c7Smrg		break;
115635d5b7c7Smrg	case CM_DEGAMMA_LUT:
115735d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
115835d5b7c7Smrg					drmmode->degamma_lut_size;
115935d5b7c7Smrg		blob_data = drmmode_crtc->degamma_lut;
116035d5b7c7Smrg		break;
116135d5b7c7Smrg	case CM_CTM:
116235d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_ctm);
116335d5b7c7Smrg		blob_data = drmmode_crtc->ctm;
116435d5b7c7Smrg		break;
116535d5b7c7Smrg	default:
116635d5b7c7Smrg		return BadName;
116735d5b7c7Smrg	}
116835d5b7c7Smrg
116935d5b7c7Smrgdo_push:
117035d5b7c7Smrg	if (blob_data) {
117135d5b7c7Smrg		ret = drmModeCreatePropertyBlob(pAMDGPUEnt->fd,
117235d5b7c7Smrg						blob_data, expected_bytes,
117335d5b7c7Smrg						&created_blob_id);
117435d5b7c7Smrg		if (ret) {
117535d5b7c7Smrg			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
117635d5b7c7Smrg				   "Creating DRM blob failed with errno %d\n",
117735d5b7c7Smrg				   ret);
117835d5b7c7Smrg			if (free_blob_data)
117935d5b7c7Smrg				free(blob_data);
118035d5b7c7Smrg			return BadRequest;
118135d5b7c7Smrg		}
118235d5b7c7Smrg	}
118335d5b7c7Smrg
118435d5b7c7Smrg	drm_prop_id = drmmode_crtc->drmmode->cm_prop_ids[cm_prop_index];
118535d5b7c7Smrg	ret = drmModeObjectSetProperty(pAMDGPUEnt->fd,
118635d5b7c7Smrg				       drmmode_crtc->mode_crtc->crtc_id,
118735d5b7c7Smrg				       DRM_MODE_OBJECT_CRTC,
118835d5b7c7Smrg				       drm_prop_id,
118935d5b7c7Smrg				       (uint64_t)created_blob_id);
119035d5b7c7Smrg
119135d5b7c7Smrg	/* If successful, kernel will have a reference already. Safe to destroy
119235d5b7c7Smrg	 * the blob either way.
119335d5b7c7Smrg	 */
119435d5b7c7Smrg	if (blob_data)
119535d5b7c7Smrg		drmModeDestroyPropertyBlob(pAMDGPUEnt->fd, created_blob_id);
119635d5b7c7Smrg
119735d5b7c7Smrg	if (ret) {
119835d5b7c7Smrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
119935d5b7c7Smrg			   "Setting DRM property blob failed with errno %d\n",
120035d5b7c7Smrg			   ret);
120135d5b7c7Smrg		if (free_blob_data)
120235d5b7c7Smrg			free(blob_data);
120335d5b7c7Smrg		return BadRequest;
120435d5b7c7Smrg	}
120535d5b7c7Smrg
120635d5b7c7Smrg	if (free_blob_data)
120735d5b7c7Smrg		free(blob_data);
120835d5b7c7Smrg
120935d5b7c7Smrg	return Success;
121035d5b7c7Smrg}
121135d5b7c7Smrg
121224b90cf4Smrgstatic void
121324b90cf4Smrgdrmmode_crtc_gamma_do_set(xf86CrtcPtr crtc, uint16_t *red, uint16_t *green,
121424b90cf4Smrg			  uint16_t *blue, int size)
121524b90cf4Smrg{
121624b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
121724b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
121835d5b7c7Smrg	int ret;
121935d5b7c7Smrg
122035d5b7c7Smrg	/* Use legacy if no support for non-legacy gamma */
122135d5b7c7Smrg	if (!drmmode_cm_enabled(drmmode_crtc->drmmode)) {
122235d5b7c7Smrg		drmModeCrtcSetGamma(pAMDGPUEnt->fd,
122335d5b7c7Smrg				    drmmode_crtc->mode_crtc->crtc_id,
122435d5b7c7Smrg				    size, red, green, blue);
122535d5b7c7Smrg		return;
122635d5b7c7Smrg	}
122724b90cf4Smrg
122835d5b7c7Smrg	ret = drmmode_crtc_push_cm_prop(crtc, CM_GAMMA_LUT);
122935d5b7c7Smrg	if (ret)
123035d5b7c7Smrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
123135d5b7c7Smrg			   "Setting Gamma LUT failed with errno %d\n",
123235d5b7c7Smrg			   ret);
123324b90cf4Smrg}
123424b90cf4Smrg
123524b90cf4SmrgBool
123624b90cf4Smrgdrmmode_set_mode(xf86CrtcPtr crtc, struct drmmode_fb *fb, DisplayModePtr mode,
123724b90cf4Smrg		 int x, int y)
123824b90cf4Smrg{
123924b90cf4Smrg	ScrnInfoPtr scrn = crtc->scrn;
124024b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
124124b90cf4Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
124224b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
124324b90cf4Smrg	uint32_t *output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
124424b90cf4Smrg	int output_count = 0;
124524b90cf4Smrg	drmModeModeInfo kmode;
124624b90cf4Smrg	Bool ret;
124724b90cf4Smrg	int i;
124824b90cf4Smrg
124924b90cf4Smrg	if (!output_ids)
125024b90cf4Smrg		return FALSE;
125124b90cf4Smrg
125224b90cf4Smrg	for (i = 0; i < xf86_config->num_output; i++) {
125324b90cf4Smrg		xf86OutputPtr output = xf86_config->output[i];
125424b90cf4Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
125524b90cf4Smrg
125624b90cf4Smrg		if (output->crtc != crtc)
125724b90cf4Smrg			continue;
125824b90cf4Smrg
125924b90cf4Smrg		output_ids[output_count] = drmmode_output->mode_output->connector_id;
126024b90cf4Smrg		output_count++;
126124b90cf4Smrg	}
126224b90cf4Smrg
126324b90cf4Smrg	drmmode_ConvertToKMode(scrn, &kmode, mode);
126424b90cf4Smrg
126524b90cf4Smrg	ret = drmModeSetCrtc(pAMDGPUEnt->fd,
126624b90cf4Smrg			     drmmode_crtc->mode_crtc->crtc_id,
126724b90cf4Smrg			     fb->handle, x, y, output_ids,
126824b90cf4Smrg			     output_count, &kmode) == 0;
126924b90cf4Smrg
127024b90cf4Smrg	if (ret) {
127124b90cf4Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, fb);
127224b90cf4Smrg	} else {
127324b90cf4Smrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
127424b90cf4Smrg			   "failed to set mode: %s\n", strerror(errno));
127524b90cf4Smrg	}
127624b90cf4Smrg
127724b90cf4Smrg	free(output_ids);
127824b90cf4Smrg	return ret;
127924b90cf4Smrg}
128024b90cf4Smrg
1281d6c0b56eSmrgstatic Bool
1282d6c0b56eSmrgdrmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
1283d6c0b56eSmrg		       Rotation rotation, int x, int y)
1284d6c0b56eSmrg{
1285d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1286d6c0b56eSmrg	ScreenPtr pScreen = pScrn->pScreen;
1287d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1288d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1289d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
1290d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
129111bf0794Smrg	unsigned scanout_id = 0;
1292d6c0b56eSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
1293d6c0b56eSmrg	int saved_x, saved_y;
1294d6c0b56eSmrg	Rotation saved_rotation;
1295d6c0b56eSmrg	DisplayModeRec saved_mode;
1296504d986fSmrg	Bool ret = FALSE;
1297d6c0b56eSmrg	int i;
129824b90cf4Smrg	struct drmmode_fb *fb = NULL;
129924b90cf4Smrg
130024b90cf4Smrg	/* The root window contents may be undefined before the WindowExposures
130124b90cf4Smrg	 * hook is called for it, so bail if we get here before that
130224b90cf4Smrg	 */
130324b90cf4Smrg	if (pScreen->WindowExposures == AMDGPUWindowExposures_oneshot)
130424b90cf4Smrg		return FALSE;
1305d6c0b56eSmrg
1306d6c0b56eSmrg	saved_mode = crtc->mode;
1307d6c0b56eSmrg	saved_x = crtc->x;
1308d6c0b56eSmrg	saved_y = crtc->y;
1309d6c0b56eSmrg	saved_rotation = crtc->rotation;
1310d6c0b56eSmrg
1311d6c0b56eSmrg	if (mode) {
1312d6c0b56eSmrg		crtc->mode = *mode;
1313d6c0b56eSmrg		crtc->x = x;
1314d6c0b56eSmrg		crtc->y = y;
1315d6c0b56eSmrg		crtc->rotation = rotation;
1316d6c0b56eSmrg
1317d6c0b56eSmrg		if (!drmmode_handle_transform(crtc))
1318d6c0b56eSmrg			goto done;
1319d6c0b56eSmrg
132011bf0794Smrg		drmmode_crtc_update_tear_free(crtc);
132111bf0794Smrg		if (drmmode_crtc->tear_free)
132211bf0794Smrg			scanout_id = drmmode_crtc->scanout_id;
132335d5b7c7Smrg		else
132435d5b7c7Smrg			drmmode_crtc->scanout_id = 0;
1325d6c0b56eSmrg
132624b90cf4Smrg		if (drmmode_crtc->prime_scanout_pixmap) {
132711bf0794Smrg			drmmode_crtc_prime_scanout_update(crtc, mode, scanout_id,
132824b90cf4Smrg							  &fb, &x, &y);
132924b90cf4Smrg		} else if (drmmode_crtc->rotate.pixmap) {
133024b90cf4Smrg			fb = amdgpu_pixmap_get_fb(drmmode_crtc->rotate.pixmap);
1331d6c0b56eSmrg			x = y = 0;
133211bf0794Smrg
133324b90cf4Smrg		} else if (!pScreen->isGPU &&
133411bf0794Smrg			   (drmmode_crtc->tear_free ||
1335504d986fSmrg			    crtc->driverIsPerformingTransform ||
1336504d986fSmrg			    info->shadow_primary)) {
133711bf0794Smrg			drmmode_crtc_scanout_update(crtc, mode, scanout_id,
133824b90cf4Smrg						    &fb, &x, &y);
1339d6c0b56eSmrg		}
1340d6c0b56eSmrg
134124b90cf4Smrg		if (!fb)
134224b90cf4Smrg			fb = amdgpu_pixmap_get_fb(pScreen->GetWindowPixmap(pScreen->root));
134324b90cf4Smrg		if (!fb) {
134424b90cf4Smrg			union gbm_bo_handle bo_handle;
134524b90cf4Smrg
134624b90cf4Smrg			bo_handle = gbm_bo_get_handle(info->front_buffer->bo.gbm);
134724b90cf4Smrg			fb = amdgpu_fb_create(pScrn, pAMDGPUEnt->fd,
134824b90cf4Smrg					      pScrn->virtualX, pScrn->virtualY,
134924b90cf4Smrg					      pScrn->displayWidth * info->pixel_bytes,
135024b90cf4Smrg					      bo_handle.u32);
135124b90cf4Smrg			/* Prevent refcnt of ad-hoc FBs from reaching 2 */
135224b90cf4Smrg			drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, NULL);
135324b90cf4Smrg			drmmode_crtc->fb = fb;
135424b90cf4Smrg		}
135524b90cf4Smrg		if (!fb) {
135624b90cf4Smrg			ErrorF("failed to add FB for modeset\n");
135724b90cf4Smrg			goto done;
1358504d986fSmrg		}
1359504d986fSmrg
136035d5b7c7Smrg		amdgpu_drm_wait_pending_flip(crtc);
136124b90cf4Smrg
136224b90cf4Smrg		if (!drmmode_set_mode(crtc, fb, mode, x, y))
1363d6c0b56eSmrg			goto done;
136424b90cf4Smrg
136524b90cf4Smrg		ret = TRUE;
1366d6c0b56eSmrg
1367d6c0b56eSmrg		if (pScreen)
1368d6c0b56eSmrg			xf86CrtcSetScreenSubpixelOrder(pScreen);
1369d6c0b56eSmrg
1370d6c0b56eSmrg		drmmode_crtc->need_modeset = FALSE;
1371d6c0b56eSmrg
1372d6c0b56eSmrg		/* go through all the outputs and force DPMS them back on? */
1373d6c0b56eSmrg		for (i = 0; i < xf86_config->num_output; i++) {
1374d6c0b56eSmrg			xf86OutputPtr output = xf86_config->output[i];
1375d6c0b56eSmrg
1376d6c0b56eSmrg			if (output->crtc != crtc)
1377d6c0b56eSmrg				continue;
1378d6c0b56eSmrg
1379d6c0b56eSmrg			output->funcs->dpms(output, DPMSModeOn);
1380d6c0b56eSmrg		}
1381d6c0b56eSmrg	}
1382d6c0b56eSmrg
1383d6c0b56eSmrg	/* Compute index of this CRTC into xf86_config->crtc */
1384d6c0b56eSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
1385d6c0b56eSmrg		if (xf86_config->crtc[i] != crtc)
1386d6c0b56eSmrg			continue;
1387d6c0b56eSmrg
1388d6c0b56eSmrg		if (!crtc->enabled || drmmode_can_use_hw_cursor(crtc))
1389d6c0b56eSmrg			info->hwcursor_disabled &= ~(1 << i);
1390d6c0b56eSmrg		else
1391d6c0b56eSmrg			info->hwcursor_disabled |= 1 << i;
1392d6c0b56eSmrg
1393d6c0b56eSmrg		break;
1394d6c0b56eSmrg	}
1395d6c0b56eSmrg
1396d6c0b56eSmrg#ifndef HAVE_XF86_CURSOR_RESET_CURSOR
1397d6c0b56eSmrg	if (!info->hwcursor_disabled)
1398d6c0b56eSmrg		xf86_reload_cursors(pScreen);
1399d6c0b56eSmrg#endif
1400d6c0b56eSmrg
1401d6c0b56eSmrgdone:
1402d6c0b56eSmrg	if (!ret) {
1403d6c0b56eSmrg		crtc->x = saved_x;
1404d6c0b56eSmrg		crtc->y = saved_y;
1405d6c0b56eSmrg		crtc->rotation = saved_rotation;
1406d6c0b56eSmrg		crtc->mode = saved_mode;
1407504d986fSmrg	} else {
1408d6c0b56eSmrg		crtc->active = TRUE;
1409d6c0b56eSmrg
141024b90cf4Smrg		if (drmmode_crtc->scanout[scanout_id].pixmap &&
141124b90cf4Smrg		    fb != amdgpu_pixmap_get_fb(drmmode_crtc->
141235d5b7c7Smrg					       scanout[scanout_id].pixmap)) {
141335d5b7c7Smrg			amdgpu_drm_abort_entry(drmmode_crtc->scanout_update_pending);
141435d5b7c7Smrg			drmmode_crtc->scanout_update_pending = 0;
1415504d986fSmrg			drmmode_crtc_scanout_free(drmmode_crtc);
141635d5b7c7Smrg		} else if (!drmmode_crtc->tear_free) {
141711bf0794Smrg			drmmode_crtc_scanout_destroy(drmmode,
141811bf0794Smrg						     &drmmode_crtc->scanout[1]);
141911bf0794Smrg		}
1420504d986fSmrg	}
1421504d986fSmrg
142235d5b7c7Smrg	amdgpu_drm_queue_handle_deferred(crtc);
1423d6c0b56eSmrg	return ret;
1424d6c0b56eSmrg}
1425d6c0b56eSmrg
1426d6c0b56eSmrgstatic void drmmode_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
1427d6c0b56eSmrg{
1428d6c0b56eSmrg
1429d6c0b56eSmrg}
1430d6c0b56eSmrg
1431d6c0b56eSmrgstatic void drmmode_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
1432d6c0b56eSmrg{
1433d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1434d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
1435d6c0b56eSmrg
143624b90cf4Smrg#if XF86_CRTC_VERSION < 7
1437d6c0b56eSmrg	if (crtc->driverIsPerformingTransform) {
1438d6c0b56eSmrg		x += crtc->x;
1439d6c0b56eSmrg		y += crtc->y;
1440d6c0b56eSmrg		xf86CrtcTransformCursorPos(crtc, &x, &y);
1441d6c0b56eSmrg	}
1442d6c0b56eSmrg#endif
1443d6c0b56eSmrg
1444d6c0b56eSmrg	drmModeMoveCursor(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
1445d6c0b56eSmrg}
1446d6c0b56eSmrg
144724b90cf4Smrg#if XF86_CRTC_VERSION < 7
1448d6c0b56eSmrg
1449d6c0b56eSmrgstatic int
1450d6c0b56eSmrgdrmmode_cursor_src_offset(Rotation rotation, int width, int height,
1451d6c0b56eSmrg			  int x_dst, int y_dst)
1452d6c0b56eSmrg{
1453d6c0b56eSmrg	int t;
1454d6c0b56eSmrg
1455d6c0b56eSmrg	switch (rotation & 0xf) {
1456d6c0b56eSmrg	case RR_Rotate_90:
1457d6c0b56eSmrg		t = x_dst;
1458d6c0b56eSmrg		x_dst = height - y_dst - 1;
1459d6c0b56eSmrg		y_dst = t;
1460d6c0b56eSmrg		break;
1461d6c0b56eSmrg	case RR_Rotate_180:
1462d6c0b56eSmrg		x_dst = width - x_dst - 1;
1463d6c0b56eSmrg		y_dst = height - y_dst - 1;
1464d6c0b56eSmrg		break;
1465d6c0b56eSmrg	case RR_Rotate_270:
1466d6c0b56eSmrg		t = x_dst;
1467d6c0b56eSmrg		x_dst = y_dst;
1468d6c0b56eSmrg		y_dst = width - t - 1;
1469d6c0b56eSmrg		break;
1470d6c0b56eSmrg	}
1471d6c0b56eSmrg
1472d6c0b56eSmrg	if (rotation & RR_Reflect_X)
1473d6c0b56eSmrg		x_dst = width - x_dst - 1;
1474d6c0b56eSmrg	if (rotation & RR_Reflect_Y)
1475d6c0b56eSmrg		y_dst = height - y_dst - 1;
1476d6c0b56eSmrg
1477d6c0b56eSmrg	return y_dst * height + x_dst;
1478d6c0b56eSmrg}
1479d6c0b56eSmrg
1480d6c0b56eSmrg#endif
1481d6c0b56eSmrg
148235d5b7c7Smrgstatic uint32_t
148335d5b7c7Smrgdrmmode_cursor_gamma_passthrough(xf86CrtcPtr crtc, uint32_t argb)
148435d5b7c7Smrg{
148535d5b7c7Smrg	uint32_t alpha = argb >> 24;
148635d5b7c7Smrg
148735d5b7c7Smrg	if (!alpha)
148835d5b7c7Smrg		return 0;
148935d5b7c7Smrg
149035d5b7c7Smrg	return argb;
149135d5b7c7Smrg}
149235d5b7c7Smrg
149324b90cf4Smrgstatic uint32_t
149424b90cf4Smrgdrmmode_cursor_gamma(xf86CrtcPtr crtc, uint32_t argb)
149524b90cf4Smrg{
149624b90cf4Smrg	uint32_t alpha = argb >> 24;
149724b90cf4Smrg	uint32_t rgb[3];
149824b90cf4Smrg	int i;
149924b90cf4Smrg
150024b90cf4Smrg	if (!alpha)
150124b90cf4Smrg		return 0;
150224b90cf4Smrg
150324b90cf4Smrg	/* Un-premultiply alpha */
150424b90cf4Smrg	for (i = 0; i < 3; i++)
150524b90cf4Smrg		rgb[i] = ((argb >> (i * 8)) & 0xff) * 0xff / alpha;
150624b90cf4Smrg
150724b90cf4Smrg	/* Apply gamma correction and pre-multiply alpha */
150824b90cf4Smrg	rgb[0] = (crtc->gamma_blue[rgb[0]] >> 8) * alpha / 0xff;
150924b90cf4Smrg	rgb[1] = (crtc->gamma_green[rgb[1]] >> 8) * alpha / 0xff;
151024b90cf4Smrg	rgb[2] = (crtc->gamma_red[rgb[2]] >> 8) * alpha / 0xff;
151124b90cf4Smrg
151224b90cf4Smrg	return alpha << 24 | rgb[2] << 16 | rgb[1] << 8 | rgb[0];
151324b90cf4Smrg}
151424b90cf4Smrg
1515d6c0b56eSmrgstatic void drmmode_do_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image, uint32_t *ptr)
1516d6c0b56eSmrg{
1517d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1518d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
151935d5b7c7Smrg	uint32_t (*cursor_gamma)(xf86CrtcPtr crtc, uint32_t argb) =
152035d5b7c7Smrg		drmmode_cursor_gamma;
152135d5b7c7Smrg
152235d5b7c7Smrg	if ((crtc->scrn->depth != 24 && crtc->scrn->depth != 32) ||
152335d5b7c7Smrg	    drmmode_cm_enabled(&info->drmmode))
152435d5b7c7Smrg		cursor_gamma = drmmode_cursor_gamma_passthrough;
1525d6c0b56eSmrg
152624b90cf4Smrg#if XF86_CRTC_VERSION < 7
1527d6c0b56eSmrg	if (crtc->driverIsPerformingTransform) {
1528d6c0b56eSmrg		uint32_t cursor_w = info->cursor_w, cursor_h = info->cursor_h;
1529d6c0b56eSmrg		int dstx, dsty;
1530d6c0b56eSmrg		int srcoffset;
1531d6c0b56eSmrg
1532d6c0b56eSmrg		for (dsty = 0; dsty < cursor_h; dsty++) {
1533d6c0b56eSmrg			for (dstx = 0; dstx < cursor_w; dstx++) {
1534d6c0b56eSmrg				srcoffset = drmmode_cursor_src_offset(crtc->rotation,
1535d6c0b56eSmrg								      cursor_w,
1536d6c0b56eSmrg								      cursor_h,
1537d6c0b56eSmrg								      dstx, dsty);
1538d6c0b56eSmrg
1539d6c0b56eSmrg				ptr[dsty * info->cursor_w + dstx] =
154035d5b7c7Smrg					cpu_to_le32(cursor_gamma(crtc, image[srcoffset]));
1541d6c0b56eSmrg			}
1542d6c0b56eSmrg		}
1543d6c0b56eSmrg	} else
1544d6c0b56eSmrg#endif
1545d6c0b56eSmrg	{
1546d6c0b56eSmrg		uint32_t cursor_size = info->cursor_w * info->cursor_h;
1547d6c0b56eSmrg		int i;
1548d6c0b56eSmrg
1549d6c0b56eSmrg		for (i = 0; i < cursor_size; i++)
155035d5b7c7Smrg			ptr[i] = cpu_to_le32(cursor_gamma(crtc, image[i]));
1551d6c0b56eSmrg	}
1552d6c0b56eSmrg}
1553d6c0b56eSmrg
1554d6c0b56eSmrgstatic void drmmode_load_cursor_argb(xf86CrtcPtr crtc, CARD32 * image)
1555d6c0b56eSmrg{
1556d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1557d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1558d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1559d6c0b56eSmrg	uint32_t cursor_size = info->cursor_w * info->cursor_h;
1560d6c0b56eSmrg
1561d6c0b56eSmrg	if (info->gbm) {
1562d6c0b56eSmrg		uint32_t ptr[cursor_size];
1563d6c0b56eSmrg
1564d6c0b56eSmrg		drmmode_do_load_cursor_argb(crtc, image, ptr);
1565d6c0b56eSmrg		gbm_bo_write(drmmode_crtc->cursor_buffer->bo.gbm, ptr, cursor_size * 4);
1566d6c0b56eSmrg	} else {
1567d6c0b56eSmrg		/* cursor should be mapped already */
1568d6c0b56eSmrg		uint32_t *ptr = (uint32_t *) (drmmode_crtc->cursor_buffer->cpu_ptr);
1569d6c0b56eSmrg
1570d6c0b56eSmrg		drmmode_do_load_cursor_argb(crtc, image, ptr);
1571d6c0b56eSmrg	}
1572d6c0b56eSmrg}
1573d6c0b56eSmrg
1574d6c0b56eSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,903,0)
1575d6c0b56eSmrg
1576d6c0b56eSmrgstatic Bool drmmode_load_cursor_argb_check(xf86CrtcPtr crtc, CARD32 * image)
1577d6c0b56eSmrg{
1578d6c0b56eSmrg	if (!drmmode_can_use_hw_cursor(crtc))
1579d6c0b56eSmrg		return FALSE;
1580d6c0b56eSmrg
1581d6c0b56eSmrg	drmmode_load_cursor_argb(crtc, image);
1582d6c0b56eSmrg	return TRUE;
1583d6c0b56eSmrg}
1584d6c0b56eSmrg
1585d6c0b56eSmrg#endif
1586d6c0b56eSmrg
1587d6c0b56eSmrgstatic void drmmode_hide_cursor(xf86CrtcPtr crtc)
1588d6c0b56eSmrg{
1589d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1590d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1591d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1592d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1593d6c0b56eSmrg
1594d6c0b56eSmrg	drmModeSetCursor(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id, 0,
1595d6c0b56eSmrg			 info->cursor_w, info->cursor_h);
1596d6c0b56eSmrg
1597d6c0b56eSmrg}
1598d6c0b56eSmrg
1599d6c0b56eSmrgstatic void drmmode_show_cursor(xf86CrtcPtr crtc)
1600d6c0b56eSmrg{
1601d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1602d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1603d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1604d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1605d6c0b56eSmrg	uint32_t bo_handle;
1606d6c0b56eSmrg	static Bool use_set_cursor2 = TRUE;
1607d6c0b56eSmrg
1608d6c0b56eSmrg	if (!amdgpu_bo_get_handle(drmmode_crtc->cursor_buffer, &bo_handle)) {
1609d6c0b56eSmrg		ErrorF("failed to get BO handle for cursor\n");
1610d6c0b56eSmrg		return;
1611d6c0b56eSmrg	}
1612d6c0b56eSmrg
1613d6c0b56eSmrg	if (use_set_cursor2) {
1614d6c0b56eSmrg		xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
1615d6c0b56eSmrg		CursorPtr cursor = xf86_config->cursor;
1616504d986fSmrg		int xhot = cursor->bits->xhot;
1617504d986fSmrg		int yhot = cursor->bits->yhot;
1618d6c0b56eSmrg		int ret;
1619d6c0b56eSmrg
1620504d986fSmrg		if (crtc->rotation != RR_Rotate_0 &&
1621504d986fSmrg		    crtc->rotation != (RR_Rotate_180 | RR_Reflect_X |
1622504d986fSmrg				       RR_Reflect_Y)) {
1623504d986fSmrg			int t;
1624504d986fSmrg
1625504d986fSmrg			/* Reflect & rotate hotspot position */
1626504d986fSmrg			if (crtc->rotation & RR_Reflect_X)
1627504d986fSmrg				xhot = info->cursor_w - xhot - 1;
1628504d986fSmrg			if (crtc->rotation & RR_Reflect_Y)
1629504d986fSmrg				yhot = info->cursor_h - yhot - 1;
1630504d986fSmrg
1631504d986fSmrg			switch (crtc->rotation & 0xf) {
1632504d986fSmrg			case RR_Rotate_90:
1633504d986fSmrg				t = xhot;
1634504d986fSmrg				xhot = yhot;
1635504d986fSmrg				yhot = info->cursor_w - t - 1;
1636504d986fSmrg				break;
1637504d986fSmrg			case RR_Rotate_180:
1638504d986fSmrg				xhot = info->cursor_w - xhot - 1;
1639504d986fSmrg				yhot = info->cursor_h - yhot - 1;
1640504d986fSmrg				break;
1641504d986fSmrg			case RR_Rotate_270:
1642504d986fSmrg				t = xhot;
1643504d986fSmrg				xhot = info->cursor_h - yhot - 1;
1644504d986fSmrg				yhot = t;
1645504d986fSmrg			}
1646504d986fSmrg		}
1647504d986fSmrg
1648d6c0b56eSmrg		ret = drmModeSetCursor2(pAMDGPUEnt->fd,
1649d6c0b56eSmrg					drmmode_crtc->mode_crtc->crtc_id,
1650d6c0b56eSmrg					bo_handle,
1651d6c0b56eSmrg					info->cursor_w, info->cursor_h,
1652504d986fSmrg					xhot, yhot);
1653d6c0b56eSmrg		if (ret == -EINVAL)
1654d6c0b56eSmrg			use_set_cursor2 = FALSE;
1655d6c0b56eSmrg		else
1656d6c0b56eSmrg			return;
1657d6c0b56eSmrg	}
1658d6c0b56eSmrg
1659d6c0b56eSmrg	drmModeSetCursor(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id, bo_handle,
1660d6c0b56eSmrg			 info->cursor_w, info->cursor_h);
1661d6c0b56eSmrg}
1662d6c0b56eSmrg
166311bf0794Smrg/* Xorg expects a non-NULL return value from drmmode_crtc_shadow_allocate, and
166411bf0794Smrg * passes that back to drmmode_crtc_scanout_create; it doesn't use it for
166511bf0794Smrg * anything else.
166611bf0794Smrg */
166711bf0794Smrgstatic void *
166811bf0794Smrgdrmmode_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
1669d6c0b56eSmrg{
1670d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1671d6c0b56eSmrg
167211bf0794Smrg	if (!drmmode_crtc_scanout_create(crtc, &drmmode_crtc->rotate, width,
167311bf0794Smrg					 height))
167411bf0794Smrg		return NULL;
167511bf0794Smrg
167611bf0794Smrg	return (void*)~0UL;
1677d6c0b56eSmrg}
1678d6c0b56eSmrg
1679d6c0b56eSmrgstatic PixmapPtr
1680d6c0b56eSmrgdrmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
1681d6c0b56eSmrg{
1682d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1683d6c0b56eSmrg
168411bf0794Smrg	if (!data) {
168511bf0794Smrg		drmmode_crtc_scanout_create(crtc, &drmmode_crtc->rotate, width,
168611bf0794Smrg					    height);
168711bf0794Smrg	}
168811bf0794Smrg
168911bf0794Smrg	return drmmode_crtc->rotate.pixmap;
1690d6c0b56eSmrg}
1691d6c0b56eSmrg
1692d6c0b56eSmrgstatic void
1693d6c0b56eSmrgdrmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap,
1694d6c0b56eSmrg			    void *data)
1695d6c0b56eSmrg{
1696d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1697d6c0b56eSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
1698d6c0b56eSmrg
1699d6c0b56eSmrg	drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->rotate);
1700d6c0b56eSmrg}
1701d6c0b56eSmrg
1702d6c0b56eSmrgstatic void
1703d6c0b56eSmrgdrmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t * red, uint16_t * green,
1704d6c0b56eSmrg		       uint16_t * blue, int size)
1705d6c0b56eSmrg{
170624b90cf4Smrg	ScrnInfoPtr scrn = crtc->scrn;
170724b90cf4Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
170824b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
170924b90cf4Smrg	int i;
1710d6c0b56eSmrg
171124b90cf4Smrg	drmmode_crtc_gamma_do_set(crtc, red, green, blue, size);
171224b90cf4Smrg
171324b90cf4Smrg	/* Compute index of this CRTC into xf86_config->crtc */
171424b90cf4Smrg	for (i = 0; xf86_config->crtc[i] != crtc; i++) {}
171524b90cf4Smrg
171624b90cf4Smrg	if (info->hwcursor_disabled & (1 << i))
171724b90cf4Smrg		return;
171824b90cf4Smrg
171924b90cf4Smrg#ifdef HAVE_XF86_CURSOR_RESET_CURSOR
172024b90cf4Smrg	xf86CursorResetCursor(scrn->pScreen);
172124b90cf4Smrg#else
172224b90cf4Smrg	xf86_reload_cursors(scrn->pScreen);
172324b90cf4Smrg#endif
1724d6c0b56eSmrg}
1725d6c0b56eSmrg
1726d6c0b56eSmrgstatic Bool drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
1727d6c0b56eSmrg{
1728d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
172911bf0794Smrg	unsigned scanout_id = drmmode_crtc->scanout_id;
1730504d986fSmrg	ScreenPtr screen = crtc->scrn->pScreen;
1731504d986fSmrg	PixmapDirtyUpdatePtr dirty;
1732d6c0b56eSmrg
1733504d986fSmrg	xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) {
173424b90cf4Smrg		if (amdgpu_dirty_src_equals(dirty, drmmode_crtc->prime_scanout_pixmap)) {
173524b90cf4Smrg			PixmapStopDirtyTracking(dirty->src, dirty->slave_dst);
173624b90cf4Smrg			break;
173724b90cf4Smrg		}
1738d6c0b56eSmrg	}
1739d6c0b56eSmrg
174024b90cf4Smrg	drmmode_crtc_scanout_free(drmmode_crtc);
174124b90cf4Smrg	drmmode_crtc->prime_scanout_pixmap = NULL;
174224b90cf4Smrg
1743504d986fSmrg	if (!ppix)
1744504d986fSmrg		return TRUE;
1745504d986fSmrg
1746504d986fSmrg	if (!drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[0],
1747504d986fSmrg					 ppix->drawable.width,
1748504d986fSmrg					 ppix->drawable.height))
1749504d986fSmrg		return FALSE;
1750d6c0b56eSmrg
175111bf0794Smrg	if (drmmode_crtc->tear_free &&
1752504d986fSmrg	    !drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1],
1753504d986fSmrg					 ppix->drawable.width,
1754504d986fSmrg					 ppix->drawable.height)) {
1755504d986fSmrg		drmmode_crtc_scanout_free(drmmode_crtc);
1756504d986fSmrg		return FALSE;
1757d6c0b56eSmrg	}
1758504d986fSmrg
175924b90cf4Smrg	drmmode_crtc->prime_scanout_pixmap = ppix;
176024b90cf4Smrg
176124b90cf4Smrg#ifdef HAS_DIRTYTRACKING_DRAWABLE_SRC
176224b90cf4Smrg	PixmapStartDirtyTracking(&ppix->drawable,
176324b90cf4Smrg				 drmmode_crtc->scanout[scanout_id].pixmap,
176424b90cf4Smrg				 0, 0, 0, 0, RR_Rotate_0);
176524b90cf4Smrg#elif defined(HAS_DIRTYTRACKING_ROTATION)
176611bf0794Smrg	PixmapStartDirtyTracking(ppix, drmmode_crtc->scanout[scanout_id].pixmap,
1767504d986fSmrg				 0, 0, 0, 0, RR_Rotate_0);
1768d6c0b56eSmrg#elif defined(HAS_DIRTYTRACKING2)
176911bf0794Smrg	PixmapStartDirtyTracking2(ppix, drmmode_crtc->scanout[scanout_id].pixmap,
1770504d986fSmrg				  0, 0, 0, 0);
1771d6c0b56eSmrg#else
177211bf0794Smrg	PixmapStartDirtyTracking(ppix, drmmode_crtc->scanout[scanout_id].pixmap, 0, 0);
1773d6c0b56eSmrg#endif
1774d6c0b56eSmrg	return TRUE;
1775d6c0b56eSmrg}
1776d6c0b56eSmrg
177735d5b7c7Smrgstatic void drmmode_crtc_destroy(xf86CrtcPtr crtc)
177835d5b7c7Smrg{
177935d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
178035d5b7c7Smrg
178135d5b7c7Smrg	drmModeFreeCrtc(drmmode_crtc->mode_crtc);
178235d5b7c7Smrg
178335d5b7c7Smrg	/* Free LUTs and CTM */
178435d5b7c7Smrg	free(drmmode_crtc->gamma_lut);
178535d5b7c7Smrg	free(drmmode_crtc->degamma_lut);
178635d5b7c7Smrg	free(drmmode_crtc->ctm);
178735d5b7c7Smrg
178835d5b7c7Smrg	free(drmmode_crtc);
178935d5b7c7Smrg	crtc->driver_private = NULL;
179035d5b7c7Smrg}
179135d5b7c7Smrg
179235d5b7c7Smrg
1793d6c0b56eSmrgstatic xf86CrtcFuncsRec drmmode_crtc_funcs = {
1794d6c0b56eSmrg	.dpms = drmmode_crtc_dpms,
1795d6c0b56eSmrg	.set_mode_major = drmmode_set_mode_major,
1796d6c0b56eSmrg	.set_cursor_colors = drmmode_set_cursor_colors,
1797d6c0b56eSmrg	.set_cursor_position = drmmode_set_cursor_position,
1798d6c0b56eSmrg	.show_cursor = drmmode_show_cursor,
1799d6c0b56eSmrg	.hide_cursor = drmmode_hide_cursor,
1800d6c0b56eSmrg	.load_cursor_argb = drmmode_load_cursor_argb,
1801d6c0b56eSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,903,0)
1802d6c0b56eSmrg	.load_cursor_argb_check = drmmode_load_cursor_argb_check,
1803d6c0b56eSmrg#endif
1804d6c0b56eSmrg
1805d6c0b56eSmrg	.gamma_set = drmmode_crtc_gamma_set,
1806d6c0b56eSmrg	.shadow_create = drmmode_crtc_shadow_create,
1807d6c0b56eSmrg	.shadow_allocate = drmmode_crtc_shadow_allocate,
1808d6c0b56eSmrg	.shadow_destroy = drmmode_crtc_shadow_destroy,
180935d5b7c7Smrg	.destroy = drmmode_crtc_destroy,
1810d6c0b56eSmrg	.set_scanout_pixmap = drmmode_set_scanout_pixmap,
1811d6c0b56eSmrg};
1812d6c0b56eSmrg
1813d6c0b56eSmrgint drmmode_get_crtc_id(xf86CrtcPtr crtc)
1814d6c0b56eSmrg{
1815d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1816d6c0b56eSmrg	return drmmode_crtc->hw_id;
1817d6c0b56eSmrg}
1818d6c0b56eSmrg
1819d6c0b56eSmrgvoid drmmode_crtc_hw_id(xf86CrtcPtr crtc)
1820d6c0b56eSmrg{
1821d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1822d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1823d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1824d6c0b56eSmrg	int r;
1825d6c0b56eSmrg
1826d6c0b56eSmrg	r = amdgpu_query_crtc_from_id(pAMDGPUEnt->pDev,
1827d6c0b56eSmrg				      drmmode_crtc->mode_crtc->crtc_id,
1828d6c0b56eSmrg				      &drmmode_crtc->hw_id);
1829d6c0b56eSmrg	if (r)
1830d6c0b56eSmrg		drmmode_crtc->hw_id = -1;
1831d6c0b56eSmrg}
1832d6c0b56eSmrg
183335d5b7c7Smrg/**
183435d5b7c7Smrg * Initialize color management properties for the given CRTC by programming
183535d5b7c7Smrg * the default gamma/degamma LUTs and CTM.
183635d5b7c7Smrg *
183735d5b7c7Smrg * If the CRTC does not support color management, or if errors occur during
183835d5b7c7Smrg * initialization, all color properties on the driver-private CRTC will left
183935d5b7c7Smrg * as NULL.
184035d5b7c7Smrg *
184135d5b7c7Smrg * @drm_fd: DRM file descriptor
184235d5b7c7Smrg * @crtc: CRTC to initialize color management on.
184335d5b7c7Smrg */
184435d5b7c7Smrgstatic void drmmode_crtc_cm_init(int drm_fd, xf86CrtcPtr crtc)
184535d5b7c7Smrg{
184635d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
184735d5b7c7Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
184835d5b7c7Smrg	int i;
184935d5b7c7Smrg
185035d5b7c7Smrg	if (!drmmode_cm_enabled(drmmode))
185135d5b7c7Smrg		return;
185235d5b7c7Smrg
185335d5b7c7Smrg	/* Init CTM to identity. Values are in S31.32 fixed-point format */
185435d5b7c7Smrg	drmmode_crtc->ctm = calloc(1, sizeof(*drmmode_crtc->ctm));
185535d5b7c7Smrg	if (!drmmode_crtc->ctm) {
185635d5b7c7Smrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
185735d5b7c7Smrg			   "Memory error initializing CTM for CRTC%d",
185835d5b7c7Smrg			   drmmode_get_crtc_id(crtc));
185935d5b7c7Smrg		return;
186035d5b7c7Smrg	}
186135d5b7c7Smrg
186235d5b7c7Smrg	drmmode_crtc->ctm->matrix[0] = drmmode_crtc->ctm->matrix[4] =
186335d5b7c7Smrg		drmmode_crtc->ctm->matrix[8] = (uint64_t)1 << 32;
186435d5b7c7Smrg
186535d5b7c7Smrg	/* Push properties to reset properties currently in hardware */
186635d5b7c7Smrg	for (i = 0; i < CM_GAMMA_LUT; i++) {
186735d5b7c7Smrg		if (drmmode_crtc_push_cm_prop(crtc, i))
186835d5b7c7Smrg			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
186935d5b7c7Smrg				   "Failed to initialize color management "
187035d5b7c7Smrg				   "property %s on CRTC%d. Property value may "
187135d5b7c7Smrg				   "not reflect actual hardware state.\n",
187235d5b7c7Smrg				   cm_prop_names[i],
187335d5b7c7Smrg				   drmmode_get_crtc_id(crtc));
187435d5b7c7Smrg	}
187535d5b7c7Smrg}
187635d5b7c7Smrg
1877d6c0b56eSmrgstatic unsigned int
1878d6c0b56eSmrgdrmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
1879d6c0b56eSmrg{
1880d6c0b56eSmrg	xf86CrtcPtr crtc;
1881d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc;
1882d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
188324b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1884d6c0b56eSmrg
188524b90cf4Smrg	crtc = xf86CrtcCreate(pScrn, &info->drmmode_crtc_funcs);
188635d5b7c7Smrg	if (!crtc)
1887d6c0b56eSmrg		return 0;
1888d6c0b56eSmrg
1889d6c0b56eSmrg	drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
1890d6c0b56eSmrg	drmmode_crtc->mode_crtc =
1891d6c0b56eSmrg	    drmModeGetCrtc(pAMDGPUEnt->fd, mode_res->crtcs[num]);
1892d6c0b56eSmrg	drmmode_crtc->drmmode = drmmode;
1893d6c0b56eSmrg	drmmode_crtc->dpms_mode = DPMSModeOff;
1894d6c0b56eSmrg	crtc->driver_private = drmmode_crtc;
1895d6c0b56eSmrg	drmmode_crtc_hw_id(crtc);
1896d6c0b56eSmrg
189735d5b7c7Smrg	drmmode_crtc_cm_init(pAMDGPUEnt->fd, crtc);
189835d5b7c7Smrg
1899d6c0b56eSmrg	/* Mark num'th crtc as in use on this device. */
1900d6c0b56eSmrg	pAMDGPUEnt->assigned_crtcs |= (1 << num);
1901d6c0b56eSmrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
1902d6c0b56eSmrg		       "Allocated crtc nr. %d to this screen.\n", num);
1903d6c0b56eSmrg
1904d6c0b56eSmrg	return 1;
1905d6c0b56eSmrg}
1906d6c0b56eSmrg
190724b90cf4Smrg/*
190824b90cf4Smrg * Update all of the property values for an output
190924b90cf4Smrg */
191024b90cf4Smrgstatic void
191124b90cf4Smrgdrmmode_output_update_properties(xf86OutputPtr output)
191224b90cf4Smrg{
191324b90cf4Smrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
191424b90cf4Smrg	int i, j, k;
191524b90cf4Smrg	int err;
191624b90cf4Smrg	drmModeConnectorPtr koutput;
191724b90cf4Smrg
191824b90cf4Smrg	/* Use the most recently fetched values from the kernel */
191924b90cf4Smrg	koutput = drmmode_output->mode_output;
192024b90cf4Smrg
192124b90cf4Smrg	if (!koutput)
192224b90cf4Smrg		return;
192324b90cf4Smrg
192424b90cf4Smrg	for (i = 0; i < drmmode_output->num_props; i++) {
192524b90cf4Smrg		drmmode_prop_ptr p = &drmmode_output->props[i];
192624b90cf4Smrg
192724b90cf4Smrg		for (j = 0; j < koutput->count_props; j++) {
192824b90cf4Smrg			if (koutput->props[j] != p->mode_prop->prop_id)
192924b90cf4Smrg				continue;
193024b90cf4Smrg
193124b90cf4Smrg			/* Check to see if the property value has changed */
193224b90cf4Smrg			if (koutput->prop_values[j] == p->value)
193324b90cf4Smrg				break;
193424b90cf4Smrg
193524b90cf4Smrg			p->value = koutput->prop_values[j];
193624b90cf4Smrg
193724b90cf4Smrg			if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
193824b90cf4Smrg				INT32 value = p->value;
193924b90cf4Smrg
194024b90cf4Smrg				err = RRChangeOutputProperty(output->randr_output,
194124b90cf4Smrg							     p->atoms[0], XA_INTEGER,
194224b90cf4Smrg							     32, PropModeReplace, 1,
194324b90cf4Smrg							     &value, FALSE, TRUE);
194424b90cf4Smrg				if (err != 0) {
194524b90cf4Smrg					xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
194624b90cf4Smrg						   "RRChangeOutputProperty error, %d\n",
194724b90cf4Smrg						   err);
194824b90cf4Smrg				}
194924b90cf4Smrg			} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
195024b90cf4Smrg				for (k = 0; k < p->mode_prop->count_enums; k++) {
195124b90cf4Smrg					if (p->mode_prop->enums[k].value == p->value)
195224b90cf4Smrg						break;
195324b90cf4Smrg				}
195424b90cf4Smrg				if (k < p->mode_prop->count_enums) {
195524b90cf4Smrg					err = RRChangeOutputProperty(output->randr_output,
195624b90cf4Smrg								     p->atoms[0], XA_ATOM,
195724b90cf4Smrg								     32, PropModeReplace, 1,
195824b90cf4Smrg								     &p->atoms[k + 1], FALSE,
195924b90cf4Smrg								     TRUE);
196024b90cf4Smrg					if (err != 0) {
196124b90cf4Smrg						xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
196224b90cf4Smrg							   "RRChangeOutputProperty error, %d\n",
196324b90cf4Smrg							   err);
196424b90cf4Smrg					}
196524b90cf4Smrg				}
196624b90cf4Smrg			}
196724b90cf4Smrg
196824b90cf4Smrg			break;
196924b90cf4Smrg		}
197024b90cf4Smrg        }
197124b90cf4Smrg}
197224b90cf4Smrg
1973d6c0b56eSmrgstatic xf86OutputStatus drmmode_output_detect(xf86OutputPtr output)
1974d6c0b56eSmrg{
1975d6c0b56eSmrg	/* go to the hw and retrieve a new output struct */
1976d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
1977d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
1978d6c0b56eSmrg	xf86OutputStatus status;
1979d6c0b56eSmrg	drmModeFreeConnector(drmmode_output->mode_output);
1980d6c0b56eSmrg
1981d6c0b56eSmrg	drmmode_output->mode_output =
1982d6c0b56eSmrg	    drmModeGetConnector(pAMDGPUEnt->fd, drmmode_output->output_id);
198324b90cf4Smrg	if (!drmmode_output->mode_output) {
198424b90cf4Smrg		drmmode_output->output_id = -1;
1985d6c0b56eSmrg		return XF86OutputStatusDisconnected;
198624b90cf4Smrg	}
198724b90cf4Smrg
198824b90cf4Smrg	drmmode_output_update_properties(output);
1989d6c0b56eSmrg
1990d6c0b56eSmrg	switch (drmmode_output->mode_output->connection) {
1991d6c0b56eSmrg	case DRM_MODE_CONNECTED:
1992d6c0b56eSmrg		status = XF86OutputStatusConnected;
1993d6c0b56eSmrg		break;
1994d6c0b56eSmrg	case DRM_MODE_DISCONNECTED:
1995d6c0b56eSmrg		status = XF86OutputStatusDisconnected;
1996d6c0b56eSmrg		break;
1997d6c0b56eSmrg	default:
1998d6c0b56eSmrg	case DRM_MODE_UNKNOWNCONNECTION:
1999d6c0b56eSmrg		status = XF86OutputStatusUnknown;
2000d6c0b56eSmrg		break;
2001d6c0b56eSmrg	}
2002d6c0b56eSmrg	return status;
2003d6c0b56eSmrg}
2004d6c0b56eSmrg
2005d6c0b56eSmrgstatic Bool
2006d6c0b56eSmrgdrmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
2007d6c0b56eSmrg{
2008d6c0b56eSmrg	return MODE_OK;
2009d6c0b56eSmrg}
2010d6c0b56eSmrg
201124b90cf4Smrgstatic int
201224b90cf4Smrgkoutput_get_prop_idx(int fd, drmModeConnectorPtr koutput,
201324b90cf4Smrg        int type, const char *name)
201424b90cf4Smrg{
201524b90cf4Smrg    int idx = -1;
201624b90cf4Smrg
201724b90cf4Smrg    for (int i = 0; i < koutput->count_props; i++) {
201824b90cf4Smrg        drmModePropertyPtr prop = drmModeGetProperty(fd, koutput->props[i]);
201924b90cf4Smrg
202024b90cf4Smrg        if (!prop)
202124b90cf4Smrg            continue;
202224b90cf4Smrg
202324b90cf4Smrg        if (drm_property_type_is(prop, type) && !strcmp(prop->name, name))
202424b90cf4Smrg            idx = i;
202524b90cf4Smrg
202624b90cf4Smrg        drmModeFreeProperty(prop);
202724b90cf4Smrg
202824b90cf4Smrg        if (idx > -1)
202924b90cf4Smrg            break;
203024b90cf4Smrg    }
203124b90cf4Smrg
203224b90cf4Smrg    return idx;
203324b90cf4Smrg}
203424b90cf4Smrg
203524b90cf4Smrgstatic int
203624b90cf4Smrgkoutput_get_prop_id(int fd, drmModeConnectorPtr koutput,
203724b90cf4Smrg        int type, const char *name)
203824b90cf4Smrg{
203924b90cf4Smrg    int idx = koutput_get_prop_idx(fd, koutput, type, name);
204024b90cf4Smrg
204124b90cf4Smrg    return (idx > -1) ? koutput->props[idx] : -1;
204224b90cf4Smrg}
204324b90cf4Smrg
204424b90cf4Smrgstatic drmModePropertyBlobPtr
204524b90cf4Smrgkoutput_get_prop_blob(int fd, drmModeConnectorPtr koutput, const char *name)
204624b90cf4Smrg{
204724b90cf4Smrg    drmModePropertyBlobPtr blob = NULL;
204824b90cf4Smrg    int idx = koutput_get_prop_idx(fd, koutput, DRM_MODE_PROP_BLOB, name);
204924b90cf4Smrg
205024b90cf4Smrg    if (idx > -1)
205124b90cf4Smrg        blob = drmModeGetPropertyBlob(fd, koutput->prop_values[idx]);
205224b90cf4Smrg
205324b90cf4Smrg    return blob;
205424b90cf4Smrg}
205524b90cf4Smrg
2056d6c0b56eSmrgstatic DisplayModePtr drmmode_output_get_modes(xf86OutputPtr output)
2057d6c0b56eSmrg{
2058d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2059d6c0b56eSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
2060d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
2061d6c0b56eSmrg	int i;
2062d6c0b56eSmrg	DisplayModePtr Modes = NULL, Mode;
2063d6c0b56eSmrg	xf86MonPtr mon = NULL;
2064d6c0b56eSmrg
2065d6c0b56eSmrg	if (!koutput)
2066d6c0b56eSmrg		return NULL;
2067d6c0b56eSmrg
206824b90cf4Smrg	drmModeFreePropertyBlob(drmmode_output->edid_blob);
206924b90cf4Smrg
2070d6c0b56eSmrg	/* look for an EDID property */
207124b90cf4Smrg	drmmode_output->edid_blob =
207224b90cf4Smrg		koutput_get_prop_blob(pAMDGPUEnt->fd, koutput, "EDID");
2073d6c0b56eSmrg
2074d6c0b56eSmrg	if (drmmode_output->edid_blob) {
2075d6c0b56eSmrg		mon = xf86InterpretEDID(output->scrn->scrnIndex,
2076d6c0b56eSmrg					drmmode_output->edid_blob->data);
2077d6c0b56eSmrg		if (mon && drmmode_output->edid_blob->length > 128)
2078d6c0b56eSmrg			mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
2079d6c0b56eSmrg	}
2080d6c0b56eSmrg	xf86OutputSetEDID(output, mon);
2081d6c0b56eSmrg
2082d6c0b56eSmrg	/* modes should already be available */
2083d6c0b56eSmrg	for (i = 0; i < koutput->count_modes; i++) {
2084d6c0b56eSmrg		Mode = xnfalloc(sizeof(DisplayModeRec));
2085d6c0b56eSmrg
2086d6c0b56eSmrg		drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i],
2087d6c0b56eSmrg					 Mode);
2088d6c0b56eSmrg		Modes = xf86ModesAdd(Modes, Mode);
2089d6c0b56eSmrg
2090d6c0b56eSmrg	}
2091d6c0b56eSmrg	return Modes;
2092d6c0b56eSmrg}
2093d6c0b56eSmrg
2094d6c0b56eSmrgstatic void drmmode_output_destroy(xf86OutputPtr output)
2095d6c0b56eSmrg{
2096d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2097d6c0b56eSmrg	int i;
2098d6c0b56eSmrg
2099d6c0b56eSmrg	if (drmmode_output->edid_blob)
2100d6c0b56eSmrg		drmModeFreePropertyBlob(drmmode_output->edid_blob);
2101d6c0b56eSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
2102d6c0b56eSmrg		drmModeFreeProperty(drmmode_output->props[i].mode_prop);
2103d6c0b56eSmrg		free(drmmode_output->props[i].atoms);
2104d6c0b56eSmrg	}
2105d6c0b56eSmrg	for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) {
2106d6c0b56eSmrg		drmModeFreeEncoder(drmmode_output->mode_encoders[i]);
2107d6c0b56eSmrg	}
2108d6c0b56eSmrg	free(drmmode_output->mode_encoders);
2109d6c0b56eSmrg	free(drmmode_output->props);
2110d6c0b56eSmrg	drmModeFreeConnector(drmmode_output->mode_output);
2111d6c0b56eSmrg	free(drmmode_output);
2112d6c0b56eSmrg	output->driver_private = NULL;
2113d6c0b56eSmrg}
2114d6c0b56eSmrg
2115d6c0b56eSmrgstatic void drmmode_output_dpms(xf86OutputPtr output, int mode)
2116d6c0b56eSmrg{
2117d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2118d6c0b56eSmrg	xf86CrtcPtr crtc = output->crtc;
2119d6c0b56eSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
2120d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
2121d6c0b56eSmrg
2122d6c0b56eSmrg	if (!koutput)
2123d6c0b56eSmrg		return;
2124d6c0b56eSmrg
212524b90cf4Smrg	if (mode != DPMSModeOn && crtc)
2126d6c0b56eSmrg		drmmode_do_crtc_dpms(crtc, mode);
2127d6c0b56eSmrg
2128d6c0b56eSmrg	drmModeConnectorSetProperty(pAMDGPUEnt->fd, koutput->connector_id,
2129d6c0b56eSmrg				    drmmode_output->dpms_enum_id, mode);
2130d6c0b56eSmrg
2131d6c0b56eSmrg	if (mode == DPMSModeOn && crtc) {
2132d6c0b56eSmrg		drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2133d6c0b56eSmrg
2134d6c0b56eSmrg		if (drmmode_crtc->need_modeset)
2135d6c0b56eSmrg			drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
2136d6c0b56eSmrg					       crtc->x, crtc->y);
2137d6c0b56eSmrg		else
2138d6c0b56eSmrg			drmmode_do_crtc_dpms(output->crtc, mode);
2139d6c0b56eSmrg	}
2140d6c0b56eSmrg}
2141d6c0b56eSmrg
2142d6c0b56eSmrgstatic Bool drmmode_property_ignore(drmModePropertyPtr prop)
2143d6c0b56eSmrg{
2144d6c0b56eSmrg	if (!prop)
2145d6c0b56eSmrg		return TRUE;
2146d6c0b56eSmrg	/* ignore blob prop */
2147d6c0b56eSmrg	if (prop->flags & DRM_MODE_PROP_BLOB)
2148d6c0b56eSmrg		return TRUE;
2149d6c0b56eSmrg	/* ignore standard property */
2150d6c0b56eSmrg	if (!strcmp(prop->name, "EDID") || !strcmp(prop->name, "DPMS"))
2151d6c0b56eSmrg		return TRUE;
2152d6c0b56eSmrg
2153d6c0b56eSmrg	return FALSE;
2154d6c0b56eSmrg}
2155d6c0b56eSmrg
2156d6c0b56eSmrgstatic void drmmode_output_create_resources(xf86OutputPtr output)
2157d6c0b56eSmrg{
215811bf0794Smrg	AMDGPUInfoPtr info = AMDGPUPTR(output->scrn);
2159d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
216035d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc;
2161d6c0b56eSmrg	drmModeConnectorPtr mode_output = drmmode_output->mode_output;
2162d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
216311bf0794Smrg	drmModePropertyPtr drmmode_prop, tearfree_prop;
2164d6c0b56eSmrg	int i, j, err;
216535d5b7c7Smrg	Atom name;
216635d5b7c7Smrg
216735d5b7c7Smrg	/* Create CONNECTOR_ID property */
216835d5b7c7Smrg	name = MakeAtom("CONNECTOR_ID", 12, TRUE);
216935d5b7c7Smrg	if (name != BAD_RESOURCE) {
217035d5b7c7Smrg		INT32 value = mode_output->connector_id;
217135d5b7c7Smrg
217235d5b7c7Smrg		err = RRConfigureOutputProperty(output->randr_output, name,
217335d5b7c7Smrg						FALSE, FALSE, TRUE, 1, &value);
217435d5b7c7Smrg		if (err != Success) {
217535d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
217635d5b7c7Smrg				   "RRConfigureOutputProperty error, %d\n", err);
217735d5b7c7Smrg		}
217835d5b7c7Smrg
217935d5b7c7Smrg		err = RRChangeOutputProperty(output->randr_output, name,
218035d5b7c7Smrg					     XA_INTEGER, 32, PropModeReplace, 1,
218135d5b7c7Smrg					     &value, FALSE, FALSE);
218235d5b7c7Smrg		if (err != Success) {
218335d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
218435d5b7c7Smrg				   "RRChangeOutputProperty error, %d\n", err);
218535d5b7c7Smrg		}
218635d5b7c7Smrg	}
2187d6c0b56eSmrg
2188d6c0b56eSmrg	drmmode_output->props =
218911bf0794Smrg		calloc(mode_output->count_props + 1, sizeof(drmmode_prop_rec));
2190d6c0b56eSmrg	if (!drmmode_output->props)
2191d6c0b56eSmrg		return;
2192d6c0b56eSmrg
2193d6c0b56eSmrg	drmmode_output->num_props = 0;
2194d6c0b56eSmrg	for (i = 0, j = 0; i < mode_output->count_props; i++) {
2195d6c0b56eSmrg		drmmode_prop =
2196d6c0b56eSmrg		    drmModeGetProperty(pAMDGPUEnt->fd, mode_output->props[i]);
2197d6c0b56eSmrg		if (drmmode_property_ignore(drmmode_prop)) {
2198d6c0b56eSmrg			drmModeFreeProperty(drmmode_prop);
2199d6c0b56eSmrg			continue;
2200d6c0b56eSmrg		}
2201d6c0b56eSmrg		drmmode_output->props[j].mode_prop = drmmode_prop;
2202d6c0b56eSmrg		drmmode_output->props[j].value = mode_output->prop_values[i];
2203d6c0b56eSmrg		drmmode_output->num_props++;
2204d6c0b56eSmrg		j++;
2205d6c0b56eSmrg	}
2206d6c0b56eSmrg
220711bf0794Smrg	/* Userspace-only property for TearFree */
220811bf0794Smrg	tearfree_prop = calloc(1, sizeof(*tearfree_prop));
220911bf0794Smrg	tearfree_prop->flags = DRM_MODE_PROP_ENUM;
221035d5b7c7Smrg	strcpy(tearfree_prop->name, "TearFree");
221111bf0794Smrg	tearfree_prop->count_enums = 3;
221211bf0794Smrg	tearfree_prop->enums = calloc(tearfree_prop->count_enums,
221311bf0794Smrg				      sizeof(*tearfree_prop->enums));
221435d5b7c7Smrg	strcpy(tearfree_prop->enums[0].name, "off");
221535d5b7c7Smrg	strcpy(tearfree_prop->enums[1].name, "on");
221611bf0794Smrg	tearfree_prop->enums[1].value = 1;
221735d5b7c7Smrg	strcpy(tearfree_prop->enums[2].name, "auto");
221811bf0794Smrg	tearfree_prop->enums[2].value = 2;
221911bf0794Smrg	drmmode_output->props[j].mode_prop = tearfree_prop;
222011bf0794Smrg	drmmode_output->props[j].value = info->tear_free;
222111bf0794Smrg	drmmode_output->tear_free = info->tear_free;
222211bf0794Smrg	drmmode_output->num_props++;
222311bf0794Smrg
2224d6c0b56eSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
2225d6c0b56eSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
2226d6c0b56eSmrg		drmmode_prop = p->mode_prop;
2227d6c0b56eSmrg
2228d6c0b56eSmrg		if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
2229d6c0b56eSmrg			INT32 range[2];
2230d6c0b56eSmrg			INT32 value = p->value;
2231d6c0b56eSmrg
2232d6c0b56eSmrg			p->num_atoms = 1;
2233d6c0b56eSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
2234d6c0b56eSmrg			if (!p->atoms)
2235d6c0b56eSmrg				continue;
2236d6c0b56eSmrg			p->atoms[0] =
2237d6c0b56eSmrg			    MakeAtom(drmmode_prop->name,
2238d6c0b56eSmrg				     strlen(drmmode_prop->name), TRUE);
2239d6c0b56eSmrg			range[0] = drmmode_prop->values[0];
2240d6c0b56eSmrg			range[1] = drmmode_prop->values[1];
2241d6c0b56eSmrg			err =
2242d6c0b56eSmrg			    RRConfigureOutputProperty(output->randr_output,
2243d6c0b56eSmrg						      p->atoms[0], FALSE, TRUE,
2244d6c0b56eSmrg						      drmmode_prop->flags &
2245d6c0b56eSmrg						      DRM_MODE_PROP_IMMUTABLE ?
2246d6c0b56eSmrg						      TRUE : FALSE, 2, range);
2247d6c0b56eSmrg			if (err != 0) {
2248d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2249d6c0b56eSmrg					   "RRConfigureOutputProperty error, %d\n",
2250d6c0b56eSmrg					   err);
2251d6c0b56eSmrg			}
2252d6c0b56eSmrg			err =
2253d6c0b56eSmrg			    RRChangeOutputProperty(output->randr_output,
2254d6c0b56eSmrg						   p->atoms[0], XA_INTEGER, 32,
2255d6c0b56eSmrg						   PropModeReplace, 1, &value,
2256d6c0b56eSmrg						   FALSE, TRUE);
2257d6c0b56eSmrg			if (err != 0) {
2258d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2259d6c0b56eSmrg					   "RRChangeOutputProperty error, %d\n",
2260d6c0b56eSmrg					   err);
2261d6c0b56eSmrg			}
2262d6c0b56eSmrg		} else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
2263d6c0b56eSmrg			p->num_atoms = drmmode_prop->count_enums + 1;
2264d6c0b56eSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
2265d6c0b56eSmrg			if (!p->atoms)
2266d6c0b56eSmrg				continue;
2267d6c0b56eSmrg			p->atoms[0] =
2268d6c0b56eSmrg			    MakeAtom(drmmode_prop->name,
2269d6c0b56eSmrg				     strlen(drmmode_prop->name), TRUE);
2270d6c0b56eSmrg			for (j = 1; j <= drmmode_prop->count_enums; j++) {
2271d6c0b56eSmrg				struct drm_mode_property_enum *e =
2272d6c0b56eSmrg				    &drmmode_prop->enums[j - 1];
2273d6c0b56eSmrg				p->atoms[j] =
2274d6c0b56eSmrg				    MakeAtom(e->name, strlen(e->name), TRUE);
2275d6c0b56eSmrg			}
2276d6c0b56eSmrg			err =
2277d6c0b56eSmrg			    RRConfigureOutputProperty(output->randr_output,
2278d6c0b56eSmrg						      p->atoms[0], FALSE, FALSE,
2279d6c0b56eSmrg						      drmmode_prop->flags &
2280d6c0b56eSmrg						      DRM_MODE_PROP_IMMUTABLE ?
2281d6c0b56eSmrg						      TRUE : FALSE,
2282d6c0b56eSmrg						      p->num_atoms - 1,
2283d6c0b56eSmrg						      (INT32 *) & p->atoms[1]);
2284d6c0b56eSmrg			if (err != 0) {
2285d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2286d6c0b56eSmrg					   "RRConfigureOutputProperty error, %d\n",
2287d6c0b56eSmrg					   err);
2288d6c0b56eSmrg			}
2289d6c0b56eSmrg			for (j = 0; j < drmmode_prop->count_enums; j++)
2290d6c0b56eSmrg				if (drmmode_prop->enums[j].value == p->value)
2291d6c0b56eSmrg					break;
2292d6c0b56eSmrg			/* there's always a matching value */
2293d6c0b56eSmrg			err =
2294d6c0b56eSmrg			    RRChangeOutputProperty(output->randr_output,
2295d6c0b56eSmrg						   p->atoms[0], XA_ATOM, 32,
2296d6c0b56eSmrg						   PropModeReplace, 1,
2297d6c0b56eSmrg						   &p->atoms[j + 1], FALSE,
2298d6c0b56eSmrg						   TRUE);
2299d6c0b56eSmrg			if (err != 0) {
2300d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2301d6c0b56eSmrg					   "RRChangeOutputProperty error, %d\n",
2302d6c0b56eSmrg					   err);
2303d6c0b56eSmrg			}
2304d6c0b56eSmrg		}
2305d6c0b56eSmrg	}
230635d5b7c7Smrg
230735d5b7c7Smrg	/* Do not configure cm properties on output if there's no support. */
230835d5b7c7Smrg	if (!drmmode_cm_enabled(drmmode_output->drmmode))
230935d5b7c7Smrg		return;
231035d5b7c7Smrg
231135d5b7c7Smrg	drmmode_crtc = output->crtc ? output->crtc->driver_private : NULL;
231235d5b7c7Smrg
231335d5b7c7Smrg	for (i = 0; i < CM_NUM_PROPS; i++)
231435d5b7c7Smrg		rr_configure_and_change_cm_property(output, drmmode_crtc, i);
231535d5b7c7Smrg}
231635d5b7c7Smrg
231735d5b7c7Smrgstatic void
231835d5b7c7Smrgdrmmode_output_set_tear_free(AMDGPUEntPtr pAMDGPUEnt,
231935d5b7c7Smrg			     drmmode_output_private_ptr drmmode_output,
232035d5b7c7Smrg			     xf86CrtcPtr crtc, int tear_free)
232135d5b7c7Smrg{
232235d5b7c7Smrg	if (drmmode_output->tear_free == tear_free)
232335d5b7c7Smrg		return;
232435d5b7c7Smrg
232535d5b7c7Smrg	drmmode_output->tear_free = tear_free;
232635d5b7c7Smrg
232735d5b7c7Smrg	if (crtc) {
232835d5b7c7Smrg		drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
232935d5b7c7Smrg				       crtc->x, crtc->y);
233035d5b7c7Smrg	}
2331d6c0b56eSmrg}
2332d6c0b56eSmrg
2333d6c0b56eSmrgstatic Bool
2334d6c0b56eSmrgdrmmode_output_set_property(xf86OutputPtr output, Atom property,
2335d6c0b56eSmrg			    RRPropertyValuePtr value)
2336d6c0b56eSmrg{
2337d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2338d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
233935d5b7c7Smrg	enum drmmode_cm_prop cm_prop_index;
2340d6c0b56eSmrg	int i;
2341d6c0b56eSmrg
234235d5b7c7Smrg	cm_prop_index = get_cm_enum_from_str(NameForAtom(property));
234335d5b7c7Smrg	if (cm_prop_index >= 0 && cm_prop_index < CM_DEGAMMA_LUT_SIZE) {
234435d5b7c7Smrg		if (!output->crtc)
234535d5b7c7Smrg			return FALSE;
234635d5b7c7Smrg		if (drmmode_crtc_stage_cm_prop(output->crtc, cm_prop_index,
234735d5b7c7Smrg					       value))
234835d5b7c7Smrg			return FALSE;
234935d5b7c7Smrg		if (drmmode_crtc_push_cm_prop(output->crtc, cm_prop_index))
235035d5b7c7Smrg			return FALSE;
235135d5b7c7Smrg		return TRUE;
235235d5b7c7Smrg	}
235335d5b7c7Smrg
2354d6c0b56eSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
2355d6c0b56eSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
2356d6c0b56eSmrg
2357d6c0b56eSmrg		if (p->atoms[0] != property)
2358d6c0b56eSmrg			continue;
2359d6c0b56eSmrg
2360d6c0b56eSmrg		if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
2361d6c0b56eSmrg			uint32_t val;
2362d6c0b56eSmrg
2363d6c0b56eSmrg			if (value->type != XA_INTEGER || value->format != 32 ||
2364d6c0b56eSmrg			    value->size != 1)
2365d6c0b56eSmrg				return FALSE;
2366d6c0b56eSmrg			val = *(uint32_t *) value->data;
2367d6c0b56eSmrg
2368d6c0b56eSmrg			drmModeConnectorSetProperty(pAMDGPUEnt->fd,
2369d6c0b56eSmrg						    drmmode_output->output_id,
2370d6c0b56eSmrg						    p->mode_prop->prop_id,
2371d6c0b56eSmrg						    (uint64_t) val);
2372d6c0b56eSmrg			return TRUE;
2373d6c0b56eSmrg		} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
2374d6c0b56eSmrg			Atom atom;
2375d6c0b56eSmrg			const char *name;
2376d6c0b56eSmrg			int j;
2377d6c0b56eSmrg
2378d6c0b56eSmrg			if (value->type != XA_ATOM || value->format != 32
2379d6c0b56eSmrg			    || value->size != 1)
2380d6c0b56eSmrg				return FALSE;
2381d6c0b56eSmrg			memcpy(&atom, value->data, 4);
238224b90cf4Smrg			if (!(name = NameForAtom(atom)))
238324b90cf4Smrg				return FALSE;
2384d6c0b56eSmrg
2385d6c0b56eSmrg			/* search for matching name string, then set its value down */
2386d6c0b56eSmrg			for (j = 0; j < p->mode_prop->count_enums; j++) {
2387d6c0b56eSmrg				if (!strcmp(p->mode_prop->enums[j].name, name)) {
238811bf0794Smrg					if (i == (drmmode_output->num_props - 1)) {
238935d5b7c7Smrg						drmmode_output_set_tear_free(pAMDGPUEnt,
239035d5b7c7Smrg									     drmmode_output,
239135d5b7c7Smrg									     output->crtc, j);
239211bf0794Smrg					} else {
239311bf0794Smrg						drmModeConnectorSetProperty(pAMDGPUEnt->fd,
239411bf0794Smrg									    drmmode_output->output_id,
239511bf0794Smrg									    p->mode_prop->prop_id,
239611bf0794Smrg									    p->mode_prop->enums[j].value);
239711bf0794Smrg					}
239811bf0794Smrg
2399d6c0b56eSmrg					return TRUE;
2400d6c0b56eSmrg				}
2401d6c0b56eSmrg			}
2402d6c0b56eSmrg		}
2403d6c0b56eSmrg	}
2404d6c0b56eSmrg
2405d6c0b56eSmrg	return TRUE;
2406d6c0b56eSmrg}
2407d6c0b56eSmrg
2408d6c0b56eSmrgstatic Bool drmmode_output_get_property(xf86OutputPtr output, Atom property)
2409d6c0b56eSmrg{
241035d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc;
241135d5b7c7Smrg	enum drmmode_cm_prop cm_prop_id;
241235d5b7c7Smrg	int ret;
241335d5b7c7Smrg
241435d5b7c7Smrg	/* First, see if it's a cm property */
241535d5b7c7Smrg	cm_prop_id = get_cm_enum_from_str(NameForAtom(property));
241635d5b7c7Smrg	if (output->crtc && cm_prop_id != CM_INVALID_PROP) {
241735d5b7c7Smrg		drmmode_crtc = output->crtc->driver_private;
241835d5b7c7Smrg
241935d5b7c7Smrg		ret = rr_configure_and_change_cm_property(output, drmmode_crtc,
242035d5b7c7Smrg							  cm_prop_id);
242135d5b7c7Smrg		if (ret) {
242235d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
242335d5b7c7Smrg				   "Error getting color property: %d\n",
242435d5b7c7Smrg				   ret);
242535d5b7c7Smrg			return FALSE;
242635d5b7c7Smrg		}
242735d5b7c7Smrg		return TRUE;
242835d5b7c7Smrg	}
242935d5b7c7Smrg
243035d5b7c7Smrg	/* Otherwise, must be an output property. */
2431d6c0b56eSmrg	return TRUE;
2432d6c0b56eSmrg}
2433d6c0b56eSmrg
2434d6c0b56eSmrgstatic const xf86OutputFuncsRec drmmode_output_funcs = {
2435d6c0b56eSmrg	.dpms = drmmode_output_dpms,
2436d6c0b56eSmrg	.create_resources = drmmode_output_create_resources,
2437d6c0b56eSmrg	.set_property = drmmode_output_set_property,
2438d6c0b56eSmrg	.get_property = drmmode_output_get_property,
2439d6c0b56eSmrg	.detect = drmmode_output_detect,
2440d6c0b56eSmrg	.mode_valid = drmmode_output_mode_valid,
2441d6c0b56eSmrg
2442d6c0b56eSmrg	.get_modes = drmmode_output_get_modes,
2443d6c0b56eSmrg	.destroy = drmmode_output_destroy
2444d6c0b56eSmrg};
2445d6c0b56eSmrg
2446d6c0b56eSmrgstatic int subpixel_conv_table[7] = { 0, SubPixelUnknown,
2447d6c0b56eSmrg	SubPixelHorizontalRGB,
2448d6c0b56eSmrg	SubPixelHorizontalBGR,
2449d6c0b56eSmrg	SubPixelVerticalRGB,
2450d6c0b56eSmrg	SubPixelVerticalBGR,
2451d6c0b56eSmrg	SubPixelNone
2452d6c0b56eSmrg};
2453d6c0b56eSmrg
2454d6c0b56eSmrgconst char *output_names[] = { "None",
2455d6c0b56eSmrg	"VGA",
2456d6c0b56eSmrg	"DVI-I",
2457d6c0b56eSmrg	"DVI-D",
2458d6c0b56eSmrg	"DVI-A",
2459d6c0b56eSmrg	"Composite",
2460d6c0b56eSmrg	"S-video",
2461d6c0b56eSmrg	"LVDS",
2462d6c0b56eSmrg	"CTV",
2463d6c0b56eSmrg	"DIN",
2464d6c0b56eSmrg	"DisplayPort",
2465d6c0b56eSmrg	"HDMI-A",
2466d6c0b56eSmrg	"HDMI-B",
2467d6c0b56eSmrg	"TV",
2468d6c0b56eSmrg	"eDP",
2469d6c0b56eSmrg	"Virtual",
2470d6c0b56eSmrg	"DSI",
2471d6c0b56eSmrg};
2472d6c0b56eSmrg
2473d6c0b56eSmrg#define NUM_OUTPUT_NAMES (sizeof(output_names) / sizeof(output_names[0]))
2474d6c0b56eSmrg
2475d6c0b56eSmrgstatic xf86OutputPtr find_output(ScrnInfoPtr pScrn, int id)
2476d6c0b56eSmrg{
2477d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
2478d6c0b56eSmrg	int i;
2479d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2480d6c0b56eSmrg		xf86OutputPtr output = xf86_config->output[i];
2481d6c0b56eSmrg		drmmode_output_private_ptr drmmode_output;
2482d6c0b56eSmrg		drmmode_output = output->driver_private;
2483d6c0b56eSmrg		if (drmmode_output->output_id == id)
2484d6c0b56eSmrg			return output;
2485d6c0b56eSmrg	}
2486d6c0b56eSmrg	return NULL;
2487d6c0b56eSmrg}
2488d6c0b56eSmrg
2489d6c0b56eSmrgstatic int parse_path_blob(drmModePropertyBlobPtr path_blob, int *conn_base_id, char **path)
2490d6c0b56eSmrg{
2491d6c0b56eSmrg	char *conn;
2492d6c0b56eSmrg	char conn_id[5];
2493d6c0b56eSmrg	int id, len;
2494d6c0b56eSmrg	char *blob_data;
2495d6c0b56eSmrg
2496d6c0b56eSmrg	if (!path_blob)
2497d6c0b56eSmrg		return -1;
2498d6c0b56eSmrg
2499d6c0b56eSmrg	blob_data = path_blob->data;
2500d6c0b56eSmrg	/* we only handle MST paths for now */
2501d6c0b56eSmrg	if (strncmp(blob_data, "mst:", 4))
2502d6c0b56eSmrg		return -1;
2503d6c0b56eSmrg
2504d6c0b56eSmrg	conn = strchr(blob_data + 4, '-');
2505d6c0b56eSmrg	if (!conn)
2506d6c0b56eSmrg		return -1;
2507d6c0b56eSmrg	len = conn - (blob_data + 4);
2508d6c0b56eSmrg	if (len + 1 > 5)
2509d6c0b56eSmrg		return -1;
2510d6c0b56eSmrg	memcpy(conn_id, blob_data + 4, len);
2511d6c0b56eSmrg	conn_id[len] = '\0';
2512d6c0b56eSmrg	id = strtoul(conn_id, NULL, 10);
2513d6c0b56eSmrg
2514d6c0b56eSmrg	*conn_base_id = id;
2515d6c0b56eSmrg
2516d6c0b56eSmrg	*path = conn + 1;
2517d6c0b56eSmrg	return 0;
2518d6c0b56eSmrg}
2519d6c0b56eSmrg
2520d6c0b56eSmrgstatic void
2521d6c0b56eSmrgdrmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
2522d6c0b56eSmrg		    drmModePropertyBlobPtr path_blob, int *num_dvi, int *num_hdmi)
2523d6c0b56eSmrg{
2524d6c0b56eSmrg	xf86OutputPtr output;
2525d6c0b56eSmrg	int conn_id;
2526d6c0b56eSmrg	char *extra_path;
2527d6c0b56eSmrg
2528d6c0b56eSmrg	output = NULL;
2529d6c0b56eSmrg	if (parse_path_blob(path_blob, &conn_id, &extra_path) == 0)
2530d6c0b56eSmrg		output = find_output(pScrn, conn_id);
2531d6c0b56eSmrg	if (output) {
2532d6c0b56eSmrg		snprintf(name, 32, "%s-%s", output->name, extra_path);
2533d6c0b56eSmrg	} else {
253424b90cf4Smrg		if (koutput->connector_type >= NUM_OUTPUT_NAMES) {
2535d6c0b56eSmrg			snprintf(name, 32, "Unknown%d-%d", koutput->connector_type, koutput->connector_type_id - 1);
253624b90cf4Smrg		} else if (pScrn->is_gpu) {
2537d6c0b56eSmrg			snprintf(name, 32, "%s-%d-%d", output_names[koutput->connector_type],
2538d6c0b56eSmrg				 pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, koutput->connector_type_id - 1);
253924b90cf4Smrg		} else {
2540d6c0b56eSmrg			/* need to do smart conversion here for compat with non-kms ATI driver */
2541d6c0b56eSmrg			if (koutput->connector_type_id == 1) {
2542d6c0b56eSmrg				switch(koutput->connector_type) {
2543d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DVII:
2544d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DVID:
2545d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DVIA:
2546d6c0b56eSmrg					snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], *num_dvi);
2547d6c0b56eSmrg					(*num_dvi)++;
2548d6c0b56eSmrg					break;
2549d6c0b56eSmrg				case DRM_MODE_CONNECTOR_HDMIA:
2550d6c0b56eSmrg				case DRM_MODE_CONNECTOR_HDMIB:
2551d6c0b56eSmrg					snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], *num_hdmi);
2552d6c0b56eSmrg					(*num_hdmi)++;
2553d6c0b56eSmrg					break;
2554d6c0b56eSmrg				case DRM_MODE_CONNECTOR_VGA:
2555d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DisplayPort:
2556d6c0b56eSmrg					snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id - 1);
2557d6c0b56eSmrg					break;
2558d6c0b56eSmrg				default:
2559d6c0b56eSmrg					snprintf(name, 32, "%s", output_names[koutput->connector_type]);
2560d6c0b56eSmrg					break;
2561d6c0b56eSmrg				}
2562d6c0b56eSmrg			} else {
2563d6c0b56eSmrg				snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id - 1);
2564d6c0b56eSmrg			}
2565d6c0b56eSmrg		}
2566d6c0b56eSmrg	}
2567d6c0b56eSmrg}
2568d6c0b56eSmrg
2569d6c0b56eSmrg
2570d6c0b56eSmrgstatic unsigned int
2571d6c0b56eSmrgdrmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num, int *num_dvi, int *num_hdmi, int dynamic)
2572d6c0b56eSmrg{
2573d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
2574d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
2575d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
2576d6c0b56eSmrg	xf86OutputPtr output;
2577d6c0b56eSmrg	drmModeConnectorPtr koutput;
2578d6c0b56eSmrg	drmModeEncoderPtr *kencoders = NULL;
2579d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output;
2580d6c0b56eSmrg	drmModePropertyBlobPtr path_blob = NULL;
258135d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
258235d5b7c7Smrg	Bool nonDesktop = FALSE;
258335d5b7c7Smrg#endif
2584d6c0b56eSmrg	char name[32];
2585d6c0b56eSmrg	int i;
2586d6c0b56eSmrg	const char *s;
2587d6c0b56eSmrg
2588d6c0b56eSmrg	koutput =
2589d6c0b56eSmrg	    drmModeGetConnector(pAMDGPUEnt->fd,
2590d6c0b56eSmrg				mode_res->connectors[num]);
2591d6c0b56eSmrg	if (!koutput)
2592d6c0b56eSmrg		return 0;
2593d6c0b56eSmrg
259424b90cf4Smrg	path_blob = koutput_get_prop_blob(pAMDGPUEnt->fd, koutput, "PATH");
2595d6c0b56eSmrg
259635d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
259735d5b7c7Smrg	i = koutput_get_prop_idx(pAMDGPUEnt->fd, koutput, DRM_MODE_PROP_RANGE,
259835d5b7c7Smrg				 "non-desktop");
259935d5b7c7Smrg	if (i >= 0)
260035d5b7c7Smrg        	nonDesktop = koutput->prop_values[i] != 0;
260135d5b7c7Smrg#endif
260235d5b7c7Smrg
2603d6c0b56eSmrg	kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders);
2604d6c0b56eSmrg	if (!kencoders) {
2605d6c0b56eSmrg		goto out_free_encoders;
2606d6c0b56eSmrg	}
2607d6c0b56eSmrg
2608d6c0b56eSmrg	for (i = 0; i < koutput->count_encoders; i++) {
2609d6c0b56eSmrg		kencoders[i] =
2610d6c0b56eSmrg		    drmModeGetEncoder(pAMDGPUEnt->fd, koutput->encoders[i]);
2611d6c0b56eSmrg		if (!kencoders[i]) {
2612d6c0b56eSmrg			goto out_free_encoders;
2613d6c0b56eSmrg		}
2614d6c0b56eSmrg	}
2615d6c0b56eSmrg
2616d6c0b56eSmrg	drmmode_create_name(pScrn, koutput, name, path_blob, num_dvi, num_hdmi);
2617d6c0b56eSmrg	if (path_blob) {
2618d6c0b56eSmrg		drmModeFreePropertyBlob(path_blob);
2619d6c0b56eSmrg	}
2620d6c0b56eSmrg
2621d6c0b56eSmrg	if (path_blob && dynamic) {
2622d6c0b56eSmrg		/* See if we have an output with this name already
2623d6c0b56eSmrg		 * and hook stuff up.
2624d6c0b56eSmrg		 */
2625d6c0b56eSmrg		for (i = 0; i < xf86_config->num_output; i++) {
2626d6c0b56eSmrg			output = xf86_config->output[i];
2627d6c0b56eSmrg
2628d6c0b56eSmrg			if (strncmp(output->name, name, 32))
2629d6c0b56eSmrg				continue;
2630d6c0b56eSmrg
2631d6c0b56eSmrg			drmmode_output = output->driver_private;
2632d6c0b56eSmrg			drmmode_output->output_id = mode_res->connectors[num];
2633d6c0b56eSmrg			drmmode_output->mode_output = koutput;
263435d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
263535d5b7c7Smrg			output->non_desktop = nonDesktop;
263635d5b7c7Smrg#endif
2637d6c0b56eSmrg			for (i = 0; i < koutput->count_encoders; i++) {
2638d6c0b56eSmrg				drmModeFreeEncoder(kencoders[i]);
2639d6c0b56eSmrg			}
2640d6c0b56eSmrg			free(kencoders);
2641d6c0b56eSmrg			return 1;
2642d6c0b56eSmrg		}
2643d6c0b56eSmrg	}
2644d6c0b56eSmrg
2645d6c0b56eSmrg	if (xf86IsEntityShared(pScrn->entityList[0])) {
2646d6c0b56eSmrg		if ((s =
2647d6c0b56eSmrg		     xf86GetOptValString(info->Options, OPTION_ZAPHOD_HEADS))) {
2648d6c0b56eSmrg			if (!AMDGPUZaphodStringMatches(pScrn, s, name))
2649d6c0b56eSmrg				goto out_free_encoders;
2650d6c0b56eSmrg		} else {
2651d6c0b56eSmrg			if (!info->IsSecondary && (num != 0))
2652d6c0b56eSmrg				goto out_free_encoders;
2653d6c0b56eSmrg			else if (info->IsSecondary && (num != 1))
2654d6c0b56eSmrg				goto out_free_encoders;
2655d6c0b56eSmrg		}
2656d6c0b56eSmrg	}
2657d6c0b56eSmrg
2658d6c0b56eSmrg	output = xf86OutputCreate(pScrn, &drmmode_output_funcs, name);
2659d6c0b56eSmrg	if (!output) {
2660d6c0b56eSmrg		goto out_free_encoders;
2661d6c0b56eSmrg	}
2662d6c0b56eSmrg
2663d6c0b56eSmrg	drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
2664d6c0b56eSmrg	if (!drmmode_output) {
2665d6c0b56eSmrg		xf86OutputDestroy(output);
2666d6c0b56eSmrg		goto out_free_encoders;
2667d6c0b56eSmrg	}
2668d6c0b56eSmrg
2669d6c0b56eSmrg	drmmode_output->output_id = mode_res->connectors[num];
2670d6c0b56eSmrg	drmmode_output->mode_output = koutput;
2671d6c0b56eSmrg	drmmode_output->mode_encoders = kencoders;
2672d6c0b56eSmrg	drmmode_output->drmmode = drmmode;
2673d6c0b56eSmrg	output->mm_width = koutput->mmWidth;
2674d6c0b56eSmrg	output->mm_height = koutput->mmHeight;
2675d6c0b56eSmrg
2676d6c0b56eSmrg	output->subpixel_order = subpixel_conv_table[koutput->subpixel];
2677d6c0b56eSmrg	output->interlaceAllowed = TRUE;
2678d6c0b56eSmrg	output->doubleScanAllowed = TRUE;
2679d6c0b56eSmrg	output->driver_private = drmmode_output;
268035d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
268135d5b7c7Smrg	output->non_desktop = nonDesktop;
268235d5b7c7Smrg#endif
2683d6c0b56eSmrg
2684d6c0b56eSmrg	output->possible_crtcs = 0xffffffff;
2685d6c0b56eSmrg	for (i = 0; i < koutput->count_encoders; i++) {
2686d6c0b56eSmrg		output->possible_crtcs &= kencoders[i]->possible_crtcs;
2687d6c0b56eSmrg	}
2688d6c0b56eSmrg	/* work out the possible clones later */
2689d6c0b56eSmrg	output->possible_clones = 0;
2690d6c0b56eSmrg
269124b90cf4Smrg	drmmode_output->dpms_enum_id =
269224b90cf4Smrg		koutput_get_prop_id(pAMDGPUEnt->fd, koutput, DRM_MODE_PROP_ENUM,
269324b90cf4Smrg				    "DPMS");
2694d6c0b56eSmrg
2695d6c0b56eSmrg	if (dynamic) {
2696d6c0b56eSmrg		output->randr_output = RROutputCreate(xf86ScrnToScreen(pScrn), output->name, strlen(output->name), output);
2697d6c0b56eSmrg		drmmode_output_create_resources(output);
2698d6c0b56eSmrg	}
2699d6c0b56eSmrg
2700d6c0b56eSmrg	return 1;
2701d6c0b56eSmrgout_free_encoders:
2702d6c0b56eSmrg	if (kencoders) {
2703d6c0b56eSmrg		for (i = 0; i < koutput->count_encoders; i++)
2704d6c0b56eSmrg			drmModeFreeEncoder(kencoders[i]);
2705d6c0b56eSmrg		free(kencoders);
2706d6c0b56eSmrg	}
2707d6c0b56eSmrg	drmModeFreeConnector(koutput);
2708d6c0b56eSmrg	return 0;
2709d6c0b56eSmrg}
2710d6c0b56eSmrg
2711d6c0b56eSmrguint32_t find_clones(ScrnInfoPtr scrn, xf86OutputPtr output)
2712d6c0b56eSmrg{
2713d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output =
2714d6c0b56eSmrg	    output->driver_private, clone_drmout;
2715d6c0b56eSmrg	int i;
2716d6c0b56eSmrg	xf86OutputPtr clone_output;
2717d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2718d6c0b56eSmrg	int index_mask = 0;
2719d6c0b56eSmrg
2720d6c0b56eSmrg	if (drmmode_output->enc_clone_mask == 0)
2721d6c0b56eSmrg		return index_mask;
2722d6c0b56eSmrg
2723d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2724d6c0b56eSmrg		clone_output = xf86_config->output[i];
2725d6c0b56eSmrg		clone_drmout = clone_output->driver_private;
2726d6c0b56eSmrg		if (output == clone_output)
2727d6c0b56eSmrg			continue;
2728d6c0b56eSmrg
2729d6c0b56eSmrg		if (clone_drmout->enc_mask == 0)
2730d6c0b56eSmrg			continue;
2731d6c0b56eSmrg		if (drmmode_output->enc_clone_mask == clone_drmout->enc_mask)
2732d6c0b56eSmrg			index_mask |= (1 << i);
2733d6c0b56eSmrg	}
2734d6c0b56eSmrg	return index_mask;
2735d6c0b56eSmrg}
2736d6c0b56eSmrg
2737d6c0b56eSmrgstatic void drmmode_clones_init(ScrnInfoPtr scrn, drmmode_ptr drmmode, drmModeResPtr mode_res)
2738d6c0b56eSmrg{
2739d6c0b56eSmrg	int i, j;
2740d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2741d6c0b56eSmrg
2742d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2743d6c0b56eSmrg		xf86OutputPtr output = xf86_config->output[i];
2744d6c0b56eSmrg		drmmode_output_private_ptr drmmode_output;
2745d6c0b56eSmrg
2746d6c0b56eSmrg		drmmode_output = output->driver_private;
2747d6c0b56eSmrg		drmmode_output->enc_clone_mask = 0xff;
2748d6c0b56eSmrg		/* and all the possible encoder clones for this output together */
2749d6c0b56eSmrg		for (j = 0; j < drmmode_output->mode_output->count_encoders;
2750d6c0b56eSmrg		     j++) {
2751d6c0b56eSmrg			int k;
2752d6c0b56eSmrg			for (k = 0; k < mode_res->count_encoders; k++) {
2753d6c0b56eSmrg				if (mode_res->encoders[k] ==
2754d6c0b56eSmrg				    drmmode_output->
2755d6c0b56eSmrg				    mode_encoders[j]->encoder_id)
2756d6c0b56eSmrg					drmmode_output->enc_mask |= (1 << k);
2757d6c0b56eSmrg			}
2758d6c0b56eSmrg
2759d6c0b56eSmrg			drmmode_output->enc_clone_mask &=
2760d6c0b56eSmrg			    drmmode_output->mode_encoders[j]->possible_clones;
2761d6c0b56eSmrg		}
2762d6c0b56eSmrg	}
2763d6c0b56eSmrg
2764d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2765d6c0b56eSmrg		xf86OutputPtr output = xf86_config->output[i];
2766d6c0b56eSmrg		output->possible_clones = find_clones(scrn, output);
2767d6c0b56eSmrg	}
2768d6c0b56eSmrg}
2769d6c0b56eSmrg
2770d6c0b56eSmrg/* returns pitch alignment in pixels */
2771d6c0b56eSmrgint drmmode_get_pitch_align(ScrnInfoPtr scrn, int bpe)
2772d6c0b56eSmrg{
2773d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
2774d6c0b56eSmrg
2775d6c0b56eSmrg	if (info->have_tiling_info)
2776d6c0b56eSmrg		/* linear aligned requirements */
2777d6c0b56eSmrg		return MAX(64, info->group_bytes / bpe);
2778d6c0b56eSmrg	else
2779d6c0b56eSmrg		/* default to 512 elements if we don't know the real
2780d6c0b56eSmrg		 * group size otherwise the kernel may reject the CS
2781d6c0b56eSmrg		 * if the group sizes don't match as the pitch won't
2782d6c0b56eSmrg		 * be aligned properly.
2783d6c0b56eSmrg		 */
2784d6c0b56eSmrg		return 512;
2785d6c0b56eSmrg}
2786d6c0b56eSmrg
2787d6c0b56eSmrgstatic Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
2788d6c0b56eSmrg{
2789d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2790d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
2791d6c0b56eSmrg	struct amdgpu_buffer *old_front = NULL;
2792d6c0b56eSmrg	ScreenPtr screen = xf86ScrnToScreen(scrn);
2793d6c0b56eSmrg	int i, pitch, old_width, old_height, old_pitch;
2794d6c0b56eSmrg	int cpp = info->pixel_bytes;
2795d6c0b56eSmrg	PixmapPtr ppix = screen->GetScreenPixmap(screen);
2796d6c0b56eSmrg	void *fb_shadow;
2797d6c0b56eSmrg	int hint = 0;
2798d6c0b56eSmrg
2799d6c0b56eSmrg	if (scrn->virtualX == width && scrn->virtualY == height)
2800d6c0b56eSmrg		return TRUE;
2801d6c0b56eSmrg
280235d5b7c7Smrg	if (width > xf86_config->maxWidth || height > xf86_config->maxHeight) {
280335d5b7c7Smrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
280435d5b7c7Smrg			   "Xorg tried resizing screen to %dx%d, but maximum "
280535d5b7c7Smrg			   "supported is %dx%d\n", width, height,
280635d5b7c7Smrg			   xf86_config->maxWidth, xf86_config->maxHeight);
280735d5b7c7Smrg		return FALSE;
280835d5b7c7Smrg	}
280935d5b7c7Smrg
2810d6c0b56eSmrg	if (info->shadow_primary)
2811d6c0b56eSmrg		hint = AMDGPU_CREATE_PIXMAP_LINEAR | AMDGPU_CREATE_PIXMAP_GTT;
2812d6c0b56eSmrg	else if (!info->use_glamor)
2813d6c0b56eSmrg		hint = AMDGPU_CREATE_PIXMAP_LINEAR;
2814d6c0b56eSmrg
2815d6c0b56eSmrg	xf86DrvMsg(scrn->scrnIndex, X_INFO,
2816d6c0b56eSmrg		   "Allocate new frame buffer %dx%d\n", width, height);
2817d6c0b56eSmrg
2818d6c0b56eSmrg	old_width = scrn->virtualX;
2819d6c0b56eSmrg	old_height = scrn->virtualY;
2820d6c0b56eSmrg	old_pitch = scrn->displayWidth;
2821d6c0b56eSmrg	old_front = info->front_buffer;
2822d6c0b56eSmrg
2823d6c0b56eSmrg	scrn->virtualX = width;
2824d6c0b56eSmrg	scrn->virtualY = height;
2825d6c0b56eSmrg
2826d6c0b56eSmrg	info->front_buffer =
2827d6c0b56eSmrg		amdgpu_alloc_pixmap_bo(scrn, scrn->virtualX, scrn->virtualY,
2828d6c0b56eSmrg				       scrn->depth, hint, scrn->bitsPerPixel,
2829d6c0b56eSmrg				       &pitch);
2830d6c0b56eSmrg	if (!info->front_buffer) {
2831d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2832d6c0b56eSmrg			   "Failed to allocate front buffer memory\n");
2833d6c0b56eSmrg		goto fail;
2834d6c0b56eSmrg	}
2835d6c0b56eSmrg
2836d6c0b56eSmrg	if (!info->use_glamor && amdgpu_bo_map(scrn, info->front_buffer) != 0) {
2837d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2838d6c0b56eSmrg			   "Failed to map front buffer memory\n");
2839d6c0b56eSmrg		goto fail;
2840d6c0b56eSmrg	}
2841d6c0b56eSmrg
2842d6c0b56eSmrg	xf86DrvMsg(scrn->scrnIndex, X_INFO, " => pitch %d bytes\n", pitch);
2843d6c0b56eSmrg	scrn->displayWidth = pitch / cpp;
2844d6c0b56eSmrg
2845d6c0b56eSmrg	if (info->use_glamor ||
2846d6c0b56eSmrg	    (info->front_buffer->flags & AMDGPU_BO_FLAGS_GBM)) {
2847d6c0b56eSmrg		screen->ModifyPixmapHeader(ppix,
2848d6c0b56eSmrg					   width, height, -1, -1, pitch, info->front_buffer->cpu_ptr);
2849d6c0b56eSmrg	} else {
2850d6c0b56eSmrg		fb_shadow = calloc(1, pitch * scrn->virtualY);
285135d5b7c7Smrg		if (!fb_shadow)
2852d6c0b56eSmrg			goto fail;
2853d6c0b56eSmrg		free(info->fb_shadow);
2854d6c0b56eSmrg		info->fb_shadow = fb_shadow;
2855d6c0b56eSmrg		screen->ModifyPixmapHeader(ppix,
2856d6c0b56eSmrg					   width, height, -1, -1, pitch,
2857d6c0b56eSmrg					   info->fb_shadow);
2858d6c0b56eSmrg	}
2859d6c0b56eSmrg
2860504d986fSmrg	if (!amdgpu_glamor_create_screen_resources(scrn->pScreen))
2861504d986fSmrg		goto fail;
2862504d986fSmrg
2863504d986fSmrg	if (info->use_glamor ||
2864504d986fSmrg	    (info->front_buffer->flags & AMDGPU_BO_FLAGS_GBM)) {
2865504d986fSmrg		if (!amdgpu_set_pixmap_bo(ppix, info->front_buffer))
2866504d986fSmrg			goto fail;
2867504d986fSmrg	}
2868d6c0b56eSmrg
286924b90cf4Smrg	amdgpu_pixmap_clear(ppix);
2870d6c0b56eSmrg	amdgpu_glamor_finish(scrn);
2871d6c0b56eSmrg
2872d6c0b56eSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
2873d6c0b56eSmrg		xf86CrtcPtr crtc = xf86_config->crtc[i];
2874d6c0b56eSmrg
2875d6c0b56eSmrg		if (!crtc->enabled)
2876d6c0b56eSmrg			continue;
2877d6c0b56eSmrg
2878d6c0b56eSmrg		drmmode_set_mode_major(crtc, &crtc->mode,
2879d6c0b56eSmrg				       crtc->rotation, crtc->x, crtc->y);
2880d6c0b56eSmrg	}
2881d6c0b56eSmrg
2882d6c0b56eSmrg	if (old_front) {
2883d6c0b56eSmrg		amdgpu_bo_unref(&old_front);
2884d6c0b56eSmrg	}
2885d6c0b56eSmrg
2886d6c0b56eSmrg	return TRUE;
2887d6c0b56eSmrg
2888d6c0b56eSmrgfail:
2889d6c0b56eSmrg	if (info->front_buffer) {
2890d6c0b56eSmrg		amdgpu_bo_unref(&info->front_buffer);
2891d6c0b56eSmrg	}
2892d6c0b56eSmrg	info->front_buffer = old_front;
2893d6c0b56eSmrg	scrn->virtualX = old_width;
2894d6c0b56eSmrg	scrn->virtualY = old_height;
2895d6c0b56eSmrg	scrn->displayWidth = old_pitch;
2896d6c0b56eSmrg
2897d6c0b56eSmrg	return FALSE;
2898d6c0b56eSmrg}
2899d6c0b56eSmrg
290035d5b7c7Smrgstatic void
290135d5b7c7Smrgdrmmode_validate_leases(ScrnInfoPtr scrn)
290235d5b7c7Smrg{
290335d5b7c7Smrg#ifdef XF86_LEASE_VERSION
290435d5b7c7Smrg	ScreenPtr screen = scrn->pScreen;
290535d5b7c7Smrg	rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
290635d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
290735d5b7c7Smrg	drmModeLesseeListPtr lessees;
290835d5b7c7Smrg	RRLeasePtr lease, next;
290935d5b7c7Smrg	int l;
291035d5b7c7Smrg
291135d5b7c7Smrg	/* We can't talk to the kernel about leases when VT switched */
291235d5b7c7Smrg	if (!scrn->vtSema)
291335d5b7c7Smrg		return;
291435d5b7c7Smrg
291535d5b7c7Smrg	lessees = drmModeListLessees(pAMDGPUEnt->fd);
291635d5b7c7Smrg	if (!lessees)
291735d5b7c7Smrg		return;
291835d5b7c7Smrg
291935d5b7c7Smrg	xorg_list_for_each_entry_safe(lease, next, &scr_priv->leases, list) {
292035d5b7c7Smrg		drmmode_lease_private_ptr lease_private = lease->devPrivate;
292135d5b7c7Smrg
292235d5b7c7Smrg		for (l = 0; l < lessees->count; l++) {
292335d5b7c7Smrg			if (lessees->lessees[l] == lease_private->lessee_id)
292435d5b7c7Smrg				break;
292535d5b7c7Smrg		}
292635d5b7c7Smrg
292735d5b7c7Smrg		/* check to see if the lease has gone away */
292835d5b7c7Smrg		if (l == lessees->count) {
292935d5b7c7Smrg			free(lease_private);
293035d5b7c7Smrg			lease->devPrivate = NULL;
293135d5b7c7Smrg			xf86CrtcLeaseTerminated(lease);
293235d5b7c7Smrg		}
293335d5b7c7Smrg	}
293435d5b7c7Smrg
293535d5b7c7Smrg	free(lessees);
293635d5b7c7Smrg#endif
293735d5b7c7Smrg}
293835d5b7c7Smrg
293935d5b7c7Smrg#ifdef XF86_LEASE_VERSION
294035d5b7c7Smrg
294135d5b7c7Smrgstatic int
294235d5b7c7Smrgdrmmode_create_lease(RRLeasePtr lease, int *fd)
294335d5b7c7Smrg{
294435d5b7c7Smrg	ScreenPtr screen = lease->screen;
294535d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
294635d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
294735d5b7c7Smrg	drmmode_lease_private_ptr lease_private;
294835d5b7c7Smrg	int noutput = lease->numOutputs;
294935d5b7c7Smrg	int ncrtc = lease->numCrtcs;
295035d5b7c7Smrg	uint32_t *objects;
295135d5b7c7Smrg	size_t nobjects;
295235d5b7c7Smrg	int lease_fd;
295335d5b7c7Smrg	int c, o;
295435d5b7c7Smrg	int i;
295535d5b7c7Smrg
295635d5b7c7Smrg	nobjects = ncrtc + noutput;
295735d5b7c7Smrg	if (nobjects == 0 || nobjects > (SIZE_MAX / 4) ||
295835d5b7c7Smrg	    ncrtc > (SIZE_MAX - noutput))
295935d5b7c7Smrg		return BadValue;
296035d5b7c7Smrg
296135d5b7c7Smrg	lease_private = calloc(1, sizeof (drmmode_lease_private_rec));
296235d5b7c7Smrg	if (!lease_private)
296335d5b7c7Smrg		return BadAlloc;
296435d5b7c7Smrg
296535d5b7c7Smrg	objects = malloc(nobjects * 4);
296635d5b7c7Smrg	if (!objects) {
296735d5b7c7Smrg		free(lease_private);
296835d5b7c7Smrg		return BadAlloc;
296935d5b7c7Smrg	}
297035d5b7c7Smrg
297135d5b7c7Smrg	i = 0;
297235d5b7c7Smrg
297335d5b7c7Smrg	/* Add CRTC ids */
297435d5b7c7Smrg	for (c = 0; c < ncrtc; c++) {
297535d5b7c7Smrg		xf86CrtcPtr crtc = lease->crtcs[c]->devPrivate;
297635d5b7c7Smrg		drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
297735d5b7c7Smrg
297835d5b7c7Smrg		objects[i++] = drmmode_crtc->mode_crtc->crtc_id;
297935d5b7c7Smrg	}
298035d5b7c7Smrg
298135d5b7c7Smrg	/* Add connector ids */
298235d5b7c7Smrg	for (o = 0; o < noutput; o++) {
298335d5b7c7Smrg		xf86OutputPtr   output = lease->outputs[o]->devPrivate;
298435d5b7c7Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
298535d5b7c7Smrg
298635d5b7c7Smrg		objects[i++] = drmmode_output->mode_output->connector_id;
298735d5b7c7Smrg	}
298835d5b7c7Smrg
298935d5b7c7Smrg	/* call kernel to create lease */
299035d5b7c7Smrg	assert (i == nobjects);
299135d5b7c7Smrg
299235d5b7c7Smrg	lease_fd = drmModeCreateLease(pAMDGPUEnt->fd, objects, nobjects, 0,
299335d5b7c7Smrg				      &lease_private->lessee_id);
299435d5b7c7Smrg
299535d5b7c7Smrg	free(objects);
299635d5b7c7Smrg
299735d5b7c7Smrg	if (lease_fd < 0) {
299835d5b7c7Smrg		free(lease_private);
299935d5b7c7Smrg		return BadMatch;
300035d5b7c7Smrg	}
300135d5b7c7Smrg
300235d5b7c7Smrg	lease->devPrivate = lease_private;
300335d5b7c7Smrg
300435d5b7c7Smrg	xf86CrtcLeaseStarted(lease);
300535d5b7c7Smrg
300635d5b7c7Smrg	*fd = lease_fd;
300735d5b7c7Smrg	return Success;
300835d5b7c7Smrg}
300935d5b7c7Smrg
301035d5b7c7Smrgstatic void
301135d5b7c7Smrgdrmmode_terminate_lease(RRLeasePtr lease)
301235d5b7c7Smrg{
301335d5b7c7Smrg	drmmode_lease_private_ptr lease_private = lease->devPrivate;
301435d5b7c7Smrg	ScreenPtr screen = lease->screen;
301535d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
301635d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
301735d5b7c7Smrg
301835d5b7c7Smrg	if (drmModeRevokeLease(pAMDGPUEnt->fd, lease_private->lessee_id) == 0) {
301935d5b7c7Smrg		free(lease_private);
302035d5b7c7Smrg		lease->devPrivate = NULL;
302135d5b7c7Smrg		xf86CrtcLeaseTerminated(lease);
302235d5b7c7Smrg	}
302335d5b7c7Smrg}
302435d5b7c7Smrg
302535d5b7c7Smrg#endif // XF86_LEASE_VERSION
302635d5b7c7Smrg
3027d6c0b56eSmrgstatic const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
302835d5b7c7Smrg	.resize = drmmode_xf86crtc_resize,
302935d5b7c7Smrg#ifdef XF86_LEASE_VERSION
303035d5b7c7Smrg	.create_lease = drmmode_create_lease,
303135d5b7c7Smrg	.terminate_lease = drmmode_terminate_lease
303235d5b7c7Smrg#endif
3033d6c0b56eSmrg};
3034d6c0b56eSmrg
3035d6c0b56eSmrgstatic void
3036d6c0b56eSmrgdrmmode_flip_abort(xf86CrtcPtr crtc, void *event_data)
3037d6c0b56eSmrg{
303824b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
303924b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
3040d6c0b56eSmrg	drmmode_flipdata_ptr flipdata = event_data;
304135d5b7c7Smrg	int crtc_id = drmmode_get_crtc_id(crtc);
304235d5b7c7Smrg	struct drmmode_fb **fb = &flipdata->fb[crtc_id];
304335d5b7c7Smrg
304435d5b7c7Smrg	if (drmmode_crtc->flip_pending == *fb) {
304535d5b7c7Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->flip_pending,
304635d5b7c7Smrg				     NULL);
304735d5b7c7Smrg	}
304835d5b7c7Smrg	drmmode_fb_reference(pAMDGPUEnt->fd, fb, NULL);
3049d6c0b56eSmrg
3050d6c0b56eSmrg	if (--flipdata->flip_count == 0) {
3051504d986fSmrg		if (!flipdata->fe_crtc)
3052504d986fSmrg			flipdata->fe_crtc = crtc;
3053504d986fSmrg		flipdata->abort(flipdata->fe_crtc, flipdata->event_data);
3054d6c0b56eSmrg		free(flipdata);
3055d6c0b56eSmrg	}
3056d6c0b56eSmrg}
3057d6c0b56eSmrg
3058d6c0b56eSmrgstatic void
3059d6c0b56eSmrgdrmmode_flip_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, void *event_data)
3060d6c0b56eSmrg{
3061d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
306224b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3063d6c0b56eSmrg	drmmode_flipdata_ptr flipdata = event_data;
306435d5b7c7Smrg	int crtc_id = drmmode_get_crtc_id(crtc);
306535d5b7c7Smrg	struct drmmode_fb **fb = &flipdata->fb[crtc_id];
3066d6c0b56eSmrg
3067d6c0b56eSmrg	/* Is this the event whose info shall be delivered to higher level? */
3068d6c0b56eSmrg	if (crtc == flipdata->fe_crtc) {
3069d6c0b56eSmrg		/* Yes: Cache msc, ust for later delivery. */
3070d6c0b56eSmrg		flipdata->fe_frame = frame;
3071d6c0b56eSmrg		flipdata->fe_usec = usec;
3072d6c0b56eSmrg	}
3073d6c0b56eSmrg
307435d5b7c7Smrg	if (drmmode_crtc->flip_pending == *fb) {
307524b90cf4Smrg		drmmode_fb_reference(pAMDGPUEnt->fd,
307624b90cf4Smrg				     &drmmode_crtc->flip_pending, NULL);
307724b90cf4Smrg	}
307835d5b7c7Smrg	drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, *fb);
307935d5b7c7Smrg	drmmode_fb_reference(pAMDGPUEnt->fd, fb, NULL);
308024b90cf4Smrg
3081d6c0b56eSmrg	if (--flipdata->flip_count == 0) {
3082504d986fSmrg		/* Deliver MSC & UST from reference/current CRTC to flip event
3083504d986fSmrg		 * handler
3084504d986fSmrg		 */
3085d6c0b56eSmrg		if (flipdata->fe_crtc)
3086504d986fSmrg			flipdata->handler(flipdata->fe_crtc, flipdata->fe_frame,
3087504d986fSmrg					  flipdata->fe_usec, flipdata->event_data);
3088504d986fSmrg		else
3089504d986fSmrg			flipdata->handler(crtc, frame, usec, flipdata->event_data);
3090d6c0b56eSmrg
3091d6c0b56eSmrg		free(flipdata);
3092d6c0b56eSmrg	}
3093d6c0b56eSmrg}
3094d6c0b56eSmrg
3095504d986fSmrg#if HAVE_NOTIFY_FD
3096504d986fSmrgstatic void drmmode_notify_fd(int fd, int notify, void *data)
3097504d986fSmrg{
3098504d986fSmrg	drmmode_ptr drmmode = data;
309935d5b7c7Smrg	amdgpu_drm_handle_event(fd, &drmmode->event_context);
3100504d986fSmrg}
3101504d986fSmrg#else
3102d6c0b56eSmrgstatic void drm_wakeup_handler(pointer data, int err, pointer p)
3103d6c0b56eSmrg{
3104d6c0b56eSmrg	drmmode_ptr drmmode = data;
3105d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(drmmode->scrn);
3106d6c0b56eSmrg	fd_set *read_mask = p;
3107d6c0b56eSmrg
3108d6c0b56eSmrg	if (err >= 0 && FD_ISSET(pAMDGPUEnt->fd, read_mask)) {
310935d5b7c7Smrg		amdgpu_drm_handle_event(pAMDGPUEnt->fd, &drmmode->event_context);
3110d6c0b56eSmrg	}
3111d6c0b56eSmrg}
3112504d986fSmrg#endif
3113d6c0b56eSmrg
311411bf0794Smrgstatic Bool drmmode_probe_page_flip_target(AMDGPUEntPtr pAMDGPUEnt)
311511bf0794Smrg{
311611bf0794Smrg	uint64_t cap_value;
311711bf0794Smrg
311811bf0794Smrg	return drmGetCap(pAMDGPUEnt->fd, DRM_CAP_PAGE_FLIP_TARGET,
311911bf0794Smrg			 &cap_value) == 0 && cap_value != 0;
312011bf0794Smrg}
312111bf0794Smrg
312211bf0794Smrgstatic int
312311bf0794Smrgdrmmode_page_flip(AMDGPUEntPtr pAMDGPUEnt, drmmode_crtc_private_ptr drmmode_crtc,
312411bf0794Smrg		  int fb_id, uint32_t flags, uintptr_t drm_queue_seq)
312511bf0794Smrg{
312611bf0794Smrg	flags |= DRM_MODE_PAGE_FLIP_EVENT;
312711bf0794Smrg	return drmModePageFlip(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id,
312811bf0794Smrg			       fb_id, flags, (void*)drm_queue_seq);
312911bf0794Smrg}
313011bf0794Smrg
313111bf0794Smrgint
313211bf0794Smrgdrmmode_page_flip_target_absolute(AMDGPUEntPtr pAMDGPUEnt,
313311bf0794Smrg				  drmmode_crtc_private_ptr drmmode_crtc,
313411bf0794Smrg				  int fb_id, uint32_t flags,
313511bf0794Smrg				  uintptr_t drm_queue_seq, uint32_t target_msc)
313611bf0794Smrg{
313711bf0794Smrg	if (pAMDGPUEnt->has_page_flip_target) {
313811bf0794Smrg		flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE;
313911bf0794Smrg		return drmModePageFlipTarget(pAMDGPUEnt->fd,
314011bf0794Smrg					     drmmode_crtc->mode_crtc->crtc_id,
314111bf0794Smrg					     fb_id, flags, (void*)drm_queue_seq,
314211bf0794Smrg					     target_msc);
314311bf0794Smrg	}
314411bf0794Smrg
314511bf0794Smrg	return drmmode_page_flip(pAMDGPUEnt, drmmode_crtc, fb_id, flags,
314611bf0794Smrg				 drm_queue_seq);
314711bf0794Smrg}
314811bf0794Smrg
314911bf0794Smrgint
315011bf0794Smrgdrmmode_page_flip_target_relative(AMDGPUEntPtr pAMDGPUEnt,
315111bf0794Smrg				  drmmode_crtc_private_ptr drmmode_crtc,
315211bf0794Smrg				  int fb_id, uint32_t flags,
315311bf0794Smrg				  uintptr_t drm_queue_seq, uint32_t target_msc)
315411bf0794Smrg{
315511bf0794Smrg	if (pAMDGPUEnt->has_page_flip_target) {
315611bf0794Smrg		flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_TARGET_RELATIVE;
315711bf0794Smrg		return drmModePageFlipTarget(pAMDGPUEnt->fd,
315811bf0794Smrg					     drmmode_crtc->mode_crtc->crtc_id,
315911bf0794Smrg					     fb_id, flags, (void*)drm_queue_seq,
316011bf0794Smrg					     target_msc);
316111bf0794Smrg	}
316211bf0794Smrg
316311bf0794Smrg	return drmmode_page_flip(pAMDGPUEnt, drmmode_crtc, fb_id, flags,
316411bf0794Smrg				 drm_queue_seq);
316511bf0794Smrg}
316611bf0794Smrg
316735d5b7c7Smrg/**
316835d5b7c7Smrg * Initialize DDX color management support. It does two things:
316935d5b7c7Smrg *
317035d5b7c7Smrg * 1. Cache DRM color management property type IDs, as they do not change. They
317135d5b7c7Smrg *    will be used later to modify color management via DRM, or to determine if
317235d5b7c7Smrg *    there's kernel support for color management.
317335d5b7c7Smrg *
317435d5b7c7Smrg * 2. Cache degamma/gamma LUT sizes, since all CRTCs have the same LUT sizes on
317535d5b7c7Smrg *    AMD hardware.
317635d5b7c7Smrg *
317735d5b7c7Smrg * If the cached ID's are all 0 after calling this function, then color
317835d5b7c7Smrg * management is not supported. For short, checking if the gamma LUT size
317935d5b7c7Smrg * property ID == 0 is sufficient.
318035d5b7c7Smrg *
318135d5b7c7Smrg * This should be called before CRTCs are initialized within pre_init, as the
318235d5b7c7Smrg * cached values will be used there.
318335d5b7c7Smrg *
318435d5b7c7Smrg * @drm_fd: DRM file descriptor
318535d5b7c7Smrg * @drmmode: drmmode object, where the cached IDs are stored
318635d5b7c7Smrg * @mode_res: The DRM mode resource containing the CRTC ids
318735d5b7c7Smrg */
318835d5b7c7Smrgstatic void drmmode_cm_init(int drm_fd, drmmode_ptr drmmode,
318935d5b7c7Smrg			    drmModeResPtr mode_res)
319035d5b7c7Smrg{
319135d5b7c7Smrg	drmModeObjectPropertiesPtr drm_props;
319235d5b7c7Smrg	drmModePropertyPtr drm_prop;
319335d5b7c7Smrg	enum drmmode_cm_prop cm_prop;
319435d5b7c7Smrg	uint32_t cm_enabled = 0;
319535d5b7c7Smrg	uint32_t cm_all_enabled = (1 << CM_NUM_PROPS) - 1;
319635d5b7c7Smrg	int i;
319735d5b7c7Smrg
319835d5b7c7Smrg	memset(drmmode->cm_prop_ids, 0, sizeof(drmmode->cm_prop_ids));
319935d5b7c7Smrg	drmmode->gamma_lut_size = drmmode->degamma_lut_size = 0;
320035d5b7c7Smrg
320135d5b7c7Smrg	if (!mode_res->crtcs)
320235d5b7c7Smrg		return;
320335d5b7c7Smrg
320435d5b7c7Smrg	/* AMD hardware has color management support on all pipes. It is
320535d5b7c7Smrg	 * therefore sufficient to only check the first CRTC.
320635d5b7c7Smrg	 */
320735d5b7c7Smrg	drm_props = drmModeObjectGetProperties(drm_fd,
320835d5b7c7Smrg					       mode_res->crtcs[0],
320935d5b7c7Smrg					       DRM_MODE_OBJECT_CRTC);
321035d5b7c7Smrg	if (!drm_props)
321135d5b7c7Smrg		return;
321235d5b7c7Smrg
321335d5b7c7Smrg	for (i = 0; i < drm_props->count_props; i++) {
321435d5b7c7Smrg		drm_prop = drmModeGetProperty(drm_fd,
321535d5b7c7Smrg					      drm_props->props[i]);
321635d5b7c7Smrg		if (!drm_prop)
321735d5b7c7Smrg			continue;
321835d5b7c7Smrg
321935d5b7c7Smrg		cm_prop = get_cm_enum_from_str(drm_prop->name);
322035d5b7c7Smrg		if (cm_prop == CM_INVALID_PROP)
322135d5b7c7Smrg			continue;
322235d5b7c7Smrg
322335d5b7c7Smrg		if (cm_prop == CM_DEGAMMA_LUT_SIZE)
322435d5b7c7Smrg			drmmode->degamma_lut_size = drm_props->prop_values[i];
322535d5b7c7Smrg		else if (cm_prop == CM_GAMMA_LUT_SIZE)
322635d5b7c7Smrg			drmmode->gamma_lut_size = drm_props->prop_values[i];
322735d5b7c7Smrg
322835d5b7c7Smrg		drmmode->cm_prop_ids[cm_prop] = drm_props->props[i];
322935d5b7c7Smrg		cm_enabled |= 1 << cm_prop;
323035d5b7c7Smrg
323135d5b7c7Smrg		drmModeFreeProperty(drm_prop);
323235d5b7c7Smrg	}
323335d5b7c7Smrg	drmModeFreeObjectProperties(drm_props);
323435d5b7c7Smrg
323535d5b7c7Smrg	/* cm is enabled only if all prop ids are found */
323635d5b7c7Smrg	if (cm_enabled == cm_all_enabled)
323735d5b7c7Smrg		return;
323835d5b7c7Smrg
323935d5b7c7Smrg	/* Otherwise, disable DDX cm support */
324035d5b7c7Smrg	memset(drmmode->cm_prop_ids, 0, sizeof(drmmode->cm_prop_ids));
324135d5b7c7Smrg	drmmode->gamma_lut_size = drmmode->degamma_lut_size = 0;
324235d5b7c7Smrg}
324335d5b7c7Smrg
3244d6c0b56eSmrgBool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
3245d6c0b56eSmrg{
3246d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
3247d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
3248d6c0b56eSmrg	int i, num_dvi = 0, num_hdmi = 0;
3249d6c0b56eSmrg	unsigned int crtcs_needed = 0;
3250d6c0b56eSmrg	drmModeResPtr mode_res;
3251d6c0b56eSmrg	char *bus_id_string, *provider_name;
3252d6c0b56eSmrg
3253d6c0b56eSmrg	xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
3254d6c0b56eSmrg
3255d6c0b56eSmrg	drmmode->scrn = pScrn;
3256d6c0b56eSmrg	mode_res = drmModeGetResources(pAMDGPUEnt->fd);
3257d6c0b56eSmrg	if (!mode_res)
3258d6c0b56eSmrg		return FALSE;
3259d6c0b56eSmrg
3260d6c0b56eSmrg	drmmode->count_crtcs = mode_res->count_crtcs;
3261d6c0b56eSmrg	xf86CrtcSetSizeRange(pScrn, 320, 200, mode_res->max_width,
3262d6c0b56eSmrg			     mode_res->max_height);
3263d6c0b56eSmrg
3264d6c0b56eSmrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
3265d6c0b56eSmrg		       "Initializing outputs ...\n");
3266d6c0b56eSmrg	for (i = 0; i < mode_res->count_connectors; i++)
3267d6c0b56eSmrg		crtcs_needed += drmmode_output_init(pScrn, drmmode, mode_res, i, &num_dvi, &num_hdmi, 0);
3268d6c0b56eSmrg
3269d6c0b56eSmrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
3270d6c0b56eSmrg		       "%d crtcs needed for screen.\n", crtcs_needed);
3271d6c0b56eSmrg
327224b90cf4Smrg	/* Need per-screen drmmode_crtc_funcs, based on our global template,
327324b90cf4Smrg	 * so we can disable some functions, depending on screen settings.
327424b90cf4Smrg	 */
327524b90cf4Smrg	info->drmmode_crtc_funcs = drmmode_crtc_funcs;
327624b90cf4Smrg
3277d6c0b56eSmrg	if (!info->use_glamor) {
3278d6c0b56eSmrg		/* Rotation requires hardware acceleration */
327924b90cf4Smrg		info->drmmode_crtc_funcs.shadow_allocate = NULL;
328024b90cf4Smrg		info->drmmode_crtc_funcs.shadow_create = NULL;
328124b90cf4Smrg		info->drmmode_crtc_funcs.shadow_destroy = NULL;
3282d6c0b56eSmrg	}
3283d6c0b56eSmrg
328435d5b7c7Smrg	drmmode_cm_init(pAMDGPUEnt->fd, drmmode, mode_res);
328535d5b7c7Smrg
328635d5b7c7Smrg	/* Spare the server the effort to compute and update unused CLUTs. */
328735d5b7c7Smrg	if (pScrn->depth == 30 && !drmmode_cm_enabled(drmmode))
328824b90cf4Smrg		info->drmmode_crtc_funcs.gamma_set = NULL;
328924b90cf4Smrg
3290d6c0b56eSmrg	for (i = 0; i < mode_res->count_crtcs; i++)
3291d6c0b56eSmrg		if (!xf86IsEntityShared(pScrn->entityList[0]) ||
3292d6c0b56eSmrg		    (crtcs_needed && !(pAMDGPUEnt->assigned_crtcs & (1 << i))))
3293d6c0b56eSmrg			crtcs_needed -= drmmode_crtc_init(pScrn, drmmode, mode_res, i);
3294d6c0b56eSmrg
3295d6c0b56eSmrg	/* All ZaphodHeads outputs provided with matching crtcs? */
3296d6c0b56eSmrg	if (xf86IsEntityShared(pScrn->entityList[0]) && (crtcs_needed > 0))
3297d6c0b56eSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
3298d6c0b56eSmrg			   "%d ZaphodHeads crtcs unavailable. Some outputs will stay off.\n",
3299d6c0b56eSmrg			   crtcs_needed);
3300d6c0b56eSmrg
3301d6c0b56eSmrg	/* workout clones */
3302d6c0b56eSmrg	drmmode_clones_init(pScrn, drmmode, mode_res);
3303d6c0b56eSmrg
3304d6c0b56eSmrg	bus_id_string = DRICreatePCIBusID(info->PciInfo);
3305d6c0b56eSmrg	XNFasprintf(&provider_name, "%s @ %s", pScrn->chipset, bus_id_string);
3306d6c0b56eSmrg	free(bus_id_string);
3307d6c0b56eSmrg	xf86ProviderSetup(pScrn, NULL, provider_name);
3308d6c0b56eSmrg	free(provider_name);
3309d6c0b56eSmrg
3310d6c0b56eSmrg	xf86InitialConfiguration(pScrn, TRUE);
3311d6c0b56eSmrg
331211bf0794Smrg	pAMDGPUEnt->has_page_flip_target = drmmode_probe_page_flip_target(pAMDGPUEnt);
331311bf0794Smrg
3314d6c0b56eSmrg	drmModeFreeResources(mode_res);
3315d6c0b56eSmrg	return TRUE;
3316d6c0b56eSmrg}
3317d6c0b56eSmrg
3318d6c0b56eSmrgvoid drmmode_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
3319d6c0b56eSmrg{
3320d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
3321d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
3322d6c0b56eSmrg
3323d6c0b56eSmrg	info->drmmode_inited = TRUE;
3324d6c0b56eSmrg	if (pAMDGPUEnt->fd_wakeup_registered != serverGeneration) {
3325504d986fSmrg#if HAVE_NOTIFY_FD
3326504d986fSmrg		SetNotifyFd(pAMDGPUEnt->fd, drmmode_notify_fd, X_NOTIFY_READ, drmmode);
3327504d986fSmrg#else
3328d6c0b56eSmrg		AddGeneralSocket(pAMDGPUEnt->fd);
3329d6c0b56eSmrg		RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA,
3330d6c0b56eSmrg					       drm_wakeup_handler, drmmode);
3331504d986fSmrg#endif
3332d6c0b56eSmrg		pAMDGPUEnt->fd_wakeup_registered = serverGeneration;
3333d6c0b56eSmrg		pAMDGPUEnt->fd_wakeup_ref = 1;
3334d6c0b56eSmrg	} else
3335d6c0b56eSmrg		pAMDGPUEnt->fd_wakeup_ref++;
3336d6c0b56eSmrg}
3337d6c0b56eSmrg
3338d6c0b56eSmrgvoid drmmode_fini(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
3339d6c0b56eSmrg{
3340504d986fSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
3341d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
3342d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
3343504d986fSmrg	int c;
3344d6c0b56eSmrg
3345d6c0b56eSmrg	if (!info->drmmode_inited)
3346d6c0b56eSmrg		return;
3347d6c0b56eSmrg
3348d6c0b56eSmrg	if (pAMDGPUEnt->fd_wakeup_registered == serverGeneration &&
3349d6c0b56eSmrg	    !--pAMDGPUEnt->fd_wakeup_ref) {
3350504d986fSmrg#if HAVE_NOTIFY_FD
3351504d986fSmrg		RemoveNotifyFd(pAMDGPUEnt->fd);
3352504d986fSmrg#else
3353d6c0b56eSmrg		RemoveGeneralSocket(pAMDGPUEnt->fd);
3354d6c0b56eSmrg		RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA,
3355d6c0b56eSmrg					     drm_wakeup_handler, drmmode);
3356504d986fSmrg#endif
3357504d986fSmrg	}
3358504d986fSmrg
335911bf0794Smrg	for (c = 0; c < config->num_crtc; c++)
336011bf0794Smrg		drmmode_crtc_scanout_free(config->crtc[c]->driver_private);
3361d6c0b56eSmrg}
3362d6c0b56eSmrg
336324b90cf4Smrgstatic void drmmode_sprite_do_set_cursor(struct amdgpu_device_priv *device_priv,
336424b90cf4Smrg					 ScrnInfoPtr scrn, int x, int y)
336524b90cf4Smrg{
336624b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
336724b90cf4Smrg	CursorPtr cursor = device_priv->cursor;
336824b90cf4Smrg	Bool sprite_visible = device_priv->sprite_visible;
336924b90cf4Smrg
337024b90cf4Smrg	if (cursor) {
337124b90cf4Smrg		x -= cursor->bits->xhot;
337224b90cf4Smrg		y -= cursor->bits->yhot;
337324b90cf4Smrg
337424b90cf4Smrg		device_priv->sprite_visible =
337524b90cf4Smrg			x < scrn->virtualX && y < scrn->virtualY &&
337624b90cf4Smrg			(x + cursor->bits->width > 0) &&
337724b90cf4Smrg			(y + cursor->bits->height > 0);
337824b90cf4Smrg	} else {
337924b90cf4Smrg		device_priv->sprite_visible = FALSE;
338024b90cf4Smrg	}
338124b90cf4Smrg
338224b90cf4Smrg	info->sprites_visible += device_priv->sprite_visible - sprite_visible;
338324b90cf4Smrg}
338424b90cf4Smrg
338535d5b7c7Smrgstatic void drmmode_sprite_set_cursor(DeviceIntPtr pDev, ScreenPtr pScreen,
338635d5b7c7Smrg				      CursorPtr pCursor, int x, int y)
338724b90cf4Smrg{
338824b90cf4Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
338924b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
339024b90cf4Smrg	struct amdgpu_device_priv *device_priv =
339124b90cf4Smrg		dixLookupScreenPrivate(&pDev->devPrivates,
339224b90cf4Smrg				       &amdgpu_device_private_key, pScreen);
339324b90cf4Smrg
339424b90cf4Smrg	device_priv->cursor = pCursor;
339524b90cf4Smrg	drmmode_sprite_do_set_cursor(device_priv, scrn, x, y);
339624b90cf4Smrg
339735d5b7c7Smrg	info->SpriteFuncs->SetCursor(pDev, pScreen, pCursor, x, y);
339824b90cf4Smrg}
339924b90cf4Smrg
340035d5b7c7Smrgstatic void drmmode_sprite_move_cursor(DeviceIntPtr pDev, ScreenPtr pScreen,
340135d5b7c7Smrg				       int x, int y)
340224b90cf4Smrg{
340324b90cf4Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
340424b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
340524b90cf4Smrg	struct amdgpu_device_priv *device_priv =
340624b90cf4Smrg		dixLookupScreenPrivate(&pDev->devPrivates,
340724b90cf4Smrg				       &amdgpu_device_private_key, pScreen);
340824b90cf4Smrg
340924b90cf4Smrg	drmmode_sprite_do_set_cursor(device_priv, scrn, x, y);
341024b90cf4Smrg
341135d5b7c7Smrg	info->SpriteFuncs->MoveCursor(pDev, pScreen, x, y);
341224b90cf4Smrg}
341324b90cf4Smrg
341435d5b7c7Smrgstatic Bool drmmode_sprite_realize_realize_cursor(DeviceIntPtr pDev,
341535d5b7c7Smrg						  ScreenPtr pScreen,
341635d5b7c7Smrg						  CursorPtr pCursor)
341735d5b7c7Smrg{
341835d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
341935d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
342035d5b7c7Smrg
342135d5b7c7Smrg	return info->SpriteFuncs->RealizeCursor(pDev, pScreen, pCursor);
342235d5b7c7Smrg}
342335d5b7c7Smrg
342435d5b7c7Smrgstatic Bool drmmode_sprite_realize_unrealize_cursor(DeviceIntPtr pDev,
342535d5b7c7Smrg						    ScreenPtr pScreen,
342635d5b7c7Smrg						    CursorPtr pCursor)
342735d5b7c7Smrg{
342835d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
342935d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
343035d5b7c7Smrg
343135d5b7c7Smrg	return info->SpriteFuncs->UnrealizeCursor(pDev, pScreen, pCursor);
343235d5b7c7Smrg}
343335d5b7c7Smrg
343435d5b7c7Smrgstatic Bool drmmode_sprite_device_cursor_initialize(DeviceIntPtr pDev,
343535d5b7c7Smrg						    ScreenPtr pScreen)
343635d5b7c7Smrg{
343735d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
343835d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
343935d5b7c7Smrg
344035d5b7c7Smrg	return info->SpriteFuncs->DeviceCursorInitialize(pDev, pScreen);
344135d5b7c7Smrg}
344235d5b7c7Smrg
344335d5b7c7Smrgstatic void drmmode_sprite_device_cursor_cleanup(DeviceIntPtr pDev,
344435d5b7c7Smrg						 ScreenPtr pScreen)
344535d5b7c7Smrg{
344635d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
344735d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
344835d5b7c7Smrg
344935d5b7c7Smrg	info->SpriteFuncs->DeviceCursorCleanup(pDev, pScreen);
345035d5b7c7Smrg}
345135d5b7c7Smrg
345235d5b7c7SmrgmiPointerSpriteFuncRec drmmode_sprite_funcs = {
345335d5b7c7Smrg	.RealizeCursor = drmmode_sprite_realize_realize_cursor,
345435d5b7c7Smrg	.UnrealizeCursor = drmmode_sprite_realize_unrealize_cursor,
345535d5b7c7Smrg	.SetCursor = drmmode_sprite_set_cursor,
345635d5b7c7Smrg	.MoveCursor = drmmode_sprite_move_cursor,
345735d5b7c7Smrg	.DeviceCursorInitialize = drmmode_sprite_device_cursor_initialize,
345835d5b7c7Smrg	.DeviceCursorCleanup = drmmode_sprite_device_cursor_cleanup,
345935d5b7c7Smrg};
346035d5b7c7Smrg
346135d5b7c7Smrg
3462d6c0b56eSmrgvoid drmmode_set_cursor(ScrnInfoPtr scrn, drmmode_ptr drmmode, int id,
3463d6c0b56eSmrg			struct amdgpu_buffer *bo)
3464d6c0b56eSmrg{
3465d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
3466d6c0b56eSmrg	xf86CrtcPtr crtc = xf86_config->crtc[id];
3467d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3468d6c0b56eSmrg
3469d6c0b56eSmrg	drmmode_crtc->cursor_buffer = bo;
3470d6c0b56eSmrg}
3471d6c0b56eSmrg
3472d6c0b56eSmrgvoid drmmode_adjust_frame(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int x, int y)
3473d6c0b56eSmrg{
3474d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
3475d6c0b56eSmrg	xf86OutputPtr output = config->output[config->compat_output];
3476d6c0b56eSmrg	xf86CrtcPtr crtc = output->crtc;
3477d6c0b56eSmrg
3478d6c0b56eSmrg	if (crtc && crtc->enabled) {
3479d6c0b56eSmrg		drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
3480d6c0b56eSmrg	}
3481d6c0b56eSmrg}
3482d6c0b56eSmrg
3483d6c0b56eSmrgBool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode,
3484d6c0b56eSmrg			       Bool set_hw)
3485d6c0b56eSmrg{
3486d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
348724b90cf4Smrg	unsigned num_desired = 0, num_on = 0;
3488d6c0b56eSmrg	int c;
3489d6c0b56eSmrg
349024b90cf4Smrg	/* First, disable all unused CRTCs */
349124b90cf4Smrg	if (set_hw) {
349224b90cf4Smrg		for (c = 0; c < config->num_crtc; c++) {
349324b90cf4Smrg			xf86CrtcPtr crtc = config->crtc[c];
349424b90cf4Smrg
349524b90cf4Smrg			/* Skip disabled CRTCs */
349624b90cf4Smrg			if (crtc->enabled)
349724b90cf4Smrg				continue;
349824b90cf4Smrg
349935d5b7c7Smrg			drmmode_crtc_dpms(crtc, DPMSModeOff);
350024b90cf4Smrg		}
350124b90cf4Smrg	}
350224b90cf4Smrg
350324b90cf4Smrg	/* Then, try setting the chosen mode on each CRTC */
3504d6c0b56eSmrg	for (c = 0; c < config->num_crtc; c++) {
3505d6c0b56eSmrg		xf86CrtcPtr crtc = config->crtc[c];
3506d6c0b56eSmrg		xf86OutputPtr output = NULL;
3507d6c0b56eSmrg		int o;
3508d6c0b56eSmrg
350924b90cf4Smrg		if (!crtc->enabled)
3510d6c0b56eSmrg			continue;
3511d6c0b56eSmrg
3512d6c0b56eSmrg		if (config->output[config->compat_output]->crtc == crtc)
3513d6c0b56eSmrg			output = config->output[config->compat_output];
3514d6c0b56eSmrg		else {
3515d6c0b56eSmrg			for (o = 0; o < config->num_output; o++)
3516d6c0b56eSmrg				if (config->output[o]->crtc == crtc) {
3517d6c0b56eSmrg					output = config->output[o];
3518d6c0b56eSmrg					break;
3519d6c0b56eSmrg				}
3520d6c0b56eSmrg		}
3521d6c0b56eSmrg		/* paranoia */
3522d6c0b56eSmrg		if (!output)
3523d6c0b56eSmrg			continue;
3524d6c0b56eSmrg
352524b90cf4Smrg		num_desired++;
352624b90cf4Smrg
3527d6c0b56eSmrg		/* Mark that we'll need to re-set the mode for sure */
3528d6c0b56eSmrg		memset(&crtc->mode, 0, sizeof(crtc->mode));
3529d6c0b56eSmrg		if (!crtc->desiredMode.CrtcHDisplay) {
3530d6c0b56eSmrg			DisplayModePtr mode = xf86OutputFindClosestMode(output,
3531d6c0b56eSmrg									pScrn->
3532d6c0b56eSmrg									currentMode);
3533d6c0b56eSmrg
353424b90cf4Smrg			if (!mode) {
353524b90cf4Smrg				xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
353624b90cf4Smrg					   "Failed to find mode for CRTC %d\n", c);
353724b90cf4Smrg				continue;
353824b90cf4Smrg			}
3539d6c0b56eSmrg			crtc->desiredMode = *mode;
3540d6c0b56eSmrg			crtc->desiredRotation = RR_Rotate_0;
3541d6c0b56eSmrg			crtc->desiredX = 0;
3542d6c0b56eSmrg			crtc->desiredY = 0;
3543d6c0b56eSmrg		}
3544d6c0b56eSmrg
3545d6c0b56eSmrg		if (set_hw) {
354624b90cf4Smrg			if (crtc->funcs->set_mode_major(crtc, &crtc->desiredMode,
354724b90cf4Smrg							crtc->desiredRotation,
354824b90cf4Smrg							crtc->desiredX,
354924b90cf4Smrg							crtc->desiredY)) {
355024b90cf4Smrg				num_on++;
355124b90cf4Smrg			} else {
355224b90cf4Smrg				xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
355324b90cf4Smrg					   "Failed to set mode on CRTC %d\n", c);
355435d5b7c7Smrg				RRCrtcSet(crtc->randr_crtc, NULL, crtc->x, crtc->y,
355535d5b7c7Smrg					  crtc->rotation, 0, NULL);
355624b90cf4Smrg			}
3557d6c0b56eSmrg		} else {
3558d6c0b56eSmrg			crtc->mode = crtc->desiredMode;
3559d6c0b56eSmrg			crtc->rotation = crtc->desiredRotation;
3560d6c0b56eSmrg			crtc->x = crtc->desiredX;
3561d6c0b56eSmrg			crtc->y = crtc->desiredY;
356224b90cf4Smrg			if (drmmode_handle_transform(crtc))
356324b90cf4Smrg				num_on++;
3564d6c0b56eSmrg		}
3565d6c0b56eSmrg	}
356624b90cf4Smrg
356724b90cf4Smrg	if (num_on == 0 && num_desired > 0) {
356824b90cf4Smrg		xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to enable any CRTC\n");
356924b90cf4Smrg		return FALSE;
357024b90cf4Smrg	}
357124b90cf4Smrg
357235d5b7c7Smrg	/* Validate leases on VT re-entry */
357335d5b7c7Smrg	drmmode_validate_leases(pScrn);
357435d5b7c7Smrg
3575d6c0b56eSmrg	return TRUE;
3576d6c0b56eSmrg}
3577d6c0b56eSmrg
3578d6c0b56eSmrgBool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn)
3579d6c0b56eSmrg{
3580d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
358135d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
358235d5b7c7Smrg	int i;
3583d6c0b56eSmrg
3584d6c0b56eSmrg	if (xf86_config->num_crtc) {
3585d6c0b56eSmrg		xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
3586d6c0b56eSmrg			       "Initializing kms color map\n");
3587d6c0b56eSmrg		if (!miCreateDefColormap(pScreen))
3588d6c0b56eSmrg			return FALSE;
358935d5b7c7Smrg
359035d5b7c7Smrg		if (pScrn->depth == 30) {
359135d5b7c7Smrg			if (!drmmode_cm_enabled(&info->drmmode))
359235d5b7c7Smrg				return TRUE;
359335d5b7c7Smrg
359435d5b7c7Smrg			for (i = 0; i < xf86_config->num_crtc; i++) {
359535d5b7c7Smrg				xf86CrtcPtr crtc = xf86_config->crtc[i];
359635d5b7c7Smrg				void *gamma;
359735d5b7c7Smrg
359835d5b7c7Smrg				if (crtc->gamma_size == 1024)
359935d5b7c7Smrg					continue;
360035d5b7c7Smrg
360135d5b7c7Smrg				gamma = malloc(1024 * 3 * sizeof(CARD16));
360235d5b7c7Smrg				if (!gamma) {
360335d5b7c7Smrg					ErrorF("Failed to allocate gamma LUT memory\n");
360435d5b7c7Smrg					return FALSE;
360535d5b7c7Smrg				}
360635d5b7c7Smrg
360735d5b7c7Smrg				free(crtc->gamma_red);
360835d5b7c7Smrg				crtc->gamma_size = 1024;
360935d5b7c7Smrg				crtc->gamma_red = gamma;
361035d5b7c7Smrg				crtc->gamma_green = crtc->gamma_red + crtc->gamma_size;
361135d5b7c7Smrg				crtc->gamma_blue = crtc->gamma_green + crtc->gamma_size;
361235d5b7c7Smrg			}
361335d5b7c7Smrg		}
361435d5b7c7Smrg
361535d5b7c7Smrg		/* All Radeons support 10 bit CLUTs. */
361635d5b7c7Smrg		if (!xf86HandleColormaps(pScreen, 1 << pScrn->rgbBits, 10,
361735d5b7c7Smrg					 NULL, NULL, CMAP_PALETTED_TRUECOLOR
3618d6c0b56eSmrg					 | CMAP_RELOAD_ON_MODE_SWITCH))
3619d6c0b56eSmrg			return FALSE;
362035d5b7c7Smrg
362135d5b7c7Smrg		for (i = 0; i < xf86_config->num_crtc; i++) {
362235d5b7c7Smrg			xf86CrtcPtr crtc = xf86_config->crtc[i];
362335d5b7c7Smrg
362435d5b7c7Smrg			drmmode_crtc_gamma_do_set(crtc, crtc->gamma_red,
362535d5b7c7Smrg						  crtc->gamma_green,
362635d5b7c7Smrg						  crtc->gamma_blue,
362735d5b7c7Smrg						  crtc->gamma_size);
362835d5b7c7Smrg		}
3629d6c0b56eSmrg	}
363035d5b7c7Smrg
3631d6c0b56eSmrg	return TRUE;
3632d6c0b56eSmrg}
3633d6c0b56eSmrg
3634504d986fSmrgstatic Bool
3635504d986fSmrgdrmmode_find_output(ScrnInfoPtr scrn, int output_id, int *num_dvi,
3636504d986fSmrg		    int *num_hdmi)
3637504d986fSmrg{
3638504d986fSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3639504d986fSmrg	int i;
3640504d986fSmrg
3641504d986fSmrg	for (i = 0; i < config->num_output; i++) {
3642504d986fSmrg		xf86OutputPtr output = config->output[i];
3643504d986fSmrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
3644504d986fSmrg
3645504d986fSmrg		if (drmmode_output->output_id == output_id) {
3646504d986fSmrg			switch(drmmode_output->mode_output->connector_type) {
3647504d986fSmrg			case DRM_MODE_CONNECTOR_DVII:
3648504d986fSmrg			case DRM_MODE_CONNECTOR_DVID:
3649504d986fSmrg			case DRM_MODE_CONNECTOR_DVIA:
3650504d986fSmrg				(*num_dvi)++;
3651504d986fSmrg				break;
3652504d986fSmrg			case DRM_MODE_CONNECTOR_HDMIA:
3653504d986fSmrg			case DRM_MODE_CONNECTOR_HDMIB:
3654504d986fSmrg				(*num_hdmi)++;
3655504d986fSmrg				break;
3656504d986fSmrg			}
3657504d986fSmrg
3658504d986fSmrg			return TRUE;
3659504d986fSmrg		}
3660504d986fSmrg	}
3661504d986fSmrg
3662504d986fSmrg	return FALSE;
3663504d986fSmrg}
3664d6c0b56eSmrg
3665d6c0b56eSmrgvoid
3666d6c0b56eSmrgamdgpu_mode_hotplug(ScrnInfoPtr scrn, drmmode_ptr drmmode)
3667d6c0b56eSmrg{
3668d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3669d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
3670d6c0b56eSmrg	drmModeResPtr mode_res;
3671d6c0b56eSmrg	int i, j;
3672d6c0b56eSmrg	Bool found;
3673d6c0b56eSmrg	Bool changed = FALSE;
3674504d986fSmrg	int num_dvi = 0, num_hdmi = 0;
3675d6c0b56eSmrg
367624b90cf4Smrg	/* Try to re-set the mode on all the connectors with a BAD link-state:
367724b90cf4Smrg	 * This may happen if a link degrades and a new modeset is necessary, using
367824b90cf4Smrg	 * different link-training parameters. If the kernel found that the current
367924b90cf4Smrg	 * mode is not achievable anymore, it should have pruned the mode before
368024b90cf4Smrg	 * sending the hotplug event. Try to re-set the currently-set mode to keep
368124b90cf4Smrg	 * the display alive, this will fail if the mode has been pruned.
368224b90cf4Smrg	 * In any case, we will send randr events for the Desktop Environment to
368324b90cf4Smrg	 * deal with it, if it wants to.
368424b90cf4Smrg	 */
368524b90cf4Smrg	for (i = 0; i < config->num_output; i++) {
368624b90cf4Smrg		xf86OutputPtr output = config->output[i];
368724b90cf4Smrg		xf86CrtcPtr crtc = output->crtc;
368824b90cf4Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
368924b90cf4Smrg
369024b90cf4Smrg		drmmode_output_detect(output);
369124b90cf4Smrg
369224b90cf4Smrg		if (!crtc || !drmmode_output->mode_output)
369324b90cf4Smrg			continue;
369424b90cf4Smrg
369524b90cf4Smrg		/* Get an updated view of the properties for the current connector and
369624b90cf4Smrg		 * look for the link-status property
369724b90cf4Smrg		 */
369824b90cf4Smrg		for (j = 0; j < drmmode_output->num_props; j++) {
369924b90cf4Smrg			drmmode_prop_ptr p = &drmmode_output->props[j];
370024b90cf4Smrg
370124b90cf4Smrg			if (!strcmp(p->mode_prop->name, "link-status")) {
370224b90cf4Smrg				if (p->value != DRM_MODE_LINK_STATUS_BAD)
370324b90cf4Smrg					break;
370424b90cf4Smrg
370524b90cf4Smrg				/* the connector got a link failure, re-set the current mode */
370624b90cf4Smrg				drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
370724b90cf4Smrg						       crtc->x, crtc->y);
370824b90cf4Smrg
370924b90cf4Smrg				xf86DrvMsg(scrn->scrnIndex, X_WARNING,
371024b90cf4Smrg					   "hotplug event: connector %u's link-state is BAD, "
371124b90cf4Smrg					   "tried resetting the current mode. You may be left"
371224b90cf4Smrg					   "with a black screen if this fails...\n",
371324b90cf4Smrg					   drmmode_output->mode_output->connector_id);
371424b90cf4Smrg
371524b90cf4Smrg				break;
371624b90cf4Smrg			}
371724b90cf4Smrg		}
371824b90cf4Smrg	}
371924b90cf4Smrg
3720d6c0b56eSmrg	mode_res = drmModeGetResources(pAMDGPUEnt->fd);
3721d6c0b56eSmrg	if (!mode_res)
3722d6c0b56eSmrg		goto out;
3723d6c0b56eSmrg
3724d6c0b56eSmrgrestart_destroy:
3725d6c0b56eSmrg	for (i = 0; i < config->num_output; i++) {
3726d6c0b56eSmrg		xf86OutputPtr output = config->output[i];
3727d6c0b56eSmrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
3728d6c0b56eSmrg		found = FALSE;
3729d6c0b56eSmrg		for (j = 0; j < mode_res->count_connectors; j++) {
3730d6c0b56eSmrg			if (mode_res->connectors[j] == drmmode_output->output_id) {
3731d6c0b56eSmrg				found = TRUE;
3732d6c0b56eSmrg				break;
3733d6c0b56eSmrg			}
3734d6c0b56eSmrg		}
3735d6c0b56eSmrg		if (found)
3736d6c0b56eSmrg			continue;
3737d6c0b56eSmrg
3738d6c0b56eSmrg		drmModeFreeConnector(drmmode_output->mode_output);
3739d6c0b56eSmrg		drmmode_output->mode_output = NULL;
3740d6c0b56eSmrg		drmmode_output->output_id = -1;
3741d6c0b56eSmrg
3742d6c0b56eSmrg		changed = TRUE;
3743d6c0b56eSmrg		if (drmmode->delete_dp_12_displays) {
3744d6c0b56eSmrg			RROutputDestroy(output->randr_output);
3745d6c0b56eSmrg			xf86OutputDestroy(output);
3746d6c0b56eSmrg			goto restart_destroy;
3747d6c0b56eSmrg		}
3748d6c0b56eSmrg	}
3749d6c0b56eSmrg
3750d6c0b56eSmrg	/* find new output ids we don't have outputs for */
3751d6c0b56eSmrg	for (i = 0; i < mode_res->count_connectors; i++) {
3752504d986fSmrg		if (drmmode_find_output(pAMDGPUEnt->primary_scrn,
3753504d986fSmrg					mode_res->connectors[i],
3754504d986fSmrg					&num_dvi, &num_hdmi) ||
3755504d986fSmrg		    (pAMDGPUEnt->secondary_scrn &&
3756504d986fSmrg		     drmmode_find_output(pAMDGPUEnt->secondary_scrn,
3757504d986fSmrg					 mode_res->connectors[i],
3758504d986fSmrg					 &num_dvi, &num_hdmi)))
3759d6c0b56eSmrg			continue;
3760d6c0b56eSmrg
3761504d986fSmrg		if (drmmode_output_init(scrn, drmmode, mode_res, i, &num_dvi,
3762504d986fSmrg					&num_hdmi, 1) != 0)
3763504d986fSmrg			changed = TRUE;
3764d6c0b56eSmrg	}
3765d6c0b56eSmrg
376635d5b7c7Smrg	/* Check to see if a lessee has disappeared */
376735d5b7c7Smrg	drmmode_validate_leases(scrn);
376835d5b7c7Smrg
3769504d986fSmrg	if (changed && dixPrivateKeyRegistered(rrPrivKey)) {
3770d6c0b56eSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,14,99,2,0)
3771d6c0b56eSmrg		RRSetChanged(xf86ScrnToScreen(scrn));
3772d6c0b56eSmrg#else
3773d6c0b56eSmrg		rrScrPrivPtr rrScrPriv = rrGetScrPriv(scrn->pScreen);
3774d6c0b56eSmrg		rrScrPriv->changed = TRUE;
3775d6c0b56eSmrg#endif
3776d6c0b56eSmrg		RRTellChanged(xf86ScrnToScreen(scrn));
3777d6c0b56eSmrg	}
3778d6c0b56eSmrg
3779d6c0b56eSmrg	drmModeFreeResources(mode_res);
3780d6c0b56eSmrgout:
3781d6c0b56eSmrg	RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
3782d6c0b56eSmrg}
3783d6c0b56eSmrg
3784d6c0b56eSmrg#ifdef HAVE_LIBUDEV
3785d6c0b56eSmrgstatic void drmmode_handle_uevents(int fd, void *closure)
3786d6c0b56eSmrg{
3787d6c0b56eSmrg	drmmode_ptr drmmode = closure;
3788d6c0b56eSmrg	ScrnInfoPtr scrn = drmmode->scrn;
3789d6c0b56eSmrg	struct udev_device *dev;
3790504d986fSmrg	Bool received = FALSE;
379111bf0794Smrg	struct timeval tv = { 0, 0 };
379211bf0794Smrg	fd_set readfd;
379311bf0794Smrg
379411bf0794Smrg	FD_ZERO(&readfd);
379511bf0794Smrg	FD_SET(fd, &readfd);
379611bf0794Smrg
379711bf0794Smrg	while (select(fd + 1, &readfd, NULL, NULL, &tv) > 0 &&
379811bf0794Smrg	       FD_ISSET(fd, &readfd)) {
379911bf0794Smrg		/* select() ensured that this will not block */
380011bf0794Smrg		dev = udev_monitor_receive_device(drmmode->uevent_monitor);
380111bf0794Smrg		if (dev) {
380211bf0794Smrg			udev_device_unref(dev);
380311bf0794Smrg			received = TRUE;
380411bf0794Smrg		}
3805504d986fSmrg	}
3806504d986fSmrg
3807504d986fSmrg	if (received)
3808504d986fSmrg		amdgpu_mode_hotplug(scrn, drmmode);
3809d6c0b56eSmrg}
3810d6c0b56eSmrg#endif
3811d6c0b56eSmrg
3812d6c0b56eSmrgvoid drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode)
3813d6c0b56eSmrg{
3814d6c0b56eSmrg#ifdef HAVE_LIBUDEV
3815d6c0b56eSmrg	struct udev *u;
3816d6c0b56eSmrg	struct udev_monitor *mon;
3817d6c0b56eSmrg
3818d6c0b56eSmrg	u = udev_new();
3819d6c0b56eSmrg	if (!u)
3820d6c0b56eSmrg		return;
3821d6c0b56eSmrg	mon = udev_monitor_new_from_netlink(u, "udev");
3822d6c0b56eSmrg	if (!mon) {
3823d6c0b56eSmrg		udev_unref(u);
3824d6c0b56eSmrg		return;
3825d6c0b56eSmrg	}
3826d6c0b56eSmrg
3827d6c0b56eSmrg	if (udev_monitor_filter_add_match_subsystem_devtype(mon,
3828d6c0b56eSmrg							    "drm",
3829d6c0b56eSmrg							    "drm_minor") < 0 ||
3830d6c0b56eSmrg	    udev_monitor_enable_receiving(mon) < 0) {
3831d6c0b56eSmrg		udev_monitor_unref(mon);
3832d6c0b56eSmrg		udev_unref(u);
3833d6c0b56eSmrg		return;
3834d6c0b56eSmrg	}
3835d6c0b56eSmrg
3836d6c0b56eSmrg	drmmode->uevent_handler =
3837d6c0b56eSmrg	    xf86AddGeneralHandler(udev_monitor_get_fd(mon),
3838d6c0b56eSmrg				  drmmode_handle_uevents, drmmode);
3839d6c0b56eSmrg
3840d6c0b56eSmrg	drmmode->uevent_monitor = mon;
3841d6c0b56eSmrg#endif
3842d6c0b56eSmrg}
3843d6c0b56eSmrg
3844d6c0b56eSmrgvoid drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode)
3845d6c0b56eSmrg{
3846d6c0b56eSmrg#ifdef HAVE_LIBUDEV
3847d6c0b56eSmrg	if (drmmode->uevent_handler) {
3848d6c0b56eSmrg		struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
3849d6c0b56eSmrg		xf86RemoveGeneralHandler(drmmode->uevent_handler);
3850d6c0b56eSmrg
3851d6c0b56eSmrg		udev_monitor_unref(drmmode->uevent_monitor);
3852d6c0b56eSmrg		udev_unref(u);
3853d6c0b56eSmrg	}
3854d6c0b56eSmrg#endif
3855d6c0b56eSmrg}
3856d6c0b56eSmrg
3857d6c0b56eSmrgBool amdgpu_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
3858d6c0b56eSmrg			PixmapPtr new_front, uint64_t id, void *data,
385924b90cf4Smrg			xf86CrtcPtr ref_crtc, amdgpu_drm_handler_proc handler,
3860504d986fSmrg			amdgpu_drm_abort_proc abort,
386111bf0794Smrg			enum drmmode_flip_sync flip_sync,
386211bf0794Smrg			uint32_t target_msc)
3863d6c0b56eSmrg{
3864d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
3865d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3866d6c0b56eSmrg	xf86CrtcPtr crtc = NULL;
3867d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private;
386811bf0794Smrg	uint32_t flip_flags = flip_sync == FLIP_ASYNC ? DRM_MODE_PAGE_FLIP_ASYNC : 0;
3869d6c0b56eSmrg	drmmode_flipdata_ptr flipdata;
387035d5b7c7Smrg	Bool handle_deferred = FALSE;
3871d6c0b56eSmrg	uintptr_t drm_queue_seq = 0;
387235d5b7c7Smrg	struct drmmode_fb *fb;
387335d5b7c7Smrg	int i = 0;
3874d6c0b56eSmrg
387535d5b7c7Smrg	flipdata = calloc(1, sizeof(*flipdata) + config->num_crtc *
387635d5b7c7Smrg			  sizeof(flipdata->fb[0]));
3877d6c0b56eSmrg	if (!flipdata) {
3878d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
3879d6c0b56eSmrg			   "flip queue: data alloc failed.\n");
3880d6c0b56eSmrg		goto error;
3881d6c0b56eSmrg	}
3882d6c0b56eSmrg
388335d5b7c7Smrg	fb = amdgpu_pixmap_get_fb(new_front);
388435d5b7c7Smrg	if (!fb) {
388524b90cf4Smrg		ErrorF("Failed to get FB for flip\n");
3886d6c0b56eSmrg		goto error;
388724b90cf4Smrg	}
3888d6c0b56eSmrg
3889d6c0b56eSmrg	/*
3890d6c0b56eSmrg	 * Queue flips on all enabled CRTCs
3891d6c0b56eSmrg	 * Note that if/when we get per-CRTC buffers, we'll have to update this.
3892d6c0b56eSmrg	 * Right now it assumes a single shared fb across all CRTCs, with the
3893d6c0b56eSmrg	 * kernel fixing up the offset of each CRTC as necessary.
3894d6c0b56eSmrg	 *
3895d6c0b56eSmrg	 * Also, flips queued on disabled or incorrectly configured displays
3896d6c0b56eSmrg	 * may never complete; this is a configuration error.
3897d6c0b56eSmrg	 */
3898d6c0b56eSmrg
3899d6c0b56eSmrg	flipdata->event_data = data;
3900d6c0b56eSmrg	flipdata->handler = handler;
3901d6c0b56eSmrg	flipdata->abort = abort;
390224b90cf4Smrg	flipdata->fe_crtc = ref_crtc;
3903d6c0b56eSmrg
3904d6c0b56eSmrg	for (i = 0; i < config->num_crtc; i++) {
3905d6c0b56eSmrg		crtc = config->crtc[i];
390624b90cf4Smrg		drmmode_crtc = crtc->driver_private;
3907d6c0b56eSmrg
390824b90cf4Smrg		if (!drmmode_crtc_can_flip(crtc) ||
390924b90cf4Smrg		    (drmmode_crtc->tear_free && crtc != ref_crtc))
3910d6c0b56eSmrg			continue;
3911d6c0b56eSmrg
3912d6c0b56eSmrg		flipdata->flip_count++;
3913d6c0b56eSmrg
3914d6c0b56eSmrg		drm_queue_seq = amdgpu_drm_queue_alloc(crtc, client, id,
3915d6c0b56eSmrg						       flipdata,
3916d6c0b56eSmrg						       drmmode_flip_handler,
3917d6c0b56eSmrg						       drmmode_flip_abort);
3918504d986fSmrg		if (drm_queue_seq == AMDGPU_DRM_QUEUE_ERROR) {
3919d6c0b56eSmrg			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
3920d6c0b56eSmrg				   "Allocating DRM queue event entry failed.\n");
3921d6c0b56eSmrg			goto error;
3922d6c0b56eSmrg		}
3923d6c0b56eSmrg
392424b90cf4Smrg		if (drmmode_crtc->tear_free) {
392524b90cf4Smrg			BoxRec extents = { .x1 = 0, .y1 = 0,
392624b90cf4Smrg					   .x2 = new_front->drawable.width,
392724b90cf4Smrg					   .y2 = new_front->drawable.height };
392824b90cf4Smrg			int scanout_id = drmmode_crtc->scanout_id ^ 1;
392924b90cf4Smrg
393024b90cf4Smrg			if (flip_sync == FLIP_ASYNC) {
393124b90cf4Smrg				if (!drmmode_wait_vblank(crtc,
393224b90cf4Smrg							 DRM_VBLANK_RELATIVE |
393324b90cf4Smrg							 DRM_VBLANK_EVENT,
393424b90cf4Smrg							 0, drm_queue_seq,
393524b90cf4Smrg							 NULL, NULL))
393624b90cf4Smrg					goto flip_error;
393724b90cf4Smrg				goto next;
393824b90cf4Smrg			}
393924b90cf4Smrg
394035d5b7c7Smrg			drmmode_fb_reference(pAMDGPUEnt->fd, &flipdata->fb[i],
394135d5b7c7Smrg					     amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap));
394235d5b7c7Smrg			if (!flipdata->fb[i]) {
394324b90cf4Smrg				ErrorF("Failed to get FB for TearFree flip\n");
394424b90cf4Smrg				goto error;
394524b90cf4Smrg			}
394624b90cf4Smrg
394724b90cf4Smrg			amdgpu_scanout_do_update(crtc, scanout_id, new_front,
394835d5b7c7Smrg						 extents);
394935d5b7c7Smrg			amdgpu_glamor_flush(crtc->scrn);
395035d5b7c7Smrg
395135d5b7c7Smrg			if (drmmode_crtc->scanout_update_pending) {
395235d5b7c7Smrg				amdgpu_drm_wait_pending_flip(crtc);
395335d5b7c7Smrg				handle_deferred = TRUE;
395435d5b7c7Smrg				amdgpu_drm_abort_entry(drmmode_crtc->scanout_update_pending);
395535d5b7c7Smrg				drmmode_crtc->scanout_update_pending = 0;
395635d5b7c7Smrg			}
395735d5b7c7Smrg		} else {
395835d5b7c7Smrg			drmmode_fb_reference(pAMDGPUEnt->fd, &flipdata->fb[i], fb);
395924b90cf4Smrg		}
396024b90cf4Smrg
396124b90cf4Smrg		if (crtc == ref_crtc) {
396211bf0794Smrg			if (drmmode_page_flip_target_absolute(pAMDGPUEnt,
396311bf0794Smrg							      drmmode_crtc,
396435d5b7c7Smrg							      flipdata->fb[i]->handle,
396511bf0794Smrg							      flip_flags,
396611bf0794Smrg							      drm_queue_seq,
396711bf0794Smrg							      target_msc) != 0)
396811bf0794Smrg				goto flip_error;
396911bf0794Smrg		} else {
397011bf0794Smrg			if (drmmode_page_flip_target_relative(pAMDGPUEnt,
397111bf0794Smrg							      drmmode_crtc,
397235d5b7c7Smrg							      flipdata->fb[i]->handle,
397311bf0794Smrg							      flip_flags,
397411bf0794Smrg							      drm_queue_seq, 0) != 0)
397511bf0794Smrg				goto flip_error;
3976d6c0b56eSmrg		}
397711bf0794Smrg
397824b90cf4Smrg		if (drmmode_crtc->tear_free) {
397924b90cf4Smrg			drmmode_crtc->scanout_id ^= 1;
398024b90cf4Smrg			drmmode_crtc->ignore_damage = TRUE;
398124b90cf4Smrg		}
398224b90cf4Smrg
398324b90cf4Smrg	next:
398435d5b7c7Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->flip_pending,
398535d5b7c7Smrg				     flipdata->fb[i]);
3986d6c0b56eSmrg		drm_queue_seq = 0;
3987d6c0b56eSmrg	}
3988d6c0b56eSmrg
398935d5b7c7Smrg	if (handle_deferred)
399035d5b7c7Smrg		amdgpu_drm_queue_handle_deferred(ref_crtc);
3991d6c0b56eSmrg	if (flipdata->flip_count > 0)
3992d6c0b56eSmrg		return TRUE;
3993d6c0b56eSmrg
399411bf0794Smrgflip_error:
399511bf0794Smrg	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed: %s\n",
399611bf0794Smrg		   strerror(errno));
399711bf0794Smrg
3998d6c0b56eSmrgerror:
3999d6c0b56eSmrg	if (drm_queue_seq)
4000d6c0b56eSmrg		amdgpu_drm_abort_entry(drm_queue_seq);
4001d6c0b56eSmrg	else if (crtc)
4002d6c0b56eSmrg		drmmode_flip_abort(crtc, flipdata);
400311bf0794Smrg	else {
400411bf0794Smrg		abort(NULL, data);
4005d6c0b56eSmrg		free(flipdata);
400611bf0794Smrg	}
4007d6c0b56eSmrg
4008d6c0b56eSmrg	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
4009d6c0b56eSmrg		   strerror(errno));
401035d5b7c7Smrg	if (handle_deferred)
401135d5b7c7Smrg		amdgpu_drm_queue_handle_deferred(ref_crtc);
4012d6c0b56eSmrg	return FALSE;
4013d6c0b56eSmrg}
4014