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"
4346845023Smrg#include <xf86drm.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/* DPMS */
51d6c0b56eSmrg#ifdef HAVE_XEXTPROTO_71
52d6c0b56eSmrg#include <X11/extensions/dpmsconst.h>
53d6c0b56eSmrg#else
54d6c0b56eSmrg#define DPMS_SERVER
55d6c0b56eSmrg#include <X11/extensions/dpms.h>
56d6c0b56eSmrg#endif
57d6c0b56eSmrg
58d6c0b56eSmrg#include <gbm.h>
59d6c0b56eSmrg
60d6c0b56eSmrg#define DEFAULT_NOMINAL_FRAME_RATE 60
61d6c0b56eSmrg
62d6c0b56eSmrgstatic Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height);
63d6c0b56eSmrg
64d6c0b56eSmrgstatic Bool
65d6c0b56eSmrgAMDGPUZaphodStringMatches(ScrnInfoPtr pScrn, const char *s, char *output_name)
66d6c0b56eSmrg{
67d6c0b56eSmrg	int i = 0;
68d6c0b56eSmrg	char s1[20];
69d6c0b56eSmrg
70d6c0b56eSmrg	do {
71d6c0b56eSmrg		switch (*s) {
72d6c0b56eSmrg		case ',':
73d6c0b56eSmrg			s1[i] = '\0';
74d6c0b56eSmrg			i = 0;
75d6c0b56eSmrg			if (strcmp(s1, output_name) == 0)
76d6c0b56eSmrg				return TRUE;
77d6c0b56eSmrg			break;
78d6c0b56eSmrg		case ' ':
79d6c0b56eSmrg		case '\t':
80d6c0b56eSmrg		case '\n':
81d6c0b56eSmrg		case '\r':
82d6c0b56eSmrg			break;
83d6c0b56eSmrg		default:
84d6c0b56eSmrg			s1[i] = *s;
85d6c0b56eSmrg			i++;
86d6c0b56eSmrg			break;
87d6c0b56eSmrg		}
88d6c0b56eSmrg	} while (*s++);
89d6c0b56eSmrg
90d6c0b56eSmrg	s1[i] = '\0';
91d6c0b56eSmrg	if (strcmp(s1, output_name) == 0)
92d6c0b56eSmrg		return TRUE;
93d6c0b56eSmrg
94d6c0b56eSmrg	return FALSE;
95d6c0b56eSmrg}
96d6c0b56eSmrg
9724b90cf4Smrg
98d6c0b56eSmrgstatic void
99d6c0b56eSmrgdrmmode_ConvertFromKMode(ScrnInfoPtr scrn,
100d6c0b56eSmrg			 drmModeModeInfo * kmode, DisplayModePtr mode)
101d6c0b56eSmrg{
102d6c0b56eSmrg	memset(mode, 0, sizeof(DisplayModeRec));
103d6c0b56eSmrg	mode->status = MODE_OK;
104d6c0b56eSmrg
105d6c0b56eSmrg	mode->Clock = kmode->clock;
106d6c0b56eSmrg
107d6c0b56eSmrg	mode->HDisplay = kmode->hdisplay;
108d6c0b56eSmrg	mode->HSyncStart = kmode->hsync_start;
109d6c0b56eSmrg	mode->HSyncEnd = kmode->hsync_end;
110d6c0b56eSmrg	mode->HTotal = kmode->htotal;
111d6c0b56eSmrg	mode->HSkew = kmode->hskew;
112d6c0b56eSmrg
113d6c0b56eSmrg	mode->VDisplay = kmode->vdisplay;
114d6c0b56eSmrg	mode->VSyncStart = kmode->vsync_start;
115d6c0b56eSmrg	mode->VSyncEnd = kmode->vsync_end;
116d6c0b56eSmrg	mode->VTotal = kmode->vtotal;
117d6c0b56eSmrg	mode->VScan = kmode->vscan;
118d6c0b56eSmrg
119d6c0b56eSmrg	mode->Flags = kmode->flags;	//& FLAG_BITS;
120d6c0b56eSmrg	mode->name = strdup(kmode->name);
121d6c0b56eSmrg
122d6c0b56eSmrg	if (kmode->type & DRM_MODE_TYPE_DRIVER)
123d6c0b56eSmrg		mode->type = M_T_DRIVER;
124d6c0b56eSmrg	if (kmode->type & DRM_MODE_TYPE_PREFERRED)
125d6c0b56eSmrg		mode->type |= M_T_PREFERRED;
126d6c0b56eSmrg	xf86SetModeCrtc(mode, scrn->adjustFlags);
127d6c0b56eSmrg}
128d6c0b56eSmrg
129d6c0b56eSmrgstatic void
130d6c0b56eSmrgdrmmode_ConvertToKMode(ScrnInfoPtr scrn,
131d6c0b56eSmrg		       drmModeModeInfo * kmode, DisplayModePtr mode)
132d6c0b56eSmrg{
133d6c0b56eSmrg	memset(kmode, 0, sizeof(*kmode));
134d6c0b56eSmrg
135d6c0b56eSmrg	kmode->clock = mode->Clock;
136d6c0b56eSmrg	kmode->hdisplay = mode->HDisplay;
137d6c0b56eSmrg	kmode->hsync_start = mode->HSyncStart;
138d6c0b56eSmrg	kmode->hsync_end = mode->HSyncEnd;
139d6c0b56eSmrg	kmode->htotal = mode->HTotal;
140d6c0b56eSmrg	kmode->hskew = mode->HSkew;
141d6c0b56eSmrg
142d6c0b56eSmrg	kmode->vdisplay = mode->VDisplay;
143d6c0b56eSmrg	kmode->vsync_start = mode->VSyncStart;
144d6c0b56eSmrg	kmode->vsync_end = mode->VSyncEnd;
145d6c0b56eSmrg	kmode->vtotal = mode->VTotal;
146d6c0b56eSmrg	kmode->vscan = mode->VScan;
147d6c0b56eSmrg
148d6c0b56eSmrg	kmode->flags = mode->Flags;	//& FLAG_BITS;
149d6c0b56eSmrg	if (mode->name)
150d6c0b56eSmrg		strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
151d6c0b56eSmrg	kmode->name[DRM_DISPLAY_MODE_LEN - 1] = 0;
152d6c0b56eSmrg
153d6c0b56eSmrg}
154d6c0b56eSmrg
15524b90cf4Smrg/*
15624b90cf4Smrg * Utility helper for drmWaitVBlank
15724b90cf4Smrg */
15824b90cf4SmrgBool
15924b90cf4Smrgdrmmode_wait_vblank(xf86CrtcPtr crtc, drmVBlankSeqType type,
16024b90cf4Smrg		    uint32_t target_seq, unsigned long signal, uint64_t *ust,
16124b90cf4Smrg		    uint32_t *result_seq)
16224b90cf4Smrg{
16324b90cf4Smrg	int crtc_id = drmmode_get_crtc_id(crtc);
16424b90cf4Smrg	ScrnInfoPtr scrn = crtc->scrn;
16524b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
16624b90cf4Smrg	drmVBlank vbl;
16724b90cf4Smrg
16824b90cf4Smrg	if (crtc_id == 1)
16924b90cf4Smrg		type |= DRM_VBLANK_SECONDARY;
17024b90cf4Smrg	else if (crtc_id > 1)
17124b90cf4Smrg		type |= (crtc_id << DRM_VBLANK_HIGH_CRTC_SHIFT) &
17224b90cf4Smrg			DRM_VBLANK_HIGH_CRTC_MASK;
17324b90cf4Smrg
17424b90cf4Smrg	vbl.request.type = type;
17524b90cf4Smrg	vbl.request.sequence = target_seq;
17624b90cf4Smrg	vbl.request.signal = signal;
17724b90cf4Smrg
17824b90cf4Smrg	if (drmWaitVBlank(pAMDGPUEnt->fd, &vbl) != 0)
17924b90cf4Smrg		return FALSE;
18024b90cf4Smrg
18124b90cf4Smrg	if (ust)
18224b90cf4Smrg		*ust = (uint64_t)vbl.reply.tval_sec * 1000000 +
18324b90cf4Smrg			vbl.reply.tval_usec;
18424b90cf4Smrg	if (result_seq)
18524b90cf4Smrg		*result_seq = vbl.reply.sequence;
18624b90cf4Smrg
18724b90cf4Smrg	return TRUE;
18824b90cf4Smrg}
18924b90cf4Smrg
190d6c0b56eSmrg/*
191d6c0b56eSmrg * Retrieves present time in microseconds that is compatible
192d6c0b56eSmrg * with units used by vblank timestamps. Depending on the kernel
193d6c0b56eSmrg * version and DRM kernel module configuration, the vblank
194d6c0b56eSmrg * timestamp can either be in real time or monotonic time
195d6c0b56eSmrg */
196d6c0b56eSmrgint drmmode_get_current_ust(int drm_fd, CARD64 * ust)
197d6c0b56eSmrg{
198d6c0b56eSmrg	uint64_t cap_value;
199d6c0b56eSmrg	int ret;
200d6c0b56eSmrg	struct timespec now;
201d6c0b56eSmrg
202d6c0b56eSmrg	ret = drmGetCap(drm_fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap_value);
203d6c0b56eSmrg	if (ret || !cap_value)
204d6c0b56eSmrg		/* old kernel or drm_timestamp_monotonic turned off */
205d6c0b56eSmrg		ret = clock_gettime(CLOCK_REALTIME, &now);
206d6c0b56eSmrg	else
207d6c0b56eSmrg		ret = clock_gettime(CLOCK_MONOTONIC, &now);
208d6c0b56eSmrg	if (ret)
209d6c0b56eSmrg		return ret;
210d6c0b56eSmrg	*ust = ((CARD64) now.tv_sec * 1000000) + ((CARD64) now.tv_nsec / 1000);
211d6c0b56eSmrg	return 0;
212d6c0b56eSmrg}
213d6c0b56eSmrg
214d6c0b56eSmrg/*
215d6c0b56eSmrg * Get current frame count and frame count timestamp of the crtc.
216d6c0b56eSmrg */
217d6c0b56eSmrgint drmmode_crtc_get_ust_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc)
218d6c0b56eSmrg{
219d6c0b56eSmrg	ScrnInfoPtr scrn = crtc->scrn;
22024b90cf4Smrg	uint32_t seq;
221d6c0b56eSmrg
22224b90cf4Smrg	if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, ust, &seq)) {
223d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
224d6c0b56eSmrg			   "get vblank counter failed: %s\n", strerror(errno));
22524b90cf4Smrg		return -1;
226d6c0b56eSmrg	}
227d6c0b56eSmrg
22824b90cf4Smrg	*msc = seq;
229d6c0b56eSmrg
230d6c0b56eSmrg	return Success;
231d6c0b56eSmrg}
232d6c0b56eSmrg
23390f2b693Smrgstatic uint32_t
23490f2b693Smrgdrmmode_crtc_get_prop_id(uint32_t drm_fd,
23590f2b693Smrg			 drmModeObjectPropertiesPtr props,
23690f2b693Smrg			 char const* name)
23790f2b693Smrg{
23890f2b693Smrg	uint32_t i, prop_id = 0;
23990f2b693Smrg
24090f2b693Smrg	for (i = 0; !prop_id && i < props->count_props; ++i) {
24190f2b693Smrg		drmModePropertyPtr drm_prop =
24290f2b693Smrg			drmModeGetProperty(drm_fd, props->props[i]);
24390f2b693Smrg
24490f2b693Smrg		if (!drm_prop)
24590f2b693Smrg			continue;
24690f2b693Smrg
24790f2b693Smrg		if (strcmp(drm_prop->name, name) == 0)
24890f2b693Smrg			prop_id = drm_prop->prop_id;
24990f2b693Smrg
25090f2b693Smrg		drmModeFreeProperty(drm_prop);
25190f2b693Smrg	}
25290f2b693Smrg
25390f2b693Smrg	return prop_id;
25490f2b693Smrg}
25590f2b693Smrg
25690f2b693Smrgstatic void drmmode_crtc_vrr_init(int drm_fd, xf86CrtcPtr crtc)
25790f2b693Smrg{
25890f2b693Smrg	drmModeObjectPropertiesPtr drm_props;
25990f2b693Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
26090f2b693Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
26190f2b693Smrg
26290f2b693Smrg	if (drmmode->vrr_prop_id)
26390f2b693Smrg		return;
26490f2b693Smrg
26590f2b693Smrg	drm_props = drmModeObjectGetProperties(drm_fd,
26690f2b693Smrg					       drmmode_crtc->mode_crtc->crtc_id,
26790f2b693Smrg					       DRM_MODE_OBJECT_CRTC);
26890f2b693Smrg
26990f2b693Smrg	if (!drm_props)
27090f2b693Smrg		return;
27190f2b693Smrg
27290f2b693Smrg	drmmode->vrr_prop_id = drmmode_crtc_get_prop_id(drm_fd,
27390f2b693Smrg							drm_props,
27490f2b693Smrg							"VRR_ENABLED");
27590f2b693Smrg
27690f2b693Smrg	drmModeFreeObjectProperties(drm_props);
27790f2b693Smrg}
27890f2b693Smrg
27990f2b693Smrgvoid drmmode_crtc_set_vrr(xf86CrtcPtr crtc, Bool enabled)
28090f2b693Smrg{
28190f2b693Smrg	ScrnInfoPtr pScrn = crtc->scrn;
28290f2b693Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
28390f2b693Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
28490f2b693Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
28590f2b693Smrg
28690f2b693Smrg	if (drmmode->vrr_prop_id &&
28790f2b693Smrg	    drmmode_crtc->vrr_enabled != enabled &&
28890f2b693Smrg	    drmModeObjectSetProperty(pAMDGPUEnt->fd,
28990f2b693Smrg				     drmmode_crtc->mode_crtc->crtc_id,
29090f2b693Smrg				     DRM_MODE_OBJECT_CRTC,
29190f2b693Smrg				     drmmode->vrr_prop_id,
29290f2b693Smrg				     enabled) == 0)
29390f2b693Smrg		drmmode_crtc->vrr_enabled = enabled;
29490f2b693Smrg}
29590f2b693Smrg
296d6c0b56eSmrgstatic void
297d6c0b56eSmrgdrmmode_do_crtc_dpms(xf86CrtcPtr crtc, int mode)
298d6c0b56eSmrg{
299d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
300d6c0b56eSmrg	ScrnInfoPtr scrn = crtc->scrn;
301d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
302d6c0b56eSmrg	CARD64 ust;
303d6c0b56eSmrg	int ret;
304d6c0b56eSmrg
305d6c0b56eSmrg	if (drmmode_crtc->dpms_mode == DPMSModeOn && mode != DPMSModeOn) {
30624b90cf4Smrg		uint32_t seq;
307d6c0b56eSmrg
30835d5b7c7Smrg		amdgpu_drm_wait_pending_flip(crtc);
309504d986fSmrg
310d6c0b56eSmrg		/*
311d6c0b56eSmrg		 * On->Off transition: record the last vblank time,
312d6c0b56eSmrg		 * sequence number and frame period.
313d6c0b56eSmrg		 */
31424b90cf4Smrg		if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, &ust,
31524b90cf4Smrg					 &seq))
316d6c0b56eSmrg			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
317d6c0b56eSmrg				   "%s cannot get last vblank counter\n",
318d6c0b56eSmrg				   __func__);
319d6c0b56eSmrg		else {
320d6c0b56eSmrg			CARD64 nominal_frame_rate, pix_in_frame;
321d6c0b56eSmrg
322d6c0b56eSmrg			drmmode_crtc->dpms_last_ust = ust;
323d6c0b56eSmrg			drmmode_crtc->dpms_last_seq = seq;
324d6c0b56eSmrg			nominal_frame_rate = crtc->mode.Clock;
325d6c0b56eSmrg			nominal_frame_rate *= 1000;
326d6c0b56eSmrg			pix_in_frame = crtc->mode.HTotal * crtc->mode.VTotal;
327d6c0b56eSmrg			if (nominal_frame_rate == 0 || pix_in_frame == 0)
328d6c0b56eSmrg				nominal_frame_rate = DEFAULT_NOMINAL_FRAME_RATE;
329d6c0b56eSmrg			else
330d6c0b56eSmrg				nominal_frame_rate /= pix_in_frame;
331d6c0b56eSmrg			drmmode_crtc->dpms_last_fps = nominal_frame_rate;
332d6c0b56eSmrg		}
33335d5b7c7Smrg
33435d5b7c7Smrg		drmmode_crtc->dpms_mode = mode;
33535d5b7c7Smrg		amdgpu_drm_queue_handle_deferred(crtc);
336d6c0b56eSmrg	} else if (drmmode_crtc->dpms_mode != DPMSModeOn && mode == DPMSModeOn) {
337d6c0b56eSmrg		/*
338d6c0b56eSmrg		 * Off->On transition: calculate and accumulate the
339d6c0b56eSmrg		 * number of interpolated vblanks while we were in Off state
340d6c0b56eSmrg		 */
341d6c0b56eSmrg		ret = drmmode_get_current_ust(pAMDGPUEnt->fd, &ust);
342d6c0b56eSmrg		if (ret)
343d6c0b56eSmrg			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
344d6c0b56eSmrg				   "%s cannot get current time\n", __func__);
345d6c0b56eSmrg		else if (drmmode_crtc->dpms_last_ust) {
346d6c0b56eSmrg			CARD64 time_elapsed, delta_seq;
347d6c0b56eSmrg			time_elapsed = ust - drmmode_crtc->dpms_last_ust;
348d6c0b56eSmrg			delta_seq = time_elapsed * drmmode_crtc->dpms_last_fps;
349d6c0b56eSmrg			delta_seq /= 1000000;
350d6c0b56eSmrg			drmmode_crtc->interpolated_vblanks += delta_seq;
351d6c0b56eSmrg
352d6c0b56eSmrg		}
35335d5b7c7Smrg
35435d5b7c7Smrg		drmmode_crtc->dpms_mode = DPMSModeOn;
355d6c0b56eSmrg	}
356d6c0b56eSmrg}
357d6c0b56eSmrg
358d6c0b56eSmrgstatic void
359d6c0b56eSmrgdrmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
360d6c0b56eSmrg{
361d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
362d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
363d6c0b56eSmrg
364d6c0b56eSmrg	/* Disable unused CRTCs and enable/disable active CRTCs */
365504d986fSmrg	if (!crtc->enabled || mode != DPMSModeOn) {
36635d5b7c7Smrg		drmmode_do_crtc_dpms(crtc, DPMSModeOff);
367d6c0b56eSmrg		drmModeSetCrtc(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id,
368d6c0b56eSmrg			       0, 0, 0, NULL, 0, NULL);
36924b90cf4Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, NULL);
370504d986fSmrg	} else if (drmmode_crtc->dpms_mode != DPMSModeOn)
371d6c0b56eSmrg		crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation,
372d6c0b56eSmrg					    crtc->x, crtc->y);
373d6c0b56eSmrg}
374d6c0b56eSmrg
37590f2b693Smrg#ifdef USE_GLAMOR
37690f2b693Smrg
377d6c0b56eSmrgstatic PixmapPtr
378d6c0b56eSmrgcreate_pixmap_for_fbcon(drmmode_ptr drmmode,
379d6c0b56eSmrg			ScrnInfoPtr pScrn, int fbcon_id)
380d6c0b56eSmrg{
38135d5b7c7Smrg	ScreenPtr pScreen = pScrn->pScreen;
382d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
38335d5b7c7Smrg	PixmapPtr pixmap = NULL;
384d6c0b56eSmrg	drmModeFBPtr fbcon;
385d6c0b56eSmrg
386d6c0b56eSmrg	fbcon = drmModeGetFB(pAMDGPUEnt->fd, fbcon_id);
38735d5b7c7Smrg	if (!fbcon)
388d6c0b56eSmrg		return NULL;
389d6c0b56eSmrg
390d6c0b56eSmrg	if (fbcon->depth != pScrn->depth ||
391d6c0b56eSmrg	    fbcon->width != pScrn->virtualX ||
392d6c0b56eSmrg	    fbcon->height != pScrn->virtualY)
393d6c0b56eSmrg		goto out_free_fb;
394d6c0b56eSmrg
39535d5b7c7Smrg	pixmap = fbCreatePixmap(pScreen, 0, 0, fbcon->depth, 0);
39635d5b7c7Smrg	if (!pixmap)
397d6c0b56eSmrg		goto out_free_fb;
398d6c0b56eSmrg
39935d5b7c7Smrg	pScreen->ModifyPixmapHeader(pixmap, fbcon->width, fbcon->height, 0, 0,
40035d5b7c7Smrg				    fbcon->pitch, NULL);
40135d5b7c7Smrg	pixmap->devPrivate.ptr = NULL;
402d6c0b56eSmrg
40335d5b7c7Smrg	if (!glamor_egl_create_textured_pixmap(pixmap, fbcon->handle,
40435d5b7c7Smrg					       pixmap->devKind)) {
40535d5b7c7Smrg		pScreen->DestroyPixmap(pixmap);
40635d5b7c7Smrg		pixmap = NULL;
407d6c0b56eSmrg	}
408d6c0b56eSmrg
409d6c0b56eSmrgout_free_fb:
410d6c0b56eSmrg	drmModeFreeFB(fbcon);
411d6c0b56eSmrg	return pixmap;
412d6c0b56eSmrg}
413d6c0b56eSmrg
41490f2b693Smrg#endif /* USE_GLAMOR */
41590f2b693Smrg
416d6c0b56eSmrgvoid drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
417d6c0b56eSmrg{
41890f2b693Smrg#ifdef USE_GLAMOR
419d6c0b56eSmrg	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
42090f2b693Smrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
421d6c0b56eSmrg	ScreenPtr pScreen = pScrn->pScreen;
42224b90cf4Smrg	PixmapPtr src, dst = pScreen->GetScreenPixmap(pScreen);
42324b90cf4Smrg	struct drmmode_fb *fb = amdgpu_pixmap_get_fb(dst);
424d6c0b56eSmrg	int fbcon_id = 0;
425d6c0b56eSmrg	GCPtr gc;
426d6c0b56eSmrg	int i;
427d6c0b56eSmrg
42890f2b693Smrg	if (!info->use_glamor)
42990f2b693Smrg		return;
43090f2b693Smrg
431d6c0b56eSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
432d6c0b56eSmrg		drmmode_crtc_private_ptr drmmode_crtc = xf86_config->crtc[i]->driver_private;
433d6c0b56eSmrg
434d6c0b56eSmrg		if (drmmode_crtc->mode_crtc->buffer_id)
435d6c0b56eSmrg			fbcon_id = drmmode_crtc->mode_crtc->buffer_id;
436d6c0b56eSmrg	}
437d6c0b56eSmrg
438d6c0b56eSmrg	if (!fbcon_id)
439d6c0b56eSmrg		return;
440d6c0b56eSmrg
44124b90cf4Smrg	if (fbcon_id == fb->handle) {
442d6c0b56eSmrg		/* in some rare case there might be no fbcon and we might already
443d6c0b56eSmrg		 * be the one with the current fb to avoid a false deadlck in
444d6c0b56eSmrg		 * kernel ttm code just do nothing as anyway there is nothing
445d6c0b56eSmrg		 * to do
446d6c0b56eSmrg		 */
447d6c0b56eSmrg		return;
448d6c0b56eSmrg	}
449d6c0b56eSmrg
450d6c0b56eSmrg	src = create_pixmap_for_fbcon(drmmode, pScrn, fbcon_id);
451d6c0b56eSmrg	if (!src)
452d6c0b56eSmrg		return;
453d6c0b56eSmrg
454d6c0b56eSmrg	gc = GetScratchGC(pScrn->depth, pScreen);
455d6c0b56eSmrg	ValidateGC(&dst->drawable, gc);
456d6c0b56eSmrg
457d6c0b56eSmrg	(*gc->ops->CopyArea)(&src->drawable, &dst->drawable, gc, 0, 0,
458d6c0b56eSmrg			     pScrn->virtualX, pScrn->virtualY, 0, 0);
459d6c0b56eSmrg
460d6c0b56eSmrg	FreeScratchGC(gc);
461d6c0b56eSmrg
462d6c0b56eSmrg	pScreen->canDoBGNoneRoot = TRUE;
46335d5b7c7Smrg	pScreen->DestroyPixmap(src);
46490f2b693Smrg#endif
465d6c0b56eSmrg
466d6c0b56eSmrg	return;
467d6c0b56eSmrg}
468d6c0b56eSmrg
46924b90cf4Smrgvoid
47090f2b693Smrgdrmmode_crtc_scanout_free(xf86CrtcPtr crtc)
471504d986fSmrg{
47290f2b693Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
47390f2b693Smrg
47490f2b693Smrg	if (drmmode_crtc->scanout_update_pending) {
47590f2b693Smrg		amdgpu_drm_wait_pending_flip(crtc);
47690f2b693Smrg		amdgpu_drm_abort_entry(drmmode_crtc->scanout_update_pending);
47790f2b693Smrg		drmmode_crtc->scanout_update_pending = 0;
47890f2b693Smrg		amdgpu_drm_queue_handle_deferred(crtc);
47990f2b693Smrg	}
48090f2b693Smrg
48146845023Smrg	drmmode_crtc_scanout_destroy(&drmmode_crtc->scanout[0]);
48246845023Smrg	drmmode_crtc_scanout_destroy(&drmmode_crtc->scanout[1]);
483d6c0b56eSmrg
48424b90cf4Smrg	if (drmmode_crtc->scanout_damage)
485504d986fSmrg		DamageDestroy(drmmode_crtc->scanout_damage);
486d6c0b56eSmrg}
487d6c0b56eSmrg
48846845023Smrgstatic Bool
48946845023Smrgdrmmode_crtc_scanout_create(xf86CrtcPtr crtc, PixmapPtr *scanout,
49011bf0794Smrg			    int width, int height)
491d6c0b56eSmrg{
492d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
49346845023Smrg	ScreenPtr screen = pScrn->pScreen;
494d6c0b56eSmrg
49546845023Smrg	if (*scanout) {
49646845023Smrg		if ((*scanout)->drawable.width == width &&
49746845023Smrg		    (*scanout)->drawable.height == height)
49846845023Smrg			return TRUE;
499d6c0b56eSmrg
50046845023Smrg		drmmode_crtc_scanout_destroy(scanout);
501d6c0b56eSmrg	}
502d6c0b56eSmrg
50346845023Smrg	*scanout = screen->CreatePixmap(screen, width, height, pScrn->depth,
50446845023Smrg					AMDGPU_CREATE_PIXMAP_SCANOUT);
50546845023Smrg	if (!*scanout) {
50624b90cf4Smrg		ErrorF("failed to create CRTC scanout pixmap\n");
50724b90cf4Smrg		goto error;
50824b90cf4Smrg	}
50924b90cf4Smrg
51046845023Smrg	if (!amdgpu_pixmap_get_fb(*scanout)) {
51124b90cf4Smrg		ErrorF("failed to create CRTC scanout FB\n");
51224b90cf4Smrgerror:
51346845023Smrg		drmmode_crtc_scanout_destroy(scanout);
51446845023Smrg		return FALSE;
515d6c0b56eSmrg	}
516d6c0b56eSmrg
51746845023Smrg	return TRUE;
518d6c0b56eSmrg}
519d6c0b56eSmrg
520d6c0b56eSmrgstatic void
521d6c0b56eSmrgamdgpu_screen_damage_report(DamagePtr damage, RegionPtr region, void *closure)
522d6c0b56eSmrg{
52324b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = closure;
52424b90cf4Smrg
52524b90cf4Smrg	if (drmmode_crtc->ignore_damage) {
52624b90cf4Smrg		RegionEmpty(&damage->damage);
52724b90cf4Smrg		drmmode_crtc->ignore_damage = FALSE;
52824b90cf4Smrg		return;
52924b90cf4Smrg	}
53024b90cf4Smrg
531d6c0b56eSmrg	/* Only keep track of the extents */
532d6c0b56eSmrg	RegionUninit(&damage->damage);
533d6c0b56eSmrg	damage->damage.data = NULL;
534d6c0b56eSmrg}
535d6c0b56eSmrg
53624b90cf4Smrgstatic void
53724b90cf4Smrgdrmmode_screen_damage_destroy(DamagePtr damage, void *closure)
53824b90cf4Smrg{
53924b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = closure;
54024b90cf4Smrg
54124b90cf4Smrg	drmmode_crtc->scanout_damage = NULL;
54224b90cf4Smrg	RegionUninit(&drmmode_crtc->scanout_last_region);
54324b90cf4Smrg}
54424b90cf4Smrg
545d6c0b56eSmrgstatic Bool
546d6c0b56eSmrgdrmmode_can_use_hw_cursor(xf86CrtcPtr crtc)
547d6c0b56eSmrg{
548d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(crtc->scrn);
549d6c0b56eSmrg
550d6c0b56eSmrg	/* Check for Option "SWcursor" */
551d6c0b56eSmrg	if (xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE))
552d6c0b56eSmrg		return FALSE;
553d6c0b56eSmrg
554d6c0b56eSmrg	/* Fall back to SW cursor if the CRTC is transformed */
555d6c0b56eSmrg	if (crtc->transformPresent)
556d6c0b56eSmrg		return FALSE;
557d6c0b56eSmrg
55824b90cf4Smrg#if XF86_CRTC_VERSION < 7
559d6c0b56eSmrg	/* Xorg doesn't correctly handle cursor position transform in the
560d6c0b56eSmrg	 * rotation case
561d6c0b56eSmrg	 */
562d6c0b56eSmrg	if (crtc->driverIsPerformingTransform &&
563d6c0b56eSmrg	    (crtc->rotation & 0xf) != RR_Rotate_0)
564d6c0b56eSmrg		return FALSE;
565d6c0b56eSmrg#endif
566d6c0b56eSmrg
567504d986fSmrg	/* HW cursor not supported with RandR 1.4 multihead up to 1.18.99.901 */
568504d986fSmrg	if (xorgGetVersion() <= XORG_VERSION_NUMERIC(1,18,99,901,0) &&
569504d986fSmrg	    !xorg_list_is_empty(&crtc->scrn->pScreen->pixmap_dirty_list))
570d6c0b56eSmrg		return FALSE;
571d6c0b56eSmrg
572d6c0b56eSmrg	return TRUE;
573d6c0b56eSmrg}
574d6c0b56eSmrg
57511bf0794Smrgstatic void
57611bf0794Smrgdrmmode_crtc_update_tear_free(xf86CrtcPtr crtc)
57711bf0794Smrg{
57811bf0794Smrg	AMDGPUInfoPtr info = AMDGPUPTR(crtc->scrn);
57911bf0794Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
58011bf0794Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
58111bf0794Smrg	int i;
58211bf0794Smrg
58311bf0794Smrg	drmmode_crtc->tear_free = FALSE;
58411bf0794Smrg
58511bf0794Smrg	for (i = 0; i < xf86_config->num_output; i++) {
58611bf0794Smrg		xf86OutputPtr output = xf86_config->output[i];
58711bf0794Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
58811bf0794Smrg
58911bf0794Smrg		if (output->crtc != crtc)
59011bf0794Smrg			continue;
59111bf0794Smrg
59211bf0794Smrg		if (drmmode_output->tear_free == 1 ||
59311bf0794Smrg		    (drmmode_output->tear_free == 2 &&
59424b90cf4Smrg		     (crtc->scrn->pScreen->isGPU ||
59511bf0794Smrg		      info->shadow_primary ||
59611bf0794Smrg		      crtc->transformPresent || crtc->rotation != RR_Rotate_0))) {
59711bf0794Smrg			drmmode_crtc->tear_free = TRUE;
59811bf0794Smrg			return;
59911bf0794Smrg		}
60011bf0794Smrg	}
60111bf0794Smrg}
60211bf0794Smrg
60311bf0794Smrg#if XF86_CRTC_VERSION < 7
60411bf0794Smrg#define XF86DriverTransformOutput TRUE
60511bf0794Smrg#define XF86DriverTransformNone FALSE
60611bf0794Smrg#endif
60711bf0794Smrg
608d6c0b56eSmrgstatic Bool
609d6c0b56eSmrgdrmmode_handle_transform(xf86CrtcPtr crtc)
610d6c0b56eSmrg{
611d6c0b56eSmrg	Bool ret;
612d6c0b56eSmrg
61324b90cf4Smrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,903,0)
61435d5b7c7Smrg	crtc->driverIsPerformingTransform = XF86DriverTransformOutput;
61524b90cf4Smrg#else
61624b90cf4Smrg	crtc->driverIsPerformingTransform = !crtc->transformPresent &&
61724b90cf4Smrg		(crtc->rotation & 0xf) == RR_Rotate_0;
61824b90cf4Smrg#endif
619d6c0b56eSmrg
620d6c0b56eSmrg	ret = xf86CrtcRotate(crtc);
621d6c0b56eSmrg
622d6c0b56eSmrg	crtc->driverIsPerformingTransform &= ret && crtc->transform_in_use;
623d6c0b56eSmrg
624d6c0b56eSmrg	return ret;
625d6c0b56eSmrg}
626d6c0b56eSmrg
62711bf0794Smrg
62811bf0794Smrgstatic void
62911bf0794Smrgdrmmode_crtc_prime_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
63024b90cf4Smrg				  unsigned scanout_id, struct drmmode_fb **fb,
63124b90cf4Smrg				  int *x, int *y)
63211bf0794Smrg{
63311bf0794Smrg	ScrnInfoPtr scrn = crtc->scrn;
63411bf0794Smrg	ScreenPtr screen = scrn->pScreen;
63511bf0794Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
63611bf0794Smrg
63746845023Smrg	if (drmmode_crtc->tear_free && !drmmode_crtc->scanout[1]) {
63811bf0794Smrg		RegionPtr region;
63911bf0794Smrg		BoxPtr box;
64011bf0794Smrg
64111bf0794Smrg		drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1],
64211bf0794Smrg					    mode->HDisplay,
64311bf0794Smrg					    mode->VDisplay);
64411bf0794Smrg		region = &drmmode_crtc->scanout_last_region;
64511bf0794Smrg		RegionUninit(region);
64611bf0794Smrg		region->data = NULL;
64711bf0794Smrg		box = RegionExtents(region);
64811bf0794Smrg		box->x1 = crtc->x;
64911bf0794Smrg		box->y1 = crtc->y;
65011bf0794Smrg		box->x2 = crtc->x + mode->HDisplay;
65111bf0794Smrg		box->y2 = crtc->y + mode->VDisplay;
65211bf0794Smrg	}
65311bf0794Smrg
65411bf0794Smrg	if (scanout_id != drmmode_crtc->scanout_id) {
65511bf0794Smrg		PixmapDirtyUpdatePtr dirty = NULL;
65611bf0794Smrg
65711bf0794Smrg		xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list,
65811bf0794Smrg					 ent) {
65924b90cf4Smrg			if (amdgpu_dirty_src_equals(dirty, drmmode_crtc->prime_scanout_pixmap)) {
66046845023Smrg				dirty->secondary_dst =
66146845023Smrg					drmmode_crtc->scanout[scanout_id];
66211bf0794Smrg				break;
66311bf0794Smrg			}
66411bf0794Smrg		}
66511bf0794Smrg
66611bf0794Smrg		if (!drmmode_crtc->tear_free) {
66711bf0794Smrg			GCPtr gc = GetScratchGC(scrn->depth, screen);
66811bf0794Smrg
66946845023Smrg			ValidateGC(&drmmode_crtc->scanout[0]->drawable, gc);
67046845023Smrg			gc->ops->CopyArea(&drmmode_crtc->scanout[1]->drawable,
67146845023Smrg					  &drmmode_crtc->scanout[0]->drawable,
67211bf0794Smrg					  gc, 0, 0, mode->HDisplay, mode->VDisplay,
67311bf0794Smrg					  0, 0);
67411bf0794Smrg			FreeScratchGC(gc);
67511bf0794Smrg			amdgpu_glamor_finish(scrn);
67611bf0794Smrg		}
67711bf0794Smrg	}
67811bf0794Smrg
67946845023Smrg	*fb = amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id]);
68011bf0794Smrg	*x = *y = 0;
68111bf0794Smrg	drmmode_crtc->scanout_id = scanout_id;
68211bf0794Smrg}
68311bf0794Smrg
68411bf0794Smrg
68511bf0794Smrgstatic void
68611bf0794Smrgdrmmode_crtc_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
68724b90cf4Smrg			    unsigned scanout_id, struct drmmode_fb **fb, int *x,
68824b90cf4Smrg			    int *y)
68911bf0794Smrg{
69011bf0794Smrg	ScrnInfoPtr scrn = crtc->scrn;
69111bf0794Smrg	ScreenPtr screen = scrn->pScreen;
69211bf0794Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
69311bf0794Smrg
69424b90cf4Smrg	drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[scanout_id],
69511bf0794Smrg				    mode->HDisplay, mode->VDisplay);
69611bf0794Smrg	if (drmmode_crtc->tear_free) {
69724b90cf4Smrg		drmmode_crtc_scanout_create(crtc,
69824b90cf4Smrg					    &drmmode_crtc->scanout[scanout_id ^ 1],
69911bf0794Smrg					    mode->HDisplay, mode->VDisplay);
70011bf0794Smrg	}
70111bf0794Smrg
70246845023Smrg	if (drmmode_crtc->scanout[scanout_id] &&
70324b90cf4Smrg	    (!drmmode_crtc->tear_free ||
70446845023Smrg	     drmmode_crtc->scanout[scanout_id ^ 1])) {
70535d5b7c7Smrg		BoxRec extents = { .x1 = 0, .y1 = 0,
70635d5b7c7Smrg				   .x2 = scrn->virtualX, .y2 = scrn->virtualY };
70711bf0794Smrg
70811bf0794Smrg		if (!drmmode_crtc->scanout_damage) {
70911bf0794Smrg			drmmode_crtc->scanout_damage =
71011bf0794Smrg				DamageCreate(amdgpu_screen_damage_report,
71124b90cf4Smrg					     drmmode_screen_damage_destroy,
71224b90cf4Smrg					     DamageReportRawRegion,
71324b90cf4Smrg					     TRUE, screen, drmmode_crtc);
71424b90cf4Smrg			DamageRegister(&screen->root->drawable,
71511bf0794Smrg				       drmmode_crtc->scanout_damage);
71611bf0794Smrg		}
71711bf0794Smrg
71846845023Smrg		*fb = amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id]);
71911bf0794Smrg		*x = *y = 0;
72011bf0794Smrg
72190f2b693Smrg		if (amdgpu_scanout_do_update(crtc, scanout_id,
72290f2b693Smrg					     screen->GetWindowPixmap(screen->root),
72390f2b693Smrg					     extents)) {
72490f2b693Smrg			RegionEmpty(DamageRegion(drmmode_crtc->scanout_damage));
72590f2b693Smrg			amdgpu_glamor_finish(scrn);
72690f2b693Smrg
72790f2b693Smrg			if (!drmmode_crtc->flip_pending) {
72890f2b693Smrg				amdgpu_drm_abort_entry(drmmode_crtc->
72990f2b693Smrg						       scanout_update_pending);
73090f2b693Smrg			}
73190f2b693Smrg		}
73211bf0794Smrg	}
73311bf0794Smrg}
73411bf0794Smrg
73535d5b7c7Smrgstatic char *cm_prop_names[] = {
73635d5b7c7Smrg	"DEGAMMA_LUT",
73735d5b7c7Smrg	"CTM",
73835d5b7c7Smrg	"GAMMA_LUT",
73935d5b7c7Smrg	"DEGAMMA_LUT_SIZE",
74035d5b7c7Smrg	"GAMMA_LUT_SIZE",
74135d5b7c7Smrg};
74235d5b7c7Smrg
74335d5b7c7Smrg/**
74435d5b7c7Smrg * Return the enum of the color management property with the given name.
74535d5b7c7Smrg */
74635d5b7c7Smrgstatic enum drmmode_cm_prop get_cm_enum_from_str(const char *prop_name)
74735d5b7c7Smrg{
74835d5b7c7Smrg	enum drmmode_cm_prop ret;
74935d5b7c7Smrg
75035d5b7c7Smrg	for (ret = 0; ret < CM_NUM_PROPS; ret++) {
75135d5b7c7Smrg		if (!strcmp(prop_name, cm_prop_names[ret]))
75235d5b7c7Smrg			return ret;
75335d5b7c7Smrg	}
75435d5b7c7Smrg	return CM_INVALID_PROP;
75535d5b7c7Smrg}
75635d5b7c7Smrg
75735d5b7c7Smrg/**
75835d5b7c7Smrg * If legacy LUT is a, and non-legacy LUT is b, then the result of b(a(x)) is
75935d5b7c7Smrg * returned in out_lut. out_lut's length is expected to be the same as the
76035d5b7c7Smrg * non-legacy LUT b.
76135d5b7c7Smrg *
76235d5b7c7Smrg * @a_(red|green|blue): The red, green, and blue components of the legacy LUT.
76335d5b7c7Smrg * @b_lut: The non-legacy LUT, in DRM's color LUT format.
76435d5b7c7Smrg * @out_lut: The composed LUT, in DRM's color LUT format.
76535d5b7c7Smrg * @len_a: Length of legacy lut.
76635d5b7c7Smrg * @len_b: Length of non-legacy lut.
76735d5b7c7Smrg */
76835d5b7c7Smrgstatic void drmmode_lut_compose(uint16_t *a_red,
76935d5b7c7Smrg				uint16_t *a_green,
77035d5b7c7Smrg				uint16_t *a_blue,
77135d5b7c7Smrg				struct drm_color_lut *b_lut,
77235d5b7c7Smrg				struct drm_color_lut *out_lut,
77335d5b7c7Smrg				uint32_t len_a, uint32_t len_b)
77435d5b7c7Smrg{
77535d5b7c7Smrg	uint32_t i_l, i_r, i;
77635d5b7c7Smrg	uint32_t i_amax, i_bmax;
77735d5b7c7Smrg	uint32_t coeff_ibmax;
77835d5b7c7Smrg	uint32_t j;
77935d5b7c7Smrg	uint64_t a_out_ibmax;
78035d5b7c7Smrg	int color;
78135d5b7c7Smrg	size_t struct_size = sizeof(struct drm_color_lut);
78235d5b7c7Smrg
78335d5b7c7Smrg	uint32_t max_lut = (1 << 16) - 1;
78435d5b7c7Smrg
78535d5b7c7Smrg	i_amax = len_a - 1;
78635d5b7c7Smrg	i_bmax = len_b - 1;
78735d5b7c7Smrg
78835d5b7c7Smrg	/* A linear interpolation is done on the legacy LUT before it is
78935d5b7c7Smrg	 * composed, to bring it up-to-size with the non-legacy LUT. The
79035d5b7c7Smrg	 * interpolation uses integers by keeping things multiplied until the
79135d5b7c7Smrg	 * last moment.
79235d5b7c7Smrg	 */
79335d5b7c7Smrg	for (color = 0; color < 3; color++) {
79435d5b7c7Smrg		uint16_t *a, *b, *out;
79535d5b7c7Smrg
79635d5b7c7Smrg		/* Set the initial pointers to the right color components. The
79735d5b7c7Smrg		 * inner for-loop will then maintain the correct offset from
79835d5b7c7Smrg		 * the initial element.
79935d5b7c7Smrg		 */
80035d5b7c7Smrg		if (color == 0) {
80135d5b7c7Smrg			a = a_red;
80235d5b7c7Smrg			b = &b_lut[0].red;
80335d5b7c7Smrg			out = &out_lut[0].red;
80435d5b7c7Smrg		} else if (color == 1) {
80535d5b7c7Smrg			a = a_green;
80635d5b7c7Smrg			b = &b_lut[0].green;
80735d5b7c7Smrg			out = &out_lut[0].green;
80835d5b7c7Smrg		} else {
80935d5b7c7Smrg			a = a_blue;
81035d5b7c7Smrg			b = &b_lut[0].blue;
81135d5b7c7Smrg			out = &out_lut[0].blue;
81235d5b7c7Smrg		}
81335d5b7c7Smrg
81435d5b7c7Smrg		for (i = 0; i < len_b; i++) {
81535d5b7c7Smrg			/* i_l and i_r tracks the left and right elements in
81635d5b7c7Smrg			 * a_lut, to the sample point i. Also handle last
81735d5b7c7Smrg			 * element edge case, when i_l = i_amax.
81835d5b7c7Smrg			 */
81935d5b7c7Smrg			i_l = i * i_amax / i_bmax;
82035d5b7c7Smrg			i_r = i_l + !!(i_amax - i_l);
82135d5b7c7Smrg
82235d5b7c7Smrg			/* coeff is intended to be in [0, 1), depending on
82335d5b7c7Smrg			 * where sample i is between i_l and i_r. We keep it
82435d5b7c7Smrg			 * multiplied with i_bmax throughout to maintain
82535d5b7c7Smrg			 * precision */
82635d5b7c7Smrg			coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
82735d5b7c7Smrg			a_out_ibmax = i_bmax * a[i_l] +
82835d5b7c7Smrg				      coeff_ibmax * (a[i_r] - a[i_l]);
82935d5b7c7Smrg
83035d5b7c7Smrg			/* j = floor((a_out/max_lut)*i_bmax).
83135d5b7c7Smrg			 * i.e. the element in LUT b that a_out maps to. We
83235d5b7c7Smrg			 * have to divide by max_lut to normalize a_out, since
83335d5b7c7Smrg			 * values in the LUTs are [0, 1<<16)
83435d5b7c7Smrg			 */
83535d5b7c7Smrg			j = a_out_ibmax / max_lut;
83635d5b7c7Smrg			*(uint16_t*)((void*)out + (i*struct_size)) =
83735d5b7c7Smrg				*(uint16_t*)((void*)b + (j*struct_size));
83835d5b7c7Smrg		}
83935d5b7c7Smrg	}
84035d5b7c7Smrg
84135d5b7c7Smrg	for (i = 0; i < len_b; i++)
84235d5b7c7Smrg		out_lut[i].reserved = 0;
84335d5b7c7Smrg}
84435d5b7c7Smrg
84535d5b7c7Smrg/**
84635d5b7c7Smrg * Resize a LUT, using linear interpolation.
84735d5b7c7Smrg *
84835d5b7c7Smrg * @in_(red|green|blue): Legacy LUT components
84935d5b7c7Smrg * @out_lut: The resized LUT is returned here, in DRM color LUT format.
85035d5b7c7Smrg * @len_in: Length of legacy LUT.
85135d5b7c7Smrg * @len_out: Length of out_lut, i.e. the target size.
85235d5b7c7Smrg */
85335d5b7c7Smrgstatic void drmmode_lut_interpolate(uint16_t *in_red,
85435d5b7c7Smrg				    uint16_t *in_green,
85535d5b7c7Smrg				    uint16_t *in_blue,
85635d5b7c7Smrg				    struct drm_color_lut *out_lut,
85735d5b7c7Smrg				    uint32_t len_in, uint32_t len_out)
85835d5b7c7Smrg{
85935d5b7c7Smrg	uint32_t i_l, i_r, i;
86035d5b7c7Smrg	uint32_t i_amax, i_bmax;
86135d5b7c7Smrg	uint32_t coeff_ibmax;
86235d5b7c7Smrg	uint64_t out_ibmax;
86335d5b7c7Smrg	int color;
86435d5b7c7Smrg	size_t struct_size = sizeof(struct drm_color_lut);
86535d5b7c7Smrg
86635d5b7c7Smrg	i_amax = len_in - 1;
86735d5b7c7Smrg	i_bmax = len_out - 1;
86835d5b7c7Smrg
86935d5b7c7Smrg	/* See @drmmode_lut_compose for details */
87035d5b7c7Smrg	for (color = 0; color < 3; color++) {
87135d5b7c7Smrg		uint16_t *in, *out;
87235d5b7c7Smrg
87335d5b7c7Smrg		if (color == 0) {
87435d5b7c7Smrg			in = in_red;
87535d5b7c7Smrg			out = &out_lut[0].red;
87635d5b7c7Smrg		} else if (color == 1) {
87735d5b7c7Smrg			in = in_green;
87835d5b7c7Smrg			out = &out_lut[0].green;
87935d5b7c7Smrg		} else {
88035d5b7c7Smrg			in = in_blue;
88135d5b7c7Smrg			out = &out_lut[0].blue;
88235d5b7c7Smrg		}
88335d5b7c7Smrg
88435d5b7c7Smrg		for (i = 0; i < len_out; i++) {
88535d5b7c7Smrg			i_l = i * i_amax / i_bmax;
88635d5b7c7Smrg			i_r = i_l + !!(i_amax - i_l);
88735d5b7c7Smrg
88835d5b7c7Smrg			coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
88935d5b7c7Smrg			out_ibmax = i_bmax * in[i_l] +
89035d5b7c7Smrg				      coeff_ibmax * (in[i_r] - in[i_l]);
89135d5b7c7Smrg
89235d5b7c7Smrg			*(uint16_t*)((void*)out + (i*struct_size)) =
89335d5b7c7Smrg				out_ibmax / i_bmax;
89435d5b7c7Smrg		}
89535d5b7c7Smrg	}
89635d5b7c7Smrg
89735d5b7c7Smrg	for (i = 0; i < len_out; i++)
89835d5b7c7Smrg		out_lut[i].reserved = 0;
89935d5b7c7Smrg}
90035d5b7c7Smrg
90135d5b7c7Smrg/**
90235d5b7c7Smrg * Configure and change a color property on a CRTC, through RandR. Only the
90335d5b7c7Smrg * specified output will be affected, even if the CRTC is attached to multiple
90435d5b7c7Smrg * outputs. Note that changes will be non-pending: the changes won't be pushed
90535d5b7c7Smrg * to kernel driver.
90635d5b7c7Smrg *
90735d5b7c7Smrg * @output: RandR output to set the property on.
90835d5b7c7Smrg * @crtc: The driver-private CRTC object containing the color properties.
90935d5b7c7Smrg *        If this is NULL, "disabled" values of 0 will be used.
91035d5b7c7Smrg * @cm_prop_index: Color management property to configure and change.
91135d5b7c7Smrg *
91235d5b7c7Smrg * Return 0 on success, X-defined error code otherwise.
91335d5b7c7Smrg */
91435d5b7c7Smrgstatic int rr_configure_and_change_cm_property(xf86OutputPtr output,
91535d5b7c7Smrg					       drmmode_crtc_private_ptr crtc,
91635d5b7c7Smrg					       enum drmmode_cm_prop cm_prop_index)
91735d5b7c7Smrg{
91835d5b7c7Smrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
91935d5b7c7Smrg	drmmode_ptr drmmode = drmmode_output->drmmode;
92035d5b7c7Smrg	Bool need_configure = TRUE;
92135d5b7c7Smrg	unsigned long length = 0;
92235d5b7c7Smrg	void *data = NULL;
92335d5b7c7Smrg	int format = 0;
92435d5b7c7Smrg	uint32_t zero = 0;
92535d5b7c7Smrg	INT32 range[2];
92635d5b7c7Smrg	Atom atom;
92735d5b7c7Smrg	int err;
92835d5b7c7Smrg
92935d5b7c7Smrg	if (cm_prop_index == CM_INVALID_PROP)
93035d5b7c7Smrg		return BadName;
93135d5b7c7Smrg
93235d5b7c7Smrg	switch(cm_prop_index) {
93335d5b7c7Smrg	case CM_GAMMA_LUT_SIZE:
93435d5b7c7Smrg		format = 32;
93535d5b7c7Smrg		length = 1;
93635d5b7c7Smrg		data = &drmmode->gamma_lut_size;
93735d5b7c7Smrg		range[0] = 0;
93835d5b7c7Smrg		range[1] = -1;
93935d5b7c7Smrg		break;
94035d5b7c7Smrg	case CM_DEGAMMA_LUT_SIZE:
94135d5b7c7Smrg		format = 32;
94235d5b7c7Smrg		length = 1;
94335d5b7c7Smrg		data = &drmmode->degamma_lut_size;
94435d5b7c7Smrg		range[0] = 0;
94535d5b7c7Smrg		range[1] = -1;
94635d5b7c7Smrg		break;
94735d5b7c7Smrg	case CM_GAMMA_LUT:
94835d5b7c7Smrg		format = 16;
94935d5b7c7Smrg		range[0] = 0;
95035d5b7c7Smrg		range[1] = (1 << 16) - 1; // Max 16 bit unsigned int.
95135d5b7c7Smrg		if (crtc && crtc->gamma_lut) {
95235d5b7c7Smrg			/* Convert from 8bit size to 16bit size */
95335d5b7c7Smrg			length = sizeof(*crtc->gamma_lut) >> 1;
95435d5b7c7Smrg			length *= drmmode->gamma_lut_size;
95535d5b7c7Smrg			data = crtc->gamma_lut;
95635d5b7c7Smrg		} else {
95735d5b7c7Smrg			length = 1;
95835d5b7c7Smrg			data = &zero;
95935d5b7c7Smrg		}
96035d5b7c7Smrg		break;
96135d5b7c7Smrg	case CM_DEGAMMA_LUT:
96235d5b7c7Smrg		format = 16;
96335d5b7c7Smrg		range[0] = 0;
96435d5b7c7Smrg		range[1] = (1 << 16) - 1;
96535d5b7c7Smrg		if (crtc && crtc->degamma_lut) {
96635d5b7c7Smrg			length = sizeof(*crtc->degamma_lut) >> 1;
96735d5b7c7Smrg			length *= drmmode->degamma_lut_size;
96835d5b7c7Smrg			data = crtc->degamma_lut;
96935d5b7c7Smrg		} else {
97035d5b7c7Smrg			length = 1;
97135d5b7c7Smrg			data = &zero;
97235d5b7c7Smrg		}
97335d5b7c7Smrg		break;
97435d5b7c7Smrg	case CM_CTM:
97535d5b7c7Smrg		/* CTM is fixed-point S31.32 format. */
97635d5b7c7Smrg		format = 32;
97735d5b7c7Smrg		need_configure = FALSE;
97835d5b7c7Smrg		if (crtc && crtc->ctm) {
97935d5b7c7Smrg			/* Convert from 8bit size to 32bit size */
98035d5b7c7Smrg			length = sizeof(*crtc->ctm) >> 2;
98135d5b7c7Smrg			data = crtc->ctm;
98235d5b7c7Smrg		} else {
98335d5b7c7Smrg			length = 1;
98435d5b7c7Smrg			data = &zero;
98535d5b7c7Smrg		}
98635d5b7c7Smrg		break;
98735d5b7c7Smrg	default:
98835d5b7c7Smrg		return BadName;
98935d5b7c7Smrg	}
99035d5b7c7Smrg
99135d5b7c7Smrg	atom = MakeAtom(cm_prop_names[cm_prop_index],
99235d5b7c7Smrg			strlen(cm_prop_names[cm_prop_index]),
99335d5b7c7Smrg			TRUE);
99435d5b7c7Smrg	if (!atom)
99535d5b7c7Smrg		return BadAlloc;
99635d5b7c7Smrg
99735d5b7c7Smrg	if (need_configure) {
99835d5b7c7Smrg		err = RRConfigureOutputProperty(output->randr_output, atom,
99935d5b7c7Smrg						FALSE, TRUE, FALSE, 2, range);
100035d5b7c7Smrg		if (err) {
100135d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
100235d5b7c7Smrg				   "Configuring color management property %s failed with %d\n",
100335d5b7c7Smrg				   cm_prop_names[cm_prop_index], err);
100435d5b7c7Smrg			return err;
100535d5b7c7Smrg		}
100635d5b7c7Smrg	}
100735d5b7c7Smrg
100835d5b7c7Smrg	/* Always issue a non-pending change. We'll push cm properties
100935d5b7c7Smrg	 * ourselves.
101035d5b7c7Smrg	 */
101135d5b7c7Smrg	err = RRChangeOutputProperty(output->randr_output, atom,
101235d5b7c7Smrg				     XA_INTEGER, format,
101335d5b7c7Smrg				     PropModeReplace,
101435d5b7c7Smrg				     length, data, FALSE, FALSE);
101535d5b7c7Smrg	if (err)
101635d5b7c7Smrg		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
101735d5b7c7Smrg			   "Changing color management property %s failed with %d\n",
101835d5b7c7Smrg			   cm_prop_names[cm_prop_index], err);
101935d5b7c7Smrg	return err;
102035d5b7c7Smrg}
102135d5b7c7Smrg
102235d5b7c7Smrg/**
102335d5b7c7Smrg* Stage a color management property. This parses the property value, according
102435d5b7c7Smrg* to the cm property type, then stores it within the driver-private CRTC
102535d5b7c7Smrg* object.
102635d5b7c7Smrg*
102735d5b7c7Smrg* @crtc: The CRTC to stage the new color management properties in
102835d5b7c7Smrg* @cm_prop_index: The color property to stage
102935d5b7c7Smrg* @value: The RandR property value to stage
103035d5b7c7Smrg*
103135d5b7c7Smrg* Return 0 on success, X-defined error code on failure.
103235d5b7c7Smrg*/
103335d5b7c7Smrgstatic int drmmode_crtc_stage_cm_prop(xf86CrtcPtr crtc,
103435d5b7c7Smrg				      enum drmmode_cm_prop cm_prop_index,
103535d5b7c7Smrg				      RRPropertyValuePtr value)
103635d5b7c7Smrg{
103735d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
103835d5b7c7Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
103935d5b7c7Smrg	size_t expected_bytes = 0;
104035d5b7c7Smrg	void **blob_data = NULL;
104135d5b7c7Smrg	Bool use_default = FALSE;
104235d5b7c7Smrg
104335d5b7c7Smrg	/* Update properties on the driver-private CRTC */
104435d5b7c7Smrg	switch (cm_prop_index) {
104535d5b7c7Smrg	case CM_GAMMA_LUT:
104635d5b7c7Smrg		/* Calculate the expected size of value in bytes */
104735d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
104835d5b7c7Smrg					drmmode->gamma_lut_size;
104935d5b7c7Smrg
105035d5b7c7Smrg		/* For gamma and degamma, we allow a default SRGB curve to be
105135d5b7c7Smrg		 * set via setting a single element
105235d5b7c7Smrg		 *
105335d5b7c7Smrg		 * Otherwise, value size is in terms of the value format.
105435d5b7c7Smrg		 * Ensure it's also in bytes (<< 1) before comparing with the
105535d5b7c7Smrg		 * expected bytes.
105635d5b7c7Smrg		 */
105735d5b7c7Smrg		if (value->size == 1)
105835d5b7c7Smrg			use_default = TRUE;
105935d5b7c7Smrg		else if (value->type != XA_INTEGER || value->format != 16 ||
106035d5b7c7Smrg			 (size_t)(value->size << 1) != expected_bytes)
106135d5b7c7Smrg			return BadLength;
106235d5b7c7Smrg
106335d5b7c7Smrg		blob_data = (void**)&drmmode_crtc->gamma_lut;
106435d5b7c7Smrg		break;
106535d5b7c7Smrg	case CM_DEGAMMA_LUT:
106635d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
106735d5b7c7Smrg					drmmode->degamma_lut_size;
106835d5b7c7Smrg
106935d5b7c7Smrg		if (value->size == 1)
107035d5b7c7Smrg			use_default = TRUE;
107135d5b7c7Smrg		else if (value->type != XA_INTEGER || value->format != 16 ||
107235d5b7c7Smrg			 (size_t)(value->size << 1) != expected_bytes)
107335d5b7c7Smrg			return BadLength;
107435d5b7c7Smrg
107535d5b7c7Smrg		blob_data = (void**)&drmmode_crtc->degamma_lut;
107635d5b7c7Smrg		break;
107735d5b7c7Smrg	case CM_CTM:
107835d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_ctm);
107935d5b7c7Smrg
108035d5b7c7Smrg		if (value->size == 1)
108135d5b7c7Smrg			use_default = TRUE;
108235d5b7c7Smrg		if (value->type != XA_INTEGER || value->format != 32 ||
108335d5b7c7Smrg		    (size_t)(value->size << 2) != expected_bytes)
108435d5b7c7Smrg			return BadLength;
108535d5b7c7Smrg
108635d5b7c7Smrg		blob_data = (void**)&drmmode_crtc->ctm;
108735d5b7c7Smrg		break;
108835d5b7c7Smrg	default:
108935d5b7c7Smrg		return BadName;
109035d5b7c7Smrg	}
109135d5b7c7Smrg
109235d5b7c7Smrg	free(*blob_data);
109335d5b7c7Smrg	if (!use_default) {
109435d5b7c7Smrg		*blob_data = malloc(expected_bytes);
109535d5b7c7Smrg		if (!*blob_data)
109635d5b7c7Smrg			return BadAlloc;
109735d5b7c7Smrg		memcpy(*blob_data, value->data, expected_bytes);
109835d5b7c7Smrg	} else
109935d5b7c7Smrg		*blob_data = NULL;
110035d5b7c7Smrg
110135d5b7c7Smrg	return Success;
110235d5b7c7Smrg}
110335d5b7c7Smrg
110435d5b7c7Smrg/**
110535d5b7c7Smrg * Push staged color management properties on the CRTC to DRM.
110635d5b7c7Smrg *
110735d5b7c7Smrg * @crtc: The CRTC containing staged properties
110835d5b7c7Smrg * @cm_prop_index: The color property to push
110935d5b7c7Smrg *
111035d5b7c7Smrg * Return 0 on success, X-defined error codes on failure.
111135d5b7c7Smrg */
111235d5b7c7Smrgstatic int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
111335d5b7c7Smrg				     enum drmmode_cm_prop cm_prop_index)
111435d5b7c7Smrg{
111535d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
111635d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
111735d5b7c7Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
111835d5b7c7Smrg	Bool free_blob_data = FALSE;
111935d5b7c7Smrg	uint32_t created_blob_id = 0;
112035d5b7c7Smrg	uint32_t drm_prop_id;
112135d5b7c7Smrg	size_t expected_bytes = 0;
112235d5b7c7Smrg	void *blob_data = NULL;
112335d5b7c7Smrg	int ret;
112435d5b7c7Smrg
112535d5b7c7Smrg	switch (cm_prop_index) {
112635d5b7c7Smrg	case CM_GAMMA_LUT:
112735d5b7c7Smrg		/* Calculate the expected size of value in bytes */
112835d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
112935d5b7c7Smrg					drmmode->gamma_lut_size;
113035d5b7c7Smrg
113135d5b7c7Smrg		/* Legacy gamma LUT is disabled on deep 30bpp color. In which
113235d5b7c7Smrg		 * case, directly use non-legacy LUT.
113335d5b7c7Smrg		 */
113435d5b7c7Smrg		if (!crtc->funcs->gamma_set) {
113535d5b7c7Smrg			blob_data = drmmode_crtc->gamma_lut;
113635d5b7c7Smrg			goto do_push;
113735d5b7c7Smrg		}
113835d5b7c7Smrg
113935d5b7c7Smrg		blob_data = malloc(expected_bytes);
114035d5b7c7Smrg		if (!blob_data)
114135d5b7c7Smrg			return BadAlloc;
114235d5b7c7Smrg
114335d5b7c7Smrg		free_blob_data = TRUE;
114435d5b7c7Smrg		/*
114535d5b7c7Smrg		 * Compose legacy and non-legacy LUT if non-legacy was set.
114635d5b7c7Smrg		 * Otherwise, interpolate legacy LUT to non-legacy size.
114735d5b7c7Smrg		 */
114835d5b7c7Smrg		if (drmmode_crtc->gamma_lut) {
114935d5b7c7Smrg			drmmode_lut_compose(crtc->gamma_red,
115035d5b7c7Smrg					    crtc->gamma_green,
115135d5b7c7Smrg					    crtc->gamma_blue,
115235d5b7c7Smrg					    drmmode_crtc->gamma_lut,
115335d5b7c7Smrg					    blob_data, crtc->gamma_size,
115435d5b7c7Smrg					    drmmode->gamma_lut_size);
115535d5b7c7Smrg		} else {
115635d5b7c7Smrg			drmmode_lut_interpolate(crtc->gamma_red,
115735d5b7c7Smrg						crtc->gamma_green,
115835d5b7c7Smrg						crtc->gamma_blue,
115935d5b7c7Smrg						blob_data,
116035d5b7c7Smrg						crtc->gamma_size,
116135d5b7c7Smrg						drmmode->gamma_lut_size);
116235d5b7c7Smrg		}
116335d5b7c7Smrg		break;
116435d5b7c7Smrg	case CM_DEGAMMA_LUT:
116535d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
116635d5b7c7Smrg					drmmode->degamma_lut_size;
116735d5b7c7Smrg		blob_data = drmmode_crtc->degamma_lut;
116835d5b7c7Smrg		break;
116935d5b7c7Smrg	case CM_CTM:
117035d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_ctm);
117135d5b7c7Smrg		blob_data = drmmode_crtc->ctm;
117235d5b7c7Smrg		break;
117335d5b7c7Smrg	default:
117435d5b7c7Smrg		return BadName;
117535d5b7c7Smrg	}
117635d5b7c7Smrg
117735d5b7c7Smrgdo_push:
117835d5b7c7Smrg	if (blob_data) {
117935d5b7c7Smrg		ret = drmModeCreatePropertyBlob(pAMDGPUEnt->fd,
118035d5b7c7Smrg						blob_data, expected_bytes,
118135d5b7c7Smrg						&created_blob_id);
118235d5b7c7Smrg		if (ret) {
118335d5b7c7Smrg			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
118435d5b7c7Smrg				   "Creating DRM blob failed with errno %d\n",
118535d5b7c7Smrg				   ret);
118635d5b7c7Smrg			if (free_blob_data)
118735d5b7c7Smrg				free(blob_data);
118835d5b7c7Smrg			return BadRequest;
118935d5b7c7Smrg		}
119035d5b7c7Smrg	}
119135d5b7c7Smrg
119235d5b7c7Smrg	drm_prop_id = drmmode_crtc->drmmode->cm_prop_ids[cm_prop_index];
119335d5b7c7Smrg	ret = drmModeObjectSetProperty(pAMDGPUEnt->fd,
119435d5b7c7Smrg				       drmmode_crtc->mode_crtc->crtc_id,
119535d5b7c7Smrg				       DRM_MODE_OBJECT_CRTC,
119635d5b7c7Smrg				       drm_prop_id,
119735d5b7c7Smrg				       (uint64_t)created_blob_id);
119835d5b7c7Smrg
119935d5b7c7Smrg	/* If successful, kernel will have a reference already. Safe to destroy
120035d5b7c7Smrg	 * the blob either way.
120135d5b7c7Smrg	 */
120235d5b7c7Smrg	if (blob_data)
120335d5b7c7Smrg		drmModeDestroyPropertyBlob(pAMDGPUEnt->fd, created_blob_id);
120435d5b7c7Smrg
120535d5b7c7Smrg	if (ret) {
120635d5b7c7Smrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
120735d5b7c7Smrg			   "Setting DRM property blob failed with errno %d\n",
120835d5b7c7Smrg			   ret);
120935d5b7c7Smrg		if (free_blob_data)
121035d5b7c7Smrg			free(blob_data);
121135d5b7c7Smrg		return BadRequest;
121235d5b7c7Smrg	}
121335d5b7c7Smrg
121435d5b7c7Smrg	if (free_blob_data)
121535d5b7c7Smrg		free(blob_data);
121635d5b7c7Smrg
121735d5b7c7Smrg	return Success;
121835d5b7c7Smrg}
121935d5b7c7Smrg
122024b90cf4Smrgstatic void
122124b90cf4Smrgdrmmode_crtc_gamma_do_set(xf86CrtcPtr crtc, uint16_t *red, uint16_t *green,
122224b90cf4Smrg			  uint16_t *blue, int size)
122324b90cf4Smrg{
122424b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
122524b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
122635d5b7c7Smrg	int ret;
122735d5b7c7Smrg
122835d5b7c7Smrg	/* Use legacy if no support for non-legacy gamma */
122935d5b7c7Smrg	if (!drmmode_cm_enabled(drmmode_crtc->drmmode)) {
123035d5b7c7Smrg		drmModeCrtcSetGamma(pAMDGPUEnt->fd,
123135d5b7c7Smrg				    drmmode_crtc->mode_crtc->crtc_id,
123235d5b7c7Smrg				    size, red, green, blue);
123335d5b7c7Smrg		return;
123435d5b7c7Smrg	}
123524b90cf4Smrg
123635d5b7c7Smrg	ret = drmmode_crtc_push_cm_prop(crtc, CM_GAMMA_LUT);
123735d5b7c7Smrg	if (ret)
123835d5b7c7Smrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
123935d5b7c7Smrg			   "Setting Gamma LUT failed with errno %d\n",
124035d5b7c7Smrg			   ret);
124124b90cf4Smrg}
124224b90cf4Smrg
124324b90cf4SmrgBool
124424b90cf4Smrgdrmmode_set_mode(xf86CrtcPtr crtc, struct drmmode_fb *fb, DisplayModePtr mode,
124524b90cf4Smrg		 int x, int y)
124624b90cf4Smrg{
124724b90cf4Smrg	ScrnInfoPtr scrn = crtc->scrn;
124824b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
124924b90cf4Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
125024b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
125124b90cf4Smrg	uint32_t *output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
125224b90cf4Smrg	int output_count = 0;
125324b90cf4Smrg	drmModeModeInfo kmode;
125424b90cf4Smrg	Bool ret;
125524b90cf4Smrg	int i;
125624b90cf4Smrg
125724b90cf4Smrg	if (!output_ids)
125824b90cf4Smrg		return FALSE;
125924b90cf4Smrg
126024b90cf4Smrg	for (i = 0; i < xf86_config->num_output; i++) {
126124b90cf4Smrg		xf86OutputPtr output = xf86_config->output[i];
126224b90cf4Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
126324b90cf4Smrg
126424b90cf4Smrg		if (output->crtc != crtc)
126524b90cf4Smrg			continue;
126624b90cf4Smrg
126724b90cf4Smrg		output_ids[output_count] = drmmode_output->mode_output->connector_id;
126824b90cf4Smrg		output_count++;
126924b90cf4Smrg	}
127024b90cf4Smrg
127124b90cf4Smrg	drmmode_ConvertToKMode(scrn, &kmode, mode);
127224b90cf4Smrg
127324b90cf4Smrg	ret = drmModeSetCrtc(pAMDGPUEnt->fd,
127424b90cf4Smrg			     drmmode_crtc->mode_crtc->crtc_id,
127524b90cf4Smrg			     fb->handle, x, y, output_ids,
127624b90cf4Smrg			     output_count, &kmode) == 0;
127724b90cf4Smrg
127824b90cf4Smrg	if (ret) {
127924b90cf4Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, fb);
128024b90cf4Smrg	} else {
128124b90cf4Smrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
128224b90cf4Smrg			   "failed to set mode: %s\n", strerror(errno));
128324b90cf4Smrg	}
128424b90cf4Smrg
128524b90cf4Smrg	free(output_ids);
128624b90cf4Smrg	return ret;
128724b90cf4Smrg}
128824b90cf4Smrg
1289d6c0b56eSmrgstatic Bool
1290d6c0b56eSmrgdrmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
1291d6c0b56eSmrg		       Rotation rotation, int x, int y)
1292d6c0b56eSmrg{
1293d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1294d6c0b56eSmrg	ScreenPtr pScreen = pScrn->pScreen;
1295d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1296d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1297d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
1298d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
129990f2b693Smrg	Bool handle_deferred = FALSE;
130011bf0794Smrg	unsigned scanout_id = 0;
1301d6c0b56eSmrg	int saved_x, saved_y;
1302d6c0b56eSmrg	Rotation saved_rotation;
1303d6c0b56eSmrg	DisplayModeRec saved_mode;
1304504d986fSmrg	Bool ret = FALSE;
1305d6c0b56eSmrg	int i;
130624b90cf4Smrg	struct drmmode_fb *fb = NULL;
130724b90cf4Smrg
130824b90cf4Smrg	/* The root window contents may be undefined before the WindowExposures
130924b90cf4Smrg	 * hook is called for it, so bail if we get here before that
131024b90cf4Smrg	 */
131124b90cf4Smrg	if (pScreen->WindowExposures == AMDGPUWindowExposures_oneshot)
131224b90cf4Smrg		return FALSE;
1313d6c0b56eSmrg
1314d6c0b56eSmrg	saved_mode = crtc->mode;
1315d6c0b56eSmrg	saved_x = crtc->x;
1316d6c0b56eSmrg	saved_y = crtc->y;
1317d6c0b56eSmrg	saved_rotation = crtc->rotation;
1318d6c0b56eSmrg
1319d6c0b56eSmrg	if (mode) {
1320d6c0b56eSmrg		crtc->mode = *mode;
1321d6c0b56eSmrg		crtc->x = x;
1322d6c0b56eSmrg		crtc->y = y;
1323d6c0b56eSmrg		crtc->rotation = rotation;
1324d6c0b56eSmrg
1325d6c0b56eSmrg		if (!drmmode_handle_transform(crtc))
1326d6c0b56eSmrg			goto done;
1327d6c0b56eSmrg
132811bf0794Smrg		drmmode_crtc_update_tear_free(crtc);
132911bf0794Smrg		if (drmmode_crtc->tear_free)
133011bf0794Smrg			scanout_id = drmmode_crtc->scanout_id;
133135d5b7c7Smrg		else
133235d5b7c7Smrg			drmmode_crtc->scanout_id = 0;
1333d6c0b56eSmrg
133424b90cf4Smrg		if (drmmode_crtc->prime_scanout_pixmap) {
133511bf0794Smrg			drmmode_crtc_prime_scanout_update(crtc, mode, scanout_id,
133624b90cf4Smrg							  &fb, &x, &y);
133746845023Smrg		} else if (drmmode_crtc->rotate) {
133846845023Smrg			fb = amdgpu_pixmap_get_fb(drmmode_crtc->rotate);
1339d6c0b56eSmrg			x = y = 0;
134011bf0794Smrg
134124b90cf4Smrg		} else if (!pScreen->isGPU &&
134211bf0794Smrg			   (drmmode_crtc->tear_free ||
1343504d986fSmrg			    crtc->driverIsPerformingTransform ||
1344504d986fSmrg			    info->shadow_primary)) {
134511bf0794Smrg			drmmode_crtc_scanout_update(crtc, mode, scanout_id,
134624b90cf4Smrg						    &fb, &x, &y);
1347d6c0b56eSmrg		}
1348d6c0b56eSmrg
134924b90cf4Smrg		if (!fb)
135024b90cf4Smrg			fb = amdgpu_pixmap_get_fb(pScreen->GetWindowPixmap(pScreen->root));
135124b90cf4Smrg		if (!fb) {
135224b90cf4Smrg			union gbm_bo_handle bo_handle;
135324b90cf4Smrg
135424b90cf4Smrg			bo_handle = gbm_bo_get_handle(info->front_buffer->bo.gbm);
135524b90cf4Smrg			fb = amdgpu_fb_create(pScrn, pAMDGPUEnt->fd,
135624b90cf4Smrg					      pScrn->virtualX, pScrn->virtualY,
135724b90cf4Smrg					      pScrn->displayWidth * info->pixel_bytes,
135824b90cf4Smrg					      bo_handle.u32);
135924b90cf4Smrg			/* Prevent refcnt of ad-hoc FBs from reaching 2 */
136024b90cf4Smrg			drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, NULL);
136124b90cf4Smrg			drmmode_crtc->fb = fb;
136224b90cf4Smrg		}
136324b90cf4Smrg		if (!fb) {
136424b90cf4Smrg			ErrorF("failed to add FB for modeset\n");
136524b90cf4Smrg			goto done;
1366504d986fSmrg		}
1367504d986fSmrg
136835d5b7c7Smrg		amdgpu_drm_wait_pending_flip(crtc);
136990f2b693Smrg		handle_deferred = TRUE;
137024b90cf4Smrg
137124b90cf4Smrg		if (!drmmode_set_mode(crtc, fb, mode, x, y))
1372d6c0b56eSmrg			goto done;
137324b90cf4Smrg
137424b90cf4Smrg		ret = TRUE;
1375d6c0b56eSmrg
1376d6c0b56eSmrg		if (pScreen)
1377d6c0b56eSmrg			xf86CrtcSetScreenSubpixelOrder(pScreen);
1378d6c0b56eSmrg
1379d6c0b56eSmrg		drmmode_crtc->need_modeset = FALSE;
1380d6c0b56eSmrg
1381d6c0b56eSmrg		/* go through all the outputs and force DPMS them back on? */
1382d6c0b56eSmrg		for (i = 0; i < xf86_config->num_output; i++) {
1383d6c0b56eSmrg			xf86OutputPtr output = xf86_config->output[i];
1384d6c0b56eSmrg
1385d6c0b56eSmrg			if (output->crtc != crtc)
1386d6c0b56eSmrg				continue;
1387d6c0b56eSmrg
1388d6c0b56eSmrg			output->funcs->dpms(output, DPMSModeOn);
1389d6c0b56eSmrg		}
1390d6c0b56eSmrg	}
1391d6c0b56eSmrg
1392d6c0b56eSmrg	/* Compute index of this CRTC into xf86_config->crtc */
1393d6c0b56eSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
1394d6c0b56eSmrg		if (xf86_config->crtc[i] != crtc)
1395d6c0b56eSmrg			continue;
1396d6c0b56eSmrg
1397d6c0b56eSmrg		if (!crtc->enabled || drmmode_can_use_hw_cursor(crtc))
1398d6c0b56eSmrg			info->hwcursor_disabled &= ~(1 << i);
1399d6c0b56eSmrg		else
1400d6c0b56eSmrg			info->hwcursor_disabled |= 1 << i;
1401d6c0b56eSmrg
1402d6c0b56eSmrg		break;
1403d6c0b56eSmrg	}
1404d6c0b56eSmrg
1405d6c0b56eSmrg#ifndef HAVE_XF86_CURSOR_RESET_CURSOR
1406d6c0b56eSmrg	if (!info->hwcursor_disabled)
1407d6c0b56eSmrg		xf86_reload_cursors(pScreen);
1408d6c0b56eSmrg#endif
1409d6c0b56eSmrg
1410d6c0b56eSmrgdone:
1411d6c0b56eSmrg	if (!ret) {
1412d6c0b56eSmrg		crtc->x = saved_x;
1413d6c0b56eSmrg		crtc->y = saved_y;
1414d6c0b56eSmrg		crtc->rotation = saved_rotation;
1415d6c0b56eSmrg		crtc->mode = saved_mode;
1416504d986fSmrg	} else {
1417d6c0b56eSmrg		crtc->active = TRUE;
1418d6c0b56eSmrg
141946845023Smrg		if (drmmode_crtc->scanout[scanout_id] &&
142046845023Smrg		    fb != amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id])) {
142190f2b693Smrg			drmmode_crtc_scanout_free(crtc);
142235d5b7c7Smrg		} else if (!drmmode_crtc->tear_free) {
142346845023Smrg			drmmode_crtc_scanout_destroy(&drmmode_crtc->scanout[1]);
142411bf0794Smrg		}
1425504d986fSmrg	}
1426504d986fSmrg
142790f2b693Smrg	if (handle_deferred)
142890f2b693Smrg		amdgpu_drm_queue_handle_deferred(crtc);
142990f2b693Smrg
1430d6c0b56eSmrg	return ret;
1431d6c0b56eSmrg}
1432d6c0b56eSmrg
1433d6c0b56eSmrgstatic void drmmode_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
1434d6c0b56eSmrg{
1435d6c0b56eSmrg
1436d6c0b56eSmrg}
1437d6c0b56eSmrg
1438d6c0b56eSmrgstatic void drmmode_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
1439d6c0b56eSmrg{
1440d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1441d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
1442d6c0b56eSmrg
144324b90cf4Smrg#if XF86_CRTC_VERSION < 7
1444d6c0b56eSmrg	if (crtc->driverIsPerformingTransform) {
1445d6c0b56eSmrg		x += crtc->x;
1446d6c0b56eSmrg		y += crtc->y;
1447d6c0b56eSmrg		xf86CrtcTransformCursorPos(crtc, &x, &y);
1448d6c0b56eSmrg	}
1449d6c0b56eSmrg#endif
1450d6c0b56eSmrg
145190f2b693Smrg	drmmode_crtc->cursor_x = x;
145290f2b693Smrg	drmmode_crtc->cursor_y = y;
145390f2b693Smrg
1454d6c0b56eSmrg	drmModeMoveCursor(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
1455d6c0b56eSmrg}
1456d6c0b56eSmrg
145724b90cf4Smrg#if XF86_CRTC_VERSION < 7
1458d6c0b56eSmrg
1459d6c0b56eSmrgstatic int
1460d6c0b56eSmrgdrmmode_cursor_src_offset(Rotation rotation, int width, int height,
1461d6c0b56eSmrg			  int x_dst, int y_dst)
1462d6c0b56eSmrg{
1463d6c0b56eSmrg	int t;
1464d6c0b56eSmrg
1465d6c0b56eSmrg	switch (rotation & 0xf) {
1466d6c0b56eSmrg	case RR_Rotate_90:
1467d6c0b56eSmrg		t = x_dst;
1468d6c0b56eSmrg		x_dst = height - y_dst - 1;
1469d6c0b56eSmrg		y_dst = t;
1470d6c0b56eSmrg		break;
1471d6c0b56eSmrg	case RR_Rotate_180:
1472d6c0b56eSmrg		x_dst = width - x_dst - 1;
1473d6c0b56eSmrg		y_dst = height - y_dst - 1;
1474d6c0b56eSmrg		break;
1475d6c0b56eSmrg	case RR_Rotate_270:
1476d6c0b56eSmrg		t = x_dst;
1477d6c0b56eSmrg		x_dst = y_dst;
1478d6c0b56eSmrg		y_dst = width - t - 1;
1479d6c0b56eSmrg		break;
1480d6c0b56eSmrg	}
1481d6c0b56eSmrg
1482d6c0b56eSmrg	if (rotation & RR_Reflect_X)
1483d6c0b56eSmrg		x_dst = width - x_dst - 1;
1484d6c0b56eSmrg	if (rotation & RR_Reflect_Y)
1485d6c0b56eSmrg		y_dst = height - y_dst - 1;
1486d6c0b56eSmrg
1487d6c0b56eSmrg	return y_dst * height + x_dst;
1488d6c0b56eSmrg}
1489d6c0b56eSmrg
1490d6c0b56eSmrg#endif
1491d6c0b56eSmrg
149290f2b693Smrgstatic Bool
149390f2b693Smrgdrmmode_cursor_pixel(xf86CrtcPtr crtc, uint32_t *argb, Bool *premultiplied,
149490f2b693Smrg		     Bool *apply_gamma)
149535d5b7c7Smrg{
149690f2b693Smrg	uint32_t alpha = *argb >> 24;
149790f2b693Smrg	uint32_t rgb[3];
149890f2b693Smrg	int i;
149935d5b7c7Smrg
150090f2b693Smrg	if (premultiplied) {
150190f2b693Smrg#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1, 18, 4, 0, 0)
150290f2b693Smrg		if (alpha == 0 && (*argb & 0xffffff) != 0) {
150390f2b693Smrg			/* Doesn't look like premultiplied alpha */
150490f2b693Smrg			*premultiplied = FALSE;
150590f2b693Smrg			return FALSE;
150690f2b693Smrg		}
150790f2b693Smrg#endif
150835d5b7c7Smrg
150990f2b693Smrg		if (!(*apply_gamma))
151090f2b693Smrg			return TRUE;
151135d5b7c7Smrg
151290f2b693Smrg		if (*argb > (alpha | alpha << 8 | alpha << 16 | alpha << 24)) {
151390f2b693Smrg			/* Un-premultiplied R/G/B would overflow gamma LUT,
151490f2b693Smrg			 * don't apply gamma correction
151590f2b693Smrg			 */
151690f2b693Smrg			*apply_gamma = FALSE;
151790f2b693Smrg			return FALSE;
151890f2b693Smrg		}
151990f2b693Smrg	}
152024b90cf4Smrg
152190f2b693Smrg	if (!alpha) {
152290f2b693Smrg		*argb = 0;
152390f2b693Smrg		return TRUE;
152490f2b693Smrg	}
152524b90cf4Smrg
152690f2b693Smrg	/* Extract RGB */
152724b90cf4Smrg	for (i = 0; i < 3; i++)
152890f2b693Smrg		rgb[i] = (*argb >> (i * 8)) & 0xff;
152924b90cf4Smrg
153090f2b693Smrg	if (premultiplied) {
153190f2b693Smrg		/* Un-premultiply alpha */
153290f2b693Smrg		for (i = 0; i < 3; i++)
153390f2b693Smrg			rgb[i] = rgb[i] * 0xff / alpha;
153490f2b693Smrg	}
153590f2b693Smrg
153690f2b693Smrg	if (*apply_gamma) {
153790f2b693Smrg		rgb[0] = crtc->gamma_blue[rgb[0]] >> 8;
153890f2b693Smrg		rgb[1] = crtc->gamma_green[rgb[1]] >> 8;
153990f2b693Smrg		rgb[2] = crtc->gamma_red[rgb[2]] >> 8;
154090f2b693Smrg	}
154190f2b693Smrg
154290f2b693Smrg	/* Premultiply alpha */
154390f2b693Smrg	for (i = 0; i < 3; i++)
154490f2b693Smrg		rgb[i] = rgb[i] * alpha / 0xff;
154524b90cf4Smrg
154690f2b693Smrg	*argb = alpha << 24 | rgb[2] << 16 | rgb[1] << 8 | rgb[0];
154790f2b693Smrg	return TRUE;
154824b90cf4Smrg}
154924b90cf4Smrg
155090f2b693Smrgstatic void drmmode_load_cursor_argb(xf86CrtcPtr crtc, CARD32 * image)
1551d6c0b56eSmrg{
155290f2b693Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1553d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1554d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
155590f2b693Smrg	unsigned id = drmmode_crtc->cursor_id;
155690f2b693Smrg	Bool premultiplied = TRUE;
155790f2b693Smrg	Bool apply_gamma = TRUE;
155890f2b693Smrg	uint32_t argb;
155990f2b693Smrg	uint32_t *ptr;
156035d5b7c7Smrg
156135d5b7c7Smrg	if ((crtc->scrn->depth != 24 && crtc->scrn->depth != 32) ||
156235d5b7c7Smrg	    drmmode_cm_enabled(&info->drmmode))
156390f2b693Smrg		apply_gamma = FALSE;
156490f2b693Smrg
156590f2b693Smrg	if (drmmode_crtc->cursor &&
156690f2b693Smrg	    XF86_CRTC_CONFIG_PTR(pScrn)->cursor != drmmode_crtc->cursor)
156790f2b693Smrg		id ^= 1;
156890f2b693Smrg
156990f2b693Smrg	ptr = (uint32_t *) (drmmode_crtc->cursor_buffer[id]->cpu_ptr);
1570d6c0b56eSmrg
157124b90cf4Smrg#if XF86_CRTC_VERSION < 7
1572d6c0b56eSmrg	if (crtc->driverIsPerformingTransform) {
1573d6c0b56eSmrg		uint32_t cursor_w = info->cursor_w, cursor_h = info->cursor_h;
1574d6c0b56eSmrg		int dstx, dsty;
1575d6c0b56eSmrg		int srcoffset;
1576d6c0b56eSmrg
157790f2b693Smrgretry_transform:
1578d6c0b56eSmrg		for (dsty = 0; dsty < cursor_h; dsty++) {
1579d6c0b56eSmrg			for (dstx = 0; dstx < cursor_w; dstx++) {
1580d6c0b56eSmrg				srcoffset = drmmode_cursor_src_offset(crtc->rotation,
1581d6c0b56eSmrg								      cursor_w,
1582d6c0b56eSmrg								      cursor_h,
1583d6c0b56eSmrg								      dstx, dsty);
158490f2b693Smrg				argb = image[srcoffset];
158590f2b693Smrg				if (!drmmode_cursor_pixel(crtc, &argb, &premultiplied,
158690f2b693Smrg							  &apply_gamma))
158790f2b693Smrg					goto retry_transform;
1588d6c0b56eSmrg
158990f2b693Smrg				ptr[dsty * info->cursor_w + dstx] = cpu_to_le32(argb);
1590d6c0b56eSmrg			}
1591d6c0b56eSmrg		}
1592d6c0b56eSmrg	} else
1593d6c0b56eSmrg#endif
1594d6c0b56eSmrg	{
1595d6c0b56eSmrg		uint32_t cursor_size = info->cursor_w * info->cursor_h;
1596d6c0b56eSmrg		int i;
1597d6c0b56eSmrg
159890f2b693Smrgretry:
159990f2b693Smrg		for (i = 0; i < cursor_size; i++) {
160090f2b693Smrg			argb = image[i];
160190f2b693Smrg			if (!drmmode_cursor_pixel(crtc, &argb, &premultiplied,
160290f2b693Smrg						  &apply_gamma))
160390f2b693Smrg				goto retry;
1604d6c0b56eSmrg
160590f2b693Smrg			ptr[i] = cpu_to_le32(argb);
160690f2b693Smrg		}
160790f2b693Smrg	}
1608d6c0b56eSmrg
160990f2b693Smrg	if (id != drmmode_crtc->cursor_id) {
161090f2b693Smrg		drmmode_crtc->cursor_id = id;
161190f2b693Smrg		crtc->funcs->show_cursor(crtc);
1612d6c0b56eSmrg	}
1613d6c0b56eSmrg}
1614d6c0b56eSmrg
1615d6c0b56eSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,903,0)
1616d6c0b56eSmrg
1617d6c0b56eSmrgstatic Bool drmmode_load_cursor_argb_check(xf86CrtcPtr crtc, CARD32 * image)
1618d6c0b56eSmrg{
1619d6c0b56eSmrg	if (!drmmode_can_use_hw_cursor(crtc))
1620d6c0b56eSmrg		return FALSE;
1621d6c0b56eSmrg
1622d6c0b56eSmrg	drmmode_load_cursor_argb(crtc, image);
1623d6c0b56eSmrg	return TRUE;
1624d6c0b56eSmrg}
1625d6c0b56eSmrg
1626d6c0b56eSmrg#endif
1627d6c0b56eSmrg
1628d6c0b56eSmrgstatic void drmmode_hide_cursor(xf86CrtcPtr crtc)
1629d6c0b56eSmrg{
1630d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1631d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1632d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1633d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1634d6c0b56eSmrg
1635d6c0b56eSmrg	drmModeSetCursor(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id, 0,
1636d6c0b56eSmrg			 info->cursor_w, info->cursor_h);
163790f2b693Smrg	drmmode_crtc->cursor = NULL;
1638d6c0b56eSmrg}
1639d6c0b56eSmrg
1640d6c0b56eSmrgstatic void drmmode_show_cursor(xf86CrtcPtr crtc)
1641d6c0b56eSmrg{
1642d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1643d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1644d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1645d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
164690f2b693Smrg	struct amdgpu_buffer *cursor_buffer =
164790f2b693Smrg		drmmode_crtc->cursor_buffer[drmmode_crtc->cursor_id];
164890f2b693Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
164990f2b693Smrg	CursorPtr cursor = xf86_config->cursor;
165090f2b693Smrg	int xhot = cursor->bits->xhot;
165190f2b693Smrg	int yhot = cursor->bits->yhot;
1652d6c0b56eSmrg	static Bool use_set_cursor2 = TRUE;
165390f2b693Smrg	struct drm_mode_cursor2 arg;
1654d6c0b56eSmrg
165590f2b693Smrg	drmmode_crtc->cursor = xf86_config->cursor;
165690f2b693Smrg
165790f2b693Smrg	memset(&arg, 0, sizeof(arg));
165890f2b693Smrg
165990f2b693Smrg	if (!amdgpu_bo_get_handle(cursor_buffer, &arg.handle)) {
1660d6c0b56eSmrg		ErrorF("failed to get BO handle for cursor\n");
1661d6c0b56eSmrg		return;
1662d6c0b56eSmrg	}
1663d6c0b56eSmrg
166490f2b693Smrg	arg.flags = DRM_MODE_CURSOR_BO;
166590f2b693Smrg	arg.crtc_id = drmmode_crtc->mode_crtc->crtc_id;
166690f2b693Smrg	arg.width = info->cursor_w;
166790f2b693Smrg	arg.height = info->cursor_h;
166890f2b693Smrg
166990f2b693Smrg	if (crtc->rotation != RR_Rotate_0 &&
167090f2b693Smrg	    crtc->rotation != (RR_Rotate_180 | RR_Reflect_X |
167190f2b693Smrg			       RR_Reflect_Y)) {
167290f2b693Smrg		int t;
167390f2b693Smrg
167490f2b693Smrg		/* Reflect & rotate hotspot position */
167590f2b693Smrg		if (crtc->rotation & RR_Reflect_X)
167690f2b693Smrg			xhot = info->cursor_w - xhot - 1;
167790f2b693Smrg		if (crtc->rotation & RR_Reflect_Y)
167890f2b693Smrg			yhot = info->cursor_h - yhot - 1;
167990f2b693Smrg
168090f2b693Smrg		switch (crtc->rotation & 0xf) {
168190f2b693Smrg		case RR_Rotate_90:
168290f2b693Smrg			t = xhot;
168390f2b693Smrg			xhot = yhot;
168490f2b693Smrg			yhot = info->cursor_w - t - 1;
168590f2b693Smrg			break;
168690f2b693Smrg		case RR_Rotate_180:
168790f2b693Smrg			xhot = info->cursor_w - xhot - 1;
168890f2b693Smrg			yhot = info->cursor_h - yhot - 1;
168990f2b693Smrg			break;
169090f2b693Smrg		case RR_Rotate_270:
169190f2b693Smrg			t = xhot;
169290f2b693Smrg			xhot = info->cursor_h - yhot - 1;
169390f2b693Smrg			yhot = t;
169490f2b693Smrg		}
169590f2b693Smrg	}
169690f2b693Smrg
169790f2b693Smrg	if (xhot != drmmode_crtc->cursor_xhot || yhot != drmmode_crtc->cursor_yhot) {
169890f2b693Smrg		arg.flags |= DRM_MODE_CURSOR_MOVE;
169990f2b693Smrg		arg.x = drmmode_crtc->cursor_x += drmmode_crtc->cursor_xhot - xhot;
170090f2b693Smrg		arg.y = drmmode_crtc->cursor_y += drmmode_crtc->cursor_yhot - yhot;
170190f2b693Smrg		drmmode_crtc->cursor_xhot = xhot;
170290f2b693Smrg		drmmode_crtc->cursor_yhot = yhot;
170390f2b693Smrg	}
170490f2b693Smrg
1705d6c0b56eSmrg	if (use_set_cursor2) {
1706d6c0b56eSmrg		int ret;
1707d6c0b56eSmrg
170890f2b693Smrg		arg.hot_x = xhot;
170990f2b693Smrg		arg.hot_y = yhot;
1710504d986fSmrg
171190f2b693Smrg		ret = drmIoctl(pAMDGPUEnt->fd, DRM_IOCTL_MODE_CURSOR2, &arg);
171246845023Smrg		if (ret == -1 && errno == EINVAL)
1713d6c0b56eSmrg			use_set_cursor2 = FALSE;
1714d6c0b56eSmrg		else
1715d6c0b56eSmrg			return;
1716d6c0b56eSmrg	}
1717d6c0b56eSmrg
171890f2b693Smrg	drmIoctl(pAMDGPUEnt->fd, DRM_IOCTL_MODE_CURSOR, &arg);
1719d6c0b56eSmrg}
1720d6c0b56eSmrg
172111bf0794Smrg/* Xorg expects a non-NULL return value from drmmode_crtc_shadow_allocate, and
172211bf0794Smrg * passes that back to drmmode_crtc_scanout_create; it doesn't use it for
172311bf0794Smrg * anything else.
172411bf0794Smrg */
172511bf0794Smrgstatic void *
172611bf0794Smrgdrmmode_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
1727d6c0b56eSmrg{
1728d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1729d6c0b56eSmrg
173011bf0794Smrg	if (!drmmode_crtc_scanout_create(crtc, &drmmode_crtc->rotate, width,
173111bf0794Smrg					 height))
173211bf0794Smrg		return NULL;
173311bf0794Smrg
173411bf0794Smrg	return (void*)~0UL;
1735d6c0b56eSmrg}
1736d6c0b56eSmrg
1737d6c0b56eSmrgstatic PixmapPtr
1738d6c0b56eSmrgdrmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
1739d6c0b56eSmrg{
1740d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1741d6c0b56eSmrg
174211bf0794Smrg	if (!data) {
174311bf0794Smrg		drmmode_crtc_scanout_create(crtc, &drmmode_crtc->rotate, width,
174411bf0794Smrg					    height);
174511bf0794Smrg	}
174611bf0794Smrg
174746845023Smrg	return drmmode_crtc->rotate;
1748d6c0b56eSmrg}
1749d6c0b56eSmrg
1750d6c0b56eSmrgstatic void
1751d6c0b56eSmrgdrmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap,
1752d6c0b56eSmrg			    void *data)
1753d6c0b56eSmrg{
1754d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1755d6c0b56eSmrg
175646845023Smrg	drmmode_crtc_scanout_destroy(&drmmode_crtc->rotate);
1757d6c0b56eSmrg}
1758d6c0b56eSmrg
1759d6c0b56eSmrgstatic void
1760d6c0b56eSmrgdrmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t * red, uint16_t * green,
1761d6c0b56eSmrg		       uint16_t * blue, int size)
1762d6c0b56eSmrg{
176324b90cf4Smrg	ScrnInfoPtr scrn = crtc->scrn;
176424b90cf4Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
176524b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
176624b90cf4Smrg	int i;
1767d6c0b56eSmrg
176824b90cf4Smrg	drmmode_crtc_gamma_do_set(crtc, red, green, blue, size);
176924b90cf4Smrg
177024b90cf4Smrg	/* Compute index of this CRTC into xf86_config->crtc */
177124b90cf4Smrg	for (i = 0; xf86_config->crtc[i] != crtc; i++) {}
177224b90cf4Smrg
177324b90cf4Smrg	if (info->hwcursor_disabled & (1 << i))
177424b90cf4Smrg		return;
177524b90cf4Smrg
177624b90cf4Smrg#ifdef HAVE_XF86_CURSOR_RESET_CURSOR
177724b90cf4Smrg	xf86CursorResetCursor(scrn->pScreen);
177824b90cf4Smrg#else
177924b90cf4Smrg	xf86_reload_cursors(scrn->pScreen);
178024b90cf4Smrg#endif
1781d6c0b56eSmrg}
1782d6c0b56eSmrg
1783d6c0b56eSmrgstatic Bool drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
1784d6c0b56eSmrg{
1785d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
178611bf0794Smrg	unsigned scanout_id = drmmode_crtc->scanout_id;
1787504d986fSmrg	ScreenPtr screen = crtc->scrn->pScreen;
1788504d986fSmrg	PixmapDirtyUpdatePtr dirty;
1789d6c0b56eSmrg
1790504d986fSmrg	xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) {
179124b90cf4Smrg		if (amdgpu_dirty_src_equals(dirty, drmmode_crtc->prime_scanout_pixmap)) {
179246845023Smrg			PixmapStopDirtyTracking(dirty->src, dirty->secondary_dst);
179324b90cf4Smrg			break;
179424b90cf4Smrg		}
1795d6c0b56eSmrg	}
1796d6c0b56eSmrg
179790f2b693Smrg	drmmode_crtc_scanout_free(crtc);
179824b90cf4Smrg	drmmode_crtc->prime_scanout_pixmap = NULL;
179924b90cf4Smrg
1800504d986fSmrg	if (!ppix)
1801504d986fSmrg		return TRUE;
1802504d986fSmrg
1803504d986fSmrg	if (!drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[0],
1804504d986fSmrg					 ppix->drawable.width,
1805504d986fSmrg					 ppix->drawable.height))
1806504d986fSmrg		return FALSE;
1807d6c0b56eSmrg
180811bf0794Smrg	if (drmmode_crtc->tear_free &&
1809504d986fSmrg	    !drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1],
1810504d986fSmrg					 ppix->drawable.width,
1811504d986fSmrg					 ppix->drawable.height)) {
181290f2b693Smrg		drmmode_crtc_scanout_free(crtc);
1813504d986fSmrg		return FALSE;
1814d6c0b56eSmrg	}
1815504d986fSmrg
181624b90cf4Smrg	drmmode_crtc->prime_scanout_pixmap = ppix;
181724b90cf4Smrg
181824b90cf4Smrg#ifdef HAS_DIRTYTRACKING_DRAWABLE_SRC
181924b90cf4Smrg	PixmapStartDirtyTracking(&ppix->drawable,
182046845023Smrg				 drmmode_crtc->scanout[scanout_id],
182124b90cf4Smrg				 0, 0, 0, 0, RR_Rotate_0);
182224b90cf4Smrg#elif defined(HAS_DIRTYTRACKING_ROTATION)
182346845023Smrg	PixmapStartDirtyTracking(ppix, drmmode_crtc->scanout[scanout_id],
1824504d986fSmrg				 0, 0, 0, 0, RR_Rotate_0);
1825d6c0b56eSmrg#elif defined(HAS_DIRTYTRACKING2)
182646845023Smrg	PixmapStartDirtyTracking2(ppix, drmmode_crtc->scanout[scanout_id],
1827504d986fSmrg				  0, 0, 0, 0);
1828d6c0b56eSmrg#else
182946845023Smrg	PixmapStartDirtyTracking(ppix, drmmode_crtc->scanout[scanout_id], 0, 0);
1830d6c0b56eSmrg#endif
1831d6c0b56eSmrg	return TRUE;
1832d6c0b56eSmrg}
1833d6c0b56eSmrg
183435d5b7c7Smrgstatic void drmmode_crtc_destroy(xf86CrtcPtr crtc)
183535d5b7c7Smrg{
183635d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
183735d5b7c7Smrg
183835d5b7c7Smrg	drmModeFreeCrtc(drmmode_crtc->mode_crtc);
183935d5b7c7Smrg
184035d5b7c7Smrg	/* Free LUTs and CTM */
184135d5b7c7Smrg	free(drmmode_crtc->gamma_lut);
184235d5b7c7Smrg	free(drmmode_crtc->degamma_lut);
184335d5b7c7Smrg	free(drmmode_crtc->ctm);
184435d5b7c7Smrg
184535d5b7c7Smrg	free(drmmode_crtc);
184635d5b7c7Smrg	crtc->driver_private = NULL;
184735d5b7c7Smrg}
184835d5b7c7Smrg
184935d5b7c7Smrg
1850d6c0b56eSmrgstatic xf86CrtcFuncsRec drmmode_crtc_funcs = {
1851d6c0b56eSmrg	.dpms = drmmode_crtc_dpms,
1852d6c0b56eSmrg	.set_mode_major = drmmode_set_mode_major,
1853d6c0b56eSmrg	.set_cursor_colors = drmmode_set_cursor_colors,
1854d6c0b56eSmrg	.set_cursor_position = drmmode_set_cursor_position,
1855d6c0b56eSmrg	.show_cursor = drmmode_show_cursor,
1856d6c0b56eSmrg	.hide_cursor = drmmode_hide_cursor,
1857d6c0b56eSmrg	.load_cursor_argb = drmmode_load_cursor_argb,
1858d6c0b56eSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,903,0)
1859d6c0b56eSmrg	.load_cursor_argb_check = drmmode_load_cursor_argb_check,
1860d6c0b56eSmrg#endif
1861d6c0b56eSmrg
1862d6c0b56eSmrg	.gamma_set = drmmode_crtc_gamma_set,
1863d6c0b56eSmrg	.shadow_create = drmmode_crtc_shadow_create,
1864d6c0b56eSmrg	.shadow_allocate = drmmode_crtc_shadow_allocate,
1865d6c0b56eSmrg	.shadow_destroy = drmmode_crtc_shadow_destroy,
186635d5b7c7Smrg	.destroy = drmmode_crtc_destroy,
1867d6c0b56eSmrg	.set_scanout_pixmap = drmmode_set_scanout_pixmap,
1868d6c0b56eSmrg};
1869d6c0b56eSmrg
1870d6c0b56eSmrgint drmmode_get_crtc_id(xf86CrtcPtr crtc)
1871d6c0b56eSmrg{
1872d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1873d6c0b56eSmrg	return drmmode_crtc->hw_id;
1874d6c0b56eSmrg}
1875d6c0b56eSmrg
1876d6c0b56eSmrgvoid drmmode_crtc_hw_id(xf86CrtcPtr crtc)
1877d6c0b56eSmrg{
1878d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1879d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1880d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1881d6c0b56eSmrg	int r;
1882d6c0b56eSmrg
1883d6c0b56eSmrg	r = amdgpu_query_crtc_from_id(pAMDGPUEnt->pDev,
1884d6c0b56eSmrg				      drmmode_crtc->mode_crtc->crtc_id,
1885d6c0b56eSmrg				      &drmmode_crtc->hw_id);
1886d6c0b56eSmrg	if (r)
1887d6c0b56eSmrg		drmmode_crtc->hw_id = -1;
1888d6c0b56eSmrg}
1889d6c0b56eSmrg
189035d5b7c7Smrg/**
189135d5b7c7Smrg * Initialize color management properties for the given CRTC by programming
189235d5b7c7Smrg * the default gamma/degamma LUTs and CTM.
189335d5b7c7Smrg *
189435d5b7c7Smrg * If the CRTC does not support color management, or if errors occur during
189535d5b7c7Smrg * initialization, all color properties on the driver-private CRTC will left
189635d5b7c7Smrg * as NULL.
189735d5b7c7Smrg *
189835d5b7c7Smrg * @drm_fd: DRM file descriptor
189935d5b7c7Smrg * @crtc: CRTC to initialize color management on.
190035d5b7c7Smrg */
190135d5b7c7Smrgstatic void drmmode_crtc_cm_init(int drm_fd, xf86CrtcPtr crtc)
190235d5b7c7Smrg{
190335d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
190435d5b7c7Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
190535d5b7c7Smrg	int i;
190635d5b7c7Smrg
190735d5b7c7Smrg	if (!drmmode_cm_enabled(drmmode))
190835d5b7c7Smrg		return;
190935d5b7c7Smrg
191035d5b7c7Smrg	/* Init CTM to identity. Values are in S31.32 fixed-point format */
191135d5b7c7Smrg	drmmode_crtc->ctm = calloc(1, sizeof(*drmmode_crtc->ctm));
191235d5b7c7Smrg	if (!drmmode_crtc->ctm) {
191335d5b7c7Smrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
191435d5b7c7Smrg			   "Memory error initializing CTM for CRTC%d",
191535d5b7c7Smrg			   drmmode_get_crtc_id(crtc));
191635d5b7c7Smrg		return;
191735d5b7c7Smrg	}
191835d5b7c7Smrg
191935d5b7c7Smrg	drmmode_crtc->ctm->matrix[0] = drmmode_crtc->ctm->matrix[4] =
192035d5b7c7Smrg		drmmode_crtc->ctm->matrix[8] = (uint64_t)1 << 32;
192135d5b7c7Smrg
192235d5b7c7Smrg	/* Push properties to reset properties currently in hardware */
192335d5b7c7Smrg	for (i = 0; i < CM_GAMMA_LUT; i++) {
192435d5b7c7Smrg		if (drmmode_crtc_push_cm_prop(crtc, i))
192535d5b7c7Smrg			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
192635d5b7c7Smrg				   "Failed to initialize color management "
192735d5b7c7Smrg				   "property %s on CRTC%d. Property value may "
192835d5b7c7Smrg				   "not reflect actual hardware state.\n",
192935d5b7c7Smrg				   cm_prop_names[i],
193035d5b7c7Smrg				   drmmode_get_crtc_id(crtc));
193135d5b7c7Smrg	}
193235d5b7c7Smrg}
193335d5b7c7Smrg
1934d6c0b56eSmrgstatic unsigned int
1935d6c0b56eSmrgdrmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
1936d6c0b56eSmrg{
1937d6c0b56eSmrg	xf86CrtcPtr crtc;
1938d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc;
1939d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
194024b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1941d6c0b56eSmrg
194224b90cf4Smrg	crtc = xf86CrtcCreate(pScrn, &info->drmmode_crtc_funcs);
194335d5b7c7Smrg	if (!crtc)
1944d6c0b56eSmrg		return 0;
1945d6c0b56eSmrg
1946d6c0b56eSmrg	drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
1947d6c0b56eSmrg	drmmode_crtc->mode_crtc =
1948d6c0b56eSmrg	    drmModeGetCrtc(pAMDGPUEnt->fd, mode_res->crtcs[num]);
1949d6c0b56eSmrg	drmmode_crtc->drmmode = drmmode;
1950d6c0b56eSmrg	drmmode_crtc->dpms_mode = DPMSModeOff;
1951d6c0b56eSmrg	crtc->driver_private = drmmode_crtc;
1952d6c0b56eSmrg	drmmode_crtc_hw_id(crtc);
1953d6c0b56eSmrg
195435d5b7c7Smrg	drmmode_crtc_cm_init(pAMDGPUEnt->fd, crtc);
195590f2b693Smrg	drmmode_crtc_vrr_init(pAMDGPUEnt->fd, crtc);
195635d5b7c7Smrg
1957d6c0b56eSmrg	/* Mark num'th crtc as in use on this device. */
1958d6c0b56eSmrg	pAMDGPUEnt->assigned_crtcs |= (1 << num);
1959d6c0b56eSmrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
1960d6c0b56eSmrg		       "Allocated crtc nr. %d to this screen.\n", num);
1961d6c0b56eSmrg
1962d6c0b56eSmrg	return 1;
1963d6c0b56eSmrg}
1964d6c0b56eSmrg
196524b90cf4Smrg/*
196624b90cf4Smrg * Update all of the property values for an output
196724b90cf4Smrg */
196824b90cf4Smrgstatic void
196924b90cf4Smrgdrmmode_output_update_properties(xf86OutputPtr output)
197024b90cf4Smrg{
197124b90cf4Smrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
197224b90cf4Smrg	int i, j, k;
197324b90cf4Smrg	int err;
197424b90cf4Smrg	drmModeConnectorPtr koutput;
197524b90cf4Smrg
197624b90cf4Smrg	/* Use the most recently fetched values from the kernel */
197724b90cf4Smrg	koutput = drmmode_output->mode_output;
197824b90cf4Smrg
197924b90cf4Smrg	if (!koutput)
198024b90cf4Smrg		return;
198124b90cf4Smrg
198224b90cf4Smrg	for (i = 0; i < drmmode_output->num_props; i++) {
198324b90cf4Smrg		drmmode_prop_ptr p = &drmmode_output->props[i];
198424b90cf4Smrg
198524b90cf4Smrg		for (j = 0; j < koutput->count_props; j++) {
198624b90cf4Smrg			if (koutput->props[j] != p->mode_prop->prop_id)
198724b90cf4Smrg				continue;
198824b90cf4Smrg
198924b90cf4Smrg			/* Check to see if the property value has changed */
199024b90cf4Smrg			if (koutput->prop_values[j] == p->value)
199124b90cf4Smrg				break;
199224b90cf4Smrg
199324b90cf4Smrg			p->value = koutput->prop_values[j];
199424b90cf4Smrg
199524b90cf4Smrg			if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
199624b90cf4Smrg				INT32 value = p->value;
199724b90cf4Smrg
199824b90cf4Smrg				err = RRChangeOutputProperty(output->randr_output,
199924b90cf4Smrg							     p->atoms[0], XA_INTEGER,
200024b90cf4Smrg							     32, PropModeReplace, 1,
200124b90cf4Smrg							     &value, FALSE, TRUE);
200224b90cf4Smrg				if (err != 0) {
200324b90cf4Smrg					xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
200424b90cf4Smrg						   "RRChangeOutputProperty error, %d\n",
200524b90cf4Smrg						   err);
200624b90cf4Smrg				}
200724b90cf4Smrg			} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
200824b90cf4Smrg				for (k = 0; k < p->mode_prop->count_enums; k++) {
200924b90cf4Smrg					if (p->mode_prop->enums[k].value == p->value)
201024b90cf4Smrg						break;
201124b90cf4Smrg				}
201224b90cf4Smrg				if (k < p->mode_prop->count_enums) {
201324b90cf4Smrg					err = RRChangeOutputProperty(output->randr_output,
201424b90cf4Smrg								     p->atoms[0], XA_ATOM,
201524b90cf4Smrg								     32, PropModeReplace, 1,
201624b90cf4Smrg								     &p->atoms[k + 1], FALSE,
201724b90cf4Smrg								     TRUE);
201824b90cf4Smrg					if (err != 0) {
201924b90cf4Smrg						xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
202024b90cf4Smrg							   "RRChangeOutputProperty error, %d\n",
202124b90cf4Smrg							   err);
202224b90cf4Smrg					}
202324b90cf4Smrg				}
202424b90cf4Smrg			}
202524b90cf4Smrg
202624b90cf4Smrg			break;
202724b90cf4Smrg		}
202824b90cf4Smrg        }
202924b90cf4Smrg}
203024b90cf4Smrg
2031d6c0b56eSmrgstatic xf86OutputStatus drmmode_output_detect(xf86OutputPtr output)
2032d6c0b56eSmrg{
2033d6c0b56eSmrg	/* go to the hw and retrieve a new output struct */
2034d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2035d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
2036d6c0b56eSmrg	xf86OutputStatus status;
2037d6c0b56eSmrg	drmModeFreeConnector(drmmode_output->mode_output);
2038d6c0b56eSmrg
2039d6c0b56eSmrg	drmmode_output->mode_output =
2040d6c0b56eSmrg	    drmModeGetConnector(pAMDGPUEnt->fd, drmmode_output->output_id);
204124b90cf4Smrg	if (!drmmode_output->mode_output) {
204224b90cf4Smrg		drmmode_output->output_id = -1;
2043d6c0b56eSmrg		return XF86OutputStatusDisconnected;
204424b90cf4Smrg	}
204524b90cf4Smrg
204624b90cf4Smrg	drmmode_output_update_properties(output);
2047d6c0b56eSmrg
2048d6c0b56eSmrg	switch (drmmode_output->mode_output->connection) {
2049d6c0b56eSmrg	case DRM_MODE_CONNECTED:
2050d6c0b56eSmrg		status = XF86OutputStatusConnected;
2051d6c0b56eSmrg		break;
2052d6c0b56eSmrg	case DRM_MODE_DISCONNECTED:
2053d6c0b56eSmrg		status = XF86OutputStatusDisconnected;
2054d6c0b56eSmrg		break;
2055d6c0b56eSmrg	default:
2056d6c0b56eSmrg	case DRM_MODE_UNKNOWNCONNECTION:
2057d6c0b56eSmrg		status = XF86OutputStatusUnknown;
2058d6c0b56eSmrg		break;
2059d6c0b56eSmrg	}
2060d6c0b56eSmrg	return status;
2061d6c0b56eSmrg}
2062d6c0b56eSmrg
2063d6c0b56eSmrgstatic Bool
2064d6c0b56eSmrgdrmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
2065d6c0b56eSmrg{
2066d6c0b56eSmrg	return MODE_OK;
2067d6c0b56eSmrg}
2068d6c0b56eSmrg
2069852bcc3bSmrgstatic void
2070852bcc3bSmrgdrmmode_output_attach_tile(xf86OutputPtr output)
2071852bcc3bSmrg{
2072852bcc3bSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1, 17, 99, 901, 0)
2073852bcc3bSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2074852bcc3bSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
2075852bcc3bSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
2076852bcc3bSmrg	struct xf86CrtcTileInfo tile_info, *set = NULL;
2077852bcc3bSmrg	int i;
2078852bcc3bSmrg
2079852bcc3bSmrg	if (!koutput) {
2080852bcc3bSmrg		xf86OutputSetTile(output, NULL);
2081852bcc3bSmrg		return;
2082852bcc3bSmrg	}
2083852bcc3bSmrg
2084852bcc3bSmrg	/* look for a TILE property */
2085852bcc3bSmrg	for (i = 0; i < koutput->count_props; i++) {
2086852bcc3bSmrg		drmModePropertyPtr props;
2087852bcc3bSmrg		props = drmModeGetProperty(pAMDGPUEnt->fd, koutput->props[i]);
2088852bcc3bSmrg		if (!props)
2089852bcc3bSmrg			continue;
2090852bcc3bSmrg
2091852bcc3bSmrg		if (!(props->flags & DRM_MODE_PROP_BLOB)) {
2092852bcc3bSmrg			drmModeFreeProperty(props);
2093852bcc3bSmrg			continue;
2094852bcc3bSmrg		}
2095852bcc3bSmrg
2096852bcc3bSmrg		if (!strcmp(props->name, "TILE")) {
2097852bcc3bSmrg			drmModeFreePropertyBlob(drmmode_output->tile_blob);
2098852bcc3bSmrg			drmmode_output->tile_blob =
2099852bcc3bSmrg				drmModeGetPropertyBlob(pAMDGPUEnt->fd,
2100852bcc3bSmrg						       koutput->prop_values[i]);
2101852bcc3bSmrg		}
2102852bcc3bSmrg		drmModeFreeProperty(props);
2103852bcc3bSmrg	}
2104852bcc3bSmrg	if (drmmode_output->tile_blob) {
2105852bcc3bSmrg		if (xf86OutputParseKMSTile(drmmode_output->tile_blob->data,
2106852bcc3bSmrg					   drmmode_output->tile_blob->length,
2107852bcc3bSmrg					   &tile_info) == TRUE)
2108852bcc3bSmrg			set = &tile_info;
2109852bcc3bSmrg	}
2110852bcc3bSmrg	xf86OutputSetTile(output, set);
2111852bcc3bSmrg#endif
2112852bcc3bSmrg}
2113852bcc3bSmrg
211424b90cf4Smrgstatic int
211524b90cf4Smrgkoutput_get_prop_idx(int fd, drmModeConnectorPtr koutput,
211624b90cf4Smrg        int type, const char *name)
211724b90cf4Smrg{
211824b90cf4Smrg    int idx = -1;
211924b90cf4Smrg
212024b90cf4Smrg    for (int i = 0; i < koutput->count_props; i++) {
212124b90cf4Smrg        drmModePropertyPtr prop = drmModeGetProperty(fd, koutput->props[i]);
212224b90cf4Smrg
212324b90cf4Smrg        if (!prop)
212424b90cf4Smrg            continue;
212524b90cf4Smrg
212624b90cf4Smrg        if (drm_property_type_is(prop, type) && !strcmp(prop->name, name))
212724b90cf4Smrg            idx = i;
212824b90cf4Smrg
212924b90cf4Smrg        drmModeFreeProperty(prop);
213024b90cf4Smrg
213124b90cf4Smrg        if (idx > -1)
213224b90cf4Smrg            break;
213324b90cf4Smrg    }
213424b90cf4Smrg
213524b90cf4Smrg    return idx;
213624b90cf4Smrg}
213724b90cf4Smrg
213824b90cf4Smrgstatic int
213924b90cf4Smrgkoutput_get_prop_id(int fd, drmModeConnectorPtr koutput,
214024b90cf4Smrg        int type, const char *name)
214124b90cf4Smrg{
214224b90cf4Smrg    int idx = koutput_get_prop_idx(fd, koutput, type, name);
214324b90cf4Smrg
214424b90cf4Smrg    return (idx > -1) ? koutput->props[idx] : -1;
214524b90cf4Smrg}
214624b90cf4Smrg
214724b90cf4Smrgstatic drmModePropertyBlobPtr
214824b90cf4Smrgkoutput_get_prop_blob(int fd, drmModeConnectorPtr koutput, const char *name)
214924b90cf4Smrg{
215024b90cf4Smrg    drmModePropertyBlobPtr blob = NULL;
215124b90cf4Smrg    int idx = koutput_get_prop_idx(fd, koutput, DRM_MODE_PROP_BLOB, name);
215224b90cf4Smrg
215324b90cf4Smrg    if (idx > -1)
215424b90cf4Smrg        blob = drmModeGetPropertyBlob(fd, koutput->prop_values[idx]);
215524b90cf4Smrg
215624b90cf4Smrg    return blob;
215724b90cf4Smrg}
215824b90cf4Smrg
2159d6c0b56eSmrgstatic DisplayModePtr drmmode_output_get_modes(xf86OutputPtr output)
2160d6c0b56eSmrg{
2161d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2162d6c0b56eSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
2163d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
2164d6c0b56eSmrg	int i;
2165d6c0b56eSmrg	DisplayModePtr Modes = NULL, Mode;
2166d6c0b56eSmrg	xf86MonPtr mon = NULL;
2167d6c0b56eSmrg
2168d6c0b56eSmrg	if (!koutput)
2169d6c0b56eSmrg		return NULL;
2170d6c0b56eSmrg
217124b90cf4Smrg	drmModeFreePropertyBlob(drmmode_output->edid_blob);
217224b90cf4Smrg
2173d6c0b56eSmrg	/* look for an EDID property */
217424b90cf4Smrg	drmmode_output->edid_blob =
217524b90cf4Smrg		koutput_get_prop_blob(pAMDGPUEnt->fd, koutput, "EDID");
2176d6c0b56eSmrg
2177d6c0b56eSmrg	if (drmmode_output->edid_blob) {
2178d6c0b56eSmrg		mon = xf86InterpretEDID(output->scrn->scrnIndex,
2179d6c0b56eSmrg					drmmode_output->edid_blob->data);
2180d6c0b56eSmrg		if (mon && drmmode_output->edid_blob->length > 128)
2181d6c0b56eSmrg			mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
2182d6c0b56eSmrg	}
2183d6c0b56eSmrg	xf86OutputSetEDID(output, mon);
2184d6c0b56eSmrg
2185852bcc3bSmrg	drmmode_output_attach_tile(output);
2186852bcc3bSmrg
2187d6c0b56eSmrg	/* modes should already be available */
2188d6c0b56eSmrg	for (i = 0; i < koutput->count_modes; i++) {
2189d6c0b56eSmrg		Mode = xnfalloc(sizeof(DisplayModeRec));
2190d6c0b56eSmrg
2191d6c0b56eSmrg		drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i],
2192d6c0b56eSmrg					 Mode);
2193d6c0b56eSmrg		Modes = xf86ModesAdd(Modes, Mode);
2194d6c0b56eSmrg
2195d6c0b56eSmrg	}
2196d6c0b56eSmrg	return Modes;
2197d6c0b56eSmrg}
2198d6c0b56eSmrg
2199d6c0b56eSmrgstatic void drmmode_output_destroy(xf86OutputPtr output)
2200d6c0b56eSmrg{
2201d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2202d6c0b56eSmrg	int i;
2203d6c0b56eSmrg
2204852bcc3bSmrg	drmModeFreePropertyBlob(drmmode_output->edid_blob);
2205852bcc3bSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1, 17, 99, 901, 0)
2206852bcc3bSmrg	drmModeFreePropertyBlob(drmmode_output->tile_blob);
2207852bcc3bSmrg#endif
2208852bcc3bSmrg
2209d6c0b56eSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
2210d6c0b56eSmrg		drmModeFreeProperty(drmmode_output->props[i].mode_prop);
2211d6c0b56eSmrg		free(drmmode_output->props[i].atoms);
2212d6c0b56eSmrg	}
2213d6c0b56eSmrg	for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) {
2214d6c0b56eSmrg		drmModeFreeEncoder(drmmode_output->mode_encoders[i]);
2215d6c0b56eSmrg	}
2216d6c0b56eSmrg	free(drmmode_output->mode_encoders);
2217d6c0b56eSmrg	free(drmmode_output->props);
2218d6c0b56eSmrg	drmModeFreeConnector(drmmode_output->mode_output);
2219d6c0b56eSmrg	free(drmmode_output);
2220d6c0b56eSmrg	output->driver_private = NULL;
2221d6c0b56eSmrg}
2222d6c0b56eSmrg
2223d6c0b56eSmrgstatic void drmmode_output_dpms(xf86OutputPtr output, int mode)
2224d6c0b56eSmrg{
2225d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2226d6c0b56eSmrg	xf86CrtcPtr crtc = output->crtc;
2227d6c0b56eSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
2228d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
2229d6c0b56eSmrg
2230d6c0b56eSmrg	if (!koutput)
2231d6c0b56eSmrg		return;
2232d6c0b56eSmrg
223324b90cf4Smrg	if (mode != DPMSModeOn && crtc)
2234d6c0b56eSmrg		drmmode_do_crtc_dpms(crtc, mode);
2235d6c0b56eSmrg
2236d6c0b56eSmrg	drmModeConnectorSetProperty(pAMDGPUEnt->fd, koutput->connector_id,
2237d6c0b56eSmrg				    drmmode_output->dpms_enum_id, mode);
2238d6c0b56eSmrg
2239d6c0b56eSmrg	if (mode == DPMSModeOn && crtc) {
2240d6c0b56eSmrg		drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2241d6c0b56eSmrg
2242d6c0b56eSmrg		if (drmmode_crtc->need_modeset)
2243d6c0b56eSmrg			drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
2244d6c0b56eSmrg					       crtc->x, crtc->y);
2245d6c0b56eSmrg		else
2246d6c0b56eSmrg			drmmode_do_crtc_dpms(output->crtc, mode);
2247d6c0b56eSmrg	}
2248d6c0b56eSmrg}
2249d6c0b56eSmrg
2250d6c0b56eSmrgstatic Bool drmmode_property_ignore(drmModePropertyPtr prop)
2251d6c0b56eSmrg{
2252d6c0b56eSmrg	if (!prop)
2253d6c0b56eSmrg		return TRUE;
2254d6c0b56eSmrg	/* ignore blob prop */
2255d6c0b56eSmrg	if (prop->flags & DRM_MODE_PROP_BLOB)
2256d6c0b56eSmrg		return TRUE;
2257d6c0b56eSmrg	/* ignore standard property */
2258d6c0b56eSmrg	if (!strcmp(prop->name, "EDID") || !strcmp(prop->name, "DPMS"))
2259d6c0b56eSmrg		return TRUE;
2260d6c0b56eSmrg
2261d6c0b56eSmrg	return FALSE;
2262d6c0b56eSmrg}
2263d6c0b56eSmrg
2264d6c0b56eSmrgstatic void drmmode_output_create_resources(xf86OutputPtr output)
2265d6c0b56eSmrg{
226611bf0794Smrg	AMDGPUInfoPtr info = AMDGPUPTR(output->scrn);
2267d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
226835d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc;
2269d6c0b56eSmrg	drmModeConnectorPtr mode_output = drmmode_output->mode_output;
2270d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
227111bf0794Smrg	drmModePropertyPtr drmmode_prop, tearfree_prop;
2272d6c0b56eSmrg	int i, j, err;
227335d5b7c7Smrg	Atom name;
227435d5b7c7Smrg
227535d5b7c7Smrg	/* Create CONNECTOR_ID property */
227635d5b7c7Smrg	name = MakeAtom("CONNECTOR_ID", 12, TRUE);
227735d5b7c7Smrg	if (name != BAD_RESOURCE) {
227835d5b7c7Smrg		INT32 value = mode_output->connector_id;
227935d5b7c7Smrg
228035d5b7c7Smrg		err = RRConfigureOutputProperty(output->randr_output, name,
228135d5b7c7Smrg						FALSE, FALSE, TRUE, 1, &value);
228235d5b7c7Smrg		if (err != Success) {
228335d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
228435d5b7c7Smrg				   "RRConfigureOutputProperty error, %d\n", err);
228535d5b7c7Smrg		}
228635d5b7c7Smrg
228735d5b7c7Smrg		err = RRChangeOutputProperty(output->randr_output, name,
228835d5b7c7Smrg					     XA_INTEGER, 32, PropModeReplace, 1,
228935d5b7c7Smrg					     &value, FALSE, FALSE);
229035d5b7c7Smrg		if (err != Success) {
229135d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
229235d5b7c7Smrg				   "RRChangeOutputProperty error, %d\n", err);
229335d5b7c7Smrg		}
229435d5b7c7Smrg	}
2295d6c0b56eSmrg
2296d6c0b56eSmrg	drmmode_output->props =
229711bf0794Smrg		calloc(mode_output->count_props + 1, sizeof(drmmode_prop_rec));
2298d6c0b56eSmrg	if (!drmmode_output->props)
2299d6c0b56eSmrg		return;
2300d6c0b56eSmrg
2301d6c0b56eSmrg	drmmode_output->num_props = 0;
2302d6c0b56eSmrg	for (i = 0, j = 0; i < mode_output->count_props; i++) {
2303d6c0b56eSmrg		drmmode_prop =
2304d6c0b56eSmrg		    drmModeGetProperty(pAMDGPUEnt->fd, mode_output->props[i]);
2305d6c0b56eSmrg		if (drmmode_property_ignore(drmmode_prop)) {
2306d6c0b56eSmrg			drmModeFreeProperty(drmmode_prop);
2307d6c0b56eSmrg			continue;
2308d6c0b56eSmrg		}
2309d6c0b56eSmrg		drmmode_output->props[j].mode_prop = drmmode_prop;
2310d6c0b56eSmrg		drmmode_output->props[j].value = mode_output->prop_values[i];
2311d6c0b56eSmrg		drmmode_output->num_props++;
2312d6c0b56eSmrg		j++;
2313d6c0b56eSmrg	}
2314d6c0b56eSmrg
231511bf0794Smrg	/* Userspace-only property for TearFree */
231611bf0794Smrg	tearfree_prop = calloc(1, sizeof(*tearfree_prop));
231711bf0794Smrg	tearfree_prop->flags = DRM_MODE_PROP_ENUM;
231835d5b7c7Smrg	strcpy(tearfree_prop->name, "TearFree");
231911bf0794Smrg	tearfree_prop->count_enums = 3;
232011bf0794Smrg	tearfree_prop->enums = calloc(tearfree_prop->count_enums,
232111bf0794Smrg				      sizeof(*tearfree_prop->enums));
232235d5b7c7Smrg	strcpy(tearfree_prop->enums[0].name, "off");
232335d5b7c7Smrg	strcpy(tearfree_prop->enums[1].name, "on");
232411bf0794Smrg	tearfree_prop->enums[1].value = 1;
232535d5b7c7Smrg	strcpy(tearfree_prop->enums[2].name, "auto");
232611bf0794Smrg	tearfree_prop->enums[2].value = 2;
232711bf0794Smrg	drmmode_output->props[j].mode_prop = tearfree_prop;
232811bf0794Smrg	drmmode_output->props[j].value = info->tear_free;
232911bf0794Smrg	drmmode_output->tear_free = info->tear_free;
233011bf0794Smrg	drmmode_output->num_props++;
233111bf0794Smrg
2332d6c0b56eSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
2333d6c0b56eSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
2334d6c0b56eSmrg		drmmode_prop = p->mode_prop;
2335d6c0b56eSmrg
2336d6c0b56eSmrg		if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
2337d6c0b56eSmrg			INT32 range[2];
2338d6c0b56eSmrg			INT32 value = p->value;
2339d6c0b56eSmrg
2340d6c0b56eSmrg			p->num_atoms = 1;
2341d6c0b56eSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
2342d6c0b56eSmrg			if (!p->atoms)
2343d6c0b56eSmrg				continue;
2344d6c0b56eSmrg			p->atoms[0] =
2345d6c0b56eSmrg			    MakeAtom(drmmode_prop->name,
2346d6c0b56eSmrg				     strlen(drmmode_prop->name), TRUE);
2347d6c0b56eSmrg			range[0] = drmmode_prop->values[0];
2348d6c0b56eSmrg			range[1] = drmmode_prop->values[1];
2349d6c0b56eSmrg			err =
2350d6c0b56eSmrg			    RRConfigureOutputProperty(output->randr_output,
2351d6c0b56eSmrg						      p->atoms[0], FALSE, TRUE,
2352d6c0b56eSmrg						      drmmode_prop->flags &
2353d6c0b56eSmrg						      DRM_MODE_PROP_IMMUTABLE ?
2354d6c0b56eSmrg						      TRUE : FALSE, 2, range);
2355d6c0b56eSmrg			if (err != 0) {
2356d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2357d6c0b56eSmrg					   "RRConfigureOutputProperty error, %d\n",
2358d6c0b56eSmrg					   err);
2359d6c0b56eSmrg			}
2360d6c0b56eSmrg			err =
2361d6c0b56eSmrg			    RRChangeOutputProperty(output->randr_output,
2362d6c0b56eSmrg						   p->atoms[0], XA_INTEGER, 32,
2363d6c0b56eSmrg						   PropModeReplace, 1, &value,
2364d6c0b56eSmrg						   FALSE, TRUE);
2365d6c0b56eSmrg			if (err != 0) {
2366d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2367d6c0b56eSmrg					   "RRChangeOutputProperty error, %d\n",
2368d6c0b56eSmrg					   err);
2369d6c0b56eSmrg			}
2370d6c0b56eSmrg		} else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
2371d6c0b56eSmrg			p->num_atoms = drmmode_prop->count_enums + 1;
2372d6c0b56eSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
2373d6c0b56eSmrg			if (!p->atoms)
2374d6c0b56eSmrg				continue;
2375d6c0b56eSmrg			p->atoms[0] =
2376d6c0b56eSmrg			    MakeAtom(drmmode_prop->name,
2377d6c0b56eSmrg				     strlen(drmmode_prop->name), TRUE);
2378d6c0b56eSmrg			for (j = 1; j <= drmmode_prop->count_enums; j++) {
2379d6c0b56eSmrg				struct drm_mode_property_enum *e =
2380d6c0b56eSmrg				    &drmmode_prop->enums[j - 1];
2381d6c0b56eSmrg				p->atoms[j] =
2382d6c0b56eSmrg				    MakeAtom(e->name, strlen(e->name), TRUE);
2383d6c0b56eSmrg			}
2384d6c0b56eSmrg			err =
2385d6c0b56eSmrg			    RRConfigureOutputProperty(output->randr_output,
2386d6c0b56eSmrg						      p->atoms[0], FALSE, FALSE,
2387d6c0b56eSmrg						      drmmode_prop->flags &
2388d6c0b56eSmrg						      DRM_MODE_PROP_IMMUTABLE ?
2389d6c0b56eSmrg						      TRUE : FALSE,
2390d6c0b56eSmrg						      p->num_atoms - 1,
2391d6c0b56eSmrg						      (INT32 *) & p->atoms[1]);
2392d6c0b56eSmrg			if (err != 0) {
2393d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2394d6c0b56eSmrg					   "RRConfigureOutputProperty error, %d\n",
2395d6c0b56eSmrg					   err);
2396d6c0b56eSmrg			}
2397d6c0b56eSmrg			for (j = 0; j < drmmode_prop->count_enums; j++)
2398d6c0b56eSmrg				if (drmmode_prop->enums[j].value == p->value)
2399d6c0b56eSmrg					break;
2400d6c0b56eSmrg			/* there's always a matching value */
2401d6c0b56eSmrg			err =
2402d6c0b56eSmrg			    RRChangeOutputProperty(output->randr_output,
2403d6c0b56eSmrg						   p->atoms[0], XA_ATOM, 32,
2404d6c0b56eSmrg						   PropModeReplace, 1,
2405d6c0b56eSmrg						   &p->atoms[j + 1], FALSE,
2406d6c0b56eSmrg						   TRUE);
2407d6c0b56eSmrg			if (err != 0) {
2408d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2409d6c0b56eSmrg					   "RRChangeOutputProperty error, %d\n",
2410d6c0b56eSmrg					   err);
2411d6c0b56eSmrg			}
2412d6c0b56eSmrg		}
2413d6c0b56eSmrg	}
241435d5b7c7Smrg
241535d5b7c7Smrg	/* Do not configure cm properties on output if there's no support. */
241635d5b7c7Smrg	if (!drmmode_cm_enabled(drmmode_output->drmmode))
241735d5b7c7Smrg		return;
241835d5b7c7Smrg
241935d5b7c7Smrg	drmmode_crtc = output->crtc ? output->crtc->driver_private : NULL;
242035d5b7c7Smrg
242135d5b7c7Smrg	for (i = 0; i < CM_NUM_PROPS; i++)
242235d5b7c7Smrg		rr_configure_and_change_cm_property(output, drmmode_crtc, i);
242335d5b7c7Smrg}
242435d5b7c7Smrg
242535d5b7c7Smrgstatic void
242635d5b7c7Smrgdrmmode_output_set_tear_free(AMDGPUEntPtr pAMDGPUEnt,
242735d5b7c7Smrg			     drmmode_output_private_ptr drmmode_output,
242835d5b7c7Smrg			     xf86CrtcPtr crtc, int tear_free)
242935d5b7c7Smrg{
243035d5b7c7Smrg	if (drmmode_output->tear_free == tear_free)
243135d5b7c7Smrg		return;
243235d5b7c7Smrg
243335d5b7c7Smrg	drmmode_output->tear_free = tear_free;
243435d5b7c7Smrg
243535d5b7c7Smrg	if (crtc) {
243635d5b7c7Smrg		drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
243735d5b7c7Smrg				       crtc->x, crtc->y);
243835d5b7c7Smrg	}
2439d6c0b56eSmrg}
2440d6c0b56eSmrg
2441d6c0b56eSmrgstatic Bool
2442d6c0b56eSmrgdrmmode_output_set_property(xf86OutputPtr output, Atom property,
2443d6c0b56eSmrg			    RRPropertyValuePtr value)
2444d6c0b56eSmrg{
2445d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2446d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
244735d5b7c7Smrg	enum drmmode_cm_prop cm_prop_index;
2448d6c0b56eSmrg	int i;
2449d6c0b56eSmrg
245035d5b7c7Smrg	cm_prop_index = get_cm_enum_from_str(NameForAtom(property));
245135d5b7c7Smrg	if (cm_prop_index >= 0 && cm_prop_index < CM_DEGAMMA_LUT_SIZE) {
245235d5b7c7Smrg		if (!output->crtc)
245335d5b7c7Smrg			return FALSE;
245435d5b7c7Smrg		if (drmmode_crtc_stage_cm_prop(output->crtc, cm_prop_index,
245535d5b7c7Smrg					       value))
245635d5b7c7Smrg			return FALSE;
245735d5b7c7Smrg		if (drmmode_crtc_push_cm_prop(output->crtc, cm_prop_index))
245835d5b7c7Smrg			return FALSE;
245935d5b7c7Smrg		return TRUE;
246035d5b7c7Smrg	}
246135d5b7c7Smrg
2462d6c0b56eSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
2463d6c0b56eSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
2464d6c0b56eSmrg
2465d6c0b56eSmrg		if (p->atoms[0] != property)
2466d6c0b56eSmrg			continue;
2467d6c0b56eSmrg
2468d6c0b56eSmrg		if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
2469d6c0b56eSmrg			uint32_t val;
2470d6c0b56eSmrg
2471d6c0b56eSmrg			if (value->type != XA_INTEGER || value->format != 32 ||
2472d6c0b56eSmrg			    value->size != 1)
2473d6c0b56eSmrg				return FALSE;
2474d6c0b56eSmrg			val = *(uint32_t *) value->data;
2475d6c0b56eSmrg
2476d6c0b56eSmrg			drmModeConnectorSetProperty(pAMDGPUEnt->fd,
2477d6c0b56eSmrg						    drmmode_output->output_id,
2478d6c0b56eSmrg						    p->mode_prop->prop_id,
2479d6c0b56eSmrg						    (uint64_t) val);
2480d6c0b56eSmrg			return TRUE;
2481d6c0b56eSmrg		} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
2482d6c0b56eSmrg			Atom atom;
2483d6c0b56eSmrg			const char *name;
2484d6c0b56eSmrg			int j;
2485d6c0b56eSmrg
2486d6c0b56eSmrg			if (value->type != XA_ATOM || value->format != 32
2487d6c0b56eSmrg			    || value->size != 1)
2488d6c0b56eSmrg				return FALSE;
2489d6c0b56eSmrg			memcpy(&atom, value->data, 4);
249024b90cf4Smrg			if (!(name = NameForAtom(atom)))
249124b90cf4Smrg				return FALSE;
2492d6c0b56eSmrg
2493d6c0b56eSmrg			/* search for matching name string, then set its value down */
2494d6c0b56eSmrg			for (j = 0; j < p->mode_prop->count_enums; j++) {
2495d6c0b56eSmrg				if (!strcmp(p->mode_prop->enums[j].name, name)) {
249611bf0794Smrg					if (i == (drmmode_output->num_props - 1)) {
249735d5b7c7Smrg						drmmode_output_set_tear_free(pAMDGPUEnt,
249835d5b7c7Smrg									     drmmode_output,
249935d5b7c7Smrg									     output->crtc, j);
250011bf0794Smrg					} else {
250111bf0794Smrg						drmModeConnectorSetProperty(pAMDGPUEnt->fd,
250211bf0794Smrg									    drmmode_output->output_id,
250311bf0794Smrg									    p->mode_prop->prop_id,
250411bf0794Smrg									    p->mode_prop->enums[j].value);
250511bf0794Smrg					}
250611bf0794Smrg
2507d6c0b56eSmrg					return TRUE;
2508d6c0b56eSmrg				}
2509d6c0b56eSmrg			}
2510d6c0b56eSmrg		}
2511d6c0b56eSmrg	}
2512d6c0b56eSmrg
2513d6c0b56eSmrg	return TRUE;
2514d6c0b56eSmrg}
2515d6c0b56eSmrg
2516d6c0b56eSmrgstatic Bool drmmode_output_get_property(xf86OutputPtr output, Atom property)
2517d6c0b56eSmrg{
251835d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc;
251935d5b7c7Smrg	enum drmmode_cm_prop cm_prop_id;
252035d5b7c7Smrg	int ret;
252135d5b7c7Smrg
252235d5b7c7Smrg	/* First, see if it's a cm property */
252335d5b7c7Smrg	cm_prop_id = get_cm_enum_from_str(NameForAtom(property));
252435d5b7c7Smrg	if (output->crtc && cm_prop_id != CM_INVALID_PROP) {
252535d5b7c7Smrg		drmmode_crtc = output->crtc->driver_private;
252635d5b7c7Smrg
252735d5b7c7Smrg		ret = rr_configure_and_change_cm_property(output, drmmode_crtc,
252835d5b7c7Smrg							  cm_prop_id);
252935d5b7c7Smrg		if (ret) {
253035d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
253135d5b7c7Smrg				   "Error getting color property: %d\n",
253235d5b7c7Smrg				   ret);
253335d5b7c7Smrg			return FALSE;
253435d5b7c7Smrg		}
253535d5b7c7Smrg		return TRUE;
253635d5b7c7Smrg	}
253735d5b7c7Smrg
253835d5b7c7Smrg	/* Otherwise, must be an output property. */
2539d6c0b56eSmrg	return TRUE;
2540d6c0b56eSmrg}
2541d6c0b56eSmrg
2542d6c0b56eSmrgstatic const xf86OutputFuncsRec drmmode_output_funcs = {
2543d6c0b56eSmrg	.dpms = drmmode_output_dpms,
2544d6c0b56eSmrg	.create_resources = drmmode_output_create_resources,
2545d6c0b56eSmrg	.set_property = drmmode_output_set_property,
2546d6c0b56eSmrg	.get_property = drmmode_output_get_property,
2547d6c0b56eSmrg	.detect = drmmode_output_detect,
2548d6c0b56eSmrg	.mode_valid = drmmode_output_mode_valid,
2549d6c0b56eSmrg
2550d6c0b56eSmrg	.get_modes = drmmode_output_get_modes,
2551d6c0b56eSmrg	.destroy = drmmode_output_destroy
2552d6c0b56eSmrg};
2553d6c0b56eSmrg
2554d6c0b56eSmrgstatic int subpixel_conv_table[7] = { 0, SubPixelUnknown,
2555d6c0b56eSmrg	SubPixelHorizontalRGB,
2556d6c0b56eSmrg	SubPixelHorizontalBGR,
2557d6c0b56eSmrg	SubPixelVerticalRGB,
2558d6c0b56eSmrg	SubPixelVerticalBGR,
2559d6c0b56eSmrg	SubPixelNone
2560d6c0b56eSmrg};
2561d6c0b56eSmrg
2562d6c0b56eSmrgconst char *output_names[] = { "None",
2563d6c0b56eSmrg	"VGA",
2564d6c0b56eSmrg	"DVI-I",
2565d6c0b56eSmrg	"DVI-D",
2566d6c0b56eSmrg	"DVI-A",
2567d6c0b56eSmrg	"Composite",
2568d6c0b56eSmrg	"S-video",
2569d6c0b56eSmrg	"LVDS",
2570d6c0b56eSmrg	"CTV",
2571d6c0b56eSmrg	"DIN",
2572d6c0b56eSmrg	"DisplayPort",
2573d6c0b56eSmrg	"HDMI-A",
2574d6c0b56eSmrg	"HDMI-B",
2575d6c0b56eSmrg	"TV",
2576d6c0b56eSmrg	"eDP",
2577d6c0b56eSmrg	"Virtual",
2578d6c0b56eSmrg	"DSI",
2579d6c0b56eSmrg};
2580d6c0b56eSmrg
2581d6c0b56eSmrg#define NUM_OUTPUT_NAMES (sizeof(output_names) / sizeof(output_names[0]))
2582d6c0b56eSmrg
2583d6c0b56eSmrgstatic xf86OutputPtr find_output(ScrnInfoPtr pScrn, int id)
2584d6c0b56eSmrg{
2585d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
2586d6c0b56eSmrg	int i;
2587d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2588d6c0b56eSmrg		xf86OutputPtr output = xf86_config->output[i];
2589d6c0b56eSmrg		drmmode_output_private_ptr drmmode_output;
2590d6c0b56eSmrg		drmmode_output = output->driver_private;
2591d6c0b56eSmrg		if (drmmode_output->output_id == id)
2592d6c0b56eSmrg			return output;
2593d6c0b56eSmrg	}
2594d6c0b56eSmrg	return NULL;
2595d6c0b56eSmrg}
2596d6c0b56eSmrg
2597d6c0b56eSmrgstatic int parse_path_blob(drmModePropertyBlobPtr path_blob, int *conn_base_id, char **path)
2598d6c0b56eSmrg{
2599d6c0b56eSmrg	char *conn;
2600d6c0b56eSmrg	char conn_id[5];
2601d6c0b56eSmrg	int id, len;
2602d6c0b56eSmrg	char *blob_data;
2603d6c0b56eSmrg
2604d6c0b56eSmrg	if (!path_blob)
2605d6c0b56eSmrg		return -1;
2606d6c0b56eSmrg
2607d6c0b56eSmrg	blob_data = path_blob->data;
2608d6c0b56eSmrg	/* we only handle MST paths for now */
2609d6c0b56eSmrg	if (strncmp(blob_data, "mst:", 4))
2610d6c0b56eSmrg		return -1;
2611d6c0b56eSmrg
2612d6c0b56eSmrg	conn = strchr(blob_data + 4, '-');
2613d6c0b56eSmrg	if (!conn)
2614d6c0b56eSmrg		return -1;
2615d6c0b56eSmrg	len = conn - (blob_data + 4);
2616d6c0b56eSmrg	if (len + 1 > 5)
2617d6c0b56eSmrg		return -1;
2618d6c0b56eSmrg	memcpy(conn_id, blob_data + 4, len);
2619d6c0b56eSmrg	conn_id[len] = '\0';
2620d6c0b56eSmrg	id = strtoul(conn_id, NULL, 10);
2621d6c0b56eSmrg
2622d6c0b56eSmrg	*conn_base_id = id;
2623d6c0b56eSmrg
2624d6c0b56eSmrg	*path = conn + 1;
2625d6c0b56eSmrg	return 0;
2626d6c0b56eSmrg}
2627d6c0b56eSmrg
2628d6c0b56eSmrgstatic void
2629d6c0b56eSmrgdrmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
2630d6c0b56eSmrg		    drmModePropertyBlobPtr path_blob, int *num_dvi, int *num_hdmi)
2631d6c0b56eSmrg{
2632d6c0b56eSmrg	xf86OutputPtr output;
2633d6c0b56eSmrg	int conn_id;
2634d6c0b56eSmrg	char *extra_path;
2635d6c0b56eSmrg
2636d6c0b56eSmrg	output = NULL;
2637d6c0b56eSmrg	if (parse_path_blob(path_blob, &conn_id, &extra_path) == 0)
2638d6c0b56eSmrg		output = find_output(pScrn, conn_id);
2639d6c0b56eSmrg	if (output) {
2640d6c0b56eSmrg		snprintf(name, 32, "%s-%s", output->name, extra_path);
2641d6c0b56eSmrg	} else {
264224b90cf4Smrg		if (koutput->connector_type >= NUM_OUTPUT_NAMES) {
2643d6c0b56eSmrg			snprintf(name, 32, "Unknown%d-%d", koutput->connector_type, koutput->connector_type_id - 1);
264424b90cf4Smrg		} else if (pScrn->is_gpu) {
2645d6c0b56eSmrg			snprintf(name, 32, "%s-%d-%d", output_names[koutput->connector_type],
2646d6c0b56eSmrg				 pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, koutput->connector_type_id - 1);
264724b90cf4Smrg		} else {
2648d6c0b56eSmrg			/* need to do smart conversion here for compat with non-kms ATI driver */
2649d6c0b56eSmrg			if (koutput->connector_type_id == 1) {
2650d6c0b56eSmrg				switch(koutput->connector_type) {
2651d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DVII:
2652d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DVID:
2653d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DVIA:
2654d6c0b56eSmrg					snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], *num_dvi);
2655d6c0b56eSmrg					(*num_dvi)++;
2656d6c0b56eSmrg					break;
2657d6c0b56eSmrg				case DRM_MODE_CONNECTOR_HDMIA:
2658d6c0b56eSmrg				case DRM_MODE_CONNECTOR_HDMIB:
2659d6c0b56eSmrg					snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], *num_hdmi);
2660d6c0b56eSmrg					(*num_hdmi)++;
2661d6c0b56eSmrg					break;
2662d6c0b56eSmrg				case DRM_MODE_CONNECTOR_VGA:
2663d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DisplayPort:
2664d6c0b56eSmrg					snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id - 1);
2665d6c0b56eSmrg					break;
2666d6c0b56eSmrg				default:
2667d6c0b56eSmrg					snprintf(name, 32, "%s", output_names[koutput->connector_type]);
2668d6c0b56eSmrg					break;
2669d6c0b56eSmrg				}
2670d6c0b56eSmrg			} else {
2671d6c0b56eSmrg				snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id - 1);
2672d6c0b56eSmrg			}
2673d6c0b56eSmrg		}
2674d6c0b56eSmrg	}
2675d6c0b56eSmrg}
2676d6c0b56eSmrg
2677d6c0b56eSmrg
2678d6c0b56eSmrgstatic unsigned int
2679d6c0b56eSmrgdrmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num, int *num_dvi, int *num_hdmi, int dynamic)
2680d6c0b56eSmrg{
2681d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
2682d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
2683d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
2684d6c0b56eSmrg	xf86OutputPtr output;
2685d6c0b56eSmrg	drmModeConnectorPtr koutput;
2686d6c0b56eSmrg	drmModeEncoderPtr *kencoders = NULL;
2687d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output;
2688d6c0b56eSmrg	drmModePropertyBlobPtr path_blob = NULL;
268935d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
269035d5b7c7Smrg	Bool nonDesktop = FALSE;
269135d5b7c7Smrg#endif
2692d6c0b56eSmrg	char name[32];
2693d6c0b56eSmrg	int i;
2694d6c0b56eSmrg	const char *s;
2695d6c0b56eSmrg
2696d6c0b56eSmrg	koutput =
2697d6c0b56eSmrg	    drmModeGetConnector(pAMDGPUEnt->fd,
2698d6c0b56eSmrg				mode_res->connectors[num]);
2699d6c0b56eSmrg	if (!koutput)
2700d6c0b56eSmrg		return 0;
2701d6c0b56eSmrg
270224b90cf4Smrg	path_blob = koutput_get_prop_blob(pAMDGPUEnt->fd, koutput, "PATH");
2703d6c0b56eSmrg
270435d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
270535d5b7c7Smrg	i = koutput_get_prop_idx(pAMDGPUEnt->fd, koutput, DRM_MODE_PROP_RANGE,
270635d5b7c7Smrg				 "non-desktop");
270735d5b7c7Smrg	if (i >= 0)
270835d5b7c7Smrg        	nonDesktop = koutput->prop_values[i] != 0;
270935d5b7c7Smrg#endif
271035d5b7c7Smrg
2711d6c0b56eSmrg	kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders);
2712d6c0b56eSmrg	if (!kencoders) {
2713d6c0b56eSmrg		goto out_free_encoders;
2714d6c0b56eSmrg	}
2715d6c0b56eSmrg
2716d6c0b56eSmrg	for (i = 0; i < koutput->count_encoders; i++) {
2717d6c0b56eSmrg		kencoders[i] =
2718d6c0b56eSmrg		    drmModeGetEncoder(pAMDGPUEnt->fd, koutput->encoders[i]);
2719d6c0b56eSmrg		if (!kencoders[i]) {
2720d6c0b56eSmrg			goto out_free_encoders;
2721d6c0b56eSmrg		}
2722d6c0b56eSmrg	}
2723d6c0b56eSmrg
2724d6c0b56eSmrg	drmmode_create_name(pScrn, koutput, name, path_blob, num_dvi, num_hdmi);
2725d6c0b56eSmrg	if (path_blob) {
2726d6c0b56eSmrg		drmModeFreePropertyBlob(path_blob);
2727d6c0b56eSmrg	}
2728d6c0b56eSmrg
2729d6c0b56eSmrg	if (path_blob && dynamic) {
2730d6c0b56eSmrg		/* See if we have an output with this name already
2731d6c0b56eSmrg		 * and hook stuff up.
2732d6c0b56eSmrg		 */
2733d6c0b56eSmrg		for (i = 0; i < xf86_config->num_output; i++) {
2734d6c0b56eSmrg			output = xf86_config->output[i];
2735d6c0b56eSmrg
2736d6c0b56eSmrg			if (strncmp(output->name, name, 32))
2737d6c0b56eSmrg				continue;
2738d6c0b56eSmrg
2739d6c0b56eSmrg			drmmode_output = output->driver_private;
2740d6c0b56eSmrg			drmmode_output->output_id = mode_res->connectors[num];
2741d6c0b56eSmrg			drmmode_output->mode_output = koutput;
274235d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
274335d5b7c7Smrg			output->non_desktop = nonDesktop;
274435d5b7c7Smrg#endif
2745d6c0b56eSmrg			for (i = 0; i < koutput->count_encoders; i++) {
2746d6c0b56eSmrg				drmModeFreeEncoder(kencoders[i]);
2747d6c0b56eSmrg			}
2748d6c0b56eSmrg			free(kencoders);
2749d6c0b56eSmrg			return 1;
2750d6c0b56eSmrg		}
2751d6c0b56eSmrg	}
2752d6c0b56eSmrg
2753d6c0b56eSmrg	if (xf86IsEntityShared(pScrn->entityList[0])) {
2754d6c0b56eSmrg		if ((s =
2755d6c0b56eSmrg		     xf86GetOptValString(info->Options, OPTION_ZAPHOD_HEADS))) {
2756d6c0b56eSmrg			if (!AMDGPUZaphodStringMatches(pScrn, s, name))
2757d6c0b56eSmrg				goto out_free_encoders;
2758d6c0b56eSmrg		} else {
275990f2b693Smrg			if (info->instance_id != num)
2760d6c0b56eSmrg				goto out_free_encoders;
2761d6c0b56eSmrg		}
2762d6c0b56eSmrg	}
2763d6c0b56eSmrg
2764d6c0b56eSmrg	output = xf86OutputCreate(pScrn, &drmmode_output_funcs, name);
2765d6c0b56eSmrg	if (!output) {
2766d6c0b56eSmrg		goto out_free_encoders;
2767d6c0b56eSmrg	}
2768d6c0b56eSmrg
2769d6c0b56eSmrg	drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
2770d6c0b56eSmrg	if (!drmmode_output) {
2771d6c0b56eSmrg		xf86OutputDestroy(output);
2772d6c0b56eSmrg		goto out_free_encoders;
2773d6c0b56eSmrg	}
2774d6c0b56eSmrg
2775d6c0b56eSmrg	drmmode_output->output_id = mode_res->connectors[num];
2776d6c0b56eSmrg	drmmode_output->mode_output = koutput;
2777d6c0b56eSmrg	drmmode_output->mode_encoders = kencoders;
2778d6c0b56eSmrg	drmmode_output->drmmode = drmmode;
2779d6c0b56eSmrg	output->mm_width = koutput->mmWidth;
2780d6c0b56eSmrg	output->mm_height = koutput->mmHeight;
2781d6c0b56eSmrg
2782d6c0b56eSmrg	output->subpixel_order = subpixel_conv_table[koutput->subpixel];
2783d6c0b56eSmrg	output->interlaceAllowed = TRUE;
2784d6c0b56eSmrg	output->doubleScanAllowed = TRUE;
2785d6c0b56eSmrg	output->driver_private = drmmode_output;
278635d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
278735d5b7c7Smrg	output->non_desktop = nonDesktop;
278835d5b7c7Smrg#endif
2789d6c0b56eSmrg
2790d6c0b56eSmrg	output->possible_crtcs = 0xffffffff;
2791d6c0b56eSmrg	for (i = 0; i < koutput->count_encoders; i++) {
2792d6c0b56eSmrg		output->possible_crtcs &= kencoders[i]->possible_crtcs;
2793d6c0b56eSmrg	}
2794d6c0b56eSmrg	/* work out the possible clones later */
2795d6c0b56eSmrg	output->possible_clones = 0;
2796d6c0b56eSmrg
279724b90cf4Smrg	drmmode_output->dpms_enum_id =
279824b90cf4Smrg		koutput_get_prop_id(pAMDGPUEnt->fd, koutput, DRM_MODE_PROP_ENUM,
279924b90cf4Smrg				    "DPMS");
2800d6c0b56eSmrg
2801d6c0b56eSmrg	if (dynamic) {
2802d6c0b56eSmrg		output->randr_output = RROutputCreate(xf86ScrnToScreen(pScrn), output->name, strlen(output->name), output);
2803d6c0b56eSmrg		drmmode_output_create_resources(output);
2804d6c0b56eSmrg	}
2805d6c0b56eSmrg
2806d6c0b56eSmrg	return 1;
2807d6c0b56eSmrgout_free_encoders:
2808d6c0b56eSmrg	if (kencoders) {
2809d6c0b56eSmrg		for (i = 0; i < koutput->count_encoders; i++)
2810d6c0b56eSmrg			drmModeFreeEncoder(kencoders[i]);
2811d6c0b56eSmrg		free(kencoders);
2812d6c0b56eSmrg	}
2813d6c0b56eSmrg	drmModeFreeConnector(koutput);
2814d6c0b56eSmrg	return 0;
2815d6c0b56eSmrg}
2816d6c0b56eSmrg
2817d6c0b56eSmrguint32_t find_clones(ScrnInfoPtr scrn, xf86OutputPtr output)
2818d6c0b56eSmrg{
2819d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output =
2820d6c0b56eSmrg	    output->driver_private, clone_drmout;
2821d6c0b56eSmrg	int i;
2822d6c0b56eSmrg	xf86OutputPtr clone_output;
2823d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2824d6c0b56eSmrg	int index_mask = 0;
2825d6c0b56eSmrg
2826d6c0b56eSmrg	if (drmmode_output->enc_clone_mask == 0)
2827d6c0b56eSmrg		return index_mask;
2828d6c0b56eSmrg
2829d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2830d6c0b56eSmrg		clone_output = xf86_config->output[i];
2831d6c0b56eSmrg		clone_drmout = clone_output->driver_private;
2832d6c0b56eSmrg		if (output == clone_output)
2833d6c0b56eSmrg			continue;
2834d6c0b56eSmrg
2835d6c0b56eSmrg		if (clone_drmout->enc_mask == 0)
2836d6c0b56eSmrg			continue;
2837d6c0b56eSmrg		if (drmmode_output->enc_clone_mask == clone_drmout->enc_mask)
2838d6c0b56eSmrg			index_mask |= (1 << i);
2839d6c0b56eSmrg	}
2840d6c0b56eSmrg	return index_mask;
2841d6c0b56eSmrg}
2842d6c0b56eSmrg
2843d6c0b56eSmrgstatic void drmmode_clones_init(ScrnInfoPtr scrn, drmmode_ptr drmmode, drmModeResPtr mode_res)
2844d6c0b56eSmrg{
2845d6c0b56eSmrg	int i, j;
2846d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2847d6c0b56eSmrg
2848d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2849d6c0b56eSmrg		xf86OutputPtr output = xf86_config->output[i];
2850d6c0b56eSmrg		drmmode_output_private_ptr drmmode_output;
2851d6c0b56eSmrg
2852d6c0b56eSmrg		drmmode_output = output->driver_private;
2853d6c0b56eSmrg		drmmode_output->enc_clone_mask = 0xff;
2854d6c0b56eSmrg		/* and all the possible encoder clones for this output together */
2855d6c0b56eSmrg		for (j = 0; j < drmmode_output->mode_output->count_encoders;
2856d6c0b56eSmrg		     j++) {
2857d6c0b56eSmrg			int k;
2858d6c0b56eSmrg			for (k = 0; k < mode_res->count_encoders; k++) {
2859d6c0b56eSmrg				if (mode_res->encoders[k] ==
2860d6c0b56eSmrg				    drmmode_output->
2861d6c0b56eSmrg				    mode_encoders[j]->encoder_id)
2862d6c0b56eSmrg					drmmode_output->enc_mask |= (1 << k);
2863d6c0b56eSmrg			}
2864d6c0b56eSmrg
2865d6c0b56eSmrg			drmmode_output->enc_clone_mask &=
2866d6c0b56eSmrg			    drmmode_output->mode_encoders[j]->possible_clones;
2867d6c0b56eSmrg		}
2868d6c0b56eSmrg	}
2869d6c0b56eSmrg
2870d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2871d6c0b56eSmrg		xf86OutputPtr output = xf86_config->output[i];
2872d6c0b56eSmrg		output->possible_clones = find_clones(scrn, output);
2873d6c0b56eSmrg	}
2874d6c0b56eSmrg}
2875d6c0b56eSmrg
2876d6c0b56eSmrg/* returns pitch alignment in pixels */
2877d6c0b56eSmrgint drmmode_get_pitch_align(ScrnInfoPtr scrn, int bpe)
2878d6c0b56eSmrg{
2879d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
2880d6c0b56eSmrg
2881d6c0b56eSmrg	if (info->have_tiling_info)
2882d6c0b56eSmrg		/* linear aligned requirements */
2883d6c0b56eSmrg		return MAX(64, info->group_bytes / bpe);
2884d6c0b56eSmrg	else
2885d6c0b56eSmrg		/* default to 512 elements if we don't know the real
2886d6c0b56eSmrg		 * group size otherwise the kernel may reject the CS
2887d6c0b56eSmrg		 * if the group sizes don't match as the pitch won't
2888d6c0b56eSmrg		 * be aligned properly.
2889d6c0b56eSmrg		 */
2890d6c0b56eSmrg		return 512;
2891d6c0b56eSmrg}
2892d6c0b56eSmrg
2893d6c0b56eSmrgstatic Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
2894d6c0b56eSmrg{
2895d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2896d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
2897d6c0b56eSmrg	struct amdgpu_buffer *old_front = NULL;
2898d6c0b56eSmrg	ScreenPtr screen = xf86ScrnToScreen(scrn);
2899d6c0b56eSmrg	int i, pitch, old_width, old_height, old_pitch;
2900d6c0b56eSmrg	int cpp = info->pixel_bytes;
2901d6c0b56eSmrg	PixmapPtr ppix = screen->GetScreenPixmap(screen);
290246845023Smrg	int hint = AMDGPU_CREATE_PIXMAP_SCANOUT;
2903d6c0b56eSmrg	void *fb_shadow;
2904d6c0b56eSmrg
2905d6c0b56eSmrg	if (scrn->virtualX == width && scrn->virtualY == height)
2906d6c0b56eSmrg		return TRUE;
2907d6c0b56eSmrg
290835d5b7c7Smrg	if (width > xf86_config->maxWidth || height > xf86_config->maxHeight) {
290935d5b7c7Smrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
291035d5b7c7Smrg			   "Xorg tried resizing screen to %dx%d, but maximum "
291135d5b7c7Smrg			   "supported is %dx%d\n", width, height,
291235d5b7c7Smrg			   xf86_config->maxWidth, xf86_config->maxHeight);
291335d5b7c7Smrg		return FALSE;
291435d5b7c7Smrg	}
291535d5b7c7Smrg
2916d6c0b56eSmrg	if (info->shadow_primary)
291746845023Smrg		hint |= AMDGPU_CREATE_PIXMAP_LINEAR | AMDGPU_CREATE_PIXMAP_GTT;
2918d6c0b56eSmrg	else if (!info->use_glamor)
291946845023Smrg		hint |= AMDGPU_CREATE_PIXMAP_LINEAR;
2920d6c0b56eSmrg
2921d6c0b56eSmrg	xf86DrvMsg(scrn->scrnIndex, X_INFO,
2922d6c0b56eSmrg		   "Allocate new frame buffer %dx%d\n", width, height);
2923d6c0b56eSmrg
2924d6c0b56eSmrg	old_width = scrn->virtualX;
2925d6c0b56eSmrg	old_height = scrn->virtualY;
2926d6c0b56eSmrg	old_pitch = scrn->displayWidth;
2927d6c0b56eSmrg	old_front = info->front_buffer;
2928d6c0b56eSmrg
2929d6c0b56eSmrg	scrn->virtualX = width;
2930d6c0b56eSmrg	scrn->virtualY = height;
2931d6c0b56eSmrg
2932d6c0b56eSmrg	info->front_buffer =
2933d6c0b56eSmrg		amdgpu_alloc_pixmap_bo(scrn, scrn->virtualX, scrn->virtualY,
2934d6c0b56eSmrg				       scrn->depth, hint, scrn->bitsPerPixel,
2935d6c0b56eSmrg				       &pitch);
2936d6c0b56eSmrg	if (!info->front_buffer) {
2937d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2938d6c0b56eSmrg			   "Failed to allocate front buffer memory\n");
2939d6c0b56eSmrg		goto fail;
2940d6c0b56eSmrg	}
2941d6c0b56eSmrg
2942d6c0b56eSmrg	if (!info->use_glamor && amdgpu_bo_map(scrn, info->front_buffer) != 0) {
2943d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2944d6c0b56eSmrg			   "Failed to map front buffer memory\n");
2945d6c0b56eSmrg		goto fail;
2946d6c0b56eSmrg	}
2947d6c0b56eSmrg
2948d6c0b56eSmrg	xf86DrvMsg(scrn->scrnIndex, X_INFO, " => pitch %d bytes\n", pitch);
2949d6c0b56eSmrg	scrn->displayWidth = pitch / cpp;
2950d6c0b56eSmrg
2951d6c0b56eSmrg	if (info->use_glamor ||
2952d6c0b56eSmrg	    (info->front_buffer->flags & AMDGPU_BO_FLAGS_GBM)) {
2953d6c0b56eSmrg		screen->ModifyPixmapHeader(ppix,
2954d6c0b56eSmrg					   width, height, -1, -1, pitch, info->front_buffer->cpu_ptr);
2955d6c0b56eSmrg	} else {
2956d6c0b56eSmrg		fb_shadow = calloc(1, pitch * scrn->virtualY);
295735d5b7c7Smrg		if (!fb_shadow)
2958d6c0b56eSmrg			goto fail;
2959d6c0b56eSmrg		free(info->fb_shadow);
2960d6c0b56eSmrg		info->fb_shadow = fb_shadow;
2961d6c0b56eSmrg		screen->ModifyPixmapHeader(ppix,
2962d6c0b56eSmrg					   width, height, -1, -1, pitch,
2963d6c0b56eSmrg					   info->fb_shadow);
2964d6c0b56eSmrg	}
2965d6c0b56eSmrg
2966504d986fSmrg	if (!amdgpu_glamor_create_screen_resources(scrn->pScreen))
2967504d986fSmrg		goto fail;
2968504d986fSmrg
296990f2b693Smrg	if (info->use_glamor || info->dri2.enabled) {
2970504d986fSmrg		if (!amdgpu_set_pixmap_bo(ppix, info->front_buffer))
2971504d986fSmrg			goto fail;
2972504d986fSmrg	}
2973d6c0b56eSmrg
297424b90cf4Smrg	amdgpu_pixmap_clear(ppix);
2975d6c0b56eSmrg	amdgpu_glamor_finish(scrn);
2976d6c0b56eSmrg
2977d6c0b56eSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
2978d6c0b56eSmrg		xf86CrtcPtr crtc = xf86_config->crtc[i];
2979d6c0b56eSmrg
2980d6c0b56eSmrg		if (!crtc->enabled)
2981d6c0b56eSmrg			continue;
2982d6c0b56eSmrg
2983d6c0b56eSmrg		drmmode_set_mode_major(crtc, &crtc->mode,
2984d6c0b56eSmrg				       crtc->rotation, crtc->x, crtc->y);
2985d6c0b56eSmrg	}
2986d6c0b56eSmrg
2987d6c0b56eSmrg	if (old_front) {
2988d6c0b56eSmrg		amdgpu_bo_unref(&old_front);
2989d6c0b56eSmrg	}
2990d6c0b56eSmrg
2991d6c0b56eSmrg	return TRUE;
2992d6c0b56eSmrg
2993d6c0b56eSmrgfail:
2994d6c0b56eSmrg	if (info->front_buffer) {
2995d6c0b56eSmrg		amdgpu_bo_unref(&info->front_buffer);
2996d6c0b56eSmrg	}
2997d6c0b56eSmrg	info->front_buffer = old_front;
2998d6c0b56eSmrg	scrn->virtualX = old_width;
2999d6c0b56eSmrg	scrn->virtualY = old_height;
3000d6c0b56eSmrg	scrn->displayWidth = old_pitch;
3001d6c0b56eSmrg
3002d6c0b56eSmrg	return FALSE;
3003d6c0b56eSmrg}
3004d6c0b56eSmrg
300535d5b7c7Smrgstatic void
300635d5b7c7Smrgdrmmode_validate_leases(ScrnInfoPtr scrn)
300735d5b7c7Smrg{
300835d5b7c7Smrg#ifdef XF86_LEASE_VERSION
300935d5b7c7Smrg	ScreenPtr screen = scrn->pScreen;
301035d5b7c7Smrg	rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
301135d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
301235d5b7c7Smrg	drmModeLesseeListPtr lessees;
301335d5b7c7Smrg	RRLeasePtr lease, next;
301435d5b7c7Smrg	int l;
301535d5b7c7Smrg
301635d5b7c7Smrg	/* We can't talk to the kernel about leases when VT switched */
301735d5b7c7Smrg	if (!scrn->vtSema)
301835d5b7c7Smrg		return;
301935d5b7c7Smrg
302035d5b7c7Smrg	lessees = drmModeListLessees(pAMDGPUEnt->fd);
302135d5b7c7Smrg	if (!lessees)
302235d5b7c7Smrg		return;
302335d5b7c7Smrg
302435d5b7c7Smrg	xorg_list_for_each_entry_safe(lease, next, &scr_priv->leases, list) {
302535d5b7c7Smrg		drmmode_lease_private_ptr lease_private = lease->devPrivate;
302635d5b7c7Smrg
302735d5b7c7Smrg		for (l = 0; l < lessees->count; l++) {
302835d5b7c7Smrg			if (lessees->lessees[l] == lease_private->lessee_id)
302935d5b7c7Smrg				break;
303035d5b7c7Smrg		}
303135d5b7c7Smrg
303235d5b7c7Smrg		/* check to see if the lease has gone away */
303335d5b7c7Smrg		if (l == lessees->count) {
303435d5b7c7Smrg			free(lease_private);
303535d5b7c7Smrg			lease->devPrivate = NULL;
303635d5b7c7Smrg			xf86CrtcLeaseTerminated(lease);
303735d5b7c7Smrg		}
303835d5b7c7Smrg	}
303935d5b7c7Smrg
304035d5b7c7Smrg	free(lessees);
304135d5b7c7Smrg#endif
304235d5b7c7Smrg}
304335d5b7c7Smrg
304435d5b7c7Smrg#ifdef XF86_LEASE_VERSION
304535d5b7c7Smrg
304635d5b7c7Smrgstatic int
304735d5b7c7Smrgdrmmode_create_lease(RRLeasePtr lease, int *fd)
304835d5b7c7Smrg{
304935d5b7c7Smrg	ScreenPtr screen = lease->screen;
305035d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
305135d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
305235d5b7c7Smrg	drmmode_lease_private_ptr lease_private;
305335d5b7c7Smrg	int noutput = lease->numOutputs;
305435d5b7c7Smrg	int ncrtc = lease->numCrtcs;
305535d5b7c7Smrg	uint32_t *objects;
305635d5b7c7Smrg	size_t nobjects;
305735d5b7c7Smrg	int lease_fd;
305835d5b7c7Smrg	int c, o;
305935d5b7c7Smrg	int i;
306035d5b7c7Smrg
306135d5b7c7Smrg	nobjects = ncrtc + noutput;
306235d5b7c7Smrg	if (nobjects == 0 || nobjects > (SIZE_MAX / 4) ||
306335d5b7c7Smrg	    ncrtc > (SIZE_MAX - noutput))
306435d5b7c7Smrg		return BadValue;
306535d5b7c7Smrg
306635d5b7c7Smrg	lease_private = calloc(1, sizeof (drmmode_lease_private_rec));
306735d5b7c7Smrg	if (!lease_private)
306835d5b7c7Smrg		return BadAlloc;
306935d5b7c7Smrg
307035d5b7c7Smrg	objects = malloc(nobjects * 4);
307135d5b7c7Smrg	if (!objects) {
307235d5b7c7Smrg		free(lease_private);
307335d5b7c7Smrg		return BadAlloc;
307435d5b7c7Smrg	}
307535d5b7c7Smrg
307635d5b7c7Smrg	i = 0;
307735d5b7c7Smrg
307835d5b7c7Smrg	/* Add CRTC ids */
307935d5b7c7Smrg	for (c = 0; c < ncrtc; c++) {
308035d5b7c7Smrg		xf86CrtcPtr crtc = lease->crtcs[c]->devPrivate;
308135d5b7c7Smrg		drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
308235d5b7c7Smrg
308335d5b7c7Smrg		objects[i++] = drmmode_crtc->mode_crtc->crtc_id;
308435d5b7c7Smrg	}
308535d5b7c7Smrg
308635d5b7c7Smrg	/* Add connector ids */
308735d5b7c7Smrg	for (o = 0; o < noutput; o++) {
308835d5b7c7Smrg		xf86OutputPtr   output = lease->outputs[o]->devPrivate;
308935d5b7c7Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
309035d5b7c7Smrg
309135d5b7c7Smrg		objects[i++] = drmmode_output->mode_output->connector_id;
309235d5b7c7Smrg	}
309335d5b7c7Smrg
309435d5b7c7Smrg	/* call kernel to create lease */
309535d5b7c7Smrg	assert (i == nobjects);
309635d5b7c7Smrg
309735d5b7c7Smrg	lease_fd = drmModeCreateLease(pAMDGPUEnt->fd, objects, nobjects, 0,
309835d5b7c7Smrg				      &lease_private->lessee_id);
309935d5b7c7Smrg
310035d5b7c7Smrg	free(objects);
310135d5b7c7Smrg
310235d5b7c7Smrg	if (lease_fd < 0) {
310335d5b7c7Smrg		free(lease_private);
310435d5b7c7Smrg		return BadMatch;
310535d5b7c7Smrg	}
310635d5b7c7Smrg
310735d5b7c7Smrg	lease->devPrivate = lease_private;
310835d5b7c7Smrg
310935d5b7c7Smrg	xf86CrtcLeaseStarted(lease);
311035d5b7c7Smrg
311135d5b7c7Smrg	*fd = lease_fd;
311235d5b7c7Smrg	return Success;
311335d5b7c7Smrg}
311435d5b7c7Smrg
311535d5b7c7Smrgstatic void
311635d5b7c7Smrgdrmmode_terminate_lease(RRLeasePtr lease)
311735d5b7c7Smrg{
311835d5b7c7Smrg	drmmode_lease_private_ptr lease_private = lease->devPrivate;
311935d5b7c7Smrg	ScreenPtr screen = lease->screen;
312035d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
312135d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
312235d5b7c7Smrg
312335d5b7c7Smrg	if (drmModeRevokeLease(pAMDGPUEnt->fd, lease_private->lessee_id) == 0) {
312435d5b7c7Smrg		free(lease_private);
312535d5b7c7Smrg		lease->devPrivate = NULL;
312635d5b7c7Smrg		xf86CrtcLeaseTerminated(lease);
312735d5b7c7Smrg	}
312835d5b7c7Smrg}
312935d5b7c7Smrg
313035d5b7c7Smrg#endif // XF86_LEASE_VERSION
313135d5b7c7Smrg
3132d6c0b56eSmrgstatic const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
313335d5b7c7Smrg	.resize = drmmode_xf86crtc_resize,
313435d5b7c7Smrg#ifdef XF86_LEASE_VERSION
313535d5b7c7Smrg	.create_lease = drmmode_create_lease,
313635d5b7c7Smrg	.terminate_lease = drmmode_terminate_lease
313735d5b7c7Smrg#endif
3138d6c0b56eSmrg};
3139d6c0b56eSmrg
3140d6c0b56eSmrgstatic void
3141d6c0b56eSmrgdrmmode_flip_abort(xf86CrtcPtr crtc, void *event_data)
3142d6c0b56eSmrg{
314324b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
314424b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
3145d6c0b56eSmrg	drmmode_flipdata_ptr flipdata = event_data;
314635d5b7c7Smrg	int crtc_id = drmmode_get_crtc_id(crtc);
314735d5b7c7Smrg	struct drmmode_fb **fb = &flipdata->fb[crtc_id];
314835d5b7c7Smrg
314935d5b7c7Smrg	if (drmmode_crtc->flip_pending == *fb) {
315035d5b7c7Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->flip_pending,
315135d5b7c7Smrg				     NULL);
315235d5b7c7Smrg	}
315335d5b7c7Smrg	drmmode_fb_reference(pAMDGPUEnt->fd, fb, NULL);
3154d6c0b56eSmrg
3155d6c0b56eSmrg	if (--flipdata->flip_count == 0) {
3156504d986fSmrg		if (!flipdata->fe_crtc)
3157504d986fSmrg			flipdata->fe_crtc = crtc;
3158504d986fSmrg		flipdata->abort(flipdata->fe_crtc, flipdata->event_data);
3159d6c0b56eSmrg		free(flipdata);
3160d6c0b56eSmrg	}
3161d6c0b56eSmrg}
3162d6c0b56eSmrg
3163d6c0b56eSmrgstatic void
3164d6c0b56eSmrgdrmmode_flip_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, void *event_data)
3165d6c0b56eSmrg{
3166d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
316724b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3168d6c0b56eSmrg	drmmode_flipdata_ptr flipdata = event_data;
316935d5b7c7Smrg	int crtc_id = drmmode_get_crtc_id(crtc);
317035d5b7c7Smrg	struct drmmode_fb **fb = &flipdata->fb[crtc_id];
3171d6c0b56eSmrg
3172d6c0b56eSmrg	/* Is this the event whose info shall be delivered to higher level? */
3173d6c0b56eSmrg	if (crtc == flipdata->fe_crtc) {
3174d6c0b56eSmrg		/* Yes: Cache msc, ust for later delivery. */
3175d6c0b56eSmrg		flipdata->fe_frame = frame;
3176d6c0b56eSmrg		flipdata->fe_usec = usec;
3177d6c0b56eSmrg	}
3178d6c0b56eSmrg
317990f2b693Smrg	if (*fb) {
318090f2b693Smrg		if (drmmode_crtc->flip_pending == *fb) {
318190f2b693Smrg			drmmode_fb_reference(pAMDGPUEnt->fd,
318290f2b693Smrg					     &drmmode_crtc->flip_pending, NULL);
318390f2b693Smrg		}
318490f2b693Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, *fb);
318590f2b693Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, fb, NULL);
318624b90cf4Smrg	}
318724b90cf4Smrg
3188d6c0b56eSmrg	if (--flipdata->flip_count == 0) {
3189504d986fSmrg		/* Deliver MSC & UST from reference/current CRTC to flip event
3190504d986fSmrg		 * handler
3191504d986fSmrg		 */
3192d6c0b56eSmrg		if (flipdata->fe_crtc)
3193504d986fSmrg			flipdata->handler(flipdata->fe_crtc, flipdata->fe_frame,
3194504d986fSmrg					  flipdata->fe_usec, flipdata->event_data);
3195504d986fSmrg		else
3196504d986fSmrg			flipdata->handler(crtc, frame, usec, flipdata->event_data);
3197d6c0b56eSmrg
3198d6c0b56eSmrg		free(flipdata);
3199d6c0b56eSmrg	}
3200d6c0b56eSmrg}
3201d6c0b56eSmrg
3202504d986fSmrg#if HAVE_NOTIFY_FD
3203504d986fSmrgstatic void drmmode_notify_fd(int fd, int notify, void *data)
3204504d986fSmrg{
3205504d986fSmrg	drmmode_ptr drmmode = data;
320635d5b7c7Smrg	amdgpu_drm_handle_event(fd, &drmmode->event_context);
3207504d986fSmrg}
3208504d986fSmrg#else
3209d6c0b56eSmrgstatic void drm_wakeup_handler(pointer data, int err, pointer p)
3210d6c0b56eSmrg{
3211d6c0b56eSmrg	drmmode_ptr drmmode = data;
3212d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(drmmode->scrn);
3213d6c0b56eSmrg	fd_set *read_mask = p;
3214d6c0b56eSmrg
3215d6c0b56eSmrg	if (err >= 0 && FD_ISSET(pAMDGPUEnt->fd, read_mask)) {
321635d5b7c7Smrg		amdgpu_drm_handle_event(pAMDGPUEnt->fd, &drmmode->event_context);
3217d6c0b56eSmrg	}
3218d6c0b56eSmrg}
3219504d986fSmrg#endif
3220d6c0b56eSmrg
322111bf0794Smrgstatic Bool drmmode_probe_page_flip_target(AMDGPUEntPtr pAMDGPUEnt)
322211bf0794Smrg{
322311bf0794Smrg	uint64_t cap_value;
322411bf0794Smrg
322511bf0794Smrg	return drmGetCap(pAMDGPUEnt->fd, DRM_CAP_PAGE_FLIP_TARGET,
322611bf0794Smrg			 &cap_value) == 0 && cap_value != 0;
322711bf0794Smrg}
322811bf0794Smrg
322911bf0794Smrgstatic int
323011bf0794Smrgdrmmode_page_flip(AMDGPUEntPtr pAMDGPUEnt, drmmode_crtc_private_ptr drmmode_crtc,
323111bf0794Smrg		  int fb_id, uint32_t flags, uintptr_t drm_queue_seq)
323211bf0794Smrg{
323311bf0794Smrg	flags |= DRM_MODE_PAGE_FLIP_EVENT;
323411bf0794Smrg	return drmModePageFlip(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id,
323511bf0794Smrg			       fb_id, flags, (void*)drm_queue_seq);
323611bf0794Smrg}
323711bf0794Smrg
323811bf0794Smrgint
323911bf0794Smrgdrmmode_page_flip_target_absolute(AMDGPUEntPtr pAMDGPUEnt,
324011bf0794Smrg				  drmmode_crtc_private_ptr drmmode_crtc,
324111bf0794Smrg				  int fb_id, uint32_t flags,
324211bf0794Smrg				  uintptr_t drm_queue_seq, uint32_t target_msc)
324311bf0794Smrg{
324411bf0794Smrg	if (pAMDGPUEnt->has_page_flip_target) {
324511bf0794Smrg		flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE;
324611bf0794Smrg		return drmModePageFlipTarget(pAMDGPUEnt->fd,
324711bf0794Smrg					     drmmode_crtc->mode_crtc->crtc_id,
324811bf0794Smrg					     fb_id, flags, (void*)drm_queue_seq,
324911bf0794Smrg					     target_msc);
325011bf0794Smrg	}
325111bf0794Smrg
325211bf0794Smrg	return drmmode_page_flip(pAMDGPUEnt, drmmode_crtc, fb_id, flags,
325311bf0794Smrg				 drm_queue_seq);
325411bf0794Smrg}
325511bf0794Smrg
325611bf0794Smrgint
325711bf0794Smrgdrmmode_page_flip_target_relative(AMDGPUEntPtr pAMDGPUEnt,
325811bf0794Smrg				  drmmode_crtc_private_ptr drmmode_crtc,
325911bf0794Smrg				  int fb_id, uint32_t flags,
326011bf0794Smrg				  uintptr_t drm_queue_seq, uint32_t target_msc)
326111bf0794Smrg{
326211bf0794Smrg	if (pAMDGPUEnt->has_page_flip_target) {
326311bf0794Smrg		flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_TARGET_RELATIVE;
326411bf0794Smrg		return drmModePageFlipTarget(pAMDGPUEnt->fd,
326511bf0794Smrg					     drmmode_crtc->mode_crtc->crtc_id,
326611bf0794Smrg					     fb_id, flags, (void*)drm_queue_seq,
326711bf0794Smrg					     target_msc);
326811bf0794Smrg	}
326911bf0794Smrg
327011bf0794Smrg	return drmmode_page_flip(pAMDGPUEnt, drmmode_crtc, fb_id, flags,
327111bf0794Smrg				 drm_queue_seq);
327211bf0794Smrg}
327311bf0794Smrg
327435d5b7c7Smrg/**
327535d5b7c7Smrg * Initialize DDX color management support. It does two things:
327635d5b7c7Smrg *
327735d5b7c7Smrg * 1. Cache DRM color management property type IDs, as they do not change. They
327835d5b7c7Smrg *    will be used later to modify color management via DRM, or to determine if
327935d5b7c7Smrg *    there's kernel support for color management.
328035d5b7c7Smrg *
328135d5b7c7Smrg * 2. Cache degamma/gamma LUT sizes, since all CRTCs have the same LUT sizes on
328235d5b7c7Smrg *    AMD hardware.
328335d5b7c7Smrg *
328435d5b7c7Smrg * If the cached ID's are all 0 after calling this function, then color
328535d5b7c7Smrg * management is not supported. For short, checking if the gamma LUT size
328635d5b7c7Smrg * property ID == 0 is sufficient.
328735d5b7c7Smrg *
328835d5b7c7Smrg * This should be called before CRTCs are initialized within pre_init, as the
328935d5b7c7Smrg * cached values will be used there.
329035d5b7c7Smrg *
329135d5b7c7Smrg * @drm_fd: DRM file descriptor
329235d5b7c7Smrg * @drmmode: drmmode object, where the cached IDs are stored
329335d5b7c7Smrg * @mode_res: The DRM mode resource containing the CRTC ids
329435d5b7c7Smrg */
329535d5b7c7Smrgstatic void drmmode_cm_init(int drm_fd, drmmode_ptr drmmode,
329635d5b7c7Smrg			    drmModeResPtr mode_res)
329735d5b7c7Smrg{
329835d5b7c7Smrg	drmModeObjectPropertiesPtr drm_props;
329935d5b7c7Smrg	drmModePropertyPtr drm_prop;
330035d5b7c7Smrg	enum drmmode_cm_prop cm_prop;
330135d5b7c7Smrg	uint32_t cm_enabled = 0;
330235d5b7c7Smrg	uint32_t cm_all_enabled = (1 << CM_NUM_PROPS) - 1;
330335d5b7c7Smrg	int i;
330435d5b7c7Smrg
330535d5b7c7Smrg	memset(drmmode->cm_prop_ids, 0, sizeof(drmmode->cm_prop_ids));
330635d5b7c7Smrg	drmmode->gamma_lut_size = drmmode->degamma_lut_size = 0;
330735d5b7c7Smrg
330835d5b7c7Smrg	if (!mode_res->crtcs)
330935d5b7c7Smrg		return;
331035d5b7c7Smrg
331135d5b7c7Smrg	/* AMD hardware has color management support on all pipes. It is
331235d5b7c7Smrg	 * therefore sufficient to only check the first CRTC.
331335d5b7c7Smrg	 */
331435d5b7c7Smrg	drm_props = drmModeObjectGetProperties(drm_fd,
331535d5b7c7Smrg					       mode_res->crtcs[0],
331635d5b7c7Smrg					       DRM_MODE_OBJECT_CRTC);
331735d5b7c7Smrg	if (!drm_props)
331835d5b7c7Smrg		return;
331935d5b7c7Smrg
332035d5b7c7Smrg	for (i = 0; i < drm_props->count_props; i++) {
332135d5b7c7Smrg		drm_prop = drmModeGetProperty(drm_fd,
332235d5b7c7Smrg					      drm_props->props[i]);
332335d5b7c7Smrg		if (!drm_prop)
332435d5b7c7Smrg			continue;
332535d5b7c7Smrg
332635d5b7c7Smrg		cm_prop = get_cm_enum_from_str(drm_prop->name);
332735d5b7c7Smrg		if (cm_prop == CM_INVALID_PROP)
332835d5b7c7Smrg			continue;
332935d5b7c7Smrg
333035d5b7c7Smrg		if (cm_prop == CM_DEGAMMA_LUT_SIZE)
333135d5b7c7Smrg			drmmode->degamma_lut_size = drm_props->prop_values[i];
333235d5b7c7Smrg		else if (cm_prop == CM_GAMMA_LUT_SIZE)
333335d5b7c7Smrg			drmmode->gamma_lut_size = drm_props->prop_values[i];
333435d5b7c7Smrg
333535d5b7c7Smrg		drmmode->cm_prop_ids[cm_prop] = drm_props->props[i];
333635d5b7c7Smrg		cm_enabled |= 1 << cm_prop;
333735d5b7c7Smrg
333835d5b7c7Smrg		drmModeFreeProperty(drm_prop);
333935d5b7c7Smrg	}
334035d5b7c7Smrg	drmModeFreeObjectProperties(drm_props);
334135d5b7c7Smrg
334235d5b7c7Smrg	/* cm is enabled only if all prop ids are found */
334335d5b7c7Smrg	if (cm_enabled == cm_all_enabled)
334435d5b7c7Smrg		return;
334535d5b7c7Smrg
334635d5b7c7Smrg	/* Otherwise, disable DDX cm support */
334735d5b7c7Smrg	memset(drmmode->cm_prop_ids, 0, sizeof(drmmode->cm_prop_ids));
334835d5b7c7Smrg	drmmode->gamma_lut_size = drmmode->degamma_lut_size = 0;
334935d5b7c7Smrg}
335035d5b7c7Smrg
3351d6c0b56eSmrgBool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
3352d6c0b56eSmrg{
3353d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
3354d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
3355d6c0b56eSmrg	int i, num_dvi = 0, num_hdmi = 0;
3356d6c0b56eSmrg	unsigned int crtcs_needed = 0;
335790f2b693Smrg	unsigned int crtcs_got = 0;
3358d6c0b56eSmrg	drmModeResPtr mode_res;
335946845023Smrg	char *provider_name;
3360d6c0b56eSmrg
3361d6c0b56eSmrg	xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
3362d6c0b56eSmrg
3363d6c0b56eSmrg	drmmode->scrn = pScrn;
3364d6c0b56eSmrg	mode_res = drmModeGetResources(pAMDGPUEnt->fd);
3365d6c0b56eSmrg	if (!mode_res)
3366d6c0b56eSmrg		return FALSE;
3367d6c0b56eSmrg
3368d6c0b56eSmrg	drmmode->count_crtcs = mode_res->count_crtcs;
3369d6c0b56eSmrg	xf86CrtcSetSizeRange(pScrn, 320, 200, mode_res->max_width,
3370d6c0b56eSmrg			     mode_res->max_height);
3371d6c0b56eSmrg
3372d6c0b56eSmrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
3373d6c0b56eSmrg		       "Initializing outputs ...\n");
3374d6c0b56eSmrg	for (i = 0; i < mode_res->count_connectors; i++)
3375d6c0b56eSmrg		crtcs_needed += drmmode_output_init(pScrn, drmmode, mode_res, i, &num_dvi, &num_hdmi, 0);
3376d6c0b56eSmrg
3377d6c0b56eSmrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
3378d6c0b56eSmrg		       "%d crtcs needed for screen.\n", crtcs_needed);
3379d6c0b56eSmrg
338024b90cf4Smrg	/* Need per-screen drmmode_crtc_funcs, based on our global template,
338124b90cf4Smrg	 * so we can disable some functions, depending on screen settings.
338224b90cf4Smrg	 */
338324b90cf4Smrg	info->drmmode_crtc_funcs = drmmode_crtc_funcs;
338424b90cf4Smrg
3385d6c0b56eSmrg	if (!info->use_glamor) {
3386d6c0b56eSmrg		/* Rotation requires hardware acceleration */
338724b90cf4Smrg		info->drmmode_crtc_funcs.shadow_allocate = NULL;
338824b90cf4Smrg		info->drmmode_crtc_funcs.shadow_create = NULL;
338924b90cf4Smrg		info->drmmode_crtc_funcs.shadow_destroy = NULL;
3390d6c0b56eSmrg	}
3391d6c0b56eSmrg
339235d5b7c7Smrg	drmmode_cm_init(pAMDGPUEnt->fd, drmmode, mode_res);
339335d5b7c7Smrg
339435d5b7c7Smrg	/* Spare the server the effort to compute and update unused CLUTs. */
339535d5b7c7Smrg	if (pScrn->depth == 30 && !drmmode_cm_enabled(drmmode))
339624b90cf4Smrg		info->drmmode_crtc_funcs.gamma_set = NULL;
339724b90cf4Smrg
339890f2b693Smrg	for (i = 0; i < mode_res->count_crtcs; i++) {
3399d6c0b56eSmrg		if (!xf86IsEntityShared(pScrn->entityList[0]) ||
340090f2b693Smrg		    (crtcs_got < crtcs_needed &&
340190f2b693Smrg		     !(pAMDGPUEnt->assigned_crtcs & (1 << i))))
340290f2b693Smrg			crtcs_got += drmmode_crtc_init(pScrn, drmmode, mode_res, i);
340390f2b693Smrg	}
3404d6c0b56eSmrg
3405d6c0b56eSmrg	/* All ZaphodHeads outputs provided with matching crtcs? */
340690f2b693Smrg	if (crtcs_got < crtcs_needed) {
340790f2b693Smrg		if (crtcs_got == 0) {
340890f2b693Smrg			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
340990f2b693Smrg				   "No ZaphodHeads CRTC available, needed %u\n",
341090f2b693Smrg				   crtcs_needed);
341190f2b693Smrg			return FALSE;
341290f2b693Smrg		}
341390f2b693Smrg
3414d6c0b56eSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
3415d6c0b56eSmrg			   "%d ZaphodHeads crtcs unavailable. Some outputs will stay off.\n",
3416d6c0b56eSmrg			   crtcs_needed);
341790f2b693Smrg	}
3418d6c0b56eSmrg
3419d6c0b56eSmrg	/* workout clones */
3420d6c0b56eSmrg	drmmode_clones_init(pScrn, drmmode, mode_res);
3421d6c0b56eSmrg
342246845023Smrg	XNFasprintf(&provider_name, "%s @ %s", pScrn->chipset, pAMDGPUEnt->busid);
3423d6c0b56eSmrg	xf86ProviderSetup(pScrn, NULL, provider_name);
3424d6c0b56eSmrg	free(provider_name);
3425d6c0b56eSmrg
3426d6c0b56eSmrg	xf86InitialConfiguration(pScrn, TRUE);
3427d6c0b56eSmrg
342811bf0794Smrg	pAMDGPUEnt->has_page_flip_target = drmmode_probe_page_flip_target(pAMDGPUEnt);
342911bf0794Smrg
3430d6c0b56eSmrg	drmModeFreeResources(mode_res);
3431d6c0b56eSmrg	return TRUE;
3432d6c0b56eSmrg}
3433d6c0b56eSmrg
3434d6c0b56eSmrgvoid drmmode_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
3435d6c0b56eSmrg{
3436d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
3437d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
3438d6c0b56eSmrg
3439d6c0b56eSmrg	info->drmmode_inited = TRUE;
3440d6c0b56eSmrg	if (pAMDGPUEnt->fd_wakeup_registered != serverGeneration) {
3441504d986fSmrg#if HAVE_NOTIFY_FD
3442504d986fSmrg		SetNotifyFd(pAMDGPUEnt->fd, drmmode_notify_fd, X_NOTIFY_READ, drmmode);
3443504d986fSmrg#else
3444d6c0b56eSmrg		AddGeneralSocket(pAMDGPUEnt->fd);
3445d6c0b56eSmrg		RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA,
3446d6c0b56eSmrg					       drm_wakeup_handler, drmmode);
3447504d986fSmrg#endif
3448d6c0b56eSmrg		pAMDGPUEnt->fd_wakeup_registered = serverGeneration;
3449d6c0b56eSmrg		pAMDGPUEnt->fd_wakeup_ref = 1;
3450d6c0b56eSmrg	} else
3451d6c0b56eSmrg		pAMDGPUEnt->fd_wakeup_ref++;
3452d6c0b56eSmrg}
3453d6c0b56eSmrg
3454d6c0b56eSmrgvoid drmmode_fini(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
3455d6c0b56eSmrg{
3456504d986fSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
3457d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
3458d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
3459504d986fSmrg	int c;
3460d6c0b56eSmrg
3461d6c0b56eSmrg	if (!info->drmmode_inited)
3462d6c0b56eSmrg		return;
3463d6c0b56eSmrg
346490f2b693Smrg	for (c = 0; c < config->num_crtc; c++)
346590f2b693Smrg		drmmode_crtc_scanout_free(config->crtc[c]);
346690f2b693Smrg
3467d6c0b56eSmrg	if (pAMDGPUEnt->fd_wakeup_registered == serverGeneration &&
3468d6c0b56eSmrg	    !--pAMDGPUEnt->fd_wakeup_ref) {
3469504d986fSmrg#if HAVE_NOTIFY_FD
3470504d986fSmrg		RemoveNotifyFd(pAMDGPUEnt->fd);
3471504d986fSmrg#else
3472d6c0b56eSmrg		RemoveGeneralSocket(pAMDGPUEnt->fd);
3473d6c0b56eSmrg		RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA,
3474d6c0b56eSmrg					     drm_wakeup_handler, drmmode);
3475504d986fSmrg#endif
3476504d986fSmrg	}
3477d6c0b56eSmrg}
3478d6c0b56eSmrg
347924b90cf4Smrgstatic void drmmode_sprite_do_set_cursor(struct amdgpu_device_priv *device_priv,
348024b90cf4Smrg					 ScrnInfoPtr scrn, int x, int y)
348124b90cf4Smrg{
348224b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
348324b90cf4Smrg	CursorPtr cursor = device_priv->cursor;
348424b90cf4Smrg	Bool sprite_visible = device_priv->sprite_visible;
348524b90cf4Smrg
348624b90cf4Smrg	if (cursor) {
348724b90cf4Smrg		x -= cursor->bits->xhot;
348824b90cf4Smrg		y -= cursor->bits->yhot;
348924b90cf4Smrg
349024b90cf4Smrg		device_priv->sprite_visible =
349124b90cf4Smrg			x < scrn->virtualX && y < scrn->virtualY &&
349224b90cf4Smrg			(x + cursor->bits->width > 0) &&
349324b90cf4Smrg			(y + cursor->bits->height > 0);
349424b90cf4Smrg	} else {
349524b90cf4Smrg		device_priv->sprite_visible = FALSE;
349624b90cf4Smrg	}
349724b90cf4Smrg
349824b90cf4Smrg	info->sprites_visible += device_priv->sprite_visible - sprite_visible;
349924b90cf4Smrg}
350024b90cf4Smrg
350135d5b7c7Smrgstatic void drmmode_sprite_set_cursor(DeviceIntPtr pDev, ScreenPtr pScreen,
350235d5b7c7Smrg				      CursorPtr pCursor, int x, int y)
350324b90cf4Smrg{
350424b90cf4Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
350524b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
350624b90cf4Smrg	struct amdgpu_device_priv *device_priv =
350724b90cf4Smrg		dixLookupScreenPrivate(&pDev->devPrivates,
350824b90cf4Smrg				       &amdgpu_device_private_key, pScreen);
350924b90cf4Smrg
351024b90cf4Smrg	device_priv->cursor = pCursor;
351124b90cf4Smrg	drmmode_sprite_do_set_cursor(device_priv, scrn, x, y);
351224b90cf4Smrg
351335d5b7c7Smrg	info->SpriteFuncs->SetCursor(pDev, pScreen, pCursor, x, y);
351424b90cf4Smrg}
351524b90cf4Smrg
351635d5b7c7Smrgstatic void drmmode_sprite_move_cursor(DeviceIntPtr pDev, ScreenPtr pScreen,
351735d5b7c7Smrg				       int x, int y)
351824b90cf4Smrg{
351924b90cf4Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
352024b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
352124b90cf4Smrg	struct amdgpu_device_priv *device_priv =
352224b90cf4Smrg		dixLookupScreenPrivate(&pDev->devPrivates,
352324b90cf4Smrg				       &amdgpu_device_private_key, pScreen);
352424b90cf4Smrg
352524b90cf4Smrg	drmmode_sprite_do_set_cursor(device_priv, scrn, x, y);
352624b90cf4Smrg
352735d5b7c7Smrg	info->SpriteFuncs->MoveCursor(pDev, pScreen, x, y);
352824b90cf4Smrg}
352924b90cf4Smrg
353035d5b7c7Smrgstatic Bool drmmode_sprite_realize_realize_cursor(DeviceIntPtr pDev,
353135d5b7c7Smrg						  ScreenPtr pScreen,
353235d5b7c7Smrg						  CursorPtr pCursor)
353335d5b7c7Smrg{
353435d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
353535d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
353635d5b7c7Smrg
353735d5b7c7Smrg	return info->SpriteFuncs->RealizeCursor(pDev, pScreen, pCursor);
353835d5b7c7Smrg}
353935d5b7c7Smrg
354035d5b7c7Smrgstatic Bool drmmode_sprite_realize_unrealize_cursor(DeviceIntPtr pDev,
354135d5b7c7Smrg						    ScreenPtr pScreen,
354235d5b7c7Smrg						    CursorPtr pCursor)
354335d5b7c7Smrg{
354435d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
354535d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
354635d5b7c7Smrg
354735d5b7c7Smrg	return info->SpriteFuncs->UnrealizeCursor(pDev, pScreen, pCursor);
354835d5b7c7Smrg}
354935d5b7c7Smrg
355035d5b7c7Smrgstatic Bool drmmode_sprite_device_cursor_initialize(DeviceIntPtr pDev,
355135d5b7c7Smrg						    ScreenPtr pScreen)
355235d5b7c7Smrg{
355335d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
355435d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
355535d5b7c7Smrg
355635d5b7c7Smrg	return info->SpriteFuncs->DeviceCursorInitialize(pDev, pScreen);
355735d5b7c7Smrg}
355835d5b7c7Smrg
355935d5b7c7Smrgstatic void drmmode_sprite_device_cursor_cleanup(DeviceIntPtr pDev,
356035d5b7c7Smrg						 ScreenPtr pScreen)
356135d5b7c7Smrg{
356235d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
356335d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
356435d5b7c7Smrg
356535d5b7c7Smrg	info->SpriteFuncs->DeviceCursorCleanup(pDev, pScreen);
356635d5b7c7Smrg}
356735d5b7c7Smrg
356835d5b7c7SmrgmiPointerSpriteFuncRec drmmode_sprite_funcs = {
356935d5b7c7Smrg	.RealizeCursor = drmmode_sprite_realize_realize_cursor,
357035d5b7c7Smrg	.UnrealizeCursor = drmmode_sprite_realize_unrealize_cursor,
357135d5b7c7Smrg	.SetCursor = drmmode_sprite_set_cursor,
357235d5b7c7Smrg	.MoveCursor = drmmode_sprite_move_cursor,
357335d5b7c7Smrg	.DeviceCursorInitialize = drmmode_sprite_device_cursor_initialize,
357435d5b7c7Smrg	.DeviceCursorCleanup = drmmode_sprite_device_cursor_cleanup,
357535d5b7c7Smrg};
357635d5b7c7Smrg
357735d5b7c7Smrg
3578d6c0b56eSmrgvoid drmmode_adjust_frame(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int x, int y)
3579d6c0b56eSmrg{
3580d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
3581d6c0b56eSmrg	xf86OutputPtr output = config->output[config->compat_output];
3582d6c0b56eSmrg	xf86CrtcPtr crtc = output->crtc;
3583d6c0b56eSmrg
3584d6c0b56eSmrg	if (crtc && crtc->enabled) {
3585d6c0b56eSmrg		drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
3586d6c0b56eSmrg	}
3587d6c0b56eSmrg}
3588d6c0b56eSmrg
3589d6c0b56eSmrgBool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode,
3590d6c0b56eSmrg			       Bool set_hw)
3591d6c0b56eSmrg{
3592d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
359324b90cf4Smrg	unsigned num_desired = 0, num_on = 0;
3594d6c0b56eSmrg	int c;
3595d6c0b56eSmrg
359624b90cf4Smrg	/* First, disable all unused CRTCs */
359724b90cf4Smrg	if (set_hw) {
359824b90cf4Smrg		for (c = 0; c < config->num_crtc; c++) {
359924b90cf4Smrg			xf86CrtcPtr crtc = config->crtc[c];
360024b90cf4Smrg
360124b90cf4Smrg			/* Skip disabled CRTCs */
360224b90cf4Smrg			if (crtc->enabled)
360324b90cf4Smrg				continue;
360424b90cf4Smrg
360535d5b7c7Smrg			drmmode_crtc_dpms(crtc, DPMSModeOff);
360624b90cf4Smrg		}
360724b90cf4Smrg	}
360824b90cf4Smrg
360924b90cf4Smrg	/* Then, try setting the chosen mode on each CRTC */
3610d6c0b56eSmrg	for (c = 0; c < config->num_crtc; c++) {
3611d6c0b56eSmrg		xf86CrtcPtr crtc = config->crtc[c];
3612d6c0b56eSmrg		xf86OutputPtr output = NULL;
3613d6c0b56eSmrg		int o;
3614d6c0b56eSmrg
361524b90cf4Smrg		if (!crtc->enabled)
3616d6c0b56eSmrg			continue;
3617d6c0b56eSmrg
3618d6c0b56eSmrg		if (config->output[config->compat_output]->crtc == crtc)
3619d6c0b56eSmrg			output = config->output[config->compat_output];
3620d6c0b56eSmrg		else {
3621d6c0b56eSmrg			for (o = 0; o < config->num_output; o++)
3622d6c0b56eSmrg				if (config->output[o]->crtc == crtc) {
3623d6c0b56eSmrg					output = config->output[o];
3624d6c0b56eSmrg					break;
3625d6c0b56eSmrg				}
3626d6c0b56eSmrg		}
3627d6c0b56eSmrg		/* paranoia */
3628d6c0b56eSmrg		if (!output)
3629d6c0b56eSmrg			continue;
3630d6c0b56eSmrg
363124b90cf4Smrg		num_desired++;
363224b90cf4Smrg
3633d6c0b56eSmrg		/* Mark that we'll need to re-set the mode for sure */
3634d6c0b56eSmrg		memset(&crtc->mode, 0, sizeof(crtc->mode));
3635d6c0b56eSmrg		if (!crtc->desiredMode.CrtcHDisplay) {
3636d6c0b56eSmrg			DisplayModePtr mode = xf86OutputFindClosestMode(output,
3637d6c0b56eSmrg									pScrn->
3638d6c0b56eSmrg									currentMode);
3639d6c0b56eSmrg
364024b90cf4Smrg			if (!mode) {
364124b90cf4Smrg				xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
364224b90cf4Smrg					   "Failed to find mode for CRTC %d\n", c);
364324b90cf4Smrg				continue;
364424b90cf4Smrg			}
3645d6c0b56eSmrg			crtc->desiredMode = *mode;
3646d6c0b56eSmrg			crtc->desiredRotation = RR_Rotate_0;
3647d6c0b56eSmrg			crtc->desiredX = 0;
3648d6c0b56eSmrg			crtc->desiredY = 0;
3649d6c0b56eSmrg		}
3650d6c0b56eSmrg
3651d6c0b56eSmrg		if (set_hw) {
365224b90cf4Smrg			if (crtc->funcs->set_mode_major(crtc, &crtc->desiredMode,
365324b90cf4Smrg							crtc->desiredRotation,
365424b90cf4Smrg							crtc->desiredX,
365524b90cf4Smrg							crtc->desiredY)) {
365624b90cf4Smrg				num_on++;
365724b90cf4Smrg			} else {
365824b90cf4Smrg				xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
365924b90cf4Smrg					   "Failed to set mode on CRTC %d\n", c);
366035d5b7c7Smrg				RRCrtcSet(crtc->randr_crtc, NULL, crtc->x, crtc->y,
366135d5b7c7Smrg					  crtc->rotation, 0, NULL);
366224b90cf4Smrg			}
3663d6c0b56eSmrg		} else {
3664d6c0b56eSmrg			crtc->mode = crtc->desiredMode;
3665d6c0b56eSmrg			crtc->rotation = crtc->desiredRotation;
3666d6c0b56eSmrg			crtc->x = crtc->desiredX;
3667d6c0b56eSmrg			crtc->y = crtc->desiredY;
366824b90cf4Smrg			if (drmmode_handle_transform(crtc))
366924b90cf4Smrg				num_on++;
3670d6c0b56eSmrg		}
3671d6c0b56eSmrg	}
367224b90cf4Smrg
367324b90cf4Smrg	if (num_on == 0 && num_desired > 0) {
367424b90cf4Smrg		xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to enable any CRTC\n");
367524b90cf4Smrg		return FALSE;
367624b90cf4Smrg	}
367724b90cf4Smrg
367835d5b7c7Smrg	/* Validate leases on VT re-entry */
367990f2b693Smrg	if (dixPrivateKeyRegistered(rrPrivKey))
368090f2b693Smrg		drmmode_validate_leases(pScrn);
368135d5b7c7Smrg
3682d6c0b56eSmrg	return TRUE;
3683d6c0b56eSmrg}
3684d6c0b56eSmrg
3685d6c0b56eSmrgBool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn)
3686d6c0b56eSmrg{
3687d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
368835d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
368935d5b7c7Smrg	int i;
3690d6c0b56eSmrg
3691d6c0b56eSmrg	if (xf86_config->num_crtc) {
3692d6c0b56eSmrg		xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
3693d6c0b56eSmrg			       "Initializing kms color map\n");
3694d6c0b56eSmrg		if (!miCreateDefColormap(pScreen))
3695d6c0b56eSmrg			return FALSE;
369635d5b7c7Smrg
369735d5b7c7Smrg		if (pScrn->depth == 30) {
369835d5b7c7Smrg			if (!drmmode_cm_enabled(&info->drmmode))
369935d5b7c7Smrg				return TRUE;
370035d5b7c7Smrg
370135d5b7c7Smrg			for (i = 0; i < xf86_config->num_crtc; i++) {
370235d5b7c7Smrg				xf86CrtcPtr crtc = xf86_config->crtc[i];
370335d5b7c7Smrg				void *gamma;
370435d5b7c7Smrg
370535d5b7c7Smrg				if (crtc->gamma_size == 1024)
370635d5b7c7Smrg					continue;
370735d5b7c7Smrg
370835d5b7c7Smrg				gamma = malloc(1024 * 3 * sizeof(CARD16));
370935d5b7c7Smrg				if (!gamma) {
371035d5b7c7Smrg					ErrorF("Failed to allocate gamma LUT memory\n");
371135d5b7c7Smrg					return FALSE;
371235d5b7c7Smrg				}
371335d5b7c7Smrg
371435d5b7c7Smrg				free(crtc->gamma_red);
371535d5b7c7Smrg				crtc->gamma_size = 1024;
371635d5b7c7Smrg				crtc->gamma_red = gamma;
371735d5b7c7Smrg				crtc->gamma_green = crtc->gamma_red + crtc->gamma_size;
371835d5b7c7Smrg				crtc->gamma_blue = crtc->gamma_green + crtc->gamma_size;
371935d5b7c7Smrg			}
372035d5b7c7Smrg		}
372135d5b7c7Smrg
372235d5b7c7Smrg		/* All Radeons support 10 bit CLUTs. */
372335d5b7c7Smrg		if (!xf86HandleColormaps(pScreen, 1 << pScrn->rgbBits, 10,
372435d5b7c7Smrg					 NULL, NULL, CMAP_PALETTED_TRUECOLOR
3725d6c0b56eSmrg					 | CMAP_RELOAD_ON_MODE_SWITCH))
3726d6c0b56eSmrg			return FALSE;
372735d5b7c7Smrg
372835d5b7c7Smrg		for (i = 0; i < xf86_config->num_crtc; i++) {
372935d5b7c7Smrg			xf86CrtcPtr crtc = xf86_config->crtc[i];
373035d5b7c7Smrg
373135d5b7c7Smrg			drmmode_crtc_gamma_do_set(crtc, crtc->gamma_red,
373235d5b7c7Smrg						  crtc->gamma_green,
373335d5b7c7Smrg						  crtc->gamma_blue,
373435d5b7c7Smrg						  crtc->gamma_size);
373535d5b7c7Smrg		}
3736d6c0b56eSmrg	}
373735d5b7c7Smrg
3738d6c0b56eSmrg	return TRUE;
3739d6c0b56eSmrg}
3740d6c0b56eSmrg
3741504d986fSmrgstatic Bool
3742504d986fSmrgdrmmode_find_output(ScrnInfoPtr scrn, int output_id, int *num_dvi,
3743504d986fSmrg		    int *num_hdmi)
3744504d986fSmrg{
3745504d986fSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3746504d986fSmrg	int i;
3747504d986fSmrg
3748504d986fSmrg	for (i = 0; i < config->num_output; i++) {
3749504d986fSmrg		xf86OutputPtr output = config->output[i];
3750504d986fSmrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
3751504d986fSmrg
3752504d986fSmrg		if (drmmode_output->output_id == output_id) {
3753504d986fSmrg			switch(drmmode_output->mode_output->connector_type) {
3754504d986fSmrg			case DRM_MODE_CONNECTOR_DVII:
3755504d986fSmrg			case DRM_MODE_CONNECTOR_DVID:
3756504d986fSmrg			case DRM_MODE_CONNECTOR_DVIA:
3757504d986fSmrg				(*num_dvi)++;
3758504d986fSmrg				break;
3759504d986fSmrg			case DRM_MODE_CONNECTOR_HDMIA:
3760504d986fSmrg			case DRM_MODE_CONNECTOR_HDMIB:
3761504d986fSmrg				(*num_hdmi)++;
3762504d986fSmrg				break;
3763504d986fSmrg			}
3764504d986fSmrg
3765504d986fSmrg			return TRUE;
3766504d986fSmrg		}
3767504d986fSmrg	}
3768504d986fSmrg
3769504d986fSmrg	return FALSE;
3770504d986fSmrg}
3771d6c0b56eSmrg
3772d6c0b56eSmrgvoid
3773d6c0b56eSmrgamdgpu_mode_hotplug(ScrnInfoPtr scrn, drmmode_ptr drmmode)
3774d6c0b56eSmrg{
3775d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3776d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
3777d6c0b56eSmrg	drmModeResPtr mode_res;
3778d6c0b56eSmrg	int i, j;
3779d6c0b56eSmrg	Bool found;
3780d6c0b56eSmrg	Bool changed = FALSE;
3781504d986fSmrg	int num_dvi = 0, num_hdmi = 0;
3782d6c0b56eSmrg
378324b90cf4Smrg	/* Try to re-set the mode on all the connectors with a BAD link-state:
378424b90cf4Smrg	 * This may happen if a link degrades and a new modeset is necessary, using
378524b90cf4Smrg	 * different link-training parameters. If the kernel found that the current
378624b90cf4Smrg	 * mode is not achievable anymore, it should have pruned the mode before
378724b90cf4Smrg	 * sending the hotplug event. Try to re-set the currently-set mode to keep
378824b90cf4Smrg	 * the display alive, this will fail if the mode has been pruned.
378924b90cf4Smrg	 * In any case, we will send randr events for the Desktop Environment to
379024b90cf4Smrg	 * deal with it, if it wants to.
379124b90cf4Smrg	 */
379224b90cf4Smrg	for (i = 0; i < config->num_output; i++) {
379324b90cf4Smrg		xf86OutputPtr output = config->output[i];
379424b90cf4Smrg		xf86CrtcPtr crtc = output->crtc;
379524b90cf4Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
379624b90cf4Smrg
379724b90cf4Smrg		drmmode_output_detect(output);
379824b90cf4Smrg
379924b90cf4Smrg		if (!crtc || !drmmode_output->mode_output)
380024b90cf4Smrg			continue;
380124b90cf4Smrg
380224b90cf4Smrg		/* Get an updated view of the properties for the current connector and
380324b90cf4Smrg		 * look for the link-status property
380424b90cf4Smrg		 */
380524b90cf4Smrg		for (j = 0; j < drmmode_output->num_props; j++) {
380624b90cf4Smrg			drmmode_prop_ptr p = &drmmode_output->props[j];
380724b90cf4Smrg
380824b90cf4Smrg			if (!strcmp(p->mode_prop->name, "link-status")) {
380924b90cf4Smrg				if (p->value != DRM_MODE_LINK_STATUS_BAD)
381024b90cf4Smrg					break;
381124b90cf4Smrg
381224b90cf4Smrg				/* the connector got a link failure, re-set the current mode */
381324b90cf4Smrg				drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
381424b90cf4Smrg						       crtc->x, crtc->y);
381524b90cf4Smrg
381624b90cf4Smrg				xf86DrvMsg(scrn->scrnIndex, X_WARNING,
381724b90cf4Smrg					   "hotplug event: connector %u's link-state is BAD, "
381824b90cf4Smrg					   "tried resetting the current mode. You may be left"
381924b90cf4Smrg					   "with a black screen if this fails...\n",
382024b90cf4Smrg					   drmmode_output->mode_output->connector_id);
382124b90cf4Smrg
382224b90cf4Smrg				break;
382324b90cf4Smrg			}
382424b90cf4Smrg		}
382524b90cf4Smrg	}
382624b90cf4Smrg
3827d6c0b56eSmrg	mode_res = drmModeGetResources(pAMDGPUEnt->fd);
3828d6c0b56eSmrg	if (!mode_res)
3829d6c0b56eSmrg		goto out;
3830d6c0b56eSmrg
3831d6c0b56eSmrgrestart_destroy:
3832d6c0b56eSmrg	for (i = 0; i < config->num_output; i++) {
3833d6c0b56eSmrg		xf86OutputPtr output = config->output[i];
3834d6c0b56eSmrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
3835d6c0b56eSmrg		found = FALSE;
3836d6c0b56eSmrg		for (j = 0; j < mode_res->count_connectors; j++) {
3837d6c0b56eSmrg			if (mode_res->connectors[j] == drmmode_output->output_id) {
3838d6c0b56eSmrg				found = TRUE;
3839d6c0b56eSmrg				break;
3840d6c0b56eSmrg			}
3841d6c0b56eSmrg		}
3842d6c0b56eSmrg		if (found)
3843d6c0b56eSmrg			continue;
3844d6c0b56eSmrg
3845d6c0b56eSmrg		drmModeFreeConnector(drmmode_output->mode_output);
3846d6c0b56eSmrg		drmmode_output->mode_output = NULL;
3847d6c0b56eSmrg		drmmode_output->output_id = -1;
3848d6c0b56eSmrg
3849d6c0b56eSmrg		changed = TRUE;
3850d6c0b56eSmrg		if (drmmode->delete_dp_12_displays) {
3851d6c0b56eSmrg			RROutputDestroy(output->randr_output);
3852d6c0b56eSmrg			xf86OutputDestroy(output);
3853d6c0b56eSmrg			goto restart_destroy;
3854d6c0b56eSmrg		}
3855d6c0b56eSmrg	}
3856d6c0b56eSmrg
3857d6c0b56eSmrg	/* find new output ids we don't have outputs for */
3858d6c0b56eSmrg	for (i = 0; i < mode_res->count_connectors; i++) {
385990f2b693Smrg		for (j = 0; j < pAMDGPUEnt->num_scrns; j++) {
386090f2b693Smrg			if (drmmode_find_output(pAMDGPUEnt->scrn[j],
386190f2b693Smrg						mode_res->connectors[i],
386290f2b693Smrg						&num_dvi, &num_hdmi))
386390f2b693Smrg				break;
386490f2b693Smrg		}
386590f2b693Smrg
386690f2b693Smrg		if (j < pAMDGPUEnt->num_scrns)
3867d6c0b56eSmrg			continue;
3868d6c0b56eSmrg
3869504d986fSmrg		if (drmmode_output_init(scrn, drmmode, mode_res, i, &num_dvi,
3870504d986fSmrg					&num_hdmi, 1) != 0)
3871504d986fSmrg			changed = TRUE;
3872d6c0b56eSmrg	}
3873d6c0b56eSmrg
387435d5b7c7Smrg	/* Check to see if a lessee has disappeared */
387535d5b7c7Smrg	drmmode_validate_leases(scrn);
387635d5b7c7Smrg
387790f2b693Smrg	if (changed) {
3878d6c0b56eSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,14,99,2,0)
3879d6c0b56eSmrg		RRSetChanged(xf86ScrnToScreen(scrn));
3880d6c0b56eSmrg#else
3881d6c0b56eSmrg		rrScrPrivPtr rrScrPriv = rrGetScrPriv(scrn->pScreen);
3882d6c0b56eSmrg		rrScrPriv->changed = TRUE;
3883d6c0b56eSmrg#endif
3884d6c0b56eSmrg		RRTellChanged(xf86ScrnToScreen(scrn));
3885d6c0b56eSmrg	}
3886d6c0b56eSmrg
3887d6c0b56eSmrg	drmModeFreeResources(mode_res);
3888d6c0b56eSmrgout:
3889d6c0b56eSmrg	RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
3890d6c0b56eSmrg}
3891d6c0b56eSmrg
3892d6c0b56eSmrg#ifdef HAVE_LIBUDEV
3893d6c0b56eSmrgstatic void drmmode_handle_uevents(int fd, void *closure)
3894d6c0b56eSmrg{
3895d6c0b56eSmrg	drmmode_ptr drmmode = closure;
3896d6c0b56eSmrg	ScrnInfoPtr scrn = drmmode->scrn;
3897d6c0b56eSmrg	struct udev_device *dev;
3898504d986fSmrg	Bool received = FALSE;
389911bf0794Smrg	struct timeval tv = { 0, 0 };
390011bf0794Smrg	fd_set readfd;
390111bf0794Smrg
390211bf0794Smrg	FD_ZERO(&readfd);
390311bf0794Smrg	FD_SET(fd, &readfd);
390411bf0794Smrg
390511bf0794Smrg	while (select(fd + 1, &readfd, NULL, NULL, &tv) > 0 &&
390611bf0794Smrg	       FD_ISSET(fd, &readfd)) {
390711bf0794Smrg		/* select() ensured that this will not block */
390811bf0794Smrg		dev = udev_monitor_receive_device(drmmode->uevent_monitor);
390911bf0794Smrg		if (dev) {
391011bf0794Smrg			udev_device_unref(dev);
391111bf0794Smrg			received = TRUE;
391211bf0794Smrg		}
3913504d986fSmrg	}
3914504d986fSmrg
3915504d986fSmrg	if (received)
3916504d986fSmrg		amdgpu_mode_hotplug(scrn, drmmode);
3917d6c0b56eSmrg}
3918d6c0b56eSmrg#endif
3919d6c0b56eSmrg
3920d6c0b56eSmrgvoid drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode)
3921d6c0b56eSmrg{
3922d6c0b56eSmrg#ifdef HAVE_LIBUDEV
3923d6c0b56eSmrg	struct udev *u;
3924d6c0b56eSmrg	struct udev_monitor *mon;
3925d6c0b56eSmrg
3926d6c0b56eSmrg	u = udev_new();
3927d6c0b56eSmrg	if (!u)
3928d6c0b56eSmrg		return;
3929d6c0b56eSmrg	mon = udev_monitor_new_from_netlink(u, "udev");
3930d6c0b56eSmrg	if (!mon) {
3931d6c0b56eSmrg		udev_unref(u);
3932d6c0b56eSmrg		return;
3933d6c0b56eSmrg	}
3934d6c0b56eSmrg
3935d6c0b56eSmrg	if (udev_monitor_filter_add_match_subsystem_devtype(mon,
3936d6c0b56eSmrg							    "drm",
3937d6c0b56eSmrg							    "drm_minor") < 0 ||
3938d6c0b56eSmrg	    udev_monitor_enable_receiving(mon) < 0) {
3939d6c0b56eSmrg		udev_monitor_unref(mon);
3940d6c0b56eSmrg		udev_unref(u);
3941d6c0b56eSmrg		return;
3942d6c0b56eSmrg	}
3943d6c0b56eSmrg
3944d6c0b56eSmrg	drmmode->uevent_handler =
3945d6c0b56eSmrg	    xf86AddGeneralHandler(udev_monitor_get_fd(mon),
3946d6c0b56eSmrg				  drmmode_handle_uevents, drmmode);
3947d6c0b56eSmrg
3948d6c0b56eSmrg	drmmode->uevent_monitor = mon;
3949d6c0b56eSmrg#endif
3950d6c0b56eSmrg}
3951d6c0b56eSmrg
3952d6c0b56eSmrgvoid drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode)
3953d6c0b56eSmrg{
3954d6c0b56eSmrg#ifdef HAVE_LIBUDEV
3955d6c0b56eSmrg	if (drmmode->uevent_handler) {
3956d6c0b56eSmrg		struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
3957d6c0b56eSmrg		xf86RemoveGeneralHandler(drmmode->uevent_handler);
3958d6c0b56eSmrg
3959d6c0b56eSmrg		udev_monitor_unref(drmmode->uevent_monitor);
3960d6c0b56eSmrg		udev_unref(u);
3961d6c0b56eSmrg	}
3962d6c0b56eSmrg#endif
3963d6c0b56eSmrg}
3964d6c0b56eSmrg
3965d6c0b56eSmrgBool amdgpu_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
3966d6c0b56eSmrg			PixmapPtr new_front, uint64_t id, void *data,
396724b90cf4Smrg			xf86CrtcPtr ref_crtc, amdgpu_drm_handler_proc handler,
3968504d986fSmrg			amdgpu_drm_abort_proc abort,
396911bf0794Smrg			enum drmmode_flip_sync flip_sync,
397011bf0794Smrg			uint32_t target_msc)
3971d6c0b56eSmrg{
3972d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
3973e49c54bcSmrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
3974d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3975d6c0b56eSmrg	xf86CrtcPtr crtc = NULL;
3976d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private;
397790f2b693Smrg	int crtc_id;
397811bf0794Smrg	uint32_t flip_flags = flip_sync == FLIP_ASYNC ? DRM_MODE_PAGE_FLIP_ASYNC : 0;
3979e49c54bcSmrg	uint32_t sec_flip_flags = flip_flags;
3980d6c0b56eSmrg	drmmode_flipdata_ptr flipdata;
398135d5b7c7Smrg	Bool handle_deferred = FALSE;
3982d6c0b56eSmrg	uintptr_t drm_queue_seq = 0;
398335d5b7c7Smrg	struct drmmode_fb *fb;
398435d5b7c7Smrg	int i = 0;
3985d6c0b56eSmrg
3986e49c54bcSmrg	/*
3987e49c54bcSmrg	 * Flip secondary non-ref_crtc crtc's async if possible and requested
3988e49c54bcSmrg	 * by xorg.conf option "AsyncFlipSecondaries". Otherwise follow the lead
3989e49c54bcSmrg	 * of flip_sync.
3990e49c54bcSmrg	 */
3991e49c54bcSmrg	if (info->can_async_flip && info->async_flip_secondaries)
3992e49c54bcSmrg		sec_flip_flags |= DRM_MODE_PAGE_FLIP_ASYNC;
3993e49c54bcSmrg
399490f2b693Smrg	flipdata = calloc(1, sizeof(*flipdata) + drmmode_crtc->drmmode->count_crtcs *
399535d5b7c7Smrg			  sizeof(flipdata->fb[0]));
3996d6c0b56eSmrg	if (!flipdata) {
3997d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
3998d6c0b56eSmrg			   "flip queue: data alloc failed.\n");
3999d6c0b56eSmrg		goto error;
4000d6c0b56eSmrg	}
4001d6c0b56eSmrg
400235d5b7c7Smrg	fb = amdgpu_pixmap_get_fb(new_front);
400335d5b7c7Smrg	if (!fb) {
400424b90cf4Smrg		ErrorF("Failed to get FB for flip\n");
4005d6c0b56eSmrg		goto error;
400624b90cf4Smrg	}
4007d6c0b56eSmrg
4008d6c0b56eSmrg	/*
4009d6c0b56eSmrg	 * Queue flips on all enabled CRTCs
4010d6c0b56eSmrg	 * Note that if/when we get per-CRTC buffers, we'll have to update this.
4011d6c0b56eSmrg	 * Right now it assumes a single shared fb across all CRTCs, with the
4012d6c0b56eSmrg	 * kernel fixing up the offset of each CRTC as necessary.
4013d6c0b56eSmrg	 *
4014d6c0b56eSmrg	 * Also, flips queued on disabled or incorrectly configured displays
4015d6c0b56eSmrg	 * may never complete; this is a configuration error.
4016d6c0b56eSmrg	 */
4017d6c0b56eSmrg
4018d6c0b56eSmrg	flipdata->event_data = data;
4019d6c0b56eSmrg	flipdata->handler = handler;
4020d6c0b56eSmrg	flipdata->abort = abort;
402124b90cf4Smrg	flipdata->fe_crtc = ref_crtc;
4022d6c0b56eSmrg
4023d6c0b56eSmrg	for (i = 0; i < config->num_crtc; i++) {
4024d6c0b56eSmrg		crtc = config->crtc[i];
402524b90cf4Smrg		drmmode_crtc = crtc->driver_private;
402690f2b693Smrg		crtc_id = drmmode_get_crtc_id(crtc);
4027d6c0b56eSmrg
402824b90cf4Smrg		if (!drmmode_crtc_can_flip(crtc) ||
402924b90cf4Smrg		    (drmmode_crtc->tear_free && crtc != ref_crtc))
4030d6c0b56eSmrg			continue;
4031d6c0b56eSmrg
4032d6c0b56eSmrg		flipdata->flip_count++;
4033d6c0b56eSmrg
4034d6c0b56eSmrg		drm_queue_seq = amdgpu_drm_queue_alloc(crtc, client, id,
4035d6c0b56eSmrg						       flipdata,
4036d6c0b56eSmrg						       drmmode_flip_handler,
403790f2b693Smrg						       drmmode_flip_abort,
403890f2b693Smrg						       TRUE);
4039504d986fSmrg		if (drm_queue_seq == AMDGPU_DRM_QUEUE_ERROR) {
4040d6c0b56eSmrg			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
4041d6c0b56eSmrg				   "Allocating DRM queue event entry failed.\n");
4042d6c0b56eSmrg			goto error;
4043d6c0b56eSmrg		}
4044d6c0b56eSmrg
404524b90cf4Smrg		if (drmmode_crtc->tear_free) {
404624b90cf4Smrg			BoxRec extents = { .x1 = 0, .y1 = 0,
404724b90cf4Smrg					   .x2 = new_front->drawable.width,
404824b90cf4Smrg					   .y2 = new_front->drawable.height };
404924b90cf4Smrg			int scanout_id = drmmode_crtc->scanout_id ^ 1;
405024b90cf4Smrg
405124b90cf4Smrg			if (flip_sync == FLIP_ASYNC) {
405224b90cf4Smrg				if (!drmmode_wait_vblank(crtc,
405324b90cf4Smrg							 DRM_VBLANK_RELATIVE |
405424b90cf4Smrg							 DRM_VBLANK_EVENT,
405524b90cf4Smrg							 0, drm_queue_seq,
405624b90cf4Smrg							 NULL, NULL))
405724b90cf4Smrg					goto flip_error;
405824b90cf4Smrg				goto next;
405924b90cf4Smrg			}
406024b90cf4Smrg
406190f2b693Smrg			drmmode_fb_reference(pAMDGPUEnt->fd, &flipdata->fb[crtc_id],
406246845023Smrg					     amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id]));
406390f2b693Smrg			if (!flipdata->fb[crtc_id]) {
406424b90cf4Smrg				ErrorF("Failed to get FB for TearFree flip\n");
406524b90cf4Smrg				goto error;
406624b90cf4Smrg			}
406724b90cf4Smrg
406824b90cf4Smrg			amdgpu_scanout_do_update(crtc, scanout_id, new_front,
406935d5b7c7Smrg						 extents);
407035d5b7c7Smrg			amdgpu_glamor_flush(crtc->scrn);
407135d5b7c7Smrg
407235d5b7c7Smrg			if (drmmode_crtc->scanout_update_pending) {
407335d5b7c7Smrg				amdgpu_drm_wait_pending_flip(crtc);
407435d5b7c7Smrg				handle_deferred = TRUE;
407535d5b7c7Smrg				amdgpu_drm_abort_entry(drmmode_crtc->scanout_update_pending);
407635d5b7c7Smrg				drmmode_crtc->scanout_update_pending = 0;
407735d5b7c7Smrg			}
407835d5b7c7Smrg		} else {
407990f2b693Smrg			drmmode_fb_reference(pAMDGPUEnt->fd, &flipdata->fb[crtc_id], fb);
408024b90cf4Smrg		}
408124b90cf4Smrg
408224b90cf4Smrg		if (crtc == ref_crtc) {
408311bf0794Smrg			if (drmmode_page_flip_target_absolute(pAMDGPUEnt,
408411bf0794Smrg							      drmmode_crtc,
408590f2b693Smrg							      flipdata->fb[crtc_id]->handle,
408611bf0794Smrg							      flip_flags,
408711bf0794Smrg							      drm_queue_seq,
408811bf0794Smrg							      target_msc) != 0)
408911bf0794Smrg				goto flip_error;
409011bf0794Smrg		} else {
409111bf0794Smrg			if (drmmode_page_flip_target_relative(pAMDGPUEnt,
409211bf0794Smrg							      drmmode_crtc,
409390f2b693Smrg							      flipdata->fb[crtc_id]->handle,
4094e49c54bcSmrg							      sec_flip_flags,
409511bf0794Smrg							      drm_queue_seq, 0) != 0)
409611bf0794Smrg				goto flip_error;
4097d6c0b56eSmrg		}
409811bf0794Smrg
409924b90cf4Smrg		if (drmmode_crtc->tear_free) {
410024b90cf4Smrg			drmmode_crtc->scanout_id ^= 1;
410124b90cf4Smrg			drmmode_crtc->ignore_damage = TRUE;
410224b90cf4Smrg		}
410324b90cf4Smrg
410435d5b7c7Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->flip_pending,
410590f2b693Smrg				     flipdata->fb[crtc_id]);
410690f2b693Smrg
410790f2b693Smrg	next:
4108d6c0b56eSmrg		drm_queue_seq = 0;
4109d6c0b56eSmrg	}
4110d6c0b56eSmrg
411135d5b7c7Smrg	if (handle_deferred)
411235d5b7c7Smrg		amdgpu_drm_queue_handle_deferred(ref_crtc);
4113d6c0b56eSmrg	if (flipdata->flip_count > 0)
4114d6c0b56eSmrg		return TRUE;
4115d6c0b56eSmrg
411611bf0794Smrgflip_error:
411711bf0794Smrg	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed: %s\n",
411811bf0794Smrg		   strerror(errno));
411911bf0794Smrg
4120d6c0b56eSmrgerror:
4121d6c0b56eSmrg	if (drm_queue_seq)
4122d6c0b56eSmrg		amdgpu_drm_abort_entry(drm_queue_seq);
4123d6c0b56eSmrg	else if (crtc)
4124d6c0b56eSmrg		drmmode_flip_abort(crtc, flipdata);
412511bf0794Smrg	else {
412611bf0794Smrg		abort(NULL, data);
4127d6c0b56eSmrg		free(flipdata);
412811bf0794Smrg	}
4129d6c0b56eSmrg
4130d6c0b56eSmrg	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
4131d6c0b56eSmrg		   strerror(errno));
413235d5b7c7Smrg	if (handle_deferred)
413335d5b7c7Smrg		amdgpu_drm_queue_handle_deferred(ref_crtc);
4134d6c0b56eSmrg	return FALSE;
4135d6c0b56eSmrg}
4136