drmmode_display.c revision 90f2b693
1d6c0b56eSmrg/*
2d6c0b56eSmrg * Copyright © 2007 Red Hat, Inc.
3d6c0b56eSmrg *
4d6c0b56eSmrg * Permission is hereby granted, free of charge, to any person obtaining a
5d6c0b56eSmrg * copy of this software and associated documentation files (the "Software"),
6d6c0b56eSmrg * to deal in the Software without restriction, including without limitation
7d6c0b56eSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8d6c0b56eSmrg * and/or sell copies of the Software, and to permit persons to whom the
9d6c0b56eSmrg * Software is furnished to do so, subject to the following conditions:
10d6c0b56eSmrg *
11d6c0b56eSmrg * The above copyright notice and this permission notice (including the next
12d6c0b56eSmrg * paragraph) shall be included in all copies or substantial portions of the
13d6c0b56eSmrg * Software.
14d6c0b56eSmrg *
15d6c0b56eSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16d6c0b56eSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17d6c0b56eSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18d6c0b56eSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19d6c0b56eSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20d6c0b56eSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21d6c0b56eSmrg * SOFTWARE.
22d6c0b56eSmrg *
23d6c0b56eSmrg * Authors:
24d6c0b56eSmrg *    Dave Airlie <airlied@redhat.com>
25d6c0b56eSmrg *
26d6c0b56eSmrg */
27d6c0b56eSmrg
28d6c0b56eSmrg#ifdef HAVE_CONFIG_H
29d6c0b56eSmrg#include "config.h"
30d6c0b56eSmrg#endif
31d6c0b56eSmrg
32d6c0b56eSmrg#include <errno.h>
33d6c0b56eSmrg#include <sys/ioctl.h>
34d6c0b56eSmrg#include <time.h>
35d6c0b56eSmrg#include "cursorstr.h"
36d6c0b56eSmrg#include "damagestr.h"
3724b90cf4Smrg#include "inputstr.h"
3824b90cf4Smrg#include "list.h"
39d6c0b56eSmrg#include "micmap.h"
4035d5b7c7Smrg#include "mipointrst.h"
41d6c0b56eSmrg#include "xf86cmap.h"
42504d986fSmrg#include "xf86Priv.h"
43d6c0b56eSmrg#include "sarea.h"
44d6c0b56eSmrg
45d6c0b56eSmrg#include "drmmode_display.h"
46d6c0b56eSmrg#include "amdgpu_bo_helper.h"
47d6c0b56eSmrg#include "amdgpu_glamor.h"
48d6c0b56eSmrg#include "amdgpu_pixmap.h"
49d6c0b56eSmrg
50d6c0b56eSmrg#include <dri.h>
51d6c0b56eSmrg
52d6c0b56eSmrg/* DPMS */
53d6c0b56eSmrg#ifdef HAVE_XEXTPROTO_71
54d6c0b56eSmrg#include <X11/extensions/dpmsconst.h>
55d6c0b56eSmrg#else
56d6c0b56eSmrg#define DPMS_SERVER
57d6c0b56eSmrg#include <X11/extensions/dpms.h>
58d6c0b56eSmrg#endif
59d6c0b56eSmrg
60d6c0b56eSmrg#include <gbm.h>
61d6c0b56eSmrg
62d6c0b56eSmrg#define DEFAULT_NOMINAL_FRAME_RATE 60
63d6c0b56eSmrg
64d6c0b56eSmrgstatic Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height);
65d6c0b56eSmrg
66d6c0b56eSmrgstatic Bool
67d6c0b56eSmrgAMDGPUZaphodStringMatches(ScrnInfoPtr pScrn, const char *s, char *output_name)
68d6c0b56eSmrg{
69d6c0b56eSmrg	int i = 0;
70d6c0b56eSmrg	char s1[20];
71d6c0b56eSmrg
72d6c0b56eSmrg	do {
73d6c0b56eSmrg		switch (*s) {
74d6c0b56eSmrg		case ',':
75d6c0b56eSmrg			s1[i] = '\0';
76d6c0b56eSmrg			i = 0;
77d6c0b56eSmrg			if (strcmp(s1, output_name) == 0)
78d6c0b56eSmrg				return TRUE;
79d6c0b56eSmrg			break;
80d6c0b56eSmrg		case ' ':
81d6c0b56eSmrg		case '\t':
82d6c0b56eSmrg		case '\n':
83d6c0b56eSmrg		case '\r':
84d6c0b56eSmrg			break;
85d6c0b56eSmrg		default:
86d6c0b56eSmrg			s1[i] = *s;
87d6c0b56eSmrg			i++;
88d6c0b56eSmrg			break;
89d6c0b56eSmrg		}
90d6c0b56eSmrg	} while (*s++);
91d6c0b56eSmrg
92d6c0b56eSmrg	s1[i] = '\0';
93d6c0b56eSmrg	if (strcmp(s1, output_name) == 0)
94d6c0b56eSmrg		return TRUE;
95d6c0b56eSmrg
96d6c0b56eSmrg	return FALSE;
97d6c0b56eSmrg}
98d6c0b56eSmrg
9924b90cf4Smrg
100d6c0b56eSmrgstatic PixmapPtr drmmode_create_bo_pixmap(ScrnInfoPtr pScrn,
101d6c0b56eSmrg					  int width, int height,
102d6c0b56eSmrg					  int depth, int bpp,
103d6c0b56eSmrg					  int pitch,
104d6c0b56eSmrg					  struct amdgpu_buffer *bo)
105d6c0b56eSmrg{
106d6c0b56eSmrg	ScreenPtr pScreen = pScrn->pScreen;
107d6c0b56eSmrg	PixmapPtr pixmap;
108d6c0b56eSmrg
109d6c0b56eSmrg	pixmap = (*pScreen->CreatePixmap)(pScreen, 0, 0, depth,
110d6c0b56eSmrg					  AMDGPU_CREATE_PIXMAP_SCANOUT);
111d6c0b56eSmrg	if (!pixmap)
112d6c0b56eSmrg		return NULL;
113d6c0b56eSmrg
114d6c0b56eSmrg	if (!(*pScreen->ModifyPixmapHeader) (pixmap, width, height,
115504d986fSmrg					     depth, bpp, pitch, NULL))
116504d986fSmrg		goto fail;
117d6c0b56eSmrg
118504d986fSmrg	if (!amdgpu_glamor_create_textured_pixmap(pixmap, bo))
119504d986fSmrg		goto fail;
120d6c0b56eSmrg
121504d986fSmrg	if (amdgpu_set_pixmap_bo(pixmap, bo))
122504d986fSmrg		return pixmap;
123d6c0b56eSmrg
124504d986fSmrgfail:
125504d986fSmrg	pScreen->DestroyPixmap(pixmap);
126504d986fSmrg	return NULL;
127d6c0b56eSmrg}
128d6c0b56eSmrg
129d6c0b56eSmrgstatic void drmmode_destroy_bo_pixmap(PixmapPtr pixmap)
130d6c0b56eSmrg{
131d6c0b56eSmrg	ScreenPtr pScreen = pixmap->drawable.pScreen;
132d6c0b56eSmrg
133d6c0b56eSmrg	(*pScreen->DestroyPixmap) (pixmap);
134d6c0b56eSmrg}
135d6c0b56eSmrg
136d6c0b56eSmrgstatic void
137d6c0b56eSmrgdrmmode_ConvertFromKMode(ScrnInfoPtr scrn,
138d6c0b56eSmrg			 drmModeModeInfo * kmode, DisplayModePtr mode)
139d6c0b56eSmrg{
140d6c0b56eSmrg	memset(mode, 0, sizeof(DisplayModeRec));
141d6c0b56eSmrg	mode->status = MODE_OK;
142d6c0b56eSmrg
143d6c0b56eSmrg	mode->Clock = kmode->clock;
144d6c0b56eSmrg
145d6c0b56eSmrg	mode->HDisplay = kmode->hdisplay;
146d6c0b56eSmrg	mode->HSyncStart = kmode->hsync_start;
147d6c0b56eSmrg	mode->HSyncEnd = kmode->hsync_end;
148d6c0b56eSmrg	mode->HTotal = kmode->htotal;
149d6c0b56eSmrg	mode->HSkew = kmode->hskew;
150d6c0b56eSmrg
151d6c0b56eSmrg	mode->VDisplay = kmode->vdisplay;
152d6c0b56eSmrg	mode->VSyncStart = kmode->vsync_start;
153d6c0b56eSmrg	mode->VSyncEnd = kmode->vsync_end;
154d6c0b56eSmrg	mode->VTotal = kmode->vtotal;
155d6c0b56eSmrg	mode->VScan = kmode->vscan;
156d6c0b56eSmrg
157d6c0b56eSmrg	mode->Flags = kmode->flags;	//& FLAG_BITS;
158d6c0b56eSmrg	mode->name = strdup(kmode->name);
159d6c0b56eSmrg
160d6c0b56eSmrg	if (kmode->type & DRM_MODE_TYPE_DRIVER)
161d6c0b56eSmrg		mode->type = M_T_DRIVER;
162d6c0b56eSmrg	if (kmode->type & DRM_MODE_TYPE_PREFERRED)
163d6c0b56eSmrg		mode->type |= M_T_PREFERRED;
164d6c0b56eSmrg	xf86SetModeCrtc(mode, scrn->adjustFlags);
165d6c0b56eSmrg}
166d6c0b56eSmrg
167d6c0b56eSmrgstatic void
168d6c0b56eSmrgdrmmode_ConvertToKMode(ScrnInfoPtr scrn,
169d6c0b56eSmrg		       drmModeModeInfo * kmode, DisplayModePtr mode)
170d6c0b56eSmrg{
171d6c0b56eSmrg	memset(kmode, 0, sizeof(*kmode));
172d6c0b56eSmrg
173d6c0b56eSmrg	kmode->clock = mode->Clock;
174d6c0b56eSmrg	kmode->hdisplay = mode->HDisplay;
175d6c0b56eSmrg	kmode->hsync_start = mode->HSyncStart;
176d6c0b56eSmrg	kmode->hsync_end = mode->HSyncEnd;
177d6c0b56eSmrg	kmode->htotal = mode->HTotal;
178d6c0b56eSmrg	kmode->hskew = mode->HSkew;
179d6c0b56eSmrg
180d6c0b56eSmrg	kmode->vdisplay = mode->VDisplay;
181d6c0b56eSmrg	kmode->vsync_start = mode->VSyncStart;
182d6c0b56eSmrg	kmode->vsync_end = mode->VSyncEnd;
183d6c0b56eSmrg	kmode->vtotal = mode->VTotal;
184d6c0b56eSmrg	kmode->vscan = mode->VScan;
185d6c0b56eSmrg
186d6c0b56eSmrg	kmode->flags = mode->Flags;	//& FLAG_BITS;
187d6c0b56eSmrg	if (mode->name)
188d6c0b56eSmrg		strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
189d6c0b56eSmrg	kmode->name[DRM_DISPLAY_MODE_LEN - 1] = 0;
190d6c0b56eSmrg
191d6c0b56eSmrg}
192d6c0b56eSmrg
19324b90cf4Smrg/*
19424b90cf4Smrg * Utility helper for drmWaitVBlank
19524b90cf4Smrg */
19624b90cf4SmrgBool
19724b90cf4Smrgdrmmode_wait_vblank(xf86CrtcPtr crtc, drmVBlankSeqType type,
19824b90cf4Smrg		    uint32_t target_seq, unsigned long signal, uint64_t *ust,
19924b90cf4Smrg		    uint32_t *result_seq)
20024b90cf4Smrg{
20124b90cf4Smrg	int crtc_id = drmmode_get_crtc_id(crtc);
20224b90cf4Smrg	ScrnInfoPtr scrn = crtc->scrn;
20324b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
20424b90cf4Smrg	drmVBlank vbl;
20524b90cf4Smrg
20624b90cf4Smrg	if (crtc_id == 1)
20724b90cf4Smrg		type |= DRM_VBLANK_SECONDARY;
20824b90cf4Smrg	else if (crtc_id > 1)
20924b90cf4Smrg		type |= (crtc_id << DRM_VBLANK_HIGH_CRTC_SHIFT) &
21024b90cf4Smrg			DRM_VBLANK_HIGH_CRTC_MASK;
21124b90cf4Smrg
21224b90cf4Smrg	vbl.request.type = type;
21324b90cf4Smrg	vbl.request.sequence = target_seq;
21424b90cf4Smrg	vbl.request.signal = signal;
21524b90cf4Smrg
21624b90cf4Smrg	if (drmWaitVBlank(pAMDGPUEnt->fd, &vbl) != 0)
21724b90cf4Smrg		return FALSE;
21824b90cf4Smrg
21924b90cf4Smrg	if (ust)
22024b90cf4Smrg		*ust = (uint64_t)vbl.reply.tval_sec * 1000000 +
22124b90cf4Smrg			vbl.reply.tval_usec;
22224b90cf4Smrg	if (result_seq)
22324b90cf4Smrg		*result_seq = vbl.reply.sequence;
22424b90cf4Smrg
22524b90cf4Smrg	return TRUE;
22624b90cf4Smrg}
22724b90cf4Smrg
228d6c0b56eSmrg/*
229d6c0b56eSmrg * Retrieves present time in microseconds that is compatible
230d6c0b56eSmrg * with units used by vblank timestamps. Depending on the kernel
231d6c0b56eSmrg * version and DRM kernel module configuration, the vblank
232d6c0b56eSmrg * timestamp can either be in real time or monotonic time
233d6c0b56eSmrg */
234d6c0b56eSmrgint drmmode_get_current_ust(int drm_fd, CARD64 * ust)
235d6c0b56eSmrg{
236d6c0b56eSmrg	uint64_t cap_value;
237d6c0b56eSmrg	int ret;
238d6c0b56eSmrg	struct timespec now;
239d6c0b56eSmrg
240d6c0b56eSmrg	ret = drmGetCap(drm_fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap_value);
241d6c0b56eSmrg	if (ret || !cap_value)
242d6c0b56eSmrg		/* old kernel or drm_timestamp_monotonic turned off */
243d6c0b56eSmrg		ret = clock_gettime(CLOCK_REALTIME, &now);
244d6c0b56eSmrg	else
245d6c0b56eSmrg		ret = clock_gettime(CLOCK_MONOTONIC, &now);
246d6c0b56eSmrg	if (ret)
247d6c0b56eSmrg		return ret;
248d6c0b56eSmrg	*ust = ((CARD64) now.tv_sec * 1000000) + ((CARD64) now.tv_nsec / 1000);
249d6c0b56eSmrg	return 0;
250d6c0b56eSmrg}
251d6c0b56eSmrg
252d6c0b56eSmrg/*
253d6c0b56eSmrg * Get current frame count and frame count timestamp of the crtc.
254d6c0b56eSmrg */
255d6c0b56eSmrgint drmmode_crtc_get_ust_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc)
256d6c0b56eSmrg{
257d6c0b56eSmrg	ScrnInfoPtr scrn = crtc->scrn;
25824b90cf4Smrg	uint32_t seq;
259d6c0b56eSmrg
26024b90cf4Smrg	if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, ust, &seq)) {
261d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
262d6c0b56eSmrg			   "get vblank counter failed: %s\n", strerror(errno));
26324b90cf4Smrg		return -1;
264d6c0b56eSmrg	}
265d6c0b56eSmrg
26624b90cf4Smrg	*msc = seq;
267d6c0b56eSmrg
268d6c0b56eSmrg	return Success;
269d6c0b56eSmrg}
270d6c0b56eSmrg
27190f2b693Smrgstatic uint32_t
27290f2b693Smrgdrmmode_crtc_get_prop_id(uint32_t drm_fd,
27390f2b693Smrg			 drmModeObjectPropertiesPtr props,
27490f2b693Smrg			 char const* name)
27590f2b693Smrg{
27690f2b693Smrg	uint32_t i, prop_id = 0;
27790f2b693Smrg
27890f2b693Smrg	for (i = 0; !prop_id && i < props->count_props; ++i) {
27990f2b693Smrg		drmModePropertyPtr drm_prop =
28090f2b693Smrg			drmModeGetProperty(drm_fd, props->props[i]);
28190f2b693Smrg
28290f2b693Smrg		if (!drm_prop)
28390f2b693Smrg			continue;
28490f2b693Smrg
28590f2b693Smrg		if (strcmp(drm_prop->name, name) == 0)
28690f2b693Smrg			prop_id = drm_prop->prop_id;
28790f2b693Smrg
28890f2b693Smrg		drmModeFreeProperty(drm_prop);
28990f2b693Smrg	}
29090f2b693Smrg
29190f2b693Smrg	return prop_id;
29290f2b693Smrg}
29390f2b693Smrg
29490f2b693Smrgstatic void drmmode_crtc_vrr_init(int drm_fd, xf86CrtcPtr crtc)
29590f2b693Smrg{
29690f2b693Smrg	drmModeObjectPropertiesPtr drm_props;
29790f2b693Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
29890f2b693Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
29990f2b693Smrg
30090f2b693Smrg	if (drmmode->vrr_prop_id)
30190f2b693Smrg		return;
30290f2b693Smrg
30390f2b693Smrg	drm_props = drmModeObjectGetProperties(drm_fd,
30490f2b693Smrg					       drmmode_crtc->mode_crtc->crtc_id,
30590f2b693Smrg					       DRM_MODE_OBJECT_CRTC);
30690f2b693Smrg
30790f2b693Smrg	if (!drm_props)
30890f2b693Smrg		return;
30990f2b693Smrg
31090f2b693Smrg	drmmode->vrr_prop_id = drmmode_crtc_get_prop_id(drm_fd,
31190f2b693Smrg							drm_props,
31290f2b693Smrg							"VRR_ENABLED");
31390f2b693Smrg
31490f2b693Smrg	drmModeFreeObjectProperties(drm_props);
31590f2b693Smrg}
31690f2b693Smrg
31790f2b693Smrgvoid drmmode_crtc_set_vrr(xf86CrtcPtr crtc, Bool enabled)
31890f2b693Smrg{
31990f2b693Smrg	ScrnInfoPtr pScrn = crtc->scrn;
32090f2b693Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
32190f2b693Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
32290f2b693Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
32390f2b693Smrg
32490f2b693Smrg	if (drmmode->vrr_prop_id &&
32590f2b693Smrg	    drmmode_crtc->vrr_enabled != enabled &&
32690f2b693Smrg	    drmModeObjectSetProperty(pAMDGPUEnt->fd,
32790f2b693Smrg				     drmmode_crtc->mode_crtc->crtc_id,
32890f2b693Smrg				     DRM_MODE_OBJECT_CRTC,
32990f2b693Smrg				     drmmode->vrr_prop_id,
33090f2b693Smrg				     enabled) == 0)
33190f2b693Smrg		drmmode_crtc->vrr_enabled = enabled;
33290f2b693Smrg}
33390f2b693Smrg
334d6c0b56eSmrgstatic void
335d6c0b56eSmrgdrmmode_do_crtc_dpms(xf86CrtcPtr crtc, int mode)
336d6c0b56eSmrg{
337d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
338d6c0b56eSmrg	ScrnInfoPtr scrn = crtc->scrn;
339d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
340d6c0b56eSmrg	CARD64 ust;
341d6c0b56eSmrg	int ret;
342d6c0b56eSmrg
343d6c0b56eSmrg	if (drmmode_crtc->dpms_mode == DPMSModeOn && mode != DPMSModeOn) {
34424b90cf4Smrg		uint32_t seq;
345d6c0b56eSmrg
34635d5b7c7Smrg		amdgpu_drm_wait_pending_flip(crtc);
347504d986fSmrg
348d6c0b56eSmrg		/*
349d6c0b56eSmrg		 * On->Off transition: record the last vblank time,
350d6c0b56eSmrg		 * sequence number and frame period.
351d6c0b56eSmrg		 */
35224b90cf4Smrg		if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, &ust,
35324b90cf4Smrg					 &seq))
354d6c0b56eSmrg			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
355d6c0b56eSmrg				   "%s cannot get last vblank counter\n",
356d6c0b56eSmrg				   __func__);
357d6c0b56eSmrg		else {
358d6c0b56eSmrg			CARD64 nominal_frame_rate, pix_in_frame;
359d6c0b56eSmrg
360d6c0b56eSmrg			drmmode_crtc->dpms_last_ust = ust;
361d6c0b56eSmrg			drmmode_crtc->dpms_last_seq = seq;
362d6c0b56eSmrg			nominal_frame_rate = crtc->mode.Clock;
363d6c0b56eSmrg			nominal_frame_rate *= 1000;
364d6c0b56eSmrg			pix_in_frame = crtc->mode.HTotal * crtc->mode.VTotal;
365d6c0b56eSmrg			if (nominal_frame_rate == 0 || pix_in_frame == 0)
366d6c0b56eSmrg				nominal_frame_rate = DEFAULT_NOMINAL_FRAME_RATE;
367d6c0b56eSmrg			else
368d6c0b56eSmrg				nominal_frame_rate /= pix_in_frame;
369d6c0b56eSmrg			drmmode_crtc->dpms_last_fps = nominal_frame_rate;
370d6c0b56eSmrg		}
37135d5b7c7Smrg
37235d5b7c7Smrg		drmmode_crtc->dpms_mode = mode;
37335d5b7c7Smrg		amdgpu_drm_queue_handle_deferred(crtc);
374d6c0b56eSmrg	} else if (drmmode_crtc->dpms_mode != DPMSModeOn && mode == DPMSModeOn) {
375d6c0b56eSmrg		/*
376d6c0b56eSmrg		 * Off->On transition: calculate and accumulate the
377d6c0b56eSmrg		 * number of interpolated vblanks while we were in Off state
378d6c0b56eSmrg		 */
379d6c0b56eSmrg		ret = drmmode_get_current_ust(pAMDGPUEnt->fd, &ust);
380d6c0b56eSmrg		if (ret)
381d6c0b56eSmrg			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
382d6c0b56eSmrg				   "%s cannot get current time\n", __func__);
383d6c0b56eSmrg		else if (drmmode_crtc->dpms_last_ust) {
384d6c0b56eSmrg			CARD64 time_elapsed, delta_seq;
385d6c0b56eSmrg			time_elapsed = ust - drmmode_crtc->dpms_last_ust;
386d6c0b56eSmrg			delta_seq = time_elapsed * drmmode_crtc->dpms_last_fps;
387d6c0b56eSmrg			delta_seq /= 1000000;
388d6c0b56eSmrg			drmmode_crtc->interpolated_vblanks += delta_seq;
389d6c0b56eSmrg
390d6c0b56eSmrg		}
39135d5b7c7Smrg
39235d5b7c7Smrg		drmmode_crtc->dpms_mode = DPMSModeOn;
393d6c0b56eSmrg	}
394d6c0b56eSmrg}
395d6c0b56eSmrg
396d6c0b56eSmrgstatic void
397d6c0b56eSmrgdrmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
398d6c0b56eSmrg{
399d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
400d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
401d6c0b56eSmrg
402d6c0b56eSmrg	/* Disable unused CRTCs and enable/disable active CRTCs */
403504d986fSmrg	if (!crtc->enabled || mode != DPMSModeOn) {
40435d5b7c7Smrg		drmmode_do_crtc_dpms(crtc, DPMSModeOff);
405d6c0b56eSmrg		drmModeSetCrtc(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id,
406d6c0b56eSmrg			       0, 0, 0, NULL, 0, NULL);
40724b90cf4Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, NULL);
408504d986fSmrg	} else if (drmmode_crtc->dpms_mode != DPMSModeOn)
409d6c0b56eSmrg		crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation,
410d6c0b56eSmrg					    crtc->x, crtc->y);
411d6c0b56eSmrg}
412d6c0b56eSmrg
41390f2b693Smrg#ifdef USE_GLAMOR
41490f2b693Smrg
415d6c0b56eSmrgstatic PixmapPtr
416d6c0b56eSmrgcreate_pixmap_for_fbcon(drmmode_ptr drmmode,
417d6c0b56eSmrg			ScrnInfoPtr pScrn, int fbcon_id)
418d6c0b56eSmrg{
41935d5b7c7Smrg	ScreenPtr pScreen = pScrn->pScreen;
420d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
42135d5b7c7Smrg	PixmapPtr pixmap = NULL;
422d6c0b56eSmrg	drmModeFBPtr fbcon;
423d6c0b56eSmrg
424d6c0b56eSmrg	fbcon = drmModeGetFB(pAMDGPUEnt->fd, fbcon_id);
42535d5b7c7Smrg	if (!fbcon)
426d6c0b56eSmrg		return NULL;
427d6c0b56eSmrg
428d6c0b56eSmrg	if (fbcon->depth != pScrn->depth ||
429d6c0b56eSmrg	    fbcon->width != pScrn->virtualX ||
430d6c0b56eSmrg	    fbcon->height != pScrn->virtualY)
431d6c0b56eSmrg		goto out_free_fb;
432d6c0b56eSmrg
43335d5b7c7Smrg	pixmap = fbCreatePixmap(pScreen, 0, 0, fbcon->depth, 0);
43435d5b7c7Smrg	if (!pixmap)
435d6c0b56eSmrg		goto out_free_fb;
436d6c0b56eSmrg
43735d5b7c7Smrg	pScreen->ModifyPixmapHeader(pixmap, fbcon->width, fbcon->height, 0, 0,
43835d5b7c7Smrg				    fbcon->pitch, NULL);
43935d5b7c7Smrg	pixmap->devPrivate.ptr = NULL;
440d6c0b56eSmrg
44135d5b7c7Smrg	if (!glamor_egl_create_textured_pixmap(pixmap, fbcon->handle,
44235d5b7c7Smrg					       pixmap->devKind)) {
44335d5b7c7Smrg		pScreen->DestroyPixmap(pixmap);
44435d5b7c7Smrg		pixmap = NULL;
445d6c0b56eSmrg	}
446d6c0b56eSmrg
447d6c0b56eSmrgout_free_fb:
448d6c0b56eSmrg	drmModeFreeFB(fbcon);
449d6c0b56eSmrg	return pixmap;
450d6c0b56eSmrg}
451d6c0b56eSmrg
45290f2b693Smrg#endif /* USE_GLAMOR */
45390f2b693Smrg
454d6c0b56eSmrgvoid drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
455d6c0b56eSmrg{
45690f2b693Smrg#ifdef USE_GLAMOR
457d6c0b56eSmrg	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
45890f2b693Smrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
459d6c0b56eSmrg	ScreenPtr pScreen = pScrn->pScreen;
46024b90cf4Smrg	PixmapPtr src, dst = pScreen->GetScreenPixmap(pScreen);
46124b90cf4Smrg	struct drmmode_fb *fb = amdgpu_pixmap_get_fb(dst);
462d6c0b56eSmrg	int fbcon_id = 0;
463d6c0b56eSmrg	GCPtr gc;
464d6c0b56eSmrg	int i;
465d6c0b56eSmrg
46690f2b693Smrg	if (!info->use_glamor)
46790f2b693Smrg		return;
46890f2b693Smrg
469d6c0b56eSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
470d6c0b56eSmrg		drmmode_crtc_private_ptr drmmode_crtc = xf86_config->crtc[i]->driver_private;
471d6c0b56eSmrg
472d6c0b56eSmrg		if (drmmode_crtc->mode_crtc->buffer_id)
473d6c0b56eSmrg			fbcon_id = drmmode_crtc->mode_crtc->buffer_id;
474d6c0b56eSmrg	}
475d6c0b56eSmrg
476d6c0b56eSmrg	if (!fbcon_id)
477d6c0b56eSmrg		return;
478d6c0b56eSmrg
47924b90cf4Smrg	if (fbcon_id == fb->handle) {
480d6c0b56eSmrg		/* in some rare case there might be no fbcon and we might already
481d6c0b56eSmrg		 * be the one with the current fb to avoid a false deadlck in
482d6c0b56eSmrg		 * kernel ttm code just do nothing as anyway there is nothing
483d6c0b56eSmrg		 * to do
484d6c0b56eSmrg		 */
485d6c0b56eSmrg		return;
486d6c0b56eSmrg	}
487d6c0b56eSmrg
488d6c0b56eSmrg	src = create_pixmap_for_fbcon(drmmode, pScrn, fbcon_id);
489d6c0b56eSmrg	if (!src)
490d6c0b56eSmrg		return;
491d6c0b56eSmrg
492d6c0b56eSmrg	gc = GetScratchGC(pScrn->depth, pScreen);
493d6c0b56eSmrg	ValidateGC(&dst->drawable, gc);
494d6c0b56eSmrg
495d6c0b56eSmrg	(*gc->ops->CopyArea)(&src->drawable, &dst->drawable, gc, 0, 0,
496d6c0b56eSmrg			     pScrn->virtualX, pScrn->virtualY, 0, 0);
497d6c0b56eSmrg
498d6c0b56eSmrg	FreeScratchGC(gc);
499d6c0b56eSmrg
500d6c0b56eSmrg	pScreen->canDoBGNoneRoot = TRUE;
50135d5b7c7Smrg	pScreen->DestroyPixmap(src);
50290f2b693Smrg#endif
503d6c0b56eSmrg
504d6c0b56eSmrg	return;
505d6c0b56eSmrg}
506d6c0b56eSmrg
50724b90cf4Smrgvoid
508d6c0b56eSmrgdrmmode_crtc_scanout_destroy(drmmode_ptr drmmode,
509d6c0b56eSmrg			     struct drmmode_scanout *scanout)
510d6c0b56eSmrg{
511d6c0b56eSmrg
512d6c0b56eSmrg	if (scanout->pixmap) {
513d6c0b56eSmrg		drmmode_destroy_bo_pixmap(scanout->pixmap);
514d6c0b56eSmrg		scanout->pixmap = NULL;
515d6c0b56eSmrg	}
516d6c0b56eSmrg
517d6c0b56eSmrg	if (scanout->bo) {
518d6c0b56eSmrg		amdgpu_bo_unref(&scanout->bo);
519d6c0b56eSmrg		scanout->bo = NULL;
520d6c0b56eSmrg	}
521504d986fSmrg}
522504d986fSmrg
52324b90cf4Smrgvoid
52490f2b693Smrgdrmmode_crtc_scanout_free(xf86CrtcPtr crtc)
525504d986fSmrg{
52690f2b693Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
52790f2b693Smrg
52890f2b693Smrg	if (drmmode_crtc->scanout_update_pending) {
52990f2b693Smrg		amdgpu_drm_wait_pending_flip(crtc);
53090f2b693Smrg		amdgpu_drm_abort_entry(drmmode_crtc->scanout_update_pending);
53190f2b693Smrg		drmmode_crtc->scanout_update_pending = 0;
53290f2b693Smrg		amdgpu_drm_queue_handle_deferred(crtc);
53390f2b693Smrg	}
53490f2b693Smrg
53524b90cf4Smrg	drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
53624b90cf4Smrg				     &drmmode_crtc->scanout[0]);
53724b90cf4Smrg	drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
53824b90cf4Smrg				     &drmmode_crtc->scanout[1]);
539d6c0b56eSmrg
54024b90cf4Smrg	if (drmmode_crtc->scanout_damage)
541504d986fSmrg		DamageDestroy(drmmode_crtc->scanout_damage);
542d6c0b56eSmrg}
543d6c0b56eSmrg
54424b90cf4SmrgPixmapPtr
54511bf0794Smrgdrmmode_crtc_scanout_create(xf86CrtcPtr crtc, struct drmmode_scanout *scanout,
54611bf0794Smrg			    int width, int height)
547d6c0b56eSmrg{
548d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
549d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
550d6c0b56eSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
55111bf0794Smrg	int pitch;
552d6c0b56eSmrg
55311bf0794Smrg	if (scanout->pixmap) {
554d6c0b56eSmrg		if (scanout->width == width && scanout->height == height)
55511bf0794Smrg			return scanout->pixmap;
556d6c0b56eSmrg
557d6c0b56eSmrg		drmmode_crtc_scanout_destroy(drmmode, scanout);
558d6c0b56eSmrg	}
559d6c0b56eSmrg
560d6c0b56eSmrg	scanout->bo = amdgpu_alloc_pixmap_bo(pScrn, width, height,
56111bf0794Smrg					     pScrn->depth, 0,
56211bf0794Smrg					     pScrn->bitsPerPixel, &pitch);
563d6c0b56eSmrg	if (!scanout->bo) {
564d6c0b56eSmrg		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
56511bf0794Smrg			   "Failed to allocate scanout buffer memory\n");
56624b90cf4Smrg		return NULL;
567d6c0b56eSmrg	}
568d6c0b56eSmrg
569d6c0b56eSmrg	scanout->pixmap = drmmode_create_bo_pixmap(pScrn,
570d6c0b56eSmrg						 width, height,
571d6c0b56eSmrg						 pScrn->depth,
572d6c0b56eSmrg						 pScrn->bitsPerPixel,
57311bf0794Smrg						 pitch, scanout->bo);
57424b90cf4Smrg	if (!scanout->pixmap) {
57524b90cf4Smrg		ErrorF("failed to create CRTC scanout pixmap\n");
57624b90cf4Smrg		goto error;
57724b90cf4Smrg	}
57824b90cf4Smrg
57924b90cf4Smrg	if (amdgpu_pixmap_get_fb(scanout->pixmap)) {
58011bf0794Smrg		scanout->width = width;
58111bf0794Smrg		scanout->height = height;
58211bf0794Smrg	} else {
58324b90cf4Smrg		ErrorF("failed to create CRTC scanout FB\n");
58424b90cf4Smrgerror:
58511bf0794Smrg		drmmode_crtc_scanout_destroy(drmmode, scanout);
586d6c0b56eSmrg	}
587d6c0b56eSmrg
58811bf0794Smrg	return scanout->pixmap;
589d6c0b56eSmrg}
590d6c0b56eSmrg
591d6c0b56eSmrgstatic void
592d6c0b56eSmrgamdgpu_screen_damage_report(DamagePtr damage, RegionPtr region, void *closure)
593d6c0b56eSmrg{
59424b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = closure;
59524b90cf4Smrg
59624b90cf4Smrg	if (drmmode_crtc->ignore_damage) {
59724b90cf4Smrg		RegionEmpty(&damage->damage);
59824b90cf4Smrg		drmmode_crtc->ignore_damage = FALSE;
59924b90cf4Smrg		return;
60024b90cf4Smrg	}
60124b90cf4Smrg
602d6c0b56eSmrg	/* Only keep track of the extents */
603d6c0b56eSmrg	RegionUninit(&damage->damage);
604d6c0b56eSmrg	damage->damage.data = NULL;
605d6c0b56eSmrg}
606d6c0b56eSmrg
60724b90cf4Smrgstatic void
60824b90cf4Smrgdrmmode_screen_damage_destroy(DamagePtr damage, void *closure)
60924b90cf4Smrg{
61024b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = closure;
61124b90cf4Smrg
61224b90cf4Smrg	drmmode_crtc->scanout_damage = NULL;
61324b90cf4Smrg	RegionUninit(&drmmode_crtc->scanout_last_region);
61424b90cf4Smrg}
61524b90cf4Smrg
616d6c0b56eSmrgstatic Bool
617d6c0b56eSmrgdrmmode_can_use_hw_cursor(xf86CrtcPtr crtc)
618d6c0b56eSmrg{
619d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(crtc->scrn);
620d6c0b56eSmrg
621d6c0b56eSmrg	/* Check for Option "SWcursor" */
622d6c0b56eSmrg	if (xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE))
623d6c0b56eSmrg		return FALSE;
624d6c0b56eSmrg
625d6c0b56eSmrg	/* Fall back to SW cursor if the CRTC is transformed */
626d6c0b56eSmrg	if (crtc->transformPresent)
627d6c0b56eSmrg		return FALSE;
628d6c0b56eSmrg
62924b90cf4Smrg#if XF86_CRTC_VERSION < 7
630d6c0b56eSmrg	/* Xorg doesn't correctly handle cursor position transform in the
631d6c0b56eSmrg	 * rotation case
632d6c0b56eSmrg	 */
633d6c0b56eSmrg	if (crtc->driverIsPerformingTransform &&
634d6c0b56eSmrg	    (crtc->rotation & 0xf) != RR_Rotate_0)
635d6c0b56eSmrg		return FALSE;
636d6c0b56eSmrg#endif
637d6c0b56eSmrg
638504d986fSmrg	/* HW cursor not supported with RandR 1.4 multihead up to 1.18.99.901 */
639504d986fSmrg	if (xorgGetVersion() <= XORG_VERSION_NUMERIC(1,18,99,901,0) &&
640504d986fSmrg	    !xorg_list_is_empty(&crtc->scrn->pScreen->pixmap_dirty_list))
641d6c0b56eSmrg		return FALSE;
642d6c0b56eSmrg
643d6c0b56eSmrg	return TRUE;
644d6c0b56eSmrg}
645d6c0b56eSmrg
64611bf0794Smrgstatic void
64711bf0794Smrgdrmmode_crtc_update_tear_free(xf86CrtcPtr crtc)
64811bf0794Smrg{
64911bf0794Smrg	AMDGPUInfoPtr info = AMDGPUPTR(crtc->scrn);
65011bf0794Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
65111bf0794Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
65211bf0794Smrg	int i;
65311bf0794Smrg
65411bf0794Smrg	drmmode_crtc->tear_free = FALSE;
65511bf0794Smrg
65611bf0794Smrg	for (i = 0; i < xf86_config->num_output; i++) {
65711bf0794Smrg		xf86OutputPtr output = xf86_config->output[i];
65811bf0794Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
65911bf0794Smrg
66011bf0794Smrg		if (output->crtc != crtc)
66111bf0794Smrg			continue;
66211bf0794Smrg
66311bf0794Smrg		if (drmmode_output->tear_free == 1 ||
66411bf0794Smrg		    (drmmode_output->tear_free == 2 &&
66524b90cf4Smrg		     (crtc->scrn->pScreen->isGPU ||
66611bf0794Smrg		      info->shadow_primary ||
66711bf0794Smrg		      crtc->transformPresent || crtc->rotation != RR_Rotate_0))) {
66811bf0794Smrg			drmmode_crtc->tear_free = TRUE;
66911bf0794Smrg			return;
67011bf0794Smrg		}
67111bf0794Smrg	}
67211bf0794Smrg}
67311bf0794Smrg
67411bf0794Smrg#if XF86_CRTC_VERSION < 7
67511bf0794Smrg#define XF86DriverTransformOutput TRUE
67611bf0794Smrg#define XF86DriverTransformNone FALSE
67711bf0794Smrg#endif
67811bf0794Smrg
679d6c0b56eSmrgstatic Bool
680d6c0b56eSmrgdrmmode_handle_transform(xf86CrtcPtr crtc)
681d6c0b56eSmrg{
682d6c0b56eSmrg	Bool ret;
683d6c0b56eSmrg
68424b90cf4Smrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,903,0)
68535d5b7c7Smrg	crtc->driverIsPerformingTransform = XF86DriverTransformOutput;
68624b90cf4Smrg#else
68724b90cf4Smrg	crtc->driverIsPerformingTransform = !crtc->transformPresent &&
68824b90cf4Smrg		(crtc->rotation & 0xf) == RR_Rotate_0;
68924b90cf4Smrg#endif
690d6c0b56eSmrg
691d6c0b56eSmrg	ret = xf86CrtcRotate(crtc);
692d6c0b56eSmrg
693d6c0b56eSmrg	crtc->driverIsPerformingTransform &= ret && crtc->transform_in_use;
694d6c0b56eSmrg
695d6c0b56eSmrg	return ret;
696d6c0b56eSmrg}
697d6c0b56eSmrg
69811bf0794Smrg
69911bf0794Smrgstatic void
70011bf0794Smrgdrmmode_crtc_prime_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
70124b90cf4Smrg				  unsigned scanout_id, struct drmmode_fb **fb,
70224b90cf4Smrg				  int *x, int *y)
70311bf0794Smrg{
70411bf0794Smrg	ScrnInfoPtr scrn = crtc->scrn;
70511bf0794Smrg	ScreenPtr screen = scrn->pScreen;
70611bf0794Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
70711bf0794Smrg
70811bf0794Smrg	if (drmmode_crtc->tear_free &&
70911bf0794Smrg	    !drmmode_crtc->scanout[1].pixmap) {
71011bf0794Smrg		RegionPtr region;
71111bf0794Smrg		BoxPtr box;
71211bf0794Smrg
71311bf0794Smrg		drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1],
71411bf0794Smrg					    mode->HDisplay,
71511bf0794Smrg					    mode->VDisplay);
71611bf0794Smrg		region = &drmmode_crtc->scanout_last_region;
71711bf0794Smrg		RegionUninit(region);
71811bf0794Smrg		region->data = NULL;
71911bf0794Smrg		box = RegionExtents(region);
72011bf0794Smrg		box->x1 = crtc->x;
72111bf0794Smrg		box->y1 = crtc->y;
72211bf0794Smrg		box->x2 = crtc->x + mode->HDisplay;
72311bf0794Smrg		box->y2 = crtc->y + mode->VDisplay;
72411bf0794Smrg	}
72511bf0794Smrg
72611bf0794Smrg	if (scanout_id != drmmode_crtc->scanout_id) {
72711bf0794Smrg		PixmapDirtyUpdatePtr dirty = NULL;
72811bf0794Smrg
72911bf0794Smrg		xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list,
73011bf0794Smrg					 ent) {
73124b90cf4Smrg			if (amdgpu_dirty_src_equals(dirty, drmmode_crtc->prime_scanout_pixmap)) {
73211bf0794Smrg				dirty->slave_dst =
73311bf0794Smrg					drmmode_crtc->scanout[scanout_id].pixmap;
73411bf0794Smrg				break;
73511bf0794Smrg			}
73611bf0794Smrg		}
73711bf0794Smrg
73811bf0794Smrg		if (!drmmode_crtc->tear_free) {
73911bf0794Smrg			GCPtr gc = GetScratchGC(scrn->depth, screen);
74011bf0794Smrg
74111bf0794Smrg			ValidateGC(&drmmode_crtc->scanout[0].pixmap->drawable, gc);
74211bf0794Smrg			gc->ops->CopyArea(&drmmode_crtc->scanout[1].pixmap->drawable,
74311bf0794Smrg					  &drmmode_crtc->scanout[0].pixmap->drawable,
74411bf0794Smrg					  gc, 0, 0, mode->HDisplay, mode->VDisplay,
74511bf0794Smrg					  0, 0);
74611bf0794Smrg			FreeScratchGC(gc);
74711bf0794Smrg			amdgpu_glamor_finish(scrn);
74811bf0794Smrg		}
74911bf0794Smrg	}
75011bf0794Smrg
75124b90cf4Smrg	*fb = amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap);
75211bf0794Smrg	*x = *y = 0;
75311bf0794Smrg	drmmode_crtc->scanout_id = scanout_id;
75411bf0794Smrg}
75511bf0794Smrg
75611bf0794Smrg
75711bf0794Smrgstatic void
75811bf0794Smrgdrmmode_crtc_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode,
75924b90cf4Smrg			    unsigned scanout_id, struct drmmode_fb **fb, int *x,
76024b90cf4Smrg			    int *y)
76111bf0794Smrg{
76211bf0794Smrg	ScrnInfoPtr scrn = crtc->scrn;
76311bf0794Smrg	ScreenPtr screen = scrn->pScreen;
76411bf0794Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
76511bf0794Smrg
76624b90cf4Smrg	drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[scanout_id],
76711bf0794Smrg				    mode->HDisplay, mode->VDisplay);
76811bf0794Smrg	if (drmmode_crtc->tear_free) {
76924b90cf4Smrg		drmmode_crtc_scanout_create(crtc,
77024b90cf4Smrg					    &drmmode_crtc->scanout[scanout_id ^ 1],
77111bf0794Smrg					    mode->HDisplay, mode->VDisplay);
77211bf0794Smrg	}
77311bf0794Smrg
77424b90cf4Smrg	if (drmmode_crtc->scanout[scanout_id].pixmap &&
77524b90cf4Smrg	    (!drmmode_crtc->tear_free ||
77624b90cf4Smrg	     drmmode_crtc->scanout[scanout_id ^ 1].pixmap)) {
77735d5b7c7Smrg		BoxRec extents = { .x1 = 0, .y1 = 0,
77835d5b7c7Smrg				   .x2 = scrn->virtualX, .y2 = scrn->virtualY };
77911bf0794Smrg
78011bf0794Smrg		if (!drmmode_crtc->scanout_damage) {
78111bf0794Smrg			drmmode_crtc->scanout_damage =
78211bf0794Smrg				DamageCreate(amdgpu_screen_damage_report,
78324b90cf4Smrg					     drmmode_screen_damage_destroy,
78424b90cf4Smrg					     DamageReportRawRegion,
78524b90cf4Smrg					     TRUE, screen, drmmode_crtc);
78624b90cf4Smrg			DamageRegister(&screen->root->drawable,
78711bf0794Smrg				       drmmode_crtc->scanout_damage);
78811bf0794Smrg		}
78911bf0794Smrg
79024b90cf4Smrg		*fb = amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap);
79111bf0794Smrg		*x = *y = 0;
79211bf0794Smrg
79390f2b693Smrg		if (amdgpu_scanout_do_update(crtc, scanout_id,
79490f2b693Smrg					     screen->GetWindowPixmap(screen->root),
79590f2b693Smrg					     extents)) {
79690f2b693Smrg			RegionEmpty(DamageRegion(drmmode_crtc->scanout_damage));
79790f2b693Smrg			amdgpu_glamor_finish(scrn);
79890f2b693Smrg
79990f2b693Smrg			if (!drmmode_crtc->flip_pending) {
80090f2b693Smrg				amdgpu_drm_abort_entry(drmmode_crtc->
80190f2b693Smrg						       scanout_update_pending);
80290f2b693Smrg			}
80390f2b693Smrg		}
80411bf0794Smrg	}
80511bf0794Smrg}
80611bf0794Smrg
80735d5b7c7Smrgstatic char *cm_prop_names[] = {
80835d5b7c7Smrg	"DEGAMMA_LUT",
80935d5b7c7Smrg	"CTM",
81035d5b7c7Smrg	"GAMMA_LUT",
81135d5b7c7Smrg	"DEGAMMA_LUT_SIZE",
81235d5b7c7Smrg	"GAMMA_LUT_SIZE",
81335d5b7c7Smrg};
81435d5b7c7Smrg
81535d5b7c7Smrg/**
81635d5b7c7Smrg * Return the enum of the color management property with the given name.
81735d5b7c7Smrg */
81835d5b7c7Smrgstatic enum drmmode_cm_prop get_cm_enum_from_str(const char *prop_name)
81935d5b7c7Smrg{
82035d5b7c7Smrg	enum drmmode_cm_prop ret;
82135d5b7c7Smrg
82235d5b7c7Smrg	for (ret = 0; ret < CM_NUM_PROPS; ret++) {
82335d5b7c7Smrg		if (!strcmp(prop_name, cm_prop_names[ret]))
82435d5b7c7Smrg			return ret;
82535d5b7c7Smrg	}
82635d5b7c7Smrg	return CM_INVALID_PROP;
82735d5b7c7Smrg}
82835d5b7c7Smrg
82935d5b7c7Smrg/**
83035d5b7c7Smrg * If legacy LUT is a, and non-legacy LUT is b, then the result of b(a(x)) is
83135d5b7c7Smrg * returned in out_lut. out_lut's length is expected to be the same as the
83235d5b7c7Smrg * non-legacy LUT b.
83335d5b7c7Smrg *
83435d5b7c7Smrg * @a_(red|green|blue): The red, green, and blue components of the legacy LUT.
83535d5b7c7Smrg * @b_lut: The non-legacy LUT, in DRM's color LUT format.
83635d5b7c7Smrg * @out_lut: The composed LUT, in DRM's color LUT format.
83735d5b7c7Smrg * @len_a: Length of legacy lut.
83835d5b7c7Smrg * @len_b: Length of non-legacy lut.
83935d5b7c7Smrg */
84035d5b7c7Smrgstatic void drmmode_lut_compose(uint16_t *a_red,
84135d5b7c7Smrg				uint16_t *a_green,
84235d5b7c7Smrg				uint16_t *a_blue,
84335d5b7c7Smrg				struct drm_color_lut *b_lut,
84435d5b7c7Smrg				struct drm_color_lut *out_lut,
84535d5b7c7Smrg				uint32_t len_a, uint32_t len_b)
84635d5b7c7Smrg{
84735d5b7c7Smrg	uint32_t i_l, i_r, i;
84835d5b7c7Smrg	uint32_t i_amax, i_bmax;
84935d5b7c7Smrg	uint32_t coeff_ibmax;
85035d5b7c7Smrg	uint32_t j;
85135d5b7c7Smrg	uint64_t a_out_ibmax;
85235d5b7c7Smrg	int color;
85335d5b7c7Smrg	size_t struct_size = sizeof(struct drm_color_lut);
85435d5b7c7Smrg
85535d5b7c7Smrg	uint32_t max_lut = (1 << 16) - 1;
85635d5b7c7Smrg
85735d5b7c7Smrg	i_amax = len_a - 1;
85835d5b7c7Smrg	i_bmax = len_b - 1;
85935d5b7c7Smrg
86035d5b7c7Smrg	/* A linear interpolation is done on the legacy LUT before it is
86135d5b7c7Smrg	 * composed, to bring it up-to-size with the non-legacy LUT. The
86235d5b7c7Smrg	 * interpolation uses integers by keeping things multiplied until the
86335d5b7c7Smrg	 * last moment.
86435d5b7c7Smrg	 */
86535d5b7c7Smrg	for (color = 0; color < 3; color++) {
86635d5b7c7Smrg		uint16_t *a, *b, *out;
86735d5b7c7Smrg
86835d5b7c7Smrg		/* Set the initial pointers to the right color components. The
86935d5b7c7Smrg		 * inner for-loop will then maintain the correct offset from
87035d5b7c7Smrg		 * the initial element.
87135d5b7c7Smrg		 */
87235d5b7c7Smrg		if (color == 0) {
87335d5b7c7Smrg			a = a_red;
87435d5b7c7Smrg			b = &b_lut[0].red;
87535d5b7c7Smrg			out = &out_lut[0].red;
87635d5b7c7Smrg		} else if (color == 1) {
87735d5b7c7Smrg			a = a_green;
87835d5b7c7Smrg			b = &b_lut[0].green;
87935d5b7c7Smrg			out = &out_lut[0].green;
88035d5b7c7Smrg		} else {
88135d5b7c7Smrg			a = a_blue;
88235d5b7c7Smrg			b = &b_lut[0].blue;
88335d5b7c7Smrg			out = &out_lut[0].blue;
88435d5b7c7Smrg		}
88535d5b7c7Smrg
88635d5b7c7Smrg		for (i = 0; i < len_b; i++) {
88735d5b7c7Smrg			/* i_l and i_r tracks the left and right elements in
88835d5b7c7Smrg			 * a_lut, to the sample point i. Also handle last
88935d5b7c7Smrg			 * element edge case, when i_l = i_amax.
89035d5b7c7Smrg			 */
89135d5b7c7Smrg			i_l = i * i_amax / i_bmax;
89235d5b7c7Smrg			i_r = i_l + !!(i_amax - i_l);
89335d5b7c7Smrg
89435d5b7c7Smrg			/* coeff is intended to be in [0, 1), depending on
89535d5b7c7Smrg			 * where sample i is between i_l and i_r. We keep it
89635d5b7c7Smrg			 * multiplied with i_bmax throughout to maintain
89735d5b7c7Smrg			 * precision */
89835d5b7c7Smrg			coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
89935d5b7c7Smrg			a_out_ibmax = i_bmax * a[i_l] +
90035d5b7c7Smrg				      coeff_ibmax * (a[i_r] - a[i_l]);
90135d5b7c7Smrg
90235d5b7c7Smrg			/* j = floor((a_out/max_lut)*i_bmax).
90335d5b7c7Smrg			 * i.e. the element in LUT b that a_out maps to. We
90435d5b7c7Smrg			 * have to divide by max_lut to normalize a_out, since
90535d5b7c7Smrg			 * values in the LUTs are [0, 1<<16)
90635d5b7c7Smrg			 */
90735d5b7c7Smrg			j = a_out_ibmax / max_lut;
90835d5b7c7Smrg			*(uint16_t*)((void*)out + (i*struct_size)) =
90935d5b7c7Smrg				*(uint16_t*)((void*)b + (j*struct_size));
91035d5b7c7Smrg		}
91135d5b7c7Smrg	}
91235d5b7c7Smrg
91335d5b7c7Smrg	for (i = 0; i < len_b; i++)
91435d5b7c7Smrg		out_lut[i].reserved = 0;
91535d5b7c7Smrg}
91635d5b7c7Smrg
91735d5b7c7Smrg/**
91835d5b7c7Smrg * Resize a LUT, using linear interpolation.
91935d5b7c7Smrg *
92035d5b7c7Smrg * @in_(red|green|blue): Legacy LUT components
92135d5b7c7Smrg * @out_lut: The resized LUT is returned here, in DRM color LUT format.
92235d5b7c7Smrg * @len_in: Length of legacy LUT.
92335d5b7c7Smrg * @len_out: Length of out_lut, i.e. the target size.
92435d5b7c7Smrg */
92535d5b7c7Smrgstatic void drmmode_lut_interpolate(uint16_t *in_red,
92635d5b7c7Smrg				    uint16_t *in_green,
92735d5b7c7Smrg				    uint16_t *in_blue,
92835d5b7c7Smrg				    struct drm_color_lut *out_lut,
92935d5b7c7Smrg				    uint32_t len_in, uint32_t len_out)
93035d5b7c7Smrg{
93135d5b7c7Smrg	uint32_t i_l, i_r, i;
93235d5b7c7Smrg	uint32_t i_amax, i_bmax;
93335d5b7c7Smrg	uint32_t coeff_ibmax;
93435d5b7c7Smrg	uint64_t out_ibmax;
93535d5b7c7Smrg	int color;
93635d5b7c7Smrg	size_t struct_size = sizeof(struct drm_color_lut);
93735d5b7c7Smrg
93835d5b7c7Smrg	i_amax = len_in - 1;
93935d5b7c7Smrg	i_bmax = len_out - 1;
94035d5b7c7Smrg
94135d5b7c7Smrg	/* See @drmmode_lut_compose for details */
94235d5b7c7Smrg	for (color = 0; color < 3; color++) {
94335d5b7c7Smrg		uint16_t *in, *out;
94435d5b7c7Smrg
94535d5b7c7Smrg		if (color == 0) {
94635d5b7c7Smrg			in = in_red;
94735d5b7c7Smrg			out = &out_lut[0].red;
94835d5b7c7Smrg		} else if (color == 1) {
94935d5b7c7Smrg			in = in_green;
95035d5b7c7Smrg			out = &out_lut[0].green;
95135d5b7c7Smrg		} else {
95235d5b7c7Smrg			in = in_blue;
95335d5b7c7Smrg			out = &out_lut[0].blue;
95435d5b7c7Smrg		}
95535d5b7c7Smrg
95635d5b7c7Smrg		for (i = 0; i < len_out; i++) {
95735d5b7c7Smrg			i_l = i * i_amax / i_bmax;
95835d5b7c7Smrg			i_r = i_l + !!(i_amax - i_l);
95935d5b7c7Smrg
96035d5b7c7Smrg			coeff_ibmax = (i * i_amax) - (i_l * i_bmax);
96135d5b7c7Smrg			out_ibmax = i_bmax * in[i_l] +
96235d5b7c7Smrg				      coeff_ibmax * (in[i_r] - in[i_l]);
96335d5b7c7Smrg
96435d5b7c7Smrg			*(uint16_t*)((void*)out + (i*struct_size)) =
96535d5b7c7Smrg				out_ibmax / i_bmax;
96635d5b7c7Smrg		}
96735d5b7c7Smrg	}
96835d5b7c7Smrg
96935d5b7c7Smrg	for (i = 0; i < len_out; i++)
97035d5b7c7Smrg		out_lut[i].reserved = 0;
97135d5b7c7Smrg}
97235d5b7c7Smrg
97335d5b7c7Smrg/**
97435d5b7c7Smrg * Configure and change a color property on a CRTC, through RandR. Only the
97535d5b7c7Smrg * specified output will be affected, even if the CRTC is attached to multiple
97635d5b7c7Smrg * outputs. Note that changes will be non-pending: the changes won't be pushed
97735d5b7c7Smrg * to kernel driver.
97835d5b7c7Smrg *
97935d5b7c7Smrg * @output: RandR output to set the property on.
98035d5b7c7Smrg * @crtc: The driver-private CRTC object containing the color properties.
98135d5b7c7Smrg *        If this is NULL, "disabled" values of 0 will be used.
98235d5b7c7Smrg * @cm_prop_index: Color management property to configure and change.
98335d5b7c7Smrg *
98435d5b7c7Smrg * Return 0 on success, X-defined error code otherwise.
98535d5b7c7Smrg */
98635d5b7c7Smrgstatic int rr_configure_and_change_cm_property(xf86OutputPtr output,
98735d5b7c7Smrg					       drmmode_crtc_private_ptr crtc,
98835d5b7c7Smrg					       enum drmmode_cm_prop cm_prop_index)
98935d5b7c7Smrg{
99035d5b7c7Smrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
99135d5b7c7Smrg	drmmode_ptr drmmode = drmmode_output->drmmode;
99235d5b7c7Smrg	Bool need_configure = TRUE;
99335d5b7c7Smrg	unsigned long length = 0;
99435d5b7c7Smrg	void *data = NULL;
99535d5b7c7Smrg	int format = 0;
99635d5b7c7Smrg	uint32_t zero = 0;
99735d5b7c7Smrg	INT32 range[2];
99835d5b7c7Smrg	Atom atom;
99935d5b7c7Smrg	int err;
100035d5b7c7Smrg
100135d5b7c7Smrg	if (cm_prop_index == CM_INVALID_PROP)
100235d5b7c7Smrg		return BadName;
100335d5b7c7Smrg
100435d5b7c7Smrg	switch(cm_prop_index) {
100535d5b7c7Smrg	case CM_GAMMA_LUT_SIZE:
100635d5b7c7Smrg		format = 32;
100735d5b7c7Smrg		length = 1;
100835d5b7c7Smrg		data = &drmmode->gamma_lut_size;
100935d5b7c7Smrg		range[0] = 0;
101035d5b7c7Smrg		range[1] = -1;
101135d5b7c7Smrg		break;
101235d5b7c7Smrg	case CM_DEGAMMA_LUT_SIZE:
101335d5b7c7Smrg		format = 32;
101435d5b7c7Smrg		length = 1;
101535d5b7c7Smrg		data = &drmmode->degamma_lut_size;
101635d5b7c7Smrg		range[0] = 0;
101735d5b7c7Smrg		range[1] = -1;
101835d5b7c7Smrg		break;
101935d5b7c7Smrg	case CM_GAMMA_LUT:
102035d5b7c7Smrg		format = 16;
102135d5b7c7Smrg		range[0] = 0;
102235d5b7c7Smrg		range[1] = (1 << 16) - 1; // Max 16 bit unsigned int.
102335d5b7c7Smrg		if (crtc && crtc->gamma_lut) {
102435d5b7c7Smrg			/* Convert from 8bit size to 16bit size */
102535d5b7c7Smrg			length = sizeof(*crtc->gamma_lut) >> 1;
102635d5b7c7Smrg			length *= drmmode->gamma_lut_size;
102735d5b7c7Smrg			data = crtc->gamma_lut;
102835d5b7c7Smrg		} else {
102935d5b7c7Smrg			length = 1;
103035d5b7c7Smrg			data = &zero;
103135d5b7c7Smrg		}
103235d5b7c7Smrg		break;
103335d5b7c7Smrg	case CM_DEGAMMA_LUT:
103435d5b7c7Smrg		format = 16;
103535d5b7c7Smrg		range[0] = 0;
103635d5b7c7Smrg		range[1] = (1 << 16) - 1;
103735d5b7c7Smrg		if (crtc && crtc->degamma_lut) {
103835d5b7c7Smrg			length = sizeof(*crtc->degamma_lut) >> 1;
103935d5b7c7Smrg			length *= drmmode->degamma_lut_size;
104035d5b7c7Smrg			data = crtc->degamma_lut;
104135d5b7c7Smrg		} else {
104235d5b7c7Smrg			length = 1;
104335d5b7c7Smrg			data = &zero;
104435d5b7c7Smrg		}
104535d5b7c7Smrg		break;
104635d5b7c7Smrg	case CM_CTM:
104735d5b7c7Smrg		/* CTM is fixed-point S31.32 format. */
104835d5b7c7Smrg		format = 32;
104935d5b7c7Smrg		need_configure = FALSE;
105035d5b7c7Smrg		if (crtc && crtc->ctm) {
105135d5b7c7Smrg			/* Convert from 8bit size to 32bit size */
105235d5b7c7Smrg			length = sizeof(*crtc->ctm) >> 2;
105335d5b7c7Smrg			data = crtc->ctm;
105435d5b7c7Smrg		} else {
105535d5b7c7Smrg			length = 1;
105635d5b7c7Smrg			data = &zero;
105735d5b7c7Smrg		}
105835d5b7c7Smrg		break;
105935d5b7c7Smrg	default:
106035d5b7c7Smrg		return BadName;
106135d5b7c7Smrg	}
106235d5b7c7Smrg
106335d5b7c7Smrg	atom = MakeAtom(cm_prop_names[cm_prop_index],
106435d5b7c7Smrg			strlen(cm_prop_names[cm_prop_index]),
106535d5b7c7Smrg			TRUE);
106635d5b7c7Smrg	if (!atom)
106735d5b7c7Smrg		return BadAlloc;
106835d5b7c7Smrg
106935d5b7c7Smrg	if (need_configure) {
107035d5b7c7Smrg		err = RRConfigureOutputProperty(output->randr_output, atom,
107135d5b7c7Smrg						FALSE, TRUE, FALSE, 2, range);
107235d5b7c7Smrg		if (err) {
107335d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
107435d5b7c7Smrg				   "Configuring color management property %s failed with %d\n",
107535d5b7c7Smrg				   cm_prop_names[cm_prop_index], err);
107635d5b7c7Smrg			return err;
107735d5b7c7Smrg		}
107835d5b7c7Smrg	}
107935d5b7c7Smrg
108035d5b7c7Smrg	/* Always issue a non-pending change. We'll push cm properties
108135d5b7c7Smrg	 * ourselves.
108235d5b7c7Smrg	 */
108335d5b7c7Smrg	err = RRChangeOutputProperty(output->randr_output, atom,
108435d5b7c7Smrg				     XA_INTEGER, format,
108535d5b7c7Smrg				     PropModeReplace,
108635d5b7c7Smrg				     length, data, FALSE, FALSE);
108735d5b7c7Smrg	if (err)
108835d5b7c7Smrg		xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
108935d5b7c7Smrg			   "Changing color management property %s failed with %d\n",
109035d5b7c7Smrg			   cm_prop_names[cm_prop_index], err);
109135d5b7c7Smrg	return err;
109235d5b7c7Smrg}
109335d5b7c7Smrg
109435d5b7c7Smrg/**
109535d5b7c7Smrg* Stage a color management property. This parses the property value, according
109635d5b7c7Smrg* to the cm property type, then stores it within the driver-private CRTC
109735d5b7c7Smrg* object.
109835d5b7c7Smrg*
109935d5b7c7Smrg* @crtc: The CRTC to stage the new color management properties in
110035d5b7c7Smrg* @cm_prop_index: The color property to stage
110135d5b7c7Smrg* @value: The RandR property value to stage
110235d5b7c7Smrg*
110335d5b7c7Smrg* Return 0 on success, X-defined error code on failure.
110435d5b7c7Smrg*/
110535d5b7c7Smrgstatic int drmmode_crtc_stage_cm_prop(xf86CrtcPtr crtc,
110635d5b7c7Smrg				      enum drmmode_cm_prop cm_prop_index,
110735d5b7c7Smrg				      RRPropertyValuePtr value)
110835d5b7c7Smrg{
110935d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
111035d5b7c7Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
111135d5b7c7Smrg	size_t expected_bytes = 0;
111235d5b7c7Smrg	void **blob_data = NULL;
111335d5b7c7Smrg	Bool use_default = FALSE;
111435d5b7c7Smrg
111535d5b7c7Smrg	/* Update properties on the driver-private CRTC */
111635d5b7c7Smrg	switch (cm_prop_index) {
111735d5b7c7Smrg	case CM_GAMMA_LUT:
111835d5b7c7Smrg		/* Calculate the expected size of value in bytes */
111935d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
112035d5b7c7Smrg					drmmode->gamma_lut_size;
112135d5b7c7Smrg
112235d5b7c7Smrg		/* For gamma and degamma, we allow a default SRGB curve to be
112335d5b7c7Smrg		 * set via setting a single element
112435d5b7c7Smrg		 *
112535d5b7c7Smrg		 * Otherwise, value size is in terms of the value format.
112635d5b7c7Smrg		 * Ensure it's also in bytes (<< 1) before comparing with the
112735d5b7c7Smrg		 * expected bytes.
112835d5b7c7Smrg		 */
112935d5b7c7Smrg		if (value->size == 1)
113035d5b7c7Smrg			use_default = TRUE;
113135d5b7c7Smrg		else if (value->type != XA_INTEGER || value->format != 16 ||
113235d5b7c7Smrg			 (size_t)(value->size << 1) != expected_bytes)
113335d5b7c7Smrg			return BadLength;
113435d5b7c7Smrg
113535d5b7c7Smrg		blob_data = (void**)&drmmode_crtc->gamma_lut;
113635d5b7c7Smrg		break;
113735d5b7c7Smrg	case CM_DEGAMMA_LUT:
113835d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
113935d5b7c7Smrg					drmmode->degamma_lut_size;
114035d5b7c7Smrg
114135d5b7c7Smrg		if (value->size == 1)
114235d5b7c7Smrg			use_default = TRUE;
114335d5b7c7Smrg		else if (value->type != XA_INTEGER || value->format != 16 ||
114435d5b7c7Smrg			 (size_t)(value->size << 1) != expected_bytes)
114535d5b7c7Smrg			return BadLength;
114635d5b7c7Smrg
114735d5b7c7Smrg		blob_data = (void**)&drmmode_crtc->degamma_lut;
114835d5b7c7Smrg		break;
114935d5b7c7Smrg	case CM_CTM:
115035d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_ctm);
115135d5b7c7Smrg
115235d5b7c7Smrg		if (value->size == 1)
115335d5b7c7Smrg			use_default = TRUE;
115435d5b7c7Smrg		if (value->type != XA_INTEGER || value->format != 32 ||
115535d5b7c7Smrg		    (size_t)(value->size << 2) != expected_bytes)
115635d5b7c7Smrg			return BadLength;
115735d5b7c7Smrg
115835d5b7c7Smrg		blob_data = (void**)&drmmode_crtc->ctm;
115935d5b7c7Smrg		break;
116035d5b7c7Smrg	default:
116135d5b7c7Smrg		return BadName;
116235d5b7c7Smrg	}
116335d5b7c7Smrg
116435d5b7c7Smrg	free(*blob_data);
116535d5b7c7Smrg	if (!use_default) {
116635d5b7c7Smrg		*blob_data = malloc(expected_bytes);
116735d5b7c7Smrg		if (!*blob_data)
116835d5b7c7Smrg			return BadAlloc;
116935d5b7c7Smrg		memcpy(*blob_data, value->data, expected_bytes);
117035d5b7c7Smrg	} else
117135d5b7c7Smrg		*blob_data = NULL;
117235d5b7c7Smrg
117335d5b7c7Smrg	return Success;
117435d5b7c7Smrg}
117535d5b7c7Smrg
117635d5b7c7Smrg/**
117735d5b7c7Smrg * Push staged color management properties on the CRTC to DRM.
117835d5b7c7Smrg *
117935d5b7c7Smrg * @crtc: The CRTC containing staged properties
118035d5b7c7Smrg * @cm_prop_index: The color property to push
118135d5b7c7Smrg *
118235d5b7c7Smrg * Return 0 on success, X-defined error codes on failure.
118335d5b7c7Smrg */
118435d5b7c7Smrgstatic int drmmode_crtc_push_cm_prop(xf86CrtcPtr crtc,
118535d5b7c7Smrg				     enum drmmode_cm_prop cm_prop_index)
118635d5b7c7Smrg{
118735d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
118835d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
118935d5b7c7Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
119035d5b7c7Smrg	Bool free_blob_data = FALSE;
119135d5b7c7Smrg	uint32_t created_blob_id = 0;
119235d5b7c7Smrg	uint32_t drm_prop_id;
119335d5b7c7Smrg	size_t expected_bytes = 0;
119435d5b7c7Smrg	void *blob_data = NULL;
119535d5b7c7Smrg	int ret;
119635d5b7c7Smrg
119735d5b7c7Smrg	switch (cm_prop_index) {
119835d5b7c7Smrg	case CM_GAMMA_LUT:
119935d5b7c7Smrg		/* Calculate the expected size of value in bytes */
120035d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
120135d5b7c7Smrg					drmmode->gamma_lut_size;
120235d5b7c7Smrg
120335d5b7c7Smrg		/* Legacy gamma LUT is disabled on deep 30bpp color. In which
120435d5b7c7Smrg		 * case, directly use non-legacy LUT.
120535d5b7c7Smrg		 */
120635d5b7c7Smrg		if (!crtc->funcs->gamma_set) {
120735d5b7c7Smrg			blob_data = drmmode_crtc->gamma_lut;
120835d5b7c7Smrg			goto do_push;
120935d5b7c7Smrg		}
121035d5b7c7Smrg
121135d5b7c7Smrg		blob_data = malloc(expected_bytes);
121235d5b7c7Smrg		if (!blob_data)
121335d5b7c7Smrg			return BadAlloc;
121435d5b7c7Smrg
121535d5b7c7Smrg		free_blob_data = TRUE;
121635d5b7c7Smrg		/*
121735d5b7c7Smrg		 * Compose legacy and non-legacy LUT if non-legacy was set.
121835d5b7c7Smrg		 * Otherwise, interpolate legacy LUT to non-legacy size.
121935d5b7c7Smrg		 */
122035d5b7c7Smrg		if (drmmode_crtc->gamma_lut) {
122135d5b7c7Smrg			drmmode_lut_compose(crtc->gamma_red,
122235d5b7c7Smrg					    crtc->gamma_green,
122335d5b7c7Smrg					    crtc->gamma_blue,
122435d5b7c7Smrg					    drmmode_crtc->gamma_lut,
122535d5b7c7Smrg					    blob_data, crtc->gamma_size,
122635d5b7c7Smrg					    drmmode->gamma_lut_size);
122735d5b7c7Smrg		} else {
122835d5b7c7Smrg			drmmode_lut_interpolate(crtc->gamma_red,
122935d5b7c7Smrg						crtc->gamma_green,
123035d5b7c7Smrg						crtc->gamma_blue,
123135d5b7c7Smrg						blob_data,
123235d5b7c7Smrg						crtc->gamma_size,
123335d5b7c7Smrg						drmmode->gamma_lut_size);
123435d5b7c7Smrg		}
123535d5b7c7Smrg		break;
123635d5b7c7Smrg	case CM_DEGAMMA_LUT:
123735d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_lut) *
123835d5b7c7Smrg					drmmode->degamma_lut_size;
123935d5b7c7Smrg		blob_data = drmmode_crtc->degamma_lut;
124035d5b7c7Smrg		break;
124135d5b7c7Smrg	case CM_CTM:
124235d5b7c7Smrg		expected_bytes = sizeof(struct drm_color_ctm);
124335d5b7c7Smrg		blob_data = drmmode_crtc->ctm;
124435d5b7c7Smrg		break;
124535d5b7c7Smrg	default:
124635d5b7c7Smrg		return BadName;
124735d5b7c7Smrg	}
124835d5b7c7Smrg
124935d5b7c7Smrgdo_push:
125035d5b7c7Smrg	if (blob_data) {
125135d5b7c7Smrg		ret = drmModeCreatePropertyBlob(pAMDGPUEnt->fd,
125235d5b7c7Smrg						blob_data, expected_bytes,
125335d5b7c7Smrg						&created_blob_id);
125435d5b7c7Smrg		if (ret) {
125535d5b7c7Smrg			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
125635d5b7c7Smrg				   "Creating DRM blob failed with errno %d\n",
125735d5b7c7Smrg				   ret);
125835d5b7c7Smrg			if (free_blob_data)
125935d5b7c7Smrg				free(blob_data);
126035d5b7c7Smrg			return BadRequest;
126135d5b7c7Smrg		}
126235d5b7c7Smrg	}
126335d5b7c7Smrg
126435d5b7c7Smrg	drm_prop_id = drmmode_crtc->drmmode->cm_prop_ids[cm_prop_index];
126535d5b7c7Smrg	ret = drmModeObjectSetProperty(pAMDGPUEnt->fd,
126635d5b7c7Smrg				       drmmode_crtc->mode_crtc->crtc_id,
126735d5b7c7Smrg				       DRM_MODE_OBJECT_CRTC,
126835d5b7c7Smrg				       drm_prop_id,
126935d5b7c7Smrg				       (uint64_t)created_blob_id);
127035d5b7c7Smrg
127135d5b7c7Smrg	/* If successful, kernel will have a reference already. Safe to destroy
127235d5b7c7Smrg	 * the blob either way.
127335d5b7c7Smrg	 */
127435d5b7c7Smrg	if (blob_data)
127535d5b7c7Smrg		drmModeDestroyPropertyBlob(pAMDGPUEnt->fd, created_blob_id);
127635d5b7c7Smrg
127735d5b7c7Smrg	if (ret) {
127835d5b7c7Smrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
127935d5b7c7Smrg			   "Setting DRM property blob failed with errno %d\n",
128035d5b7c7Smrg			   ret);
128135d5b7c7Smrg		if (free_blob_data)
128235d5b7c7Smrg			free(blob_data);
128335d5b7c7Smrg		return BadRequest;
128435d5b7c7Smrg	}
128535d5b7c7Smrg
128635d5b7c7Smrg	if (free_blob_data)
128735d5b7c7Smrg		free(blob_data);
128835d5b7c7Smrg
128935d5b7c7Smrg	return Success;
129035d5b7c7Smrg}
129135d5b7c7Smrg
129224b90cf4Smrgstatic void
129324b90cf4Smrgdrmmode_crtc_gamma_do_set(xf86CrtcPtr crtc, uint16_t *red, uint16_t *green,
129424b90cf4Smrg			  uint16_t *blue, int size)
129524b90cf4Smrg{
129624b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
129724b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
129835d5b7c7Smrg	int ret;
129935d5b7c7Smrg
130035d5b7c7Smrg	/* Use legacy if no support for non-legacy gamma */
130135d5b7c7Smrg	if (!drmmode_cm_enabled(drmmode_crtc->drmmode)) {
130235d5b7c7Smrg		drmModeCrtcSetGamma(pAMDGPUEnt->fd,
130335d5b7c7Smrg				    drmmode_crtc->mode_crtc->crtc_id,
130435d5b7c7Smrg				    size, red, green, blue);
130535d5b7c7Smrg		return;
130635d5b7c7Smrg	}
130724b90cf4Smrg
130835d5b7c7Smrg	ret = drmmode_crtc_push_cm_prop(crtc, CM_GAMMA_LUT);
130935d5b7c7Smrg	if (ret)
131035d5b7c7Smrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
131135d5b7c7Smrg			   "Setting Gamma LUT failed with errno %d\n",
131235d5b7c7Smrg			   ret);
131324b90cf4Smrg}
131424b90cf4Smrg
131524b90cf4SmrgBool
131624b90cf4Smrgdrmmode_set_mode(xf86CrtcPtr crtc, struct drmmode_fb *fb, DisplayModePtr mode,
131724b90cf4Smrg		 int x, int y)
131824b90cf4Smrg{
131924b90cf4Smrg	ScrnInfoPtr scrn = crtc->scrn;
132024b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
132124b90cf4Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
132224b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
132324b90cf4Smrg	uint32_t *output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
132424b90cf4Smrg	int output_count = 0;
132524b90cf4Smrg	drmModeModeInfo kmode;
132624b90cf4Smrg	Bool ret;
132724b90cf4Smrg	int i;
132824b90cf4Smrg
132924b90cf4Smrg	if (!output_ids)
133024b90cf4Smrg		return FALSE;
133124b90cf4Smrg
133224b90cf4Smrg	for (i = 0; i < xf86_config->num_output; i++) {
133324b90cf4Smrg		xf86OutputPtr output = xf86_config->output[i];
133424b90cf4Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
133524b90cf4Smrg
133624b90cf4Smrg		if (output->crtc != crtc)
133724b90cf4Smrg			continue;
133824b90cf4Smrg
133924b90cf4Smrg		output_ids[output_count] = drmmode_output->mode_output->connector_id;
134024b90cf4Smrg		output_count++;
134124b90cf4Smrg	}
134224b90cf4Smrg
134324b90cf4Smrg	drmmode_ConvertToKMode(scrn, &kmode, mode);
134424b90cf4Smrg
134524b90cf4Smrg	ret = drmModeSetCrtc(pAMDGPUEnt->fd,
134624b90cf4Smrg			     drmmode_crtc->mode_crtc->crtc_id,
134724b90cf4Smrg			     fb->handle, x, y, output_ids,
134824b90cf4Smrg			     output_count, &kmode) == 0;
134924b90cf4Smrg
135024b90cf4Smrg	if (ret) {
135124b90cf4Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, fb);
135224b90cf4Smrg	} else {
135324b90cf4Smrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
135424b90cf4Smrg			   "failed to set mode: %s\n", strerror(errno));
135524b90cf4Smrg	}
135624b90cf4Smrg
135724b90cf4Smrg	free(output_ids);
135824b90cf4Smrg	return ret;
135924b90cf4Smrg}
136024b90cf4Smrg
1361d6c0b56eSmrgstatic Bool
1362d6c0b56eSmrgdrmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
1363d6c0b56eSmrg		       Rotation rotation, int x, int y)
1364d6c0b56eSmrg{
1365d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1366d6c0b56eSmrg	ScreenPtr pScreen = pScrn->pScreen;
1367d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1368d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1369d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
1370d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
137190f2b693Smrg	Bool handle_deferred = FALSE;
137211bf0794Smrg	unsigned scanout_id = 0;
1373d6c0b56eSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
1374d6c0b56eSmrg	int saved_x, saved_y;
1375d6c0b56eSmrg	Rotation saved_rotation;
1376d6c0b56eSmrg	DisplayModeRec saved_mode;
1377504d986fSmrg	Bool ret = FALSE;
1378d6c0b56eSmrg	int i;
137924b90cf4Smrg	struct drmmode_fb *fb = NULL;
138024b90cf4Smrg
138124b90cf4Smrg	/* The root window contents may be undefined before the WindowExposures
138224b90cf4Smrg	 * hook is called for it, so bail if we get here before that
138324b90cf4Smrg	 */
138424b90cf4Smrg	if (pScreen->WindowExposures == AMDGPUWindowExposures_oneshot)
138524b90cf4Smrg		return FALSE;
1386d6c0b56eSmrg
1387d6c0b56eSmrg	saved_mode = crtc->mode;
1388d6c0b56eSmrg	saved_x = crtc->x;
1389d6c0b56eSmrg	saved_y = crtc->y;
1390d6c0b56eSmrg	saved_rotation = crtc->rotation;
1391d6c0b56eSmrg
1392d6c0b56eSmrg	if (mode) {
1393d6c0b56eSmrg		crtc->mode = *mode;
1394d6c0b56eSmrg		crtc->x = x;
1395d6c0b56eSmrg		crtc->y = y;
1396d6c0b56eSmrg		crtc->rotation = rotation;
1397d6c0b56eSmrg
1398d6c0b56eSmrg		if (!drmmode_handle_transform(crtc))
1399d6c0b56eSmrg			goto done;
1400d6c0b56eSmrg
140111bf0794Smrg		drmmode_crtc_update_tear_free(crtc);
140211bf0794Smrg		if (drmmode_crtc->tear_free)
140311bf0794Smrg			scanout_id = drmmode_crtc->scanout_id;
140435d5b7c7Smrg		else
140535d5b7c7Smrg			drmmode_crtc->scanout_id = 0;
1406d6c0b56eSmrg
140724b90cf4Smrg		if (drmmode_crtc->prime_scanout_pixmap) {
140811bf0794Smrg			drmmode_crtc_prime_scanout_update(crtc, mode, scanout_id,
140924b90cf4Smrg							  &fb, &x, &y);
141024b90cf4Smrg		} else if (drmmode_crtc->rotate.pixmap) {
141124b90cf4Smrg			fb = amdgpu_pixmap_get_fb(drmmode_crtc->rotate.pixmap);
1412d6c0b56eSmrg			x = y = 0;
141311bf0794Smrg
141424b90cf4Smrg		} else if (!pScreen->isGPU &&
141511bf0794Smrg			   (drmmode_crtc->tear_free ||
1416504d986fSmrg			    crtc->driverIsPerformingTransform ||
1417504d986fSmrg			    info->shadow_primary)) {
141811bf0794Smrg			drmmode_crtc_scanout_update(crtc, mode, scanout_id,
141924b90cf4Smrg						    &fb, &x, &y);
1420d6c0b56eSmrg		}
1421d6c0b56eSmrg
142224b90cf4Smrg		if (!fb)
142324b90cf4Smrg			fb = amdgpu_pixmap_get_fb(pScreen->GetWindowPixmap(pScreen->root));
142424b90cf4Smrg		if (!fb) {
142524b90cf4Smrg			union gbm_bo_handle bo_handle;
142624b90cf4Smrg
142724b90cf4Smrg			bo_handle = gbm_bo_get_handle(info->front_buffer->bo.gbm);
142824b90cf4Smrg			fb = amdgpu_fb_create(pScrn, pAMDGPUEnt->fd,
142924b90cf4Smrg					      pScrn->virtualX, pScrn->virtualY,
143024b90cf4Smrg					      pScrn->displayWidth * info->pixel_bytes,
143124b90cf4Smrg					      bo_handle.u32);
143224b90cf4Smrg			/* Prevent refcnt of ad-hoc FBs from reaching 2 */
143324b90cf4Smrg			drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, NULL);
143424b90cf4Smrg			drmmode_crtc->fb = fb;
143524b90cf4Smrg		}
143624b90cf4Smrg		if (!fb) {
143724b90cf4Smrg			ErrorF("failed to add FB for modeset\n");
143824b90cf4Smrg			goto done;
1439504d986fSmrg		}
1440504d986fSmrg
144135d5b7c7Smrg		amdgpu_drm_wait_pending_flip(crtc);
144290f2b693Smrg		handle_deferred = TRUE;
144324b90cf4Smrg
144424b90cf4Smrg		if (!drmmode_set_mode(crtc, fb, mode, x, y))
1445d6c0b56eSmrg			goto done;
144624b90cf4Smrg
144724b90cf4Smrg		ret = TRUE;
1448d6c0b56eSmrg
1449d6c0b56eSmrg		if (pScreen)
1450d6c0b56eSmrg			xf86CrtcSetScreenSubpixelOrder(pScreen);
1451d6c0b56eSmrg
1452d6c0b56eSmrg		drmmode_crtc->need_modeset = FALSE;
1453d6c0b56eSmrg
1454d6c0b56eSmrg		/* go through all the outputs and force DPMS them back on? */
1455d6c0b56eSmrg		for (i = 0; i < xf86_config->num_output; i++) {
1456d6c0b56eSmrg			xf86OutputPtr output = xf86_config->output[i];
1457d6c0b56eSmrg
1458d6c0b56eSmrg			if (output->crtc != crtc)
1459d6c0b56eSmrg				continue;
1460d6c0b56eSmrg
1461d6c0b56eSmrg			output->funcs->dpms(output, DPMSModeOn);
1462d6c0b56eSmrg		}
1463d6c0b56eSmrg	}
1464d6c0b56eSmrg
1465d6c0b56eSmrg	/* Compute index of this CRTC into xf86_config->crtc */
1466d6c0b56eSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
1467d6c0b56eSmrg		if (xf86_config->crtc[i] != crtc)
1468d6c0b56eSmrg			continue;
1469d6c0b56eSmrg
1470d6c0b56eSmrg		if (!crtc->enabled || drmmode_can_use_hw_cursor(crtc))
1471d6c0b56eSmrg			info->hwcursor_disabled &= ~(1 << i);
1472d6c0b56eSmrg		else
1473d6c0b56eSmrg			info->hwcursor_disabled |= 1 << i;
1474d6c0b56eSmrg
1475d6c0b56eSmrg		break;
1476d6c0b56eSmrg	}
1477d6c0b56eSmrg
1478d6c0b56eSmrg#ifndef HAVE_XF86_CURSOR_RESET_CURSOR
1479d6c0b56eSmrg	if (!info->hwcursor_disabled)
1480d6c0b56eSmrg		xf86_reload_cursors(pScreen);
1481d6c0b56eSmrg#endif
1482d6c0b56eSmrg
1483d6c0b56eSmrgdone:
1484d6c0b56eSmrg	if (!ret) {
1485d6c0b56eSmrg		crtc->x = saved_x;
1486d6c0b56eSmrg		crtc->y = saved_y;
1487d6c0b56eSmrg		crtc->rotation = saved_rotation;
1488d6c0b56eSmrg		crtc->mode = saved_mode;
1489504d986fSmrg	} else {
1490d6c0b56eSmrg		crtc->active = TRUE;
1491d6c0b56eSmrg
149224b90cf4Smrg		if (drmmode_crtc->scanout[scanout_id].pixmap &&
149324b90cf4Smrg		    fb != amdgpu_pixmap_get_fb(drmmode_crtc->
149435d5b7c7Smrg					       scanout[scanout_id].pixmap)) {
149590f2b693Smrg			drmmode_crtc_scanout_free(crtc);
149635d5b7c7Smrg		} else if (!drmmode_crtc->tear_free) {
149711bf0794Smrg			drmmode_crtc_scanout_destroy(drmmode,
149811bf0794Smrg						     &drmmode_crtc->scanout[1]);
149911bf0794Smrg		}
1500504d986fSmrg	}
1501504d986fSmrg
150290f2b693Smrg	if (handle_deferred)
150390f2b693Smrg		amdgpu_drm_queue_handle_deferred(crtc);
150490f2b693Smrg
1505d6c0b56eSmrg	return ret;
1506d6c0b56eSmrg}
1507d6c0b56eSmrg
1508d6c0b56eSmrgstatic void drmmode_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
1509d6c0b56eSmrg{
1510d6c0b56eSmrg
1511d6c0b56eSmrg}
1512d6c0b56eSmrg
1513d6c0b56eSmrgstatic void drmmode_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
1514d6c0b56eSmrg{
1515d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1516d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
1517d6c0b56eSmrg
151824b90cf4Smrg#if XF86_CRTC_VERSION < 7
1519d6c0b56eSmrg	if (crtc->driverIsPerformingTransform) {
1520d6c0b56eSmrg		x += crtc->x;
1521d6c0b56eSmrg		y += crtc->y;
1522d6c0b56eSmrg		xf86CrtcTransformCursorPos(crtc, &x, &y);
1523d6c0b56eSmrg	}
1524d6c0b56eSmrg#endif
1525d6c0b56eSmrg
152690f2b693Smrg	drmmode_crtc->cursor_x = x;
152790f2b693Smrg	drmmode_crtc->cursor_y = y;
152890f2b693Smrg
1529d6c0b56eSmrg	drmModeMoveCursor(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
1530d6c0b56eSmrg}
1531d6c0b56eSmrg
153224b90cf4Smrg#if XF86_CRTC_VERSION < 7
1533d6c0b56eSmrg
1534d6c0b56eSmrgstatic int
1535d6c0b56eSmrgdrmmode_cursor_src_offset(Rotation rotation, int width, int height,
1536d6c0b56eSmrg			  int x_dst, int y_dst)
1537d6c0b56eSmrg{
1538d6c0b56eSmrg	int t;
1539d6c0b56eSmrg
1540d6c0b56eSmrg	switch (rotation & 0xf) {
1541d6c0b56eSmrg	case RR_Rotate_90:
1542d6c0b56eSmrg		t = x_dst;
1543d6c0b56eSmrg		x_dst = height - y_dst - 1;
1544d6c0b56eSmrg		y_dst = t;
1545d6c0b56eSmrg		break;
1546d6c0b56eSmrg	case RR_Rotate_180:
1547d6c0b56eSmrg		x_dst = width - x_dst - 1;
1548d6c0b56eSmrg		y_dst = height - y_dst - 1;
1549d6c0b56eSmrg		break;
1550d6c0b56eSmrg	case RR_Rotate_270:
1551d6c0b56eSmrg		t = x_dst;
1552d6c0b56eSmrg		x_dst = y_dst;
1553d6c0b56eSmrg		y_dst = width - t - 1;
1554d6c0b56eSmrg		break;
1555d6c0b56eSmrg	}
1556d6c0b56eSmrg
1557d6c0b56eSmrg	if (rotation & RR_Reflect_X)
1558d6c0b56eSmrg		x_dst = width - x_dst - 1;
1559d6c0b56eSmrg	if (rotation & RR_Reflect_Y)
1560d6c0b56eSmrg		y_dst = height - y_dst - 1;
1561d6c0b56eSmrg
1562d6c0b56eSmrg	return y_dst * height + x_dst;
1563d6c0b56eSmrg}
1564d6c0b56eSmrg
1565d6c0b56eSmrg#endif
1566d6c0b56eSmrg
156790f2b693Smrgstatic Bool
156890f2b693Smrgdrmmode_cursor_pixel(xf86CrtcPtr crtc, uint32_t *argb, Bool *premultiplied,
156990f2b693Smrg		     Bool *apply_gamma)
157035d5b7c7Smrg{
157190f2b693Smrg	uint32_t alpha = *argb >> 24;
157290f2b693Smrg	uint32_t rgb[3];
157390f2b693Smrg	int i;
157435d5b7c7Smrg
157590f2b693Smrg	if (premultiplied) {
157690f2b693Smrg#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1, 18, 4, 0, 0)
157790f2b693Smrg		if (alpha == 0 && (*argb & 0xffffff) != 0) {
157890f2b693Smrg			/* Doesn't look like premultiplied alpha */
157990f2b693Smrg			*premultiplied = FALSE;
158090f2b693Smrg			return FALSE;
158190f2b693Smrg		}
158290f2b693Smrg#endif
158335d5b7c7Smrg
158490f2b693Smrg		if (!(*apply_gamma))
158590f2b693Smrg			return TRUE;
158635d5b7c7Smrg
158790f2b693Smrg		if (*argb > (alpha | alpha << 8 | alpha << 16 | alpha << 24)) {
158890f2b693Smrg			/* Un-premultiplied R/G/B would overflow gamma LUT,
158990f2b693Smrg			 * don't apply gamma correction
159090f2b693Smrg			 */
159190f2b693Smrg			*apply_gamma = FALSE;
159290f2b693Smrg			return FALSE;
159390f2b693Smrg		}
159490f2b693Smrg	}
159524b90cf4Smrg
159690f2b693Smrg	if (!alpha) {
159790f2b693Smrg		*argb = 0;
159890f2b693Smrg		return TRUE;
159990f2b693Smrg	}
160024b90cf4Smrg
160190f2b693Smrg	/* Extract RGB */
160224b90cf4Smrg	for (i = 0; i < 3; i++)
160390f2b693Smrg		rgb[i] = (*argb >> (i * 8)) & 0xff;
160424b90cf4Smrg
160590f2b693Smrg	if (premultiplied) {
160690f2b693Smrg		/* Un-premultiply alpha */
160790f2b693Smrg		for (i = 0; i < 3; i++)
160890f2b693Smrg			rgb[i] = rgb[i] * 0xff / alpha;
160990f2b693Smrg	}
161090f2b693Smrg
161190f2b693Smrg	if (*apply_gamma) {
161290f2b693Smrg		rgb[0] = crtc->gamma_blue[rgb[0]] >> 8;
161390f2b693Smrg		rgb[1] = crtc->gamma_green[rgb[1]] >> 8;
161490f2b693Smrg		rgb[2] = crtc->gamma_red[rgb[2]] >> 8;
161590f2b693Smrg	}
161690f2b693Smrg
161790f2b693Smrg	/* Premultiply alpha */
161890f2b693Smrg	for (i = 0; i < 3; i++)
161990f2b693Smrg		rgb[i] = rgb[i] * alpha / 0xff;
162024b90cf4Smrg
162190f2b693Smrg	*argb = alpha << 24 | rgb[2] << 16 | rgb[1] << 8 | rgb[0];
162290f2b693Smrg	return TRUE;
162324b90cf4Smrg}
162424b90cf4Smrg
162590f2b693Smrgstatic void drmmode_load_cursor_argb(xf86CrtcPtr crtc, CARD32 * image)
1626d6c0b56eSmrg{
162790f2b693Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1628d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1629d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
163090f2b693Smrg	unsigned id = drmmode_crtc->cursor_id;
163190f2b693Smrg	Bool premultiplied = TRUE;
163290f2b693Smrg	Bool apply_gamma = TRUE;
163390f2b693Smrg	uint32_t argb;
163490f2b693Smrg	uint32_t *ptr;
163535d5b7c7Smrg
163635d5b7c7Smrg	if ((crtc->scrn->depth != 24 && crtc->scrn->depth != 32) ||
163735d5b7c7Smrg	    drmmode_cm_enabled(&info->drmmode))
163890f2b693Smrg		apply_gamma = FALSE;
163990f2b693Smrg
164090f2b693Smrg	if (drmmode_crtc->cursor &&
164190f2b693Smrg	    XF86_CRTC_CONFIG_PTR(pScrn)->cursor != drmmode_crtc->cursor)
164290f2b693Smrg		id ^= 1;
164390f2b693Smrg
164490f2b693Smrg	ptr = (uint32_t *) (drmmode_crtc->cursor_buffer[id]->cpu_ptr);
1645d6c0b56eSmrg
164624b90cf4Smrg#if XF86_CRTC_VERSION < 7
1647d6c0b56eSmrg	if (crtc->driverIsPerformingTransform) {
1648d6c0b56eSmrg		uint32_t cursor_w = info->cursor_w, cursor_h = info->cursor_h;
1649d6c0b56eSmrg		int dstx, dsty;
1650d6c0b56eSmrg		int srcoffset;
1651d6c0b56eSmrg
165290f2b693Smrgretry_transform:
1653d6c0b56eSmrg		for (dsty = 0; dsty < cursor_h; dsty++) {
1654d6c0b56eSmrg			for (dstx = 0; dstx < cursor_w; dstx++) {
1655d6c0b56eSmrg				srcoffset = drmmode_cursor_src_offset(crtc->rotation,
1656d6c0b56eSmrg								      cursor_w,
1657d6c0b56eSmrg								      cursor_h,
1658d6c0b56eSmrg								      dstx, dsty);
165990f2b693Smrg				argb = image[srcoffset];
166090f2b693Smrg				if (!drmmode_cursor_pixel(crtc, &argb, &premultiplied,
166190f2b693Smrg							  &apply_gamma))
166290f2b693Smrg					goto retry_transform;
1663d6c0b56eSmrg
166490f2b693Smrg				ptr[dsty * info->cursor_w + dstx] = cpu_to_le32(argb);
1665d6c0b56eSmrg			}
1666d6c0b56eSmrg		}
1667d6c0b56eSmrg	} else
1668d6c0b56eSmrg#endif
1669d6c0b56eSmrg	{
1670d6c0b56eSmrg		uint32_t cursor_size = info->cursor_w * info->cursor_h;
1671d6c0b56eSmrg		int i;
1672d6c0b56eSmrg
167390f2b693Smrgretry:
167490f2b693Smrg		for (i = 0; i < cursor_size; i++) {
167590f2b693Smrg			argb = image[i];
167690f2b693Smrg			if (!drmmode_cursor_pixel(crtc, &argb, &premultiplied,
167790f2b693Smrg						  &apply_gamma))
167890f2b693Smrg				goto retry;
1679d6c0b56eSmrg
168090f2b693Smrg			ptr[i] = cpu_to_le32(argb);
168190f2b693Smrg		}
168290f2b693Smrg	}
1683d6c0b56eSmrg
168490f2b693Smrg	if (id != drmmode_crtc->cursor_id) {
168590f2b693Smrg		drmmode_crtc->cursor_id = id;
168690f2b693Smrg		crtc->funcs->show_cursor(crtc);
1687d6c0b56eSmrg	}
1688d6c0b56eSmrg}
1689d6c0b56eSmrg
1690d6c0b56eSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,903,0)
1691d6c0b56eSmrg
1692d6c0b56eSmrgstatic Bool drmmode_load_cursor_argb_check(xf86CrtcPtr crtc, CARD32 * image)
1693d6c0b56eSmrg{
1694d6c0b56eSmrg	if (!drmmode_can_use_hw_cursor(crtc))
1695d6c0b56eSmrg		return FALSE;
1696d6c0b56eSmrg
1697d6c0b56eSmrg	drmmode_load_cursor_argb(crtc, image);
1698d6c0b56eSmrg	return TRUE;
1699d6c0b56eSmrg}
1700d6c0b56eSmrg
1701d6c0b56eSmrg#endif
1702d6c0b56eSmrg
1703d6c0b56eSmrgstatic void drmmode_hide_cursor(xf86CrtcPtr crtc)
1704d6c0b56eSmrg{
1705d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1706d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1707d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1708d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1709d6c0b56eSmrg
1710d6c0b56eSmrg	drmModeSetCursor(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id, 0,
1711d6c0b56eSmrg			 info->cursor_w, info->cursor_h);
171290f2b693Smrg	drmmode_crtc->cursor = NULL;
1713d6c0b56eSmrg}
1714d6c0b56eSmrg
1715d6c0b56eSmrgstatic void drmmode_show_cursor(xf86CrtcPtr crtc)
1716d6c0b56eSmrg{
1717d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1718d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
1719d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1720d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
172190f2b693Smrg	struct amdgpu_buffer *cursor_buffer =
172290f2b693Smrg		drmmode_crtc->cursor_buffer[drmmode_crtc->cursor_id];
172390f2b693Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
172490f2b693Smrg	CursorPtr cursor = xf86_config->cursor;
172590f2b693Smrg	int xhot = cursor->bits->xhot;
172690f2b693Smrg	int yhot = cursor->bits->yhot;
1727d6c0b56eSmrg	static Bool use_set_cursor2 = TRUE;
172890f2b693Smrg	struct drm_mode_cursor2 arg;
1729d6c0b56eSmrg
173090f2b693Smrg	drmmode_crtc->cursor = xf86_config->cursor;
173190f2b693Smrg
173290f2b693Smrg	memset(&arg, 0, sizeof(arg));
173390f2b693Smrg
173490f2b693Smrg	if (!amdgpu_bo_get_handle(cursor_buffer, &arg.handle)) {
1735d6c0b56eSmrg		ErrorF("failed to get BO handle for cursor\n");
1736d6c0b56eSmrg		return;
1737d6c0b56eSmrg	}
1738d6c0b56eSmrg
173990f2b693Smrg	arg.flags = DRM_MODE_CURSOR_BO;
174090f2b693Smrg	arg.crtc_id = drmmode_crtc->mode_crtc->crtc_id;
174190f2b693Smrg	arg.width = info->cursor_w;
174290f2b693Smrg	arg.height = info->cursor_h;
174390f2b693Smrg
174490f2b693Smrg	if (crtc->rotation != RR_Rotate_0 &&
174590f2b693Smrg	    crtc->rotation != (RR_Rotate_180 | RR_Reflect_X |
174690f2b693Smrg			       RR_Reflect_Y)) {
174790f2b693Smrg		int t;
174890f2b693Smrg
174990f2b693Smrg		/* Reflect & rotate hotspot position */
175090f2b693Smrg		if (crtc->rotation & RR_Reflect_X)
175190f2b693Smrg			xhot = info->cursor_w - xhot - 1;
175290f2b693Smrg		if (crtc->rotation & RR_Reflect_Y)
175390f2b693Smrg			yhot = info->cursor_h - yhot - 1;
175490f2b693Smrg
175590f2b693Smrg		switch (crtc->rotation & 0xf) {
175690f2b693Smrg		case RR_Rotate_90:
175790f2b693Smrg			t = xhot;
175890f2b693Smrg			xhot = yhot;
175990f2b693Smrg			yhot = info->cursor_w - t - 1;
176090f2b693Smrg			break;
176190f2b693Smrg		case RR_Rotate_180:
176290f2b693Smrg			xhot = info->cursor_w - xhot - 1;
176390f2b693Smrg			yhot = info->cursor_h - yhot - 1;
176490f2b693Smrg			break;
176590f2b693Smrg		case RR_Rotate_270:
176690f2b693Smrg			t = xhot;
176790f2b693Smrg			xhot = info->cursor_h - yhot - 1;
176890f2b693Smrg			yhot = t;
176990f2b693Smrg		}
177090f2b693Smrg	}
177190f2b693Smrg
177290f2b693Smrg	if (xhot != drmmode_crtc->cursor_xhot || yhot != drmmode_crtc->cursor_yhot) {
177390f2b693Smrg		arg.flags |= DRM_MODE_CURSOR_MOVE;
177490f2b693Smrg		arg.x = drmmode_crtc->cursor_x += drmmode_crtc->cursor_xhot - xhot;
177590f2b693Smrg		arg.y = drmmode_crtc->cursor_y += drmmode_crtc->cursor_yhot - yhot;
177690f2b693Smrg		drmmode_crtc->cursor_xhot = xhot;
177790f2b693Smrg		drmmode_crtc->cursor_yhot = yhot;
177890f2b693Smrg	}
177990f2b693Smrg
1780d6c0b56eSmrg	if (use_set_cursor2) {
1781d6c0b56eSmrg		int ret;
1782d6c0b56eSmrg
178390f2b693Smrg		arg.hot_x = xhot;
178490f2b693Smrg		arg.hot_y = yhot;
1785504d986fSmrg
178690f2b693Smrg		ret = drmIoctl(pAMDGPUEnt->fd, DRM_IOCTL_MODE_CURSOR2, &arg);
1787d6c0b56eSmrg		if (ret == -EINVAL)
1788d6c0b56eSmrg			use_set_cursor2 = FALSE;
1789d6c0b56eSmrg		else
1790d6c0b56eSmrg			return;
1791d6c0b56eSmrg	}
1792d6c0b56eSmrg
179390f2b693Smrg	drmIoctl(pAMDGPUEnt->fd, DRM_IOCTL_MODE_CURSOR, &arg);
1794d6c0b56eSmrg}
1795d6c0b56eSmrg
179611bf0794Smrg/* Xorg expects a non-NULL return value from drmmode_crtc_shadow_allocate, and
179711bf0794Smrg * passes that back to drmmode_crtc_scanout_create; it doesn't use it for
179811bf0794Smrg * anything else.
179911bf0794Smrg */
180011bf0794Smrgstatic void *
180111bf0794Smrgdrmmode_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
1802d6c0b56eSmrg{
1803d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1804d6c0b56eSmrg
180511bf0794Smrg	if (!drmmode_crtc_scanout_create(crtc, &drmmode_crtc->rotate, width,
180611bf0794Smrg					 height))
180711bf0794Smrg		return NULL;
180811bf0794Smrg
180911bf0794Smrg	return (void*)~0UL;
1810d6c0b56eSmrg}
1811d6c0b56eSmrg
1812d6c0b56eSmrgstatic PixmapPtr
1813d6c0b56eSmrgdrmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
1814d6c0b56eSmrg{
1815d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1816d6c0b56eSmrg
181711bf0794Smrg	if (!data) {
181811bf0794Smrg		drmmode_crtc_scanout_create(crtc, &drmmode_crtc->rotate, width,
181911bf0794Smrg					    height);
182011bf0794Smrg	}
182111bf0794Smrg
182211bf0794Smrg	return drmmode_crtc->rotate.pixmap;
1823d6c0b56eSmrg}
1824d6c0b56eSmrg
1825d6c0b56eSmrgstatic void
1826d6c0b56eSmrgdrmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap,
1827d6c0b56eSmrg			    void *data)
1828d6c0b56eSmrg{
1829d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1830d6c0b56eSmrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
1831d6c0b56eSmrg
1832d6c0b56eSmrg	drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->rotate);
1833d6c0b56eSmrg}
1834d6c0b56eSmrg
1835d6c0b56eSmrgstatic void
1836d6c0b56eSmrgdrmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t * red, uint16_t * green,
1837d6c0b56eSmrg		       uint16_t * blue, int size)
1838d6c0b56eSmrg{
183924b90cf4Smrg	ScrnInfoPtr scrn = crtc->scrn;
184024b90cf4Smrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
184124b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
184224b90cf4Smrg	int i;
1843d6c0b56eSmrg
184424b90cf4Smrg	drmmode_crtc_gamma_do_set(crtc, red, green, blue, size);
184524b90cf4Smrg
184624b90cf4Smrg	/* Compute index of this CRTC into xf86_config->crtc */
184724b90cf4Smrg	for (i = 0; xf86_config->crtc[i] != crtc; i++) {}
184824b90cf4Smrg
184924b90cf4Smrg	if (info->hwcursor_disabled & (1 << i))
185024b90cf4Smrg		return;
185124b90cf4Smrg
185224b90cf4Smrg#ifdef HAVE_XF86_CURSOR_RESET_CURSOR
185324b90cf4Smrg	xf86CursorResetCursor(scrn->pScreen);
185424b90cf4Smrg#else
185524b90cf4Smrg	xf86_reload_cursors(scrn->pScreen);
185624b90cf4Smrg#endif
1857d6c0b56eSmrg}
1858d6c0b56eSmrg
1859d6c0b56eSmrgstatic Bool drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
1860d6c0b56eSmrg{
1861d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
186211bf0794Smrg	unsigned scanout_id = drmmode_crtc->scanout_id;
1863504d986fSmrg	ScreenPtr screen = crtc->scrn->pScreen;
1864504d986fSmrg	PixmapDirtyUpdatePtr dirty;
1865d6c0b56eSmrg
1866504d986fSmrg	xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) {
186724b90cf4Smrg		if (amdgpu_dirty_src_equals(dirty, drmmode_crtc->prime_scanout_pixmap)) {
186824b90cf4Smrg			PixmapStopDirtyTracking(dirty->src, dirty->slave_dst);
186924b90cf4Smrg			break;
187024b90cf4Smrg		}
1871d6c0b56eSmrg	}
1872d6c0b56eSmrg
187390f2b693Smrg	drmmode_crtc_scanout_free(crtc);
187424b90cf4Smrg	drmmode_crtc->prime_scanout_pixmap = NULL;
187524b90cf4Smrg
1876504d986fSmrg	if (!ppix)
1877504d986fSmrg		return TRUE;
1878504d986fSmrg
1879504d986fSmrg	if (!drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[0],
1880504d986fSmrg					 ppix->drawable.width,
1881504d986fSmrg					 ppix->drawable.height))
1882504d986fSmrg		return FALSE;
1883d6c0b56eSmrg
188411bf0794Smrg	if (drmmode_crtc->tear_free &&
1885504d986fSmrg	    !drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1],
1886504d986fSmrg					 ppix->drawable.width,
1887504d986fSmrg					 ppix->drawable.height)) {
188890f2b693Smrg		drmmode_crtc_scanout_free(crtc);
1889504d986fSmrg		return FALSE;
1890d6c0b56eSmrg	}
1891504d986fSmrg
189224b90cf4Smrg	drmmode_crtc->prime_scanout_pixmap = ppix;
189324b90cf4Smrg
189424b90cf4Smrg#ifdef HAS_DIRTYTRACKING_DRAWABLE_SRC
189524b90cf4Smrg	PixmapStartDirtyTracking(&ppix->drawable,
189624b90cf4Smrg				 drmmode_crtc->scanout[scanout_id].pixmap,
189724b90cf4Smrg				 0, 0, 0, 0, RR_Rotate_0);
189824b90cf4Smrg#elif defined(HAS_DIRTYTRACKING_ROTATION)
189911bf0794Smrg	PixmapStartDirtyTracking(ppix, drmmode_crtc->scanout[scanout_id].pixmap,
1900504d986fSmrg				 0, 0, 0, 0, RR_Rotate_0);
1901d6c0b56eSmrg#elif defined(HAS_DIRTYTRACKING2)
190211bf0794Smrg	PixmapStartDirtyTracking2(ppix, drmmode_crtc->scanout[scanout_id].pixmap,
1903504d986fSmrg				  0, 0, 0, 0);
1904d6c0b56eSmrg#else
190511bf0794Smrg	PixmapStartDirtyTracking(ppix, drmmode_crtc->scanout[scanout_id].pixmap, 0, 0);
1906d6c0b56eSmrg#endif
1907d6c0b56eSmrg	return TRUE;
1908d6c0b56eSmrg}
1909d6c0b56eSmrg
191035d5b7c7Smrgstatic void drmmode_crtc_destroy(xf86CrtcPtr crtc)
191135d5b7c7Smrg{
191235d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
191335d5b7c7Smrg
191435d5b7c7Smrg	drmModeFreeCrtc(drmmode_crtc->mode_crtc);
191535d5b7c7Smrg
191635d5b7c7Smrg	/* Free LUTs and CTM */
191735d5b7c7Smrg	free(drmmode_crtc->gamma_lut);
191835d5b7c7Smrg	free(drmmode_crtc->degamma_lut);
191935d5b7c7Smrg	free(drmmode_crtc->ctm);
192035d5b7c7Smrg
192135d5b7c7Smrg	free(drmmode_crtc);
192235d5b7c7Smrg	crtc->driver_private = NULL;
192335d5b7c7Smrg}
192435d5b7c7Smrg
192535d5b7c7Smrg
1926d6c0b56eSmrgstatic xf86CrtcFuncsRec drmmode_crtc_funcs = {
1927d6c0b56eSmrg	.dpms = drmmode_crtc_dpms,
1928d6c0b56eSmrg	.set_mode_major = drmmode_set_mode_major,
1929d6c0b56eSmrg	.set_cursor_colors = drmmode_set_cursor_colors,
1930d6c0b56eSmrg	.set_cursor_position = drmmode_set_cursor_position,
1931d6c0b56eSmrg	.show_cursor = drmmode_show_cursor,
1932d6c0b56eSmrg	.hide_cursor = drmmode_hide_cursor,
1933d6c0b56eSmrg	.load_cursor_argb = drmmode_load_cursor_argb,
1934d6c0b56eSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,903,0)
1935d6c0b56eSmrg	.load_cursor_argb_check = drmmode_load_cursor_argb_check,
1936d6c0b56eSmrg#endif
1937d6c0b56eSmrg
1938d6c0b56eSmrg	.gamma_set = drmmode_crtc_gamma_set,
1939d6c0b56eSmrg	.shadow_create = drmmode_crtc_shadow_create,
1940d6c0b56eSmrg	.shadow_allocate = drmmode_crtc_shadow_allocate,
1941d6c0b56eSmrg	.shadow_destroy = drmmode_crtc_shadow_destroy,
194235d5b7c7Smrg	.destroy = drmmode_crtc_destroy,
1943d6c0b56eSmrg	.set_scanout_pixmap = drmmode_set_scanout_pixmap,
1944d6c0b56eSmrg};
1945d6c0b56eSmrg
1946d6c0b56eSmrgint drmmode_get_crtc_id(xf86CrtcPtr crtc)
1947d6c0b56eSmrg{
1948d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1949d6c0b56eSmrg	return drmmode_crtc->hw_id;
1950d6c0b56eSmrg}
1951d6c0b56eSmrg
1952d6c0b56eSmrgvoid drmmode_crtc_hw_id(xf86CrtcPtr crtc)
1953d6c0b56eSmrg{
1954d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1955d6c0b56eSmrg	ScrnInfoPtr pScrn = crtc->scrn;
1956d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
1957d6c0b56eSmrg	int r;
1958d6c0b56eSmrg
1959d6c0b56eSmrg	r = amdgpu_query_crtc_from_id(pAMDGPUEnt->pDev,
1960d6c0b56eSmrg				      drmmode_crtc->mode_crtc->crtc_id,
1961d6c0b56eSmrg				      &drmmode_crtc->hw_id);
1962d6c0b56eSmrg	if (r)
1963d6c0b56eSmrg		drmmode_crtc->hw_id = -1;
1964d6c0b56eSmrg}
1965d6c0b56eSmrg
196635d5b7c7Smrg/**
196735d5b7c7Smrg * Initialize color management properties for the given CRTC by programming
196835d5b7c7Smrg * the default gamma/degamma LUTs and CTM.
196935d5b7c7Smrg *
197035d5b7c7Smrg * If the CRTC does not support color management, or if errors occur during
197135d5b7c7Smrg * initialization, all color properties on the driver-private CRTC will left
197235d5b7c7Smrg * as NULL.
197335d5b7c7Smrg *
197435d5b7c7Smrg * @drm_fd: DRM file descriptor
197535d5b7c7Smrg * @crtc: CRTC to initialize color management on.
197635d5b7c7Smrg */
197735d5b7c7Smrgstatic void drmmode_crtc_cm_init(int drm_fd, xf86CrtcPtr crtc)
197835d5b7c7Smrg{
197935d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
198035d5b7c7Smrg	drmmode_ptr drmmode = drmmode_crtc->drmmode;
198135d5b7c7Smrg	int i;
198235d5b7c7Smrg
198335d5b7c7Smrg	if (!drmmode_cm_enabled(drmmode))
198435d5b7c7Smrg		return;
198535d5b7c7Smrg
198635d5b7c7Smrg	/* Init CTM to identity. Values are in S31.32 fixed-point format */
198735d5b7c7Smrg	drmmode_crtc->ctm = calloc(1, sizeof(*drmmode_crtc->ctm));
198835d5b7c7Smrg	if (!drmmode_crtc->ctm) {
198935d5b7c7Smrg		xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
199035d5b7c7Smrg			   "Memory error initializing CTM for CRTC%d",
199135d5b7c7Smrg			   drmmode_get_crtc_id(crtc));
199235d5b7c7Smrg		return;
199335d5b7c7Smrg	}
199435d5b7c7Smrg
199535d5b7c7Smrg	drmmode_crtc->ctm->matrix[0] = drmmode_crtc->ctm->matrix[4] =
199635d5b7c7Smrg		drmmode_crtc->ctm->matrix[8] = (uint64_t)1 << 32;
199735d5b7c7Smrg
199835d5b7c7Smrg	/* Push properties to reset properties currently in hardware */
199935d5b7c7Smrg	for (i = 0; i < CM_GAMMA_LUT; i++) {
200035d5b7c7Smrg		if (drmmode_crtc_push_cm_prop(crtc, i))
200135d5b7c7Smrg			xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
200235d5b7c7Smrg				   "Failed to initialize color management "
200335d5b7c7Smrg				   "property %s on CRTC%d. Property value may "
200435d5b7c7Smrg				   "not reflect actual hardware state.\n",
200535d5b7c7Smrg				   cm_prop_names[i],
200635d5b7c7Smrg				   drmmode_get_crtc_id(crtc));
200735d5b7c7Smrg	}
200835d5b7c7Smrg}
200935d5b7c7Smrg
2010d6c0b56eSmrgstatic unsigned int
2011d6c0b56eSmrgdrmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
2012d6c0b56eSmrg{
2013d6c0b56eSmrg	xf86CrtcPtr crtc;
2014d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc;
2015d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
201624b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
2017d6c0b56eSmrg
201824b90cf4Smrg	crtc = xf86CrtcCreate(pScrn, &info->drmmode_crtc_funcs);
201935d5b7c7Smrg	if (!crtc)
2020d6c0b56eSmrg		return 0;
2021d6c0b56eSmrg
2022d6c0b56eSmrg	drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
2023d6c0b56eSmrg	drmmode_crtc->mode_crtc =
2024d6c0b56eSmrg	    drmModeGetCrtc(pAMDGPUEnt->fd, mode_res->crtcs[num]);
2025d6c0b56eSmrg	drmmode_crtc->drmmode = drmmode;
2026d6c0b56eSmrg	drmmode_crtc->dpms_mode = DPMSModeOff;
2027d6c0b56eSmrg	crtc->driver_private = drmmode_crtc;
2028d6c0b56eSmrg	drmmode_crtc_hw_id(crtc);
2029d6c0b56eSmrg
203035d5b7c7Smrg	drmmode_crtc_cm_init(pAMDGPUEnt->fd, crtc);
203190f2b693Smrg	drmmode_crtc_vrr_init(pAMDGPUEnt->fd, crtc);
203235d5b7c7Smrg
2033d6c0b56eSmrg	/* Mark num'th crtc as in use on this device. */
2034d6c0b56eSmrg	pAMDGPUEnt->assigned_crtcs |= (1 << num);
2035d6c0b56eSmrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
2036d6c0b56eSmrg		       "Allocated crtc nr. %d to this screen.\n", num);
2037d6c0b56eSmrg
2038d6c0b56eSmrg	return 1;
2039d6c0b56eSmrg}
2040d6c0b56eSmrg
204124b90cf4Smrg/*
204224b90cf4Smrg * Update all of the property values for an output
204324b90cf4Smrg */
204424b90cf4Smrgstatic void
204524b90cf4Smrgdrmmode_output_update_properties(xf86OutputPtr output)
204624b90cf4Smrg{
204724b90cf4Smrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
204824b90cf4Smrg	int i, j, k;
204924b90cf4Smrg	int err;
205024b90cf4Smrg	drmModeConnectorPtr koutput;
205124b90cf4Smrg
205224b90cf4Smrg	/* Use the most recently fetched values from the kernel */
205324b90cf4Smrg	koutput = drmmode_output->mode_output;
205424b90cf4Smrg
205524b90cf4Smrg	if (!koutput)
205624b90cf4Smrg		return;
205724b90cf4Smrg
205824b90cf4Smrg	for (i = 0; i < drmmode_output->num_props; i++) {
205924b90cf4Smrg		drmmode_prop_ptr p = &drmmode_output->props[i];
206024b90cf4Smrg
206124b90cf4Smrg		for (j = 0; j < koutput->count_props; j++) {
206224b90cf4Smrg			if (koutput->props[j] != p->mode_prop->prop_id)
206324b90cf4Smrg				continue;
206424b90cf4Smrg
206524b90cf4Smrg			/* Check to see if the property value has changed */
206624b90cf4Smrg			if (koutput->prop_values[j] == p->value)
206724b90cf4Smrg				break;
206824b90cf4Smrg
206924b90cf4Smrg			p->value = koutput->prop_values[j];
207024b90cf4Smrg
207124b90cf4Smrg			if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
207224b90cf4Smrg				INT32 value = p->value;
207324b90cf4Smrg
207424b90cf4Smrg				err = RRChangeOutputProperty(output->randr_output,
207524b90cf4Smrg							     p->atoms[0], XA_INTEGER,
207624b90cf4Smrg							     32, PropModeReplace, 1,
207724b90cf4Smrg							     &value, FALSE, TRUE);
207824b90cf4Smrg				if (err != 0) {
207924b90cf4Smrg					xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
208024b90cf4Smrg						   "RRChangeOutputProperty error, %d\n",
208124b90cf4Smrg						   err);
208224b90cf4Smrg				}
208324b90cf4Smrg			} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
208424b90cf4Smrg				for (k = 0; k < p->mode_prop->count_enums; k++) {
208524b90cf4Smrg					if (p->mode_prop->enums[k].value == p->value)
208624b90cf4Smrg						break;
208724b90cf4Smrg				}
208824b90cf4Smrg				if (k < p->mode_prop->count_enums) {
208924b90cf4Smrg					err = RRChangeOutputProperty(output->randr_output,
209024b90cf4Smrg								     p->atoms[0], XA_ATOM,
209124b90cf4Smrg								     32, PropModeReplace, 1,
209224b90cf4Smrg								     &p->atoms[k + 1], FALSE,
209324b90cf4Smrg								     TRUE);
209424b90cf4Smrg					if (err != 0) {
209524b90cf4Smrg						xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
209624b90cf4Smrg							   "RRChangeOutputProperty error, %d\n",
209724b90cf4Smrg							   err);
209824b90cf4Smrg					}
209924b90cf4Smrg				}
210024b90cf4Smrg			}
210124b90cf4Smrg
210224b90cf4Smrg			break;
210324b90cf4Smrg		}
210424b90cf4Smrg        }
210524b90cf4Smrg}
210624b90cf4Smrg
2107d6c0b56eSmrgstatic xf86OutputStatus drmmode_output_detect(xf86OutputPtr output)
2108d6c0b56eSmrg{
2109d6c0b56eSmrg	/* go to the hw and retrieve a new output struct */
2110d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2111d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
2112d6c0b56eSmrg	xf86OutputStatus status;
2113d6c0b56eSmrg	drmModeFreeConnector(drmmode_output->mode_output);
2114d6c0b56eSmrg
2115d6c0b56eSmrg	drmmode_output->mode_output =
2116d6c0b56eSmrg	    drmModeGetConnector(pAMDGPUEnt->fd, drmmode_output->output_id);
211724b90cf4Smrg	if (!drmmode_output->mode_output) {
211824b90cf4Smrg		drmmode_output->output_id = -1;
2119d6c0b56eSmrg		return XF86OutputStatusDisconnected;
212024b90cf4Smrg	}
212124b90cf4Smrg
212224b90cf4Smrg	drmmode_output_update_properties(output);
2123d6c0b56eSmrg
2124d6c0b56eSmrg	switch (drmmode_output->mode_output->connection) {
2125d6c0b56eSmrg	case DRM_MODE_CONNECTED:
2126d6c0b56eSmrg		status = XF86OutputStatusConnected;
2127d6c0b56eSmrg		break;
2128d6c0b56eSmrg	case DRM_MODE_DISCONNECTED:
2129d6c0b56eSmrg		status = XF86OutputStatusDisconnected;
2130d6c0b56eSmrg		break;
2131d6c0b56eSmrg	default:
2132d6c0b56eSmrg	case DRM_MODE_UNKNOWNCONNECTION:
2133d6c0b56eSmrg		status = XF86OutputStatusUnknown;
2134d6c0b56eSmrg		break;
2135d6c0b56eSmrg	}
2136d6c0b56eSmrg	return status;
2137d6c0b56eSmrg}
2138d6c0b56eSmrg
2139d6c0b56eSmrgstatic Bool
2140d6c0b56eSmrgdrmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
2141d6c0b56eSmrg{
2142d6c0b56eSmrg	return MODE_OK;
2143d6c0b56eSmrg}
2144d6c0b56eSmrg
214524b90cf4Smrgstatic int
214624b90cf4Smrgkoutput_get_prop_idx(int fd, drmModeConnectorPtr koutput,
214724b90cf4Smrg        int type, const char *name)
214824b90cf4Smrg{
214924b90cf4Smrg    int idx = -1;
215024b90cf4Smrg
215124b90cf4Smrg    for (int i = 0; i < koutput->count_props; i++) {
215224b90cf4Smrg        drmModePropertyPtr prop = drmModeGetProperty(fd, koutput->props[i]);
215324b90cf4Smrg
215424b90cf4Smrg        if (!prop)
215524b90cf4Smrg            continue;
215624b90cf4Smrg
215724b90cf4Smrg        if (drm_property_type_is(prop, type) && !strcmp(prop->name, name))
215824b90cf4Smrg            idx = i;
215924b90cf4Smrg
216024b90cf4Smrg        drmModeFreeProperty(prop);
216124b90cf4Smrg
216224b90cf4Smrg        if (idx > -1)
216324b90cf4Smrg            break;
216424b90cf4Smrg    }
216524b90cf4Smrg
216624b90cf4Smrg    return idx;
216724b90cf4Smrg}
216824b90cf4Smrg
216924b90cf4Smrgstatic int
217024b90cf4Smrgkoutput_get_prop_id(int fd, drmModeConnectorPtr koutput,
217124b90cf4Smrg        int type, const char *name)
217224b90cf4Smrg{
217324b90cf4Smrg    int idx = koutput_get_prop_idx(fd, koutput, type, name);
217424b90cf4Smrg
217524b90cf4Smrg    return (idx > -1) ? koutput->props[idx] : -1;
217624b90cf4Smrg}
217724b90cf4Smrg
217824b90cf4Smrgstatic drmModePropertyBlobPtr
217924b90cf4Smrgkoutput_get_prop_blob(int fd, drmModeConnectorPtr koutput, const char *name)
218024b90cf4Smrg{
218124b90cf4Smrg    drmModePropertyBlobPtr blob = NULL;
218224b90cf4Smrg    int idx = koutput_get_prop_idx(fd, koutput, DRM_MODE_PROP_BLOB, name);
218324b90cf4Smrg
218424b90cf4Smrg    if (idx > -1)
218524b90cf4Smrg        blob = drmModeGetPropertyBlob(fd, koutput->prop_values[idx]);
218624b90cf4Smrg
218724b90cf4Smrg    return blob;
218824b90cf4Smrg}
218924b90cf4Smrg
2190d6c0b56eSmrgstatic DisplayModePtr drmmode_output_get_modes(xf86OutputPtr output)
2191d6c0b56eSmrg{
2192d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2193d6c0b56eSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
2194d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
2195d6c0b56eSmrg	int i;
2196d6c0b56eSmrg	DisplayModePtr Modes = NULL, Mode;
2197d6c0b56eSmrg	xf86MonPtr mon = NULL;
2198d6c0b56eSmrg
2199d6c0b56eSmrg	if (!koutput)
2200d6c0b56eSmrg		return NULL;
2201d6c0b56eSmrg
220224b90cf4Smrg	drmModeFreePropertyBlob(drmmode_output->edid_blob);
220324b90cf4Smrg
2204d6c0b56eSmrg	/* look for an EDID property */
220524b90cf4Smrg	drmmode_output->edid_blob =
220624b90cf4Smrg		koutput_get_prop_blob(pAMDGPUEnt->fd, koutput, "EDID");
2207d6c0b56eSmrg
2208d6c0b56eSmrg	if (drmmode_output->edid_blob) {
2209d6c0b56eSmrg		mon = xf86InterpretEDID(output->scrn->scrnIndex,
2210d6c0b56eSmrg					drmmode_output->edid_blob->data);
2211d6c0b56eSmrg		if (mon && drmmode_output->edid_blob->length > 128)
2212d6c0b56eSmrg			mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
2213d6c0b56eSmrg	}
2214d6c0b56eSmrg	xf86OutputSetEDID(output, mon);
2215d6c0b56eSmrg
2216d6c0b56eSmrg	/* modes should already be available */
2217d6c0b56eSmrg	for (i = 0; i < koutput->count_modes; i++) {
2218d6c0b56eSmrg		Mode = xnfalloc(sizeof(DisplayModeRec));
2219d6c0b56eSmrg
2220d6c0b56eSmrg		drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i],
2221d6c0b56eSmrg					 Mode);
2222d6c0b56eSmrg		Modes = xf86ModesAdd(Modes, Mode);
2223d6c0b56eSmrg
2224d6c0b56eSmrg	}
2225d6c0b56eSmrg	return Modes;
2226d6c0b56eSmrg}
2227d6c0b56eSmrg
2228d6c0b56eSmrgstatic void drmmode_output_destroy(xf86OutputPtr output)
2229d6c0b56eSmrg{
2230d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2231d6c0b56eSmrg	int i;
2232d6c0b56eSmrg
2233d6c0b56eSmrg	if (drmmode_output->edid_blob)
2234d6c0b56eSmrg		drmModeFreePropertyBlob(drmmode_output->edid_blob);
2235d6c0b56eSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
2236d6c0b56eSmrg		drmModeFreeProperty(drmmode_output->props[i].mode_prop);
2237d6c0b56eSmrg		free(drmmode_output->props[i].atoms);
2238d6c0b56eSmrg	}
2239d6c0b56eSmrg	for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) {
2240d6c0b56eSmrg		drmModeFreeEncoder(drmmode_output->mode_encoders[i]);
2241d6c0b56eSmrg	}
2242d6c0b56eSmrg	free(drmmode_output->mode_encoders);
2243d6c0b56eSmrg	free(drmmode_output->props);
2244d6c0b56eSmrg	drmModeFreeConnector(drmmode_output->mode_output);
2245d6c0b56eSmrg	free(drmmode_output);
2246d6c0b56eSmrg	output->driver_private = NULL;
2247d6c0b56eSmrg}
2248d6c0b56eSmrg
2249d6c0b56eSmrgstatic void drmmode_output_dpms(xf86OutputPtr output, int mode)
2250d6c0b56eSmrg{
2251d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2252d6c0b56eSmrg	xf86CrtcPtr crtc = output->crtc;
2253d6c0b56eSmrg	drmModeConnectorPtr koutput = drmmode_output->mode_output;
2254d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
2255d6c0b56eSmrg
2256d6c0b56eSmrg	if (!koutput)
2257d6c0b56eSmrg		return;
2258d6c0b56eSmrg
225924b90cf4Smrg	if (mode != DPMSModeOn && crtc)
2260d6c0b56eSmrg		drmmode_do_crtc_dpms(crtc, mode);
2261d6c0b56eSmrg
2262d6c0b56eSmrg	drmModeConnectorSetProperty(pAMDGPUEnt->fd, koutput->connector_id,
2263d6c0b56eSmrg				    drmmode_output->dpms_enum_id, mode);
2264d6c0b56eSmrg
2265d6c0b56eSmrg	if (mode == DPMSModeOn && crtc) {
2266d6c0b56eSmrg		drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2267d6c0b56eSmrg
2268d6c0b56eSmrg		if (drmmode_crtc->need_modeset)
2269d6c0b56eSmrg			drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
2270d6c0b56eSmrg					       crtc->x, crtc->y);
2271d6c0b56eSmrg		else
2272d6c0b56eSmrg			drmmode_do_crtc_dpms(output->crtc, mode);
2273d6c0b56eSmrg	}
2274d6c0b56eSmrg}
2275d6c0b56eSmrg
2276d6c0b56eSmrgstatic Bool drmmode_property_ignore(drmModePropertyPtr prop)
2277d6c0b56eSmrg{
2278d6c0b56eSmrg	if (!prop)
2279d6c0b56eSmrg		return TRUE;
2280d6c0b56eSmrg	/* ignore blob prop */
2281d6c0b56eSmrg	if (prop->flags & DRM_MODE_PROP_BLOB)
2282d6c0b56eSmrg		return TRUE;
2283d6c0b56eSmrg	/* ignore standard property */
2284d6c0b56eSmrg	if (!strcmp(prop->name, "EDID") || !strcmp(prop->name, "DPMS"))
2285d6c0b56eSmrg		return TRUE;
2286d6c0b56eSmrg
2287d6c0b56eSmrg	return FALSE;
2288d6c0b56eSmrg}
2289d6c0b56eSmrg
2290d6c0b56eSmrgstatic void drmmode_output_create_resources(xf86OutputPtr output)
2291d6c0b56eSmrg{
229211bf0794Smrg	AMDGPUInfoPtr info = AMDGPUPTR(output->scrn);
2293d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
229435d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc;
2295d6c0b56eSmrg	drmModeConnectorPtr mode_output = drmmode_output->mode_output;
2296d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
229711bf0794Smrg	drmModePropertyPtr drmmode_prop, tearfree_prop;
2298d6c0b56eSmrg	int i, j, err;
229935d5b7c7Smrg	Atom name;
230035d5b7c7Smrg
230135d5b7c7Smrg	/* Create CONNECTOR_ID property */
230235d5b7c7Smrg	name = MakeAtom("CONNECTOR_ID", 12, TRUE);
230335d5b7c7Smrg	if (name != BAD_RESOURCE) {
230435d5b7c7Smrg		INT32 value = mode_output->connector_id;
230535d5b7c7Smrg
230635d5b7c7Smrg		err = RRConfigureOutputProperty(output->randr_output, name,
230735d5b7c7Smrg						FALSE, FALSE, TRUE, 1, &value);
230835d5b7c7Smrg		if (err != Success) {
230935d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
231035d5b7c7Smrg				   "RRConfigureOutputProperty error, %d\n", err);
231135d5b7c7Smrg		}
231235d5b7c7Smrg
231335d5b7c7Smrg		err = RRChangeOutputProperty(output->randr_output, name,
231435d5b7c7Smrg					     XA_INTEGER, 32, PropModeReplace, 1,
231535d5b7c7Smrg					     &value, FALSE, FALSE);
231635d5b7c7Smrg		if (err != Success) {
231735d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
231835d5b7c7Smrg				   "RRChangeOutputProperty error, %d\n", err);
231935d5b7c7Smrg		}
232035d5b7c7Smrg	}
2321d6c0b56eSmrg
2322d6c0b56eSmrg	drmmode_output->props =
232311bf0794Smrg		calloc(mode_output->count_props + 1, sizeof(drmmode_prop_rec));
2324d6c0b56eSmrg	if (!drmmode_output->props)
2325d6c0b56eSmrg		return;
2326d6c0b56eSmrg
2327d6c0b56eSmrg	drmmode_output->num_props = 0;
2328d6c0b56eSmrg	for (i = 0, j = 0; i < mode_output->count_props; i++) {
2329d6c0b56eSmrg		drmmode_prop =
2330d6c0b56eSmrg		    drmModeGetProperty(pAMDGPUEnt->fd, mode_output->props[i]);
2331d6c0b56eSmrg		if (drmmode_property_ignore(drmmode_prop)) {
2332d6c0b56eSmrg			drmModeFreeProperty(drmmode_prop);
2333d6c0b56eSmrg			continue;
2334d6c0b56eSmrg		}
2335d6c0b56eSmrg		drmmode_output->props[j].mode_prop = drmmode_prop;
2336d6c0b56eSmrg		drmmode_output->props[j].value = mode_output->prop_values[i];
2337d6c0b56eSmrg		drmmode_output->num_props++;
2338d6c0b56eSmrg		j++;
2339d6c0b56eSmrg	}
2340d6c0b56eSmrg
234111bf0794Smrg	/* Userspace-only property for TearFree */
234211bf0794Smrg	tearfree_prop = calloc(1, sizeof(*tearfree_prop));
234311bf0794Smrg	tearfree_prop->flags = DRM_MODE_PROP_ENUM;
234435d5b7c7Smrg	strcpy(tearfree_prop->name, "TearFree");
234511bf0794Smrg	tearfree_prop->count_enums = 3;
234611bf0794Smrg	tearfree_prop->enums = calloc(tearfree_prop->count_enums,
234711bf0794Smrg				      sizeof(*tearfree_prop->enums));
234835d5b7c7Smrg	strcpy(tearfree_prop->enums[0].name, "off");
234935d5b7c7Smrg	strcpy(tearfree_prop->enums[1].name, "on");
235011bf0794Smrg	tearfree_prop->enums[1].value = 1;
235135d5b7c7Smrg	strcpy(tearfree_prop->enums[2].name, "auto");
235211bf0794Smrg	tearfree_prop->enums[2].value = 2;
235311bf0794Smrg	drmmode_output->props[j].mode_prop = tearfree_prop;
235411bf0794Smrg	drmmode_output->props[j].value = info->tear_free;
235511bf0794Smrg	drmmode_output->tear_free = info->tear_free;
235611bf0794Smrg	drmmode_output->num_props++;
235711bf0794Smrg
2358d6c0b56eSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
2359d6c0b56eSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
2360d6c0b56eSmrg		drmmode_prop = p->mode_prop;
2361d6c0b56eSmrg
2362d6c0b56eSmrg		if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
2363d6c0b56eSmrg			INT32 range[2];
2364d6c0b56eSmrg			INT32 value = p->value;
2365d6c0b56eSmrg
2366d6c0b56eSmrg			p->num_atoms = 1;
2367d6c0b56eSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
2368d6c0b56eSmrg			if (!p->atoms)
2369d6c0b56eSmrg				continue;
2370d6c0b56eSmrg			p->atoms[0] =
2371d6c0b56eSmrg			    MakeAtom(drmmode_prop->name,
2372d6c0b56eSmrg				     strlen(drmmode_prop->name), TRUE);
2373d6c0b56eSmrg			range[0] = drmmode_prop->values[0];
2374d6c0b56eSmrg			range[1] = drmmode_prop->values[1];
2375d6c0b56eSmrg			err =
2376d6c0b56eSmrg			    RRConfigureOutputProperty(output->randr_output,
2377d6c0b56eSmrg						      p->atoms[0], FALSE, TRUE,
2378d6c0b56eSmrg						      drmmode_prop->flags &
2379d6c0b56eSmrg						      DRM_MODE_PROP_IMMUTABLE ?
2380d6c0b56eSmrg						      TRUE : FALSE, 2, range);
2381d6c0b56eSmrg			if (err != 0) {
2382d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2383d6c0b56eSmrg					   "RRConfigureOutputProperty error, %d\n",
2384d6c0b56eSmrg					   err);
2385d6c0b56eSmrg			}
2386d6c0b56eSmrg			err =
2387d6c0b56eSmrg			    RRChangeOutputProperty(output->randr_output,
2388d6c0b56eSmrg						   p->atoms[0], XA_INTEGER, 32,
2389d6c0b56eSmrg						   PropModeReplace, 1, &value,
2390d6c0b56eSmrg						   FALSE, TRUE);
2391d6c0b56eSmrg			if (err != 0) {
2392d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2393d6c0b56eSmrg					   "RRChangeOutputProperty error, %d\n",
2394d6c0b56eSmrg					   err);
2395d6c0b56eSmrg			}
2396d6c0b56eSmrg		} else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
2397d6c0b56eSmrg			p->num_atoms = drmmode_prop->count_enums + 1;
2398d6c0b56eSmrg			p->atoms = calloc(p->num_atoms, sizeof(Atom));
2399d6c0b56eSmrg			if (!p->atoms)
2400d6c0b56eSmrg				continue;
2401d6c0b56eSmrg			p->atoms[0] =
2402d6c0b56eSmrg			    MakeAtom(drmmode_prop->name,
2403d6c0b56eSmrg				     strlen(drmmode_prop->name), TRUE);
2404d6c0b56eSmrg			for (j = 1; j <= drmmode_prop->count_enums; j++) {
2405d6c0b56eSmrg				struct drm_mode_property_enum *e =
2406d6c0b56eSmrg				    &drmmode_prop->enums[j - 1];
2407d6c0b56eSmrg				p->atoms[j] =
2408d6c0b56eSmrg				    MakeAtom(e->name, strlen(e->name), TRUE);
2409d6c0b56eSmrg			}
2410d6c0b56eSmrg			err =
2411d6c0b56eSmrg			    RRConfigureOutputProperty(output->randr_output,
2412d6c0b56eSmrg						      p->atoms[0], FALSE, FALSE,
2413d6c0b56eSmrg						      drmmode_prop->flags &
2414d6c0b56eSmrg						      DRM_MODE_PROP_IMMUTABLE ?
2415d6c0b56eSmrg						      TRUE : FALSE,
2416d6c0b56eSmrg						      p->num_atoms - 1,
2417d6c0b56eSmrg						      (INT32 *) & p->atoms[1]);
2418d6c0b56eSmrg			if (err != 0) {
2419d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2420d6c0b56eSmrg					   "RRConfigureOutputProperty error, %d\n",
2421d6c0b56eSmrg					   err);
2422d6c0b56eSmrg			}
2423d6c0b56eSmrg			for (j = 0; j < drmmode_prop->count_enums; j++)
2424d6c0b56eSmrg				if (drmmode_prop->enums[j].value == p->value)
2425d6c0b56eSmrg					break;
2426d6c0b56eSmrg			/* there's always a matching value */
2427d6c0b56eSmrg			err =
2428d6c0b56eSmrg			    RRChangeOutputProperty(output->randr_output,
2429d6c0b56eSmrg						   p->atoms[0], XA_ATOM, 32,
2430d6c0b56eSmrg						   PropModeReplace, 1,
2431d6c0b56eSmrg						   &p->atoms[j + 1], FALSE,
2432d6c0b56eSmrg						   TRUE);
2433d6c0b56eSmrg			if (err != 0) {
2434d6c0b56eSmrg				xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2435d6c0b56eSmrg					   "RRChangeOutputProperty error, %d\n",
2436d6c0b56eSmrg					   err);
2437d6c0b56eSmrg			}
2438d6c0b56eSmrg		}
2439d6c0b56eSmrg	}
244035d5b7c7Smrg
244135d5b7c7Smrg	/* Do not configure cm properties on output if there's no support. */
244235d5b7c7Smrg	if (!drmmode_cm_enabled(drmmode_output->drmmode))
244335d5b7c7Smrg		return;
244435d5b7c7Smrg
244535d5b7c7Smrg	drmmode_crtc = output->crtc ? output->crtc->driver_private : NULL;
244635d5b7c7Smrg
244735d5b7c7Smrg	for (i = 0; i < CM_NUM_PROPS; i++)
244835d5b7c7Smrg		rr_configure_and_change_cm_property(output, drmmode_crtc, i);
244935d5b7c7Smrg}
245035d5b7c7Smrg
245135d5b7c7Smrgstatic void
245235d5b7c7Smrgdrmmode_output_set_tear_free(AMDGPUEntPtr pAMDGPUEnt,
245335d5b7c7Smrg			     drmmode_output_private_ptr drmmode_output,
245435d5b7c7Smrg			     xf86CrtcPtr crtc, int tear_free)
245535d5b7c7Smrg{
245635d5b7c7Smrg	if (drmmode_output->tear_free == tear_free)
245735d5b7c7Smrg		return;
245835d5b7c7Smrg
245935d5b7c7Smrg	drmmode_output->tear_free = tear_free;
246035d5b7c7Smrg
246135d5b7c7Smrg	if (crtc) {
246235d5b7c7Smrg		drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
246335d5b7c7Smrg				       crtc->x, crtc->y);
246435d5b7c7Smrg	}
2465d6c0b56eSmrg}
2466d6c0b56eSmrg
2467d6c0b56eSmrgstatic Bool
2468d6c0b56eSmrgdrmmode_output_set_property(xf86OutputPtr output, Atom property,
2469d6c0b56eSmrg			    RRPropertyValuePtr value)
2470d6c0b56eSmrg{
2471d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output = output->driver_private;
2472d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(output->scrn);
247335d5b7c7Smrg	enum drmmode_cm_prop cm_prop_index;
2474d6c0b56eSmrg	int i;
2475d6c0b56eSmrg
247635d5b7c7Smrg	cm_prop_index = get_cm_enum_from_str(NameForAtom(property));
247735d5b7c7Smrg	if (cm_prop_index >= 0 && cm_prop_index < CM_DEGAMMA_LUT_SIZE) {
247835d5b7c7Smrg		if (!output->crtc)
247935d5b7c7Smrg			return FALSE;
248035d5b7c7Smrg		if (drmmode_crtc_stage_cm_prop(output->crtc, cm_prop_index,
248135d5b7c7Smrg					       value))
248235d5b7c7Smrg			return FALSE;
248335d5b7c7Smrg		if (drmmode_crtc_push_cm_prop(output->crtc, cm_prop_index))
248435d5b7c7Smrg			return FALSE;
248535d5b7c7Smrg		return TRUE;
248635d5b7c7Smrg	}
248735d5b7c7Smrg
2488d6c0b56eSmrg	for (i = 0; i < drmmode_output->num_props; i++) {
2489d6c0b56eSmrg		drmmode_prop_ptr p = &drmmode_output->props[i];
2490d6c0b56eSmrg
2491d6c0b56eSmrg		if (p->atoms[0] != property)
2492d6c0b56eSmrg			continue;
2493d6c0b56eSmrg
2494d6c0b56eSmrg		if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
2495d6c0b56eSmrg			uint32_t val;
2496d6c0b56eSmrg
2497d6c0b56eSmrg			if (value->type != XA_INTEGER || value->format != 32 ||
2498d6c0b56eSmrg			    value->size != 1)
2499d6c0b56eSmrg				return FALSE;
2500d6c0b56eSmrg			val = *(uint32_t *) value->data;
2501d6c0b56eSmrg
2502d6c0b56eSmrg			drmModeConnectorSetProperty(pAMDGPUEnt->fd,
2503d6c0b56eSmrg						    drmmode_output->output_id,
2504d6c0b56eSmrg						    p->mode_prop->prop_id,
2505d6c0b56eSmrg						    (uint64_t) val);
2506d6c0b56eSmrg			return TRUE;
2507d6c0b56eSmrg		} else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
2508d6c0b56eSmrg			Atom atom;
2509d6c0b56eSmrg			const char *name;
2510d6c0b56eSmrg			int j;
2511d6c0b56eSmrg
2512d6c0b56eSmrg			if (value->type != XA_ATOM || value->format != 32
2513d6c0b56eSmrg			    || value->size != 1)
2514d6c0b56eSmrg				return FALSE;
2515d6c0b56eSmrg			memcpy(&atom, value->data, 4);
251624b90cf4Smrg			if (!(name = NameForAtom(atom)))
251724b90cf4Smrg				return FALSE;
2518d6c0b56eSmrg
2519d6c0b56eSmrg			/* search for matching name string, then set its value down */
2520d6c0b56eSmrg			for (j = 0; j < p->mode_prop->count_enums; j++) {
2521d6c0b56eSmrg				if (!strcmp(p->mode_prop->enums[j].name, name)) {
252211bf0794Smrg					if (i == (drmmode_output->num_props - 1)) {
252335d5b7c7Smrg						drmmode_output_set_tear_free(pAMDGPUEnt,
252435d5b7c7Smrg									     drmmode_output,
252535d5b7c7Smrg									     output->crtc, j);
252611bf0794Smrg					} else {
252711bf0794Smrg						drmModeConnectorSetProperty(pAMDGPUEnt->fd,
252811bf0794Smrg									    drmmode_output->output_id,
252911bf0794Smrg									    p->mode_prop->prop_id,
253011bf0794Smrg									    p->mode_prop->enums[j].value);
253111bf0794Smrg					}
253211bf0794Smrg
2533d6c0b56eSmrg					return TRUE;
2534d6c0b56eSmrg				}
2535d6c0b56eSmrg			}
2536d6c0b56eSmrg		}
2537d6c0b56eSmrg	}
2538d6c0b56eSmrg
2539d6c0b56eSmrg	return TRUE;
2540d6c0b56eSmrg}
2541d6c0b56eSmrg
2542d6c0b56eSmrgstatic Bool drmmode_output_get_property(xf86OutputPtr output, Atom property)
2543d6c0b56eSmrg{
254435d5b7c7Smrg	drmmode_crtc_private_ptr drmmode_crtc;
254535d5b7c7Smrg	enum drmmode_cm_prop cm_prop_id;
254635d5b7c7Smrg	int ret;
254735d5b7c7Smrg
254835d5b7c7Smrg	/* First, see if it's a cm property */
254935d5b7c7Smrg	cm_prop_id = get_cm_enum_from_str(NameForAtom(property));
255035d5b7c7Smrg	if (output->crtc && cm_prop_id != CM_INVALID_PROP) {
255135d5b7c7Smrg		drmmode_crtc = output->crtc->driver_private;
255235d5b7c7Smrg
255335d5b7c7Smrg		ret = rr_configure_and_change_cm_property(output, drmmode_crtc,
255435d5b7c7Smrg							  cm_prop_id);
255535d5b7c7Smrg		if (ret) {
255635d5b7c7Smrg			xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
255735d5b7c7Smrg				   "Error getting color property: %d\n",
255835d5b7c7Smrg				   ret);
255935d5b7c7Smrg			return FALSE;
256035d5b7c7Smrg		}
256135d5b7c7Smrg		return TRUE;
256235d5b7c7Smrg	}
256335d5b7c7Smrg
256435d5b7c7Smrg	/* Otherwise, must be an output property. */
2565d6c0b56eSmrg	return TRUE;
2566d6c0b56eSmrg}
2567d6c0b56eSmrg
2568d6c0b56eSmrgstatic const xf86OutputFuncsRec drmmode_output_funcs = {
2569d6c0b56eSmrg	.dpms = drmmode_output_dpms,
2570d6c0b56eSmrg	.create_resources = drmmode_output_create_resources,
2571d6c0b56eSmrg	.set_property = drmmode_output_set_property,
2572d6c0b56eSmrg	.get_property = drmmode_output_get_property,
2573d6c0b56eSmrg	.detect = drmmode_output_detect,
2574d6c0b56eSmrg	.mode_valid = drmmode_output_mode_valid,
2575d6c0b56eSmrg
2576d6c0b56eSmrg	.get_modes = drmmode_output_get_modes,
2577d6c0b56eSmrg	.destroy = drmmode_output_destroy
2578d6c0b56eSmrg};
2579d6c0b56eSmrg
2580d6c0b56eSmrgstatic int subpixel_conv_table[7] = { 0, SubPixelUnknown,
2581d6c0b56eSmrg	SubPixelHorizontalRGB,
2582d6c0b56eSmrg	SubPixelHorizontalBGR,
2583d6c0b56eSmrg	SubPixelVerticalRGB,
2584d6c0b56eSmrg	SubPixelVerticalBGR,
2585d6c0b56eSmrg	SubPixelNone
2586d6c0b56eSmrg};
2587d6c0b56eSmrg
2588d6c0b56eSmrgconst char *output_names[] = { "None",
2589d6c0b56eSmrg	"VGA",
2590d6c0b56eSmrg	"DVI-I",
2591d6c0b56eSmrg	"DVI-D",
2592d6c0b56eSmrg	"DVI-A",
2593d6c0b56eSmrg	"Composite",
2594d6c0b56eSmrg	"S-video",
2595d6c0b56eSmrg	"LVDS",
2596d6c0b56eSmrg	"CTV",
2597d6c0b56eSmrg	"DIN",
2598d6c0b56eSmrg	"DisplayPort",
2599d6c0b56eSmrg	"HDMI-A",
2600d6c0b56eSmrg	"HDMI-B",
2601d6c0b56eSmrg	"TV",
2602d6c0b56eSmrg	"eDP",
2603d6c0b56eSmrg	"Virtual",
2604d6c0b56eSmrg	"DSI",
2605d6c0b56eSmrg};
2606d6c0b56eSmrg
2607d6c0b56eSmrg#define NUM_OUTPUT_NAMES (sizeof(output_names) / sizeof(output_names[0]))
2608d6c0b56eSmrg
2609d6c0b56eSmrgstatic xf86OutputPtr find_output(ScrnInfoPtr pScrn, int id)
2610d6c0b56eSmrg{
2611d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
2612d6c0b56eSmrg	int i;
2613d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2614d6c0b56eSmrg		xf86OutputPtr output = xf86_config->output[i];
2615d6c0b56eSmrg		drmmode_output_private_ptr drmmode_output;
2616d6c0b56eSmrg		drmmode_output = output->driver_private;
2617d6c0b56eSmrg		if (drmmode_output->output_id == id)
2618d6c0b56eSmrg			return output;
2619d6c0b56eSmrg	}
2620d6c0b56eSmrg	return NULL;
2621d6c0b56eSmrg}
2622d6c0b56eSmrg
2623d6c0b56eSmrgstatic int parse_path_blob(drmModePropertyBlobPtr path_blob, int *conn_base_id, char **path)
2624d6c0b56eSmrg{
2625d6c0b56eSmrg	char *conn;
2626d6c0b56eSmrg	char conn_id[5];
2627d6c0b56eSmrg	int id, len;
2628d6c0b56eSmrg	char *blob_data;
2629d6c0b56eSmrg
2630d6c0b56eSmrg	if (!path_blob)
2631d6c0b56eSmrg		return -1;
2632d6c0b56eSmrg
2633d6c0b56eSmrg	blob_data = path_blob->data;
2634d6c0b56eSmrg	/* we only handle MST paths for now */
2635d6c0b56eSmrg	if (strncmp(blob_data, "mst:", 4))
2636d6c0b56eSmrg		return -1;
2637d6c0b56eSmrg
2638d6c0b56eSmrg	conn = strchr(blob_data + 4, '-');
2639d6c0b56eSmrg	if (!conn)
2640d6c0b56eSmrg		return -1;
2641d6c0b56eSmrg	len = conn - (blob_data + 4);
2642d6c0b56eSmrg	if (len + 1 > 5)
2643d6c0b56eSmrg		return -1;
2644d6c0b56eSmrg	memcpy(conn_id, blob_data + 4, len);
2645d6c0b56eSmrg	conn_id[len] = '\0';
2646d6c0b56eSmrg	id = strtoul(conn_id, NULL, 10);
2647d6c0b56eSmrg
2648d6c0b56eSmrg	*conn_base_id = id;
2649d6c0b56eSmrg
2650d6c0b56eSmrg	*path = conn + 1;
2651d6c0b56eSmrg	return 0;
2652d6c0b56eSmrg}
2653d6c0b56eSmrg
2654d6c0b56eSmrgstatic void
2655d6c0b56eSmrgdrmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
2656d6c0b56eSmrg		    drmModePropertyBlobPtr path_blob, int *num_dvi, int *num_hdmi)
2657d6c0b56eSmrg{
2658d6c0b56eSmrg	xf86OutputPtr output;
2659d6c0b56eSmrg	int conn_id;
2660d6c0b56eSmrg	char *extra_path;
2661d6c0b56eSmrg
2662d6c0b56eSmrg	output = NULL;
2663d6c0b56eSmrg	if (parse_path_blob(path_blob, &conn_id, &extra_path) == 0)
2664d6c0b56eSmrg		output = find_output(pScrn, conn_id);
2665d6c0b56eSmrg	if (output) {
2666d6c0b56eSmrg		snprintf(name, 32, "%s-%s", output->name, extra_path);
2667d6c0b56eSmrg	} else {
266824b90cf4Smrg		if (koutput->connector_type >= NUM_OUTPUT_NAMES) {
2669d6c0b56eSmrg			snprintf(name, 32, "Unknown%d-%d", koutput->connector_type, koutput->connector_type_id - 1);
267024b90cf4Smrg		} else if (pScrn->is_gpu) {
2671d6c0b56eSmrg			snprintf(name, 32, "%s-%d-%d", output_names[koutput->connector_type],
2672d6c0b56eSmrg				 pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, koutput->connector_type_id - 1);
267324b90cf4Smrg		} else {
2674d6c0b56eSmrg			/* need to do smart conversion here for compat with non-kms ATI driver */
2675d6c0b56eSmrg			if (koutput->connector_type_id == 1) {
2676d6c0b56eSmrg				switch(koutput->connector_type) {
2677d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DVII:
2678d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DVID:
2679d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DVIA:
2680d6c0b56eSmrg					snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], *num_dvi);
2681d6c0b56eSmrg					(*num_dvi)++;
2682d6c0b56eSmrg					break;
2683d6c0b56eSmrg				case DRM_MODE_CONNECTOR_HDMIA:
2684d6c0b56eSmrg				case DRM_MODE_CONNECTOR_HDMIB:
2685d6c0b56eSmrg					snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], *num_hdmi);
2686d6c0b56eSmrg					(*num_hdmi)++;
2687d6c0b56eSmrg					break;
2688d6c0b56eSmrg				case DRM_MODE_CONNECTOR_VGA:
2689d6c0b56eSmrg				case DRM_MODE_CONNECTOR_DisplayPort:
2690d6c0b56eSmrg					snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id - 1);
2691d6c0b56eSmrg					break;
2692d6c0b56eSmrg				default:
2693d6c0b56eSmrg					snprintf(name, 32, "%s", output_names[koutput->connector_type]);
2694d6c0b56eSmrg					break;
2695d6c0b56eSmrg				}
2696d6c0b56eSmrg			} else {
2697d6c0b56eSmrg				snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id - 1);
2698d6c0b56eSmrg			}
2699d6c0b56eSmrg		}
2700d6c0b56eSmrg	}
2701d6c0b56eSmrg}
2702d6c0b56eSmrg
2703d6c0b56eSmrg
2704d6c0b56eSmrgstatic unsigned int
2705d6c0b56eSmrgdrmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num, int *num_dvi, int *num_hdmi, int dynamic)
2706d6c0b56eSmrg{
2707d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
2708d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
2709d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
2710d6c0b56eSmrg	xf86OutputPtr output;
2711d6c0b56eSmrg	drmModeConnectorPtr koutput;
2712d6c0b56eSmrg	drmModeEncoderPtr *kencoders = NULL;
2713d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output;
2714d6c0b56eSmrg	drmModePropertyBlobPtr path_blob = NULL;
271535d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
271635d5b7c7Smrg	Bool nonDesktop = FALSE;
271735d5b7c7Smrg#endif
2718d6c0b56eSmrg	char name[32];
2719d6c0b56eSmrg	int i;
2720d6c0b56eSmrg	const char *s;
2721d6c0b56eSmrg
2722d6c0b56eSmrg	koutput =
2723d6c0b56eSmrg	    drmModeGetConnector(pAMDGPUEnt->fd,
2724d6c0b56eSmrg				mode_res->connectors[num]);
2725d6c0b56eSmrg	if (!koutput)
2726d6c0b56eSmrg		return 0;
2727d6c0b56eSmrg
272824b90cf4Smrg	path_blob = koutput_get_prop_blob(pAMDGPUEnt->fd, koutput, "PATH");
2729d6c0b56eSmrg
273035d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
273135d5b7c7Smrg	i = koutput_get_prop_idx(pAMDGPUEnt->fd, koutput, DRM_MODE_PROP_RANGE,
273235d5b7c7Smrg				 "non-desktop");
273335d5b7c7Smrg	if (i >= 0)
273435d5b7c7Smrg        	nonDesktop = koutput->prop_values[i] != 0;
273535d5b7c7Smrg#endif
273635d5b7c7Smrg
2737d6c0b56eSmrg	kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders);
2738d6c0b56eSmrg	if (!kencoders) {
2739d6c0b56eSmrg		goto out_free_encoders;
2740d6c0b56eSmrg	}
2741d6c0b56eSmrg
2742d6c0b56eSmrg	for (i = 0; i < koutput->count_encoders; i++) {
2743d6c0b56eSmrg		kencoders[i] =
2744d6c0b56eSmrg		    drmModeGetEncoder(pAMDGPUEnt->fd, koutput->encoders[i]);
2745d6c0b56eSmrg		if (!kencoders[i]) {
2746d6c0b56eSmrg			goto out_free_encoders;
2747d6c0b56eSmrg		}
2748d6c0b56eSmrg	}
2749d6c0b56eSmrg
2750d6c0b56eSmrg	drmmode_create_name(pScrn, koutput, name, path_blob, num_dvi, num_hdmi);
2751d6c0b56eSmrg	if (path_blob) {
2752d6c0b56eSmrg		drmModeFreePropertyBlob(path_blob);
2753d6c0b56eSmrg	}
2754d6c0b56eSmrg
2755d6c0b56eSmrg	if (path_blob && dynamic) {
2756d6c0b56eSmrg		/* See if we have an output with this name already
2757d6c0b56eSmrg		 * and hook stuff up.
2758d6c0b56eSmrg		 */
2759d6c0b56eSmrg		for (i = 0; i < xf86_config->num_output; i++) {
2760d6c0b56eSmrg			output = xf86_config->output[i];
2761d6c0b56eSmrg
2762d6c0b56eSmrg			if (strncmp(output->name, name, 32))
2763d6c0b56eSmrg				continue;
2764d6c0b56eSmrg
2765d6c0b56eSmrg			drmmode_output = output->driver_private;
2766d6c0b56eSmrg			drmmode_output->output_id = mode_res->connectors[num];
2767d6c0b56eSmrg			drmmode_output->mode_output = koutput;
276835d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
276935d5b7c7Smrg			output->non_desktop = nonDesktop;
277035d5b7c7Smrg#endif
2771d6c0b56eSmrg			for (i = 0; i < koutput->count_encoders; i++) {
2772d6c0b56eSmrg				drmModeFreeEncoder(kencoders[i]);
2773d6c0b56eSmrg			}
2774d6c0b56eSmrg			free(kencoders);
2775d6c0b56eSmrg			return 1;
2776d6c0b56eSmrg		}
2777d6c0b56eSmrg	}
2778d6c0b56eSmrg
2779d6c0b56eSmrg	if (xf86IsEntityShared(pScrn->entityList[0])) {
2780d6c0b56eSmrg		if ((s =
2781d6c0b56eSmrg		     xf86GetOptValString(info->Options, OPTION_ZAPHOD_HEADS))) {
2782d6c0b56eSmrg			if (!AMDGPUZaphodStringMatches(pScrn, s, name))
2783d6c0b56eSmrg				goto out_free_encoders;
2784d6c0b56eSmrg		} else {
278590f2b693Smrg			if (info->instance_id != num)
2786d6c0b56eSmrg				goto out_free_encoders;
2787d6c0b56eSmrg		}
2788d6c0b56eSmrg	}
2789d6c0b56eSmrg
2790d6c0b56eSmrg	output = xf86OutputCreate(pScrn, &drmmode_output_funcs, name);
2791d6c0b56eSmrg	if (!output) {
2792d6c0b56eSmrg		goto out_free_encoders;
2793d6c0b56eSmrg	}
2794d6c0b56eSmrg
2795d6c0b56eSmrg	drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
2796d6c0b56eSmrg	if (!drmmode_output) {
2797d6c0b56eSmrg		xf86OutputDestroy(output);
2798d6c0b56eSmrg		goto out_free_encoders;
2799d6c0b56eSmrg	}
2800d6c0b56eSmrg
2801d6c0b56eSmrg	drmmode_output->output_id = mode_res->connectors[num];
2802d6c0b56eSmrg	drmmode_output->mode_output = koutput;
2803d6c0b56eSmrg	drmmode_output->mode_encoders = kencoders;
2804d6c0b56eSmrg	drmmode_output->drmmode = drmmode;
2805d6c0b56eSmrg	output->mm_width = koutput->mmWidth;
2806d6c0b56eSmrg	output->mm_height = koutput->mmHeight;
2807d6c0b56eSmrg
2808d6c0b56eSmrg	output->subpixel_order = subpixel_conv_table[koutput->subpixel];
2809d6c0b56eSmrg	output->interlaceAllowed = TRUE;
2810d6c0b56eSmrg	output->doubleScanAllowed = TRUE;
2811d6c0b56eSmrg	output->driver_private = drmmode_output;
281235d5b7c7Smrg#if XF86_CRTC_VERSION >= 8
281335d5b7c7Smrg	output->non_desktop = nonDesktop;
281435d5b7c7Smrg#endif
2815d6c0b56eSmrg
2816d6c0b56eSmrg	output->possible_crtcs = 0xffffffff;
2817d6c0b56eSmrg	for (i = 0; i < koutput->count_encoders; i++) {
2818d6c0b56eSmrg		output->possible_crtcs &= kencoders[i]->possible_crtcs;
2819d6c0b56eSmrg	}
2820d6c0b56eSmrg	/* work out the possible clones later */
2821d6c0b56eSmrg	output->possible_clones = 0;
2822d6c0b56eSmrg
282324b90cf4Smrg	drmmode_output->dpms_enum_id =
282424b90cf4Smrg		koutput_get_prop_id(pAMDGPUEnt->fd, koutput, DRM_MODE_PROP_ENUM,
282524b90cf4Smrg				    "DPMS");
2826d6c0b56eSmrg
2827d6c0b56eSmrg	if (dynamic) {
2828d6c0b56eSmrg		output->randr_output = RROutputCreate(xf86ScrnToScreen(pScrn), output->name, strlen(output->name), output);
2829d6c0b56eSmrg		drmmode_output_create_resources(output);
2830d6c0b56eSmrg	}
2831d6c0b56eSmrg
2832d6c0b56eSmrg	return 1;
2833d6c0b56eSmrgout_free_encoders:
2834d6c0b56eSmrg	if (kencoders) {
2835d6c0b56eSmrg		for (i = 0; i < koutput->count_encoders; i++)
2836d6c0b56eSmrg			drmModeFreeEncoder(kencoders[i]);
2837d6c0b56eSmrg		free(kencoders);
2838d6c0b56eSmrg	}
2839d6c0b56eSmrg	drmModeFreeConnector(koutput);
2840d6c0b56eSmrg	return 0;
2841d6c0b56eSmrg}
2842d6c0b56eSmrg
2843d6c0b56eSmrguint32_t find_clones(ScrnInfoPtr scrn, xf86OutputPtr output)
2844d6c0b56eSmrg{
2845d6c0b56eSmrg	drmmode_output_private_ptr drmmode_output =
2846d6c0b56eSmrg	    output->driver_private, clone_drmout;
2847d6c0b56eSmrg	int i;
2848d6c0b56eSmrg	xf86OutputPtr clone_output;
2849d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2850d6c0b56eSmrg	int index_mask = 0;
2851d6c0b56eSmrg
2852d6c0b56eSmrg	if (drmmode_output->enc_clone_mask == 0)
2853d6c0b56eSmrg		return index_mask;
2854d6c0b56eSmrg
2855d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2856d6c0b56eSmrg		clone_output = xf86_config->output[i];
2857d6c0b56eSmrg		clone_drmout = clone_output->driver_private;
2858d6c0b56eSmrg		if (output == clone_output)
2859d6c0b56eSmrg			continue;
2860d6c0b56eSmrg
2861d6c0b56eSmrg		if (clone_drmout->enc_mask == 0)
2862d6c0b56eSmrg			continue;
2863d6c0b56eSmrg		if (drmmode_output->enc_clone_mask == clone_drmout->enc_mask)
2864d6c0b56eSmrg			index_mask |= (1 << i);
2865d6c0b56eSmrg	}
2866d6c0b56eSmrg	return index_mask;
2867d6c0b56eSmrg}
2868d6c0b56eSmrg
2869d6c0b56eSmrgstatic void drmmode_clones_init(ScrnInfoPtr scrn, drmmode_ptr drmmode, drmModeResPtr mode_res)
2870d6c0b56eSmrg{
2871d6c0b56eSmrg	int i, j;
2872d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2873d6c0b56eSmrg
2874d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2875d6c0b56eSmrg		xf86OutputPtr output = xf86_config->output[i];
2876d6c0b56eSmrg		drmmode_output_private_ptr drmmode_output;
2877d6c0b56eSmrg
2878d6c0b56eSmrg		drmmode_output = output->driver_private;
2879d6c0b56eSmrg		drmmode_output->enc_clone_mask = 0xff;
2880d6c0b56eSmrg		/* and all the possible encoder clones for this output together */
2881d6c0b56eSmrg		for (j = 0; j < drmmode_output->mode_output->count_encoders;
2882d6c0b56eSmrg		     j++) {
2883d6c0b56eSmrg			int k;
2884d6c0b56eSmrg			for (k = 0; k < mode_res->count_encoders; k++) {
2885d6c0b56eSmrg				if (mode_res->encoders[k] ==
2886d6c0b56eSmrg				    drmmode_output->
2887d6c0b56eSmrg				    mode_encoders[j]->encoder_id)
2888d6c0b56eSmrg					drmmode_output->enc_mask |= (1 << k);
2889d6c0b56eSmrg			}
2890d6c0b56eSmrg
2891d6c0b56eSmrg			drmmode_output->enc_clone_mask &=
2892d6c0b56eSmrg			    drmmode_output->mode_encoders[j]->possible_clones;
2893d6c0b56eSmrg		}
2894d6c0b56eSmrg	}
2895d6c0b56eSmrg
2896d6c0b56eSmrg	for (i = 0; i < xf86_config->num_output; i++) {
2897d6c0b56eSmrg		xf86OutputPtr output = xf86_config->output[i];
2898d6c0b56eSmrg		output->possible_clones = find_clones(scrn, output);
2899d6c0b56eSmrg	}
2900d6c0b56eSmrg}
2901d6c0b56eSmrg
2902d6c0b56eSmrg/* returns pitch alignment in pixels */
2903d6c0b56eSmrgint drmmode_get_pitch_align(ScrnInfoPtr scrn, int bpe)
2904d6c0b56eSmrg{
2905d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
2906d6c0b56eSmrg
2907d6c0b56eSmrg	if (info->have_tiling_info)
2908d6c0b56eSmrg		/* linear aligned requirements */
2909d6c0b56eSmrg		return MAX(64, info->group_bytes / bpe);
2910d6c0b56eSmrg	else
2911d6c0b56eSmrg		/* default to 512 elements if we don't know the real
2912d6c0b56eSmrg		 * group size otherwise the kernel may reject the CS
2913d6c0b56eSmrg		 * if the group sizes don't match as the pitch won't
2914d6c0b56eSmrg		 * be aligned properly.
2915d6c0b56eSmrg		 */
2916d6c0b56eSmrg		return 512;
2917d6c0b56eSmrg}
2918d6c0b56eSmrg
2919d6c0b56eSmrgstatic Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
2920d6c0b56eSmrg{
2921d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2922d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
2923d6c0b56eSmrg	struct amdgpu_buffer *old_front = NULL;
2924d6c0b56eSmrg	ScreenPtr screen = xf86ScrnToScreen(scrn);
2925d6c0b56eSmrg	int i, pitch, old_width, old_height, old_pitch;
2926d6c0b56eSmrg	int cpp = info->pixel_bytes;
2927d6c0b56eSmrg	PixmapPtr ppix = screen->GetScreenPixmap(screen);
2928d6c0b56eSmrg	void *fb_shadow;
2929d6c0b56eSmrg	int hint = 0;
2930d6c0b56eSmrg
2931d6c0b56eSmrg	if (scrn->virtualX == width && scrn->virtualY == height)
2932d6c0b56eSmrg		return TRUE;
2933d6c0b56eSmrg
293435d5b7c7Smrg	if (width > xf86_config->maxWidth || height > xf86_config->maxHeight) {
293535d5b7c7Smrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
293635d5b7c7Smrg			   "Xorg tried resizing screen to %dx%d, but maximum "
293735d5b7c7Smrg			   "supported is %dx%d\n", width, height,
293835d5b7c7Smrg			   xf86_config->maxWidth, xf86_config->maxHeight);
293935d5b7c7Smrg		return FALSE;
294035d5b7c7Smrg	}
294135d5b7c7Smrg
2942d6c0b56eSmrg	if (info->shadow_primary)
2943d6c0b56eSmrg		hint = AMDGPU_CREATE_PIXMAP_LINEAR | AMDGPU_CREATE_PIXMAP_GTT;
2944d6c0b56eSmrg	else if (!info->use_glamor)
2945d6c0b56eSmrg		hint = AMDGPU_CREATE_PIXMAP_LINEAR;
2946d6c0b56eSmrg
2947d6c0b56eSmrg	xf86DrvMsg(scrn->scrnIndex, X_INFO,
2948d6c0b56eSmrg		   "Allocate new frame buffer %dx%d\n", width, height);
2949d6c0b56eSmrg
2950d6c0b56eSmrg	old_width = scrn->virtualX;
2951d6c0b56eSmrg	old_height = scrn->virtualY;
2952d6c0b56eSmrg	old_pitch = scrn->displayWidth;
2953d6c0b56eSmrg	old_front = info->front_buffer;
2954d6c0b56eSmrg
2955d6c0b56eSmrg	scrn->virtualX = width;
2956d6c0b56eSmrg	scrn->virtualY = height;
2957d6c0b56eSmrg
2958d6c0b56eSmrg	info->front_buffer =
2959d6c0b56eSmrg		amdgpu_alloc_pixmap_bo(scrn, scrn->virtualX, scrn->virtualY,
2960d6c0b56eSmrg				       scrn->depth, hint, scrn->bitsPerPixel,
2961d6c0b56eSmrg				       &pitch);
2962d6c0b56eSmrg	if (!info->front_buffer) {
2963d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2964d6c0b56eSmrg			   "Failed to allocate front buffer memory\n");
2965d6c0b56eSmrg		goto fail;
2966d6c0b56eSmrg	}
2967d6c0b56eSmrg
2968d6c0b56eSmrg	if (!info->use_glamor && amdgpu_bo_map(scrn, info->front_buffer) != 0) {
2969d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2970d6c0b56eSmrg			   "Failed to map front buffer memory\n");
2971d6c0b56eSmrg		goto fail;
2972d6c0b56eSmrg	}
2973d6c0b56eSmrg
2974d6c0b56eSmrg	xf86DrvMsg(scrn->scrnIndex, X_INFO, " => pitch %d bytes\n", pitch);
2975d6c0b56eSmrg	scrn->displayWidth = pitch / cpp;
2976d6c0b56eSmrg
2977d6c0b56eSmrg	if (info->use_glamor ||
2978d6c0b56eSmrg	    (info->front_buffer->flags & AMDGPU_BO_FLAGS_GBM)) {
2979d6c0b56eSmrg		screen->ModifyPixmapHeader(ppix,
2980d6c0b56eSmrg					   width, height, -1, -1, pitch, info->front_buffer->cpu_ptr);
2981d6c0b56eSmrg	} else {
2982d6c0b56eSmrg		fb_shadow = calloc(1, pitch * scrn->virtualY);
298335d5b7c7Smrg		if (!fb_shadow)
2984d6c0b56eSmrg			goto fail;
2985d6c0b56eSmrg		free(info->fb_shadow);
2986d6c0b56eSmrg		info->fb_shadow = fb_shadow;
2987d6c0b56eSmrg		screen->ModifyPixmapHeader(ppix,
2988d6c0b56eSmrg					   width, height, -1, -1, pitch,
2989d6c0b56eSmrg					   info->fb_shadow);
2990d6c0b56eSmrg	}
2991d6c0b56eSmrg
2992504d986fSmrg	if (!amdgpu_glamor_create_screen_resources(scrn->pScreen))
2993504d986fSmrg		goto fail;
2994504d986fSmrg
299590f2b693Smrg	if (info->use_glamor || info->dri2.enabled) {
2996504d986fSmrg		if (!amdgpu_set_pixmap_bo(ppix, info->front_buffer))
2997504d986fSmrg			goto fail;
2998504d986fSmrg	}
2999d6c0b56eSmrg
300024b90cf4Smrg	amdgpu_pixmap_clear(ppix);
3001d6c0b56eSmrg	amdgpu_glamor_finish(scrn);
3002d6c0b56eSmrg
3003d6c0b56eSmrg	for (i = 0; i < xf86_config->num_crtc; i++) {
3004d6c0b56eSmrg		xf86CrtcPtr crtc = xf86_config->crtc[i];
3005d6c0b56eSmrg
3006d6c0b56eSmrg		if (!crtc->enabled)
3007d6c0b56eSmrg			continue;
3008d6c0b56eSmrg
3009d6c0b56eSmrg		drmmode_set_mode_major(crtc, &crtc->mode,
3010d6c0b56eSmrg				       crtc->rotation, crtc->x, crtc->y);
3011d6c0b56eSmrg	}
3012d6c0b56eSmrg
3013d6c0b56eSmrg	if (old_front) {
3014d6c0b56eSmrg		amdgpu_bo_unref(&old_front);
3015d6c0b56eSmrg	}
3016d6c0b56eSmrg
3017d6c0b56eSmrg	return TRUE;
3018d6c0b56eSmrg
3019d6c0b56eSmrgfail:
3020d6c0b56eSmrg	if (info->front_buffer) {
3021d6c0b56eSmrg		amdgpu_bo_unref(&info->front_buffer);
3022d6c0b56eSmrg	}
3023d6c0b56eSmrg	info->front_buffer = old_front;
3024d6c0b56eSmrg	scrn->virtualX = old_width;
3025d6c0b56eSmrg	scrn->virtualY = old_height;
3026d6c0b56eSmrg	scrn->displayWidth = old_pitch;
3027d6c0b56eSmrg
3028d6c0b56eSmrg	return FALSE;
3029d6c0b56eSmrg}
3030d6c0b56eSmrg
303135d5b7c7Smrgstatic void
303235d5b7c7Smrgdrmmode_validate_leases(ScrnInfoPtr scrn)
303335d5b7c7Smrg{
303435d5b7c7Smrg#ifdef XF86_LEASE_VERSION
303535d5b7c7Smrg	ScreenPtr screen = scrn->pScreen;
303635d5b7c7Smrg	rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
303735d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
303835d5b7c7Smrg	drmModeLesseeListPtr lessees;
303935d5b7c7Smrg	RRLeasePtr lease, next;
304035d5b7c7Smrg	int l;
304135d5b7c7Smrg
304235d5b7c7Smrg	/* We can't talk to the kernel about leases when VT switched */
304335d5b7c7Smrg	if (!scrn->vtSema)
304435d5b7c7Smrg		return;
304535d5b7c7Smrg
304635d5b7c7Smrg	lessees = drmModeListLessees(pAMDGPUEnt->fd);
304735d5b7c7Smrg	if (!lessees)
304835d5b7c7Smrg		return;
304935d5b7c7Smrg
305035d5b7c7Smrg	xorg_list_for_each_entry_safe(lease, next, &scr_priv->leases, list) {
305135d5b7c7Smrg		drmmode_lease_private_ptr lease_private = lease->devPrivate;
305235d5b7c7Smrg
305335d5b7c7Smrg		for (l = 0; l < lessees->count; l++) {
305435d5b7c7Smrg			if (lessees->lessees[l] == lease_private->lessee_id)
305535d5b7c7Smrg				break;
305635d5b7c7Smrg		}
305735d5b7c7Smrg
305835d5b7c7Smrg		/* check to see if the lease has gone away */
305935d5b7c7Smrg		if (l == lessees->count) {
306035d5b7c7Smrg			free(lease_private);
306135d5b7c7Smrg			lease->devPrivate = NULL;
306235d5b7c7Smrg			xf86CrtcLeaseTerminated(lease);
306335d5b7c7Smrg		}
306435d5b7c7Smrg	}
306535d5b7c7Smrg
306635d5b7c7Smrg	free(lessees);
306735d5b7c7Smrg#endif
306835d5b7c7Smrg}
306935d5b7c7Smrg
307035d5b7c7Smrg#ifdef XF86_LEASE_VERSION
307135d5b7c7Smrg
307235d5b7c7Smrgstatic int
307335d5b7c7Smrgdrmmode_create_lease(RRLeasePtr lease, int *fd)
307435d5b7c7Smrg{
307535d5b7c7Smrg	ScreenPtr screen = lease->screen;
307635d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
307735d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
307835d5b7c7Smrg	drmmode_lease_private_ptr lease_private;
307935d5b7c7Smrg	int noutput = lease->numOutputs;
308035d5b7c7Smrg	int ncrtc = lease->numCrtcs;
308135d5b7c7Smrg	uint32_t *objects;
308235d5b7c7Smrg	size_t nobjects;
308335d5b7c7Smrg	int lease_fd;
308435d5b7c7Smrg	int c, o;
308535d5b7c7Smrg	int i;
308635d5b7c7Smrg
308735d5b7c7Smrg	nobjects = ncrtc + noutput;
308835d5b7c7Smrg	if (nobjects == 0 || nobjects > (SIZE_MAX / 4) ||
308935d5b7c7Smrg	    ncrtc > (SIZE_MAX - noutput))
309035d5b7c7Smrg		return BadValue;
309135d5b7c7Smrg
309235d5b7c7Smrg	lease_private = calloc(1, sizeof (drmmode_lease_private_rec));
309335d5b7c7Smrg	if (!lease_private)
309435d5b7c7Smrg		return BadAlloc;
309535d5b7c7Smrg
309635d5b7c7Smrg	objects = malloc(nobjects * 4);
309735d5b7c7Smrg	if (!objects) {
309835d5b7c7Smrg		free(lease_private);
309935d5b7c7Smrg		return BadAlloc;
310035d5b7c7Smrg	}
310135d5b7c7Smrg
310235d5b7c7Smrg	i = 0;
310335d5b7c7Smrg
310435d5b7c7Smrg	/* Add CRTC ids */
310535d5b7c7Smrg	for (c = 0; c < ncrtc; c++) {
310635d5b7c7Smrg		xf86CrtcPtr crtc = lease->crtcs[c]->devPrivate;
310735d5b7c7Smrg		drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
310835d5b7c7Smrg
310935d5b7c7Smrg		objects[i++] = drmmode_crtc->mode_crtc->crtc_id;
311035d5b7c7Smrg	}
311135d5b7c7Smrg
311235d5b7c7Smrg	/* Add connector ids */
311335d5b7c7Smrg	for (o = 0; o < noutput; o++) {
311435d5b7c7Smrg		xf86OutputPtr   output = lease->outputs[o]->devPrivate;
311535d5b7c7Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
311635d5b7c7Smrg
311735d5b7c7Smrg		objects[i++] = drmmode_output->mode_output->connector_id;
311835d5b7c7Smrg	}
311935d5b7c7Smrg
312035d5b7c7Smrg	/* call kernel to create lease */
312135d5b7c7Smrg	assert (i == nobjects);
312235d5b7c7Smrg
312335d5b7c7Smrg	lease_fd = drmModeCreateLease(pAMDGPUEnt->fd, objects, nobjects, 0,
312435d5b7c7Smrg				      &lease_private->lessee_id);
312535d5b7c7Smrg
312635d5b7c7Smrg	free(objects);
312735d5b7c7Smrg
312835d5b7c7Smrg	if (lease_fd < 0) {
312935d5b7c7Smrg		free(lease_private);
313035d5b7c7Smrg		return BadMatch;
313135d5b7c7Smrg	}
313235d5b7c7Smrg
313335d5b7c7Smrg	lease->devPrivate = lease_private;
313435d5b7c7Smrg
313535d5b7c7Smrg	xf86CrtcLeaseStarted(lease);
313635d5b7c7Smrg
313735d5b7c7Smrg	*fd = lease_fd;
313835d5b7c7Smrg	return Success;
313935d5b7c7Smrg}
314035d5b7c7Smrg
314135d5b7c7Smrgstatic void
314235d5b7c7Smrgdrmmode_terminate_lease(RRLeasePtr lease)
314335d5b7c7Smrg{
314435d5b7c7Smrg	drmmode_lease_private_ptr lease_private = lease->devPrivate;
314535d5b7c7Smrg	ScreenPtr screen = lease->screen;
314635d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
314735d5b7c7Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
314835d5b7c7Smrg
314935d5b7c7Smrg	if (drmModeRevokeLease(pAMDGPUEnt->fd, lease_private->lessee_id) == 0) {
315035d5b7c7Smrg		free(lease_private);
315135d5b7c7Smrg		lease->devPrivate = NULL;
315235d5b7c7Smrg		xf86CrtcLeaseTerminated(lease);
315335d5b7c7Smrg	}
315435d5b7c7Smrg}
315535d5b7c7Smrg
315635d5b7c7Smrg#endif // XF86_LEASE_VERSION
315735d5b7c7Smrg
3158d6c0b56eSmrgstatic const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
315935d5b7c7Smrg	.resize = drmmode_xf86crtc_resize,
316035d5b7c7Smrg#ifdef XF86_LEASE_VERSION
316135d5b7c7Smrg	.create_lease = drmmode_create_lease,
316235d5b7c7Smrg	.terminate_lease = drmmode_terminate_lease
316335d5b7c7Smrg#endif
3164d6c0b56eSmrg};
3165d6c0b56eSmrg
3166d6c0b56eSmrgstatic void
3167d6c0b56eSmrgdrmmode_flip_abort(xf86CrtcPtr crtc, void *event_data)
3168d6c0b56eSmrg{
316924b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
317024b90cf4Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
3171d6c0b56eSmrg	drmmode_flipdata_ptr flipdata = event_data;
317235d5b7c7Smrg	int crtc_id = drmmode_get_crtc_id(crtc);
317335d5b7c7Smrg	struct drmmode_fb **fb = &flipdata->fb[crtc_id];
317435d5b7c7Smrg
317535d5b7c7Smrg	if (drmmode_crtc->flip_pending == *fb) {
317635d5b7c7Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->flip_pending,
317735d5b7c7Smrg				     NULL);
317835d5b7c7Smrg	}
317935d5b7c7Smrg	drmmode_fb_reference(pAMDGPUEnt->fd, fb, NULL);
3180d6c0b56eSmrg
3181d6c0b56eSmrg	if (--flipdata->flip_count == 0) {
3182504d986fSmrg		if (!flipdata->fe_crtc)
3183504d986fSmrg			flipdata->fe_crtc = crtc;
3184504d986fSmrg		flipdata->abort(flipdata->fe_crtc, flipdata->event_data);
3185d6c0b56eSmrg		free(flipdata);
3186d6c0b56eSmrg	}
3187d6c0b56eSmrg}
3188d6c0b56eSmrg
3189d6c0b56eSmrgstatic void
3190d6c0b56eSmrgdrmmode_flip_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec, void *event_data)
3191d6c0b56eSmrg{
3192d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
319324b90cf4Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3194d6c0b56eSmrg	drmmode_flipdata_ptr flipdata = event_data;
319535d5b7c7Smrg	int crtc_id = drmmode_get_crtc_id(crtc);
319635d5b7c7Smrg	struct drmmode_fb **fb = &flipdata->fb[crtc_id];
3197d6c0b56eSmrg
3198d6c0b56eSmrg	/* Is this the event whose info shall be delivered to higher level? */
3199d6c0b56eSmrg	if (crtc == flipdata->fe_crtc) {
3200d6c0b56eSmrg		/* Yes: Cache msc, ust for later delivery. */
3201d6c0b56eSmrg		flipdata->fe_frame = frame;
3202d6c0b56eSmrg		flipdata->fe_usec = usec;
3203d6c0b56eSmrg	}
3204d6c0b56eSmrg
320590f2b693Smrg	if (*fb) {
320690f2b693Smrg		if (drmmode_crtc->flip_pending == *fb) {
320790f2b693Smrg			drmmode_fb_reference(pAMDGPUEnt->fd,
320890f2b693Smrg					     &drmmode_crtc->flip_pending, NULL);
320990f2b693Smrg		}
321090f2b693Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->fb, *fb);
321190f2b693Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, fb, NULL);
321224b90cf4Smrg	}
321324b90cf4Smrg
3214d6c0b56eSmrg	if (--flipdata->flip_count == 0) {
3215504d986fSmrg		/* Deliver MSC & UST from reference/current CRTC to flip event
3216504d986fSmrg		 * handler
3217504d986fSmrg		 */
3218d6c0b56eSmrg		if (flipdata->fe_crtc)
3219504d986fSmrg			flipdata->handler(flipdata->fe_crtc, flipdata->fe_frame,
3220504d986fSmrg					  flipdata->fe_usec, flipdata->event_data);
3221504d986fSmrg		else
3222504d986fSmrg			flipdata->handler(crtc, frame, usec, flipdata->event_data);
3223d6c0b56eSmrg
3224d6c0b56eSmrg		free(flipdata);
3225d6c0b56eSmrg	}
3226d6c0b56eSmrg}
3227d6c0b56eSmrg
3228504d986fSmrg#if HAVE_NOTIFY_FD
3229504d986fSmrgstatic void drmmode_notify_fd(int fd, int notify, void *data)
3230504d986fSmrg{
3231504d986fSmrg	drmmode_ptr drmmode = data;
323235d5b7c7Smrg	amdgpu_drm_handle_event(fd, &drmmode->event_context);
3233504d986fSmrg}
3234504d986fSmrg#else
3235d6c0b56eSmrgstatic void drm_wakeup_handler(pointer data, int err, pointer p)
3236d6c0b56eSmrg{
3237d6c0b56eSmrg	drmmode_ptr drmmode = data;
3238d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(drmmode->scrn);
3239d6c0b56eSmrg	fd_set *read_mask = p;
3240d6c0b56eSmrg
3241d6c0b56eSmrg	if (err >= 0 && FD_ISSET(pAMDGPUEnt->fd, read_mask)) {
324235d5b7c7Smrg		amdgpu_drm_handle_event(pAMDGPUEnt->fd, &drmmode->event_context);
3243d6c0b56eSmrg	}
3244d6c0b56eSmrg}
3245504d986fSmrg#endif
3246d6c0b56eSmrg
324711bf0794Smrgstatic Bool drmmode_probe_page_flip_target(AMDGPUEntPtr pAMDGPUEnt)
324811bf0794Smrg{
324911bf0794Smrg	uint64_t cap_value;
325011bf0794Smrg
325111bf0794Smrg	return drmGetCap(pAMDGPUEnt->fd, DRM_CAP_PAGE_FLIP_TARGET,
325211bf0794Smrg			 &cap_value) == 0 && cap_value != 0;
325311bf0794Smrg}
325411bf0794Smrg
325511bf0794Smrgstatic int
325611bf0794Smrgdrmmode_page_flip(AMDGPUEntPtr pAMDGPUEnt, drmmode_crtc_private_ptr drmmode_crtc,
325711bf0794Smrg		  int fb_id, uint32_t flags, uintptr_t drm_queue_seq)
325811bf0794Smrg{
325911bf0794Smrg	flags |= DRM_MODE_PAGE_FLIP_EVENT;
326011bf0794Smrg	return drmModePageFlip(pAMDGPUEnt->fd, drmmode_crtc->mode_crtc->crtc_id,
326111bf0794Smrg			       fb_id, flags, (void*)drm_queue_seq);
326211bf0794Smrg}
326311bf0794Smrg
326411bf0794Smrgint
326511bf0794Smrgdrmmode_page_flip_target_absolute(AMDGPUEntPtr pAMDGPUEnt,
326611bf0794Smrg				  drmmode_crtc_private_ptr drmmode_crtc,
326711bf0794Smrg				  int fb_id, uint32_t flags,
326811bf0794Smrg				  uintptr_t drm_queue_seq, uint32_t target_msc)
326911bf0794Smrg{
327011bf0794Smrg	if (pAMDGPUEnt->has_page_flip_target) {
327111bf0794Smrg		flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE;
327211bf0794Smrg		return drmModePageFlipTarget(pAMDGPUEnt->fd,
327311bf0794Smrg					     drmmode_crtc->mode_crtc->crtc_id,
327411bf0794Smrg					     fb_id, flags, (void*)drm_queue_seq,
327511bf0794Smrg					     target_msc);
327611bf0794Smrg	}
327711bf0794Smrg
327811bf0794Smrg	return drmmode_page_flip(pAMDGPUEnt, drmmode_crtc, fb_id, flags,
327911bf0794Smrg				 drm_queue_seq);
328011bf0794Smrg}
328111bf0794Smrg
328211bf0794Smrgint
328311bf0794Smrgdrmmode_page_flip_target_relative(AMDGPUEntPtr pAMDGPUEnt,
328411bf0794Smrg				  drmmode_crtc_private_ptr drmmode_crtc,
328511bf0794Smrg				  int fb_id, uint32_t flags,
328611bf0794Smrg				  uintptr_t drm_queue_seq, uint32_t target_msc)
328711bf0794Smrg{
328811bf0794Smrg	if (pAMDGPUEnt->has_page_flip_target) {
328911bf0794Smrg		flags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_TARGET_RELATIVE;
329011bf0794Smrg		return drmModePageFlipTarget(pAMDGPUEnt->fd,
329111bf0794Smrg					     drmmode_crtc->mode_crtc->crtc_id,
329211bf0794Smrg					     fb_id, flags, (void*)drm_queue_seq,
329311bf0794Smrg					     target_msc);
329411bf0794Smrg	}
329511bf0794Smrg
329611bf0794Smrg	return drmmode_page_flip(pAMDGPUEnt, drmmode_crtc, fb_id, flags,
329711bf0794Smrg				 drm_queue_seq);
329811bf0794Smrg}
329911bf0794Smrg
330035d5b7c7Smrg/**
330135d5b7c7Smrg * Initialize DDX color management support. It does two things:
330235d5b7c7Smrg *
330335d5b7c7Smrg * 1. Cache DRM color management property type IDs, as they do not change. They
330435d5b7c7Smrg *    will be used later to modify color management via DRM, or to determine if
330535d5b7c7Smrg *    there's kernel support for color management.
330635d5b7c7Smrg *
330735d5b7c7Smrg * 2. Cache degamma/gamma LUT sizes, since all CRTCs have the same LUT sizes on
330835d5b7c7Smrg *    AMD hardware.
330935d5b7c7Smrg *
331035d5b7c7Smrg * If the cached ID's are all 0 after calling this function, then color
331135d5b7c7Smrg * management is not supported. For short, checking if the gamma LUT size
331235d5b7c7Smrg * property ID == 0 is sufficient.
331335d5b7c7Smrg *
331435d5b7c7Smrg * This should be called before CRTCs are initialized within pre_init, as the
331535d5b7c7Smrg * cached values will be used there.
331635d5b7c7Smrg *
331735d5b7c7Smrg * @drm_fd: DRM file descriptor
331835d5b7c7Smrg * @drmmode: drmmode object, where the cached IDs are stored
331935d5b7c7Smrg * @mode_res: The DRM mode resource containing the CRTC ids
332035d5b7c7Smrg */
332135d5b7c7Smrgstatic void drmmode_cm_init(int drm_fd, drmmode_ptr drmmode,
332235d5b7c7Smrg			    drmModeResPtr mode_res)
332335d5b7c7Smrg{
332435d5b7c7Smrg	drmModeObjectPropertiesPtr drm_props;
332535d5b7c7Smrg	drmModePropertyPtr drm_prop;
332635d5b7c7Smrg	enum drmmode_cm_prop cm_prop;
332735d5b7c7Smrg	uint32_t cm_enabled = 0;
332835d5b7c7Smrg	uint32_t cm_all_enabled = (1 << CM_NUM_PROPS) - 1;
332935d5b7c7Smrg	int i;
333035d5b7c7Smrg
333135d5b7c7Smrg	memset(drmmode->cm_prop_ids, 0, sizeof(drmmode->cm_prop_ids));
333235d5b7c7Smrg	drmmode->gamma_lut_size = drmmode->degamma_lut_size = 0;
333335d5b7c7Smrg
333435d5b7c7Smrg	if (!mode_res->crtcs)
333535d5b7c7Smrg		return;
333635d5b7c7Smrg
333735d5b7c7Smrg	/* AMD hardware has color management support on all pipes. It is
333835d5b7c7Smrg	 * therefore sufficient to only check the first CRTC.
333935d5b7c7Smrg	 */
334035d5b7c7Smrg	drm_props = drmModeObjectGetProperties(drm_fd,
334135d5b7c7Smrg					       mode_res->crtcs[0],
334235d5b7c7Smrg					       DRM_MODE_OBJECT_CRTC);
334335d5b7c7Smrg	if (!drm_props)
334435d5b7c7Smrg		return;
334535d5b7c7Smrg
334635d5b7c7Smrg	for (i = 0; i < drm_props->count_props; i++) {
334735d5b7c7Smrg		drm_prop = drmModeGetProperty(drm_fd,
334835d5b7c7Smrg					      drm_props->props[i]);
334935d5b7c7Smrg		if (!drm_prop)
335035d5b7c7Smrg			continue;
335135d5b7c7Smrg
335235d5b7c7Smrg		cm_prop = get_cm_enum_from_str(drm_prop->name);
335335d5b7c7Smrg		if (cm_prop == CM_INVALID_PROP)
335435d5b7c7Smrg			continue;
335535d5b7c7Smrg
335635d5b7c7Smrg		if (cm_prop == CM_DEGAMMA_LUT_SIZE)
335735d5b7c7Smrg			drmmode->degamma_lut_size = drm_props->prop_values[i];
335835d5b7c7Smrg		else if (cm_prop == CM_GAMMA_LUT_SIZE)
335935d5b7c7Smrg			drmmode->gamma_lut_size = drm_props->prop_values[i];
336035d5b7c7Smrg
336135d5b7c7Smrg		drmmode->cm_prop_ids[cm_prop] = drm_props->props[i];
336235d5b7c7Smrg		cm_enabled |= 1 << cm_prop;
336335d5b7c7Smrg
336435d5b7c7Smrg		drmModeFreeProperty(drm_prop);
336535d5b7c7Smrg	}
336635d5b7c7Smrg	drmModeFreeObjectProperties(drm_props);
336735d5b7c7Smrg
336835d5b7c7Smrg	/* cm is enabled only if all prop ids are found */
336935d5b7c7Smrg	if (cm_enabled == cm_all_enabled)
337035d5b7c7Smrg		return;
337135d5b7c7Smrg
337235d5b7c7Smrg	/* Otherwise, disable DDX cm support */
337335d5b7c7Smrg	memset(drmmode->cm_prop_ids, 0, sizeof(drmmode->cm_prop_ids));
337435d5b7c7Smrg	drmmode->gamma_lut_size = drmmode->degamma_lut_size = 0;
337535d5b7c7Smrg}
337635d5b7c7Smrg
3377d6c0b56eSmrgBool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
3378d6c0b56eSmrg{
3379d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
3380d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
3381d6c0b56eSmrg	int i, num_dvi = 0, num_hdmi = 0;
3382d6c0b56eSmrg	unsigned int crtcs_needed = 0;
338390f2b693Smrg	unsigned int crtcs_got = 0;
3384d6c0b56eSmrg	drmModeResPtr mode_res;
3385d6c0b56eSmrg	char *bus_id_string, *provider_name;
3386d6c0b56eSmrg
3387d6c0b56eSmrg	xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
3388d6c0b56eSmrg
3389d6c0b56eSmrg	drmmode->scrn = pScrn;
3390d6c0b56eSmrg	mode_res = drmModeGetResources(pAMDGPUEnt->fd);
3391d6c0b56eSmrg	if (!mode_res)
3392d6c0b56eSmrg		return FALSE;
3393d6c0b56eSmrg
3394d6c0b56eSmrg	drmmode->count_crtcs = mode_res->count_crtcs;
3395d6c0b56eSmrg	xf86CrtcSetSizeRange(pScrn, 320, 200, mode_res->max_width,
3396d6c0b56eSmrg			     mode_res->max_height);
3397d6c0b56eSmrg
3398d6c0b56eSmrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
3399d6c0b56eSmrg		       "Initializing outputs ...\n");
3400d6c0b56eSmrg	for (i = 0; i < mode_res->count_connectors; i++)
3401d6c0b56eSmrg		crtcs_needed += drmmode_output_init(pScrn, drmmode, mode_res, i, &num_dvi, &num_hdmi, 0);
3402d6c0b56eSmrg
3403d6c0b56eSmrg	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
3404d6c0b56eSmrg		       "%d crtcs needed for screen.\n", crtcs_needed);
3405d6c0b56eSmrg
340624b90cf4Smrg	/* Need per-screen drmmode_crtc_funcs, based on our global template,
340724b90cf4Smrg	 * so we can disable some functions, depending on screen settings.
340824b90cf4Smrg	 */
340924b90cf4Smrg	info->drmmode_crtc_funcs = drmmode_crtc_funcs;
341024b90cf4Smrg
3411d6c0b56eSmrg	if (!info->use_glamor) {
3412d6c0b56eSmrg		/* Rotation requires hardware acceleration */
341324b90cf4Smrg		info->drmmode_crtc_funcs.shadow_allocate = NULL;
341424b90cf4Smrg		info->drmmode_crtc_funcs.shadow_create = NULL;
341524b90cf4Smrg		info->drmmode_crtc_funcs.shadow_destroy = NULL;
3416d6c0b56eSmrg	}
3417d6c0b56eSmrg
341835d5b7c7Smrg	drmmode_cm_init(pAMDGPUEnt->fd, drmmode, mode_res);
341935d5b7c7Smrg
342035d5b7c7Smrg	/* Spare the server the effort to compute and update unused CLUTs. */
342135d5b7c7Smrg	if (pScrn->depth == 30 && !drmmode_cm_enabled(drmmode))
342224b90cf4Smrg		info->drmmode_crtc_funcs.gamma_set = NULL;
342324b90cf4Smrg
342490f2b693Smrg	for (i = 0; i < mode_res->count_crtcs; i++) {
3425d6c0b56eSmrg		if (!xf86IsEntityShared(pScrn->entityList[0]) ||
342690f2b693Smrg		    (crtcs_got < crtcs_needed &&
342790f2b693Smrg		     !(pAMDGPUEnt->assigned_crtcs & (1 << i))))
342890f2b693Smrg			crtcs_got += drmmode_crtc_init(pScrn, drmmode, mode_res, i);
342990f2b693Smrg	}
3430d6c0b56eSmrg
3431d6c0b56eSmrg	/* All ZaphodHeads outputs provided with matching crtcs? */
343290f2b693Smrg	if (crtcs_got < crtcs_needed) {
343390f2b693Smrg		if (crtcs_got == 0) {
343490f2b693Smrg			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
343590f2b693Smrg				   "No ZaphodHeads CRTC available, needed %u\n",
343690f2b693Smrg				   crtcs_needed);
343790f2b693Smrg			return FALSE;
343890f2b693Smrg		}
343990f2b693Smrg
3440d6c0b56eSmrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
3441d6c0b56eSmrg			   "%d ZaphodHeads crtcs unavailable. Some outputs will stay off.\n",
3442d6c0b56eSmrg			   crtcs_needed);
344390f2b693Smrg	}
3444d6c0b56eSmrg
3445d6c0b56eSmrg	/* workout clones */
3446d6c0b56eSmrg	drmmode_clones_init(pScrn, drmmode, mode_res);
3447d6c0b56eSmrg
3448d6c0b56eSmrg	bus_id_string = DRICreatePCIBusID(info->PciInfo);
3449d6c0b56eSmrg	XNFasprintf(&provider_name, "%s @ %s", pScrn->chipset, bus_id_string);
3450d6c0b56eSmrg	free(bus_id_string);
3451d6c0b56eSmrg	xf86ProviderSetup(pScrn, NULL, provider_name);
3452d6c0b56eSmrg	free(provider_name);
3453d6c0b56eSmrg
3454d6c0b56eSmrg	xf86InitialConfiguration(pScrn, TRUE);
3455d6c0b56eSmrg
345611bf0794Smrg	pAMDGPUEnt->has_page_flip_target = drmmode_probe_page_flip_target(pAMDGPUEnt);
345711bf0794Smrg
3458d6c0b56eSmrg	drmModeFreeResources(mode_res);
3459d6c0b56eSmrg	return TRUE;
3460d6c0b56eSmrg}
3461d6c0b56eSmrg
3462d6c0b56eSmrgvoid drmmode_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
3463d6c0b56eSmrg{
3464d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
3465d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
3466d6c0b56eSmrg
3467d6c0b56eSmrg	info->drmmode_inited = TRUE;
3468d6c0b56eSmrg	if (pAMDGPUEnt->fd_wakeup_registered != serverGeneration) {
3469504d986fSmrg#if HAVE_NOTIFY_FD
3470504d986fSmrg		SetNotifyFd(pAMDGPUEnt->fd, drmmode_notify_fd, X_NOTIFY_READ, drmmode);
3471504d986fSmrg#else
3472d6c0b56eSmrg		AddGeneralSocket(pAMDGPUEnt->fd);
3473d6c0b56eSmrg		RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA,
3474d6c0b56eSmrg					       drm_wakeup_handler, drmmode);
3475504d986fSmrg#endif
3476d6c0b56eSmrg		pAMDGPUEnt->fd_wakeup_registered = serverGeneration;
3477d6c0b56eSmrg		pAMDGPUEnt->fd_wakeup_ref = 1;
3478d6c0b56eSmrg	} else
3479d6c0b56eSmrg		pAMDGPUEnt->fd_wakeup_ref++;
3480d6c0b56eSmrg}
3481d6c0b56eSmrg
3482d6c0b56eSmrgvoid drmmode_fini(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
3483d6c0b56eSmrg{
3484504d986fSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
3485d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
3486d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
3487504d986fSmrg	int c;
3488d6c0b56eSmrg
3489d6c0b56eSmrg	if (!info->drmmode_inited)
3490d6c0b56eSmrg		return;
3491d6c0b56eSmrg
349290f2b693Smrg	for (c = 0; c < config->num_crtc; c++)
349390f2b693Smrg		drmmode_crtc_scanout_free(config->crtc[c]);
349490f2b693Smrg
3495d6c0b56eSmrg	if (pAMDGPUEnt->fd_wakeup_registered == serverGeneration &&
3496d6c0b56eSmrg	    !--pAMDGPUEnt->fd_wakeup_ref) {
3497504d986fSmrg#if HAVE_NOTIFY_FD
3498504d986fSmrg		RemoveNotifyFd(pAMDGPUEnt->fd);
3499504d986fSmrg#else
3500d6c0b56eSmrg		RemoveGeneralSocket(pAMDGPUEnt->fd);
3501d6c0b56eSmrg		RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA,
3502d6c0b56eSmrg					     drm_wakeup_handler, drmmode);
3503504d986fSmrg#endif
3504504d986fSmrg	}
3505d6c0b56eSmrg}
3506d6c0b56eSmrg
350724b90cf4Smrgstatic void drmmode_sprite_do_set_cursor(struct amdgpu_device_priv *device_priv,
350824b90cf4Smrg					 ScrnInfoPtr scrn, int x, int y)
350924b90cf4Smrg{
351024b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
351124b90cf4Smrg	CursorPtr cursor = device_priv->cursor;
351224b90cf4Smrg	Bool sprite_visible = device_priv->sprite_visible;
351324b90cf4Smrg
351424b90cf4Smrg	if (cursor) {
351524b90cf4Smrg		x -= cursor->bits->xhot;
351624b90cf4Smrg		y -= cursor->bits->yhot;
351724b90cf4Smrg
351824b90cf4Smrg		device_priv->sprite_visible =
351924b90cf4Smrg			x < scrn->virtualX && y < scrn->virtualY &&
352024b90cf4Smrg			(x + cursor->bits->width > 0) &&
352124b90cf4Smrg			(y + cursor->bits->height > 0);
352224b90cf4Smrg	} else {
352324b90cf4Smrg		device_priv->sprite_visible = FALSE;
352424b90cf4Smrg	}
352524b90cf4Smrg
352624b90cf4Smrg	info->sprites_visible += device_priv->sprite_visible - sprite_visible;
352724b90cf4Smrg}
352824b90cf4Smrg
352935d5b7c7Smrgstatic void drmmode_sprite_set_cursor(DeviceIntPtr pDev, ScreenPtr pScreen,
353035d5b7c7Smrg				      CursorPtr pCursor, int x, int y)
353124b90cf4Smrg{
353224b90cf4Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
353324b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
353424b90cf4Smrg	struct amdgpu_device_priv *device_priv =
353524b90cf4Smrg		dixLookupScreenPrivate(&pDev->devPrivates,
353624b90cf4Smrg				       &amdgpu_device_private_key, pScreen);
353724b90cf4Smrg
353824b90cf4Smrg	device_priv->cursor = pCursor;
353924b90cf4Smrg	drmmode_sprite_do_set_cursor(device_priv, scrn, x, y);
354024b90cf4Smrg
354135d5b7c7Smrg	info->SpriteFuncs->SetCursor(pDev, pScreen, pCursor, x, y);
354224b90cf4Smrg}
354324b90cf4Smrg
354435d5b7c7Smrgstatic void drmmode_sprite_move_cursor(DeviceIntPtr pDev, ScreenPtr pScreen,
354535d5b7c7Smrg				       int x, int y)
354624b90cf4Smrg{
354724b90cf4Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
354824b90cf4Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
354924b90cf4Smrg	struct amdgpu_device_priv *device_priv =
355024b90cf4Smrg		dixLookupScreenPrivate(&pDev->devPrivates,
355124b90cf4Smrg				       &amdgpu_device_private_key, pScreen);
355224b90cf4Smrg
355324b90cf4Smrg	drmmode_sprite_do_set_cursor(device_priv, scrn, x, y);
355424b90cf4Smrg
355535d5b7c7Smrg	info->SpriteFuncs->MoveCursor(pDev, pScreen, x, y);
355624b90cf4Smrg}
355724b90cf4Smrg
355835d5b7c7Smrgstatic Bool drmmode_sprite_realize_realize_cursor(DeviceIntPtr pDev,
355935d5b7c7Smrg						  ScreenPtr pScreen,
356035d5b7c7Smrg						  CursorPtr pCursor)
356135d5b7c7Smrg{
356235d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
356335d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
356435d5b7c7Smrg
356535d5b7c7Smrg	return info->SpriteFuncs->RealizeCursor(pDev, pScreen, pCursor);
356635d5b7c7Smrg}
356735d5b7c7Smrg
356835d5b7c7Smrgstatic Bool drmmode_sprite_realize_unrealize_cursor(DeviceIntPtr pDev,
356935d5b7c7Smrg						    ScreenPtr pScreen,
357035d5b7c7Smrg						    CursorPtr pCursor)
357135d5b7c7Smrg{
357235d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
357335d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
357435d5b7c7Smrg
357535d5b7c7Smrg	return info->SpriteFuncs->UnrealizeCursor(pDev, pScreen, pCursor);
357635d5b7c7Smrg}
357735d5b7c7Smrg
357835d5b7c7Smrgstatic Bool drmmode_sprite_device_cursor_initialize(DeviceIntPtr pDev,
357935d5b7c7Smrg						    ScreenPtr pScreen)
358035d5b7c7Smrg{
358135d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
358235d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
358335d5b7c7Smrg
358435d5b7c7Smrg	return info->SpriteFuncs->DeviceCursorInitialize(pDev, pScreen);
358535d5b7c7Smrg}
358635d5b7c7Smrg
358735d5b7c7Smrgstatic void drmmode_sprite_device_cursor_cleanup(DeviceIntPtr pDev,
358835d5b7c7Smrg						 ScreenPtr pScreen)
358935d5b7c7Smrg{
359035d5b7c7Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
359135d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
359235d5b7c7Smrg
359335d5b7c7Smrg	info->SpriteFuncs->DeviceCursorCleanup(pDev, pScreen);
359435d5b7c7Smrg}
359535d5b7c7Smrg
359635d5b7c7SmrgmiPointerSpriteFuncRec drmmode_sprite_funcs = {
359735d5b7c7Smrg	.RealizeCursor = drmmode_sprite_realize_realize_cursor,
359835d5b7c7Smrg	.UnrealizeCursor = drmmode_sprite_realize_unrealize_cursor,
359935d5b7c7Smrg	.SetCursor = drmmode_sprite_set_cursor,
360035d5b7c7Smrg	.MoveCursor = drmmode_sprite_move_cursor,
360135d5b7c7Smrg	.DeviceCursorInitialize = drmmode_sprite_device_cursor_initialize,
360235d5b7c7Smrg	.DeviceCursorCleanup = drmmode_sprite_device_cursor_cleanup,
360335d5b7c7Smrg};
360435d5b7c7Smrg
360535d5b7c7Smrg
3606d6c0b56eSmrgvoid drmmode_adjust_frame(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int x, int y)
3607d6c0b56eSmrg{
3608d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
3609d6c0b56eSmrg	xf86OutputPtr output = config->output[config->compat_output];
3610d6c0b56eSmrg	xf86CrtcPtr crtc = output->crtc;
3611d6c0b56eSmrg
3612d6c0b56eSmrg	if (crtc && crtc->enabled) {
3613d6c0b56eSmrg		drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
3614d6c0b56eSmrg	}
3615d6c0b56eSmrg}
3616d6c0b56eSmrg
3617d6c0b56eSmrgBool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode,
3618d6c0b56eSmrg			       Bool set_hw)
3619d6c0b56eSmrg{
3620d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
362124b90cf4Smrg	unsigned num_desired = 0, num_on = 0;
3622d6c0b56eSmrg	int c;
3623d6c0b56eSmrg
362424b90cf4Smrg	/* First, disable all unused CRTCs */
362524b90cf4Smrg	if (set_hw) {
362624b90cf4Smrg		for (c = 0; c < config->num_crtc; c++) {
362724b90cf4Smrg			xf86CrtcPtr crtc = config->crtc[c];
362824b90cf4Smrg
362924b90cf4Smrg			/* Skip disabled CRTCs */
363024b90cf4Smrg			if (crtc->enabled)
363124b90cf4Smrg				continue;
363224b90cf4Smrg
363335d5b7c7Smrg			drmmode_crtc_dpms(crtc, DPMSModeOff);
363424b90cf4Smrg		}
363524b90cf4Smrg	}
363624b90cf4Smrg
363724b90cf4Smrg	/* Then, try setting the chosen mode on each CRTC */
3638d6c0b56eSmrg	for (c = 0; c < config->num_crtc; c++) {
3639d6c0b56eSmrg		xf86CrtcPtr crtc = config->crtc[c];
3640d6c0b56eSmrg		xf86OutputPtr output = NULL;
3641d6c0b56eSmrg		int o;
3642d6c0b56eSmrg
364324b90cf4Smrg		if (!crtc->enabled)
3644d6c0b56eSmrg			continue;
3645d6c0b56eSmrg
3646d6c0b56eSmrg		if (config->output[config->compat_output]->crtc == crtc)
3647d6c0b56eSmrg			output = config->output[config->compat_output];
3648d6c0b56eSmrg		else {
3649d6c0b56eSmrg			for (o = 0; o < config->num_output; o++)
3650d6c0b56eSmrg				if (config->output[o]->crtc == crtc) {
3651d6c0b56eSmrg					output = config->output[o];
3652d6c0b56eSmrg					break;
3653d6c0b56eSmrg				}
3654d6c0b56eSmrg		}
3655d6c0b56eSmrg		/* paranoia */
3656d6c0b56eSmrg		if (!output)
3657d6c0b56eSmrg			continue;
3658d6c0b56eSmrg
365924b90cf4Smrg		num_desired++;
366024b90cf4Smrg
3661d6c0b56eSmrg		/* Mark that we'll need to re-set the mode for sure */
3662d6c0b56eSmrg		memset(&crtc->mode, 0, sizeof(crtc->mode));
3663d6c0b56eSmrg		if (!crtc->desiredMode.CrtcHDisplay) {
3664d6c0b56eSmrg			DisplayModePtr mode = xf86OutputFindClosestMode(output,
3665d6c0b56eSmrg									pScrn->
3666d6c0b56eSmrg									currentMode);
3667d6c0b56eSmrg
366824b90cf4Smrg			if (!mode) {
366924b90cf4Smrg				xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
367024b90cf4Smrg					   "Failed to find mode for CRTC %d\n", c);
367124b90cf4Smrg				continue;
367224b90cf4Smrg			}
3673d6c0b56eSmrg			crtc->desiredMode = *mode;
3674d6c0b56eSmrg			crtc->desiredRotation = RR_Rotate_0;
3675d6c0b56eSmrg			crtc->desiredX = 0;
3676d6c0b56eSmrg			crtc->desiredY = 0;
3677d6c0b56eSmrg		}
3678d6c0b56eSmrg
3679d6c0b56eSmrg		if (set_hw) {
368024b90cf4Smrg			if (crtc->funcs->set_mode_major(crtc, &crtc->desiredMode,
368124b90cf4Smrg							crtc->desiredRotation,
368224b90cf4Smrg							crtc->desiredX,
368324b90cf4Smrg							crtc->desiredY)) {
368424b90cf4Smrg				num_on++;
368524b90cf4Smrg			} else {
368624b90cf4Smrg				xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
368724b90cf4Smrg					   "Failed to set mode on CRTC %d\n", c);
368835d5b7c7Smrg				RRCrtcSet(crtc->randr_crtc, NULL, crtc->x, crtc->y,
368935d5b7c7Smrg					  crtc->rotation, 0, NULL);
369024b90cf4Smrg			}
3691d6c0b56eSmrg		} else {
3692d6c0b56eSmrg			crtc->mode = crtc->desiredMode;
3693d6c0b56eSmrg			crtc->rotation = crtc->desiredRotation;
3694d6c0b56eSmrg			crtc->x = crtc->desiredX;
3695d6c0b56eSmrg			crtc->y = crtc->desiredY;
369624b90cf4Smrg			if (drmmode_handle_transform(crtc))
369724b90cf4Smrg				num_on++;
3698d6c0b56eSmrg		}
3699d6c0b56eSmrg	}
370024b90cf4Smrg
370124b90cf4Smrg	if (num_on == 0 && num_desired > 0) {
370224b90cf4Smrg		xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to enable any CRTC\n");
370324b90cf4Smrg		return FALSE;
370424b90cf4Smrg	}
370524b90cf4Smrg
370635d5b7c7Smrg	/* Validate leases on VT re-entry */
370790f2b693Smrg	if (dixPrivateKeyRegistered(rrPrivKey))
370890f2b693Smrg		drmmode_validate_leases(pScrn);
370935d5b7c7Smrg
3710d6c0b56eSmrg	return TRUE;
3711d6c0b56eSmrg}
3712d6c0b56eSmrg
3713d6c0b56eSmrgBool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn)
3714d6c0b56eSmrg{
3715d6c0b56eSmrg	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
371635d5b7c7Smrg	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
371735d5b7c7Smrg	int i;
3718d6c0b56eSmrg
3719d6c0b56eSmrg	if (xf86_config->num_crtc) {
3720d6c0b56eSmrg		xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG,
3721d6c0b56eSmrg			       "Initializing kms color map\n");
3722d6c0b56eSmrg		if (!miCreateDefColormap(pScreen))
3723d6c0b56eSmrg			return FALSE;
372435d5b7c7Smrg
372535d5b7c7Smrg		if (pScrn->depth == 30) {
372635d5b7c7Smrg			if (!drmmode_cm_enabled(&info->drmmode))
372735d5b7c7Smrg				return TRUE;
372835d5b7c7Smrg
372935d5b7c7Smrg			for (i = 0; i < xf86_config->num_crtc; i++) {
373035d5b7c7Smrg				xf86CrtcPtr crtc = xf86_config->crtc[i];
373135d5b7c7Smrg				void *gamma;
373235d5b7c7Smrg
373335d5b7c7Smrg				if (crtc->gamma_size == 1024)
373435d5b7c7Smrg					continue;
373535d5b7c7Smrg
373635d5b7c7Smrg				gamma = malloc(1024 * 3 * sizeof(CARD16));
373735d5b7c7Smrg				if (!gamma) {
373835d5b7c7Smrg					ErrorF("Failed to allocate gamma LUT memory\n");
373935d5b7c7Smrg					return FALSE;
374035d5b7c7Smrg				}
374135d5b7c7Smrg
374235d5b7c7Smrg				free(crtc->gamma_red);
374335d5b7c7Smrg				crtc->gamma_size = 1024;
374435d5b7c7Smrg				crtc->gamma_red = gamma;
374535d5b7c7Smrg				crtc->gamma_green = crtc->gamma_red + crtc->gamma_size;
374635d5b7c7Smrg				crtc->gamma_blue = crtc->gamma_green + crtc->gamma_size;
374735d5b7c7Smrg			}
374835d5b7c7Smrg		}
374935d5b7c7Smrg
375035d5b7c7Smrg		/* All Radeons support 10 bit CLUTs. */
375135d5b7c7Smrg		if (!xf86HandleColormaps(pScreen, 1 << pScrn->rgbBits, 10,
375235d5b7c7Smrg					 NULL, NULL, CMAP_PALETTED_TRUECOLOR
3753d6c0b56eSmrg					 | CMAP_RELOAD_ON_MODE_SWITCH))
3754d6c0b56eSmrg			return FALSE;
375535d5b7c7Smrg
375635d5b7c7Smrg		for (i = 0; i < xf86_config->num_crtc; i++) {
375735d5b7c7Smrg			xf86CrtcPtr crtc = xf86_config->crtc[i];
375835d5b7c7Smrg
375935d5b7c7Smrg			drmmode_crtc_gamma_do_set(crtc, crtc->gamma_red,
376035d5b7c7Smrg						  crtc->gamma_green,
376135d5b7c7Smrg						  crtc->gamma_blue,
376235d5b7c7Smrg						  crtc->gamma_size);
376335d5b7c7Smrg		}
3764d6c0b56eSmrg	}
376535d5b7c7Smrg
3766d6c0b56eSmrg	return TRUE;
3767d6c0b56eSmrg}
3768d6c0b56eSmrg
3769504d986fSmrgstatic Bool
3770504d986fSmrgdrmmode_find_output(ScrnInfoPtr scrn, int output_id, int *num_dvi,
3771504d986fSmrg		    int *num_hdmi)
3772504d986fSmrg{
3773504d986fSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3774504d986fSmrg	int i;
3775504d986fSmrg
3776504d986fSmrg	for (i = 0; i < config->num_output; i++) {
3777504d986fSmrg		xf86OutputPtr output = config->output[i];
3778504d986fSmrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
3779504d986fSmrg
3780504d986fSmrg		if (drmmode_output->output_id == output_id) {
3781504d986fSmrg			switch(drmmode_output->mode_output->connector_type) {
3782504d986fSmrg			case DRM_MODE_CONNECTOR_DVII:
3783504d986fSmrg			case DRM_MODE_CONNECTOR_DVID:
3784504d986fSmrg			case DRM_MODE_CONNECTOR_DVIA:
3785504d986fSmrg				(*num_dvi)++;
3786504d986fSmrg				break;
3787504d986fSmrg			case DRM_MODE_CONNECTOR_HDMIA:
3788504d986fSmrg			case DRM_MODE_CONNECTOR_HDMIB:
3789504d986fSmrg				(*num_hdmi)++;
3790504d986fSmrg				break;
3791504d986fSmrg			}
3792504d986fSmrg
3793504d986fSmrg			return TRUE;
3794504d986fSmrg		}
3795504d986fSmrg	}
3796504d986fSmrg
3797504d986fSmrg	return FALSE;
3798504d986fSmrg}
3799d6c0b56eSmrg
3800d6c0b56eSmrgvoid
3801d6c0b56eSmrgamdgpu_mode_hotplug(ScrnInfoPtr scrn, drmmode_ptr drmmode)
3802d6c0b56eSmrg{
3803d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
3804d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
3805d6c0b56eSmrg	drmModeResPtr mode_res;
3806d6c0b56eSmrg	int i, j;
3807d6c0b56eSmrg	Bool found;
3808d6c0b56eSmrg	Bool changed = FALSE;
3809504d986fSmrg	int num_dvi = 0, num_hdmi = 0;
3810d6c0b56eSmrg
381124b90cf4Smrg	/* Try to re-set the mode on all the connectors with a BAD link-state:
381224b90cf4Smrg	 * This may happen if a link degrades and a new modeset is necessary, using
381324b90cf4Smrg	 * different link-training parameters. If the kernel found that the current
381424b90cf4Smrg	 * mode is not achievable anymore, it should have pruned the mode before
381524b90cf4Smrg	 * sending the hotplug event. Try to re-set the currently-set mode to keep
381624b90cf4Smrg	 * the display alive, this will fail if the mode has been pruned.
381724b90cf4Smrg	 * In any case, we will send randr events for the Desktop Environment to
381824b90cf4Smrg	 * deal with it, if it wants to.
381924b90cf4Smrg	 */
382024b90cf4Smrg	for (i = 0; i < config->num_output; i++) {
382124b90cf4Smrg		xf86OutputPtr output = config->output[i];
382224b90cf4Smrg		xf86CrtcPtr crtc = output->crtc;
382324b90cf4Smrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
382424b90cf4Smrg
382524b90cf4Smrg		drmmode_output_detect(output);
382624b90cf4Smrg
382724b90cf4Smrg		if (!crtc || !drmmode_output->mode_output)
382824b90cf4Smrg			continue;
382924b90cf4Smrg
383024b90cf4Smrg		/* Get an updated view of the properties for the current connector and
383124b90cf4Smrg		 * look for the link-status property
383224b90cf4Smrg		 */
383324b90cf4Smrg		for (j = 0; j < drmmode_output->num_props; j++) {
383424b90cf4Smrg			drmmode_prop_ptr p = &drmmode_output->props[j];
383524b90cf4Smrg
383624b90cf4Smrg			if (!strcmp(p->mode_prop->name, "link-status")) {
383724b90cf4Smrg				if (p->value != DRM_MODE_LINK_STATUS_BAD)
383824b90cf4Smrg					break;
383924b90cf4Smrg
384024b90cf4Smrg				/* the connector got a link failure, re-set the current mode */
384124b90cf4Smrg				drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
384224b90cf4Smrg						       crtc->x, crtc->y);
384324b90cf4Smrg
384424b90cf4Smrg				xf86DrvMsg(scrn->scrnIndex, X_WARNING,
384524b90cf4Smrg					   "hotplug event: connector %u's link-state is BAD, "
384624b90cf4Smrg					   "tried resetting the current mode. You may be left"
384724b90cf4Smrg					   "with a black screen if this fails...\n",
384824b90cf4Smrg					   drmmode_output->mode_output->connector_id);
384924b90cf4Smrg
385024b90cf4Smrg				break;
385124b90cf4Smrg			}
385224b90cf4Smrg		}
385324b90cf4Smrg	}
385424b90cf4Smrg
3855d6c0b56eSmrg	mode_res = drmModeGetResources(pAMDGPUEnt->fd);
3856d6c0b56eSmrg	if (!mode_res)
3857d6c0b56eSmrg		goto out;
3858d6c0b56eSmrg
3859d6c0b56eSmrgrestart_destroy:
3860d6c0b56eSmrg	for (i = 0; i < config->num_output; i++) {
3861d6c0b56eSmrg		xf86OutputPtr output = config->output[i];
3862d6c0b56eSmrg		drmmode_output_private_ptr drmmode_output = output->driver_private;
3863d6c0b56eSmrg		found = FALSE;
3864d6c0b56eSmrg		for (j = 0; j < mode_res->count_connectors; j++) {
3865d6c0b56eSmrg			if (mode_res->connectors[j] == drmmode_output->output_id) {
3866d6c0b56eSmrg				found = TRUE;
3867d6c0b56eSmrg				break;
3868d6c0b56eSmrg			}
3869d6c0b56eSmrg		}
3870d6c0b56eSmrg		if (found)
3871d6c0b56eSmrg			continue;
3872d6c0b56eSmrg
3873d6c0b56eSmrg		drmModeFreeConnector(drmmode_output->mode_output);
3874d6c0b56eSmrg		drmmode_output->mode_output = NULL;
3875d6c0b56eSmrg		drmmode_output->output_id = -1;
3876d6c0b56eSmrg
3877d6c0b56eSmrg		changed = TRUE;
3878d6c0b56eSmrg		if (drmmode->delete_dp_12_displays) {
3879d6c0b56eSmrg			RROutputDestroy(output->randr_output);
3880d6c0b56eSmrg			xf86OutputDestroy(output);
3881d6c0b56eSmrg			goto restart_destroy;
3882d6c0b56eSmrg		}
3883d6c0b56eSmrg	}
3884d6c0b56eSmrg
3885d6c0b56eSmrg	/* find new output ids we don't have outputs for */
3886d6c0b56eSmrg	for (i = 0; i < mode_res->count_connectors; i++) {
388790f2b693Smrg		for (j = 0; j < pAMDGPUEnt->num_scrns; j++) {
388890f2b693Smrg			if (drmmode_find_output(pAMDGPUEnt->scrn[j],
388990f2b693Smrg						mode_res->connectors[i],
389090f2b693Smrg						&num_dvi, &num_hdmi))
389190f2b693Smrg				break;
389290f2b693Smrg		}
389390f2b693Smrg
389490f2b693Smrg		if (j < pAMDGPUEnt->num_scrns)
3895d6c0b56eSmrg			continue;
3896d6c0b56eSmrg
3897504d986fSmrg		if (drmmode_output_init(scrn, drmmode, mode_res, i, &num_dvi,
3898504d986fSmrg					&num_hdmi, 1) != 0)
3899504d986fSmrg			changed = TRUE;
3900d6c0b56eSmrg	}
3901d6c0b56eSmrg
390235d5b7c7Smrg	/* Check to see if a lessee has disappeared */
390335d5b7c7Smrg	drmmode_validate_leases(scrn);
390435d5b7c7Smrg
390590f2b693Smrg	if (changed) {
3906d6c0b56eSmrg#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,14,99,2,0)
3907d6c0b56eSmrg		RRSetChanged(xf86ScrnToScreen(scrn));
3908d6c0b56eSmrg#else
3909d6c0b56eSmrg		rrScrPrivPtr rrScrPriv = rrGetScrPriv(scrn->pScreen);
3910d6c0b56eSmrg		rrScrPriv->changed = TRUE;
3911d6c0b56eSmrg#endif
3912d6c0b56eSmrg		RRTellChanged(xf86ScrnToScreen(scrn));
3913d6c0b56eSmrg	}
3914d6c0b56eSmrg
3915d6c0b56eSmrg	drmModeFreeResources(mode_res);
3916d6c0b56eSmrgout:
3917d6c0b56eSmrg	RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
3918d6c0b56eSmrg}
3919d6c0b56eSmrg
3920d6c0b56eSmrg#ifdef HAVE_LIBUDEV
3921d6c0b56eSmrgstatic void drmmode_handle_uevents(int fd, void *closure)
3922d6c0b56eSmrg{
3923d6c0b56eSmrg	drmmode_ptr drmmode = closure;
3924d6c0b56eSmrg	ScrnInfoPtr scrn = drmmode->scrn;
3925d6c0b56eSmrg	struct udev_device *dev;
3926504d986fSmrg	Bool received = FALSE;
392711bf0794Smrg	struct timeval tv = { 0, 0 };
392811bf0794Smrg	fd_set readfd;
392911bf0794Smrg
393011bf0794Smrg	FD_ZERO(&readfd);
393111bf0794Smrg	FD_SET(fd, &readfd);
393211bf0794Smrg
393311bf0794Smrg	while (select(fd + 1, &readfd, NULL, NULL, &tv) > 0 &&
393411bf0794Smrg	       FD_ISSET(fd, &readfd)) {
393511bf0794Smrg		/* select() ensured that this will not block */
393611bf0794Smrg		dev = udev_monitor_receive_device(drmmode->uevent_monitor);
393711bf0794Smrg		if (dev) {
393811bf0794Smrg			udev_device_unref(dev);
393911bf0794Smrg			received = TRUE;
394011bf0794Smrg		}
3941504d986fSmrg	}
3942504d986fSmrg
3943504d986fSmrg	if (received)
3944504d986fSmrg		amdgpu_mode_hotplug(scrn, drmmode);
3945d6c0b56eSmrg}
3946d6c0b56eSmrg#endif
3947d6c0b56eSmrg
3948d6c0b56eSmrgvoid drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode)
3949d6c0b56eSmrg{
3950d6c0b56eSmrg#ifdef HAVE_LIBUDEV
3951d6c0b56eSmrg	struct udev *u;
3952d6c0b56eSmrg	struct udev_monitor *mon;
3953d6c0b56eSmrg
3954d6c0b56eSmrg	u = udev_new();
3955d6c0b56eSmrg	if (!u)
3956d6c0b56eSmrg		return;
3957d6c0b56eSmrg	mon = udev_monitor_new_from_netlink(u, "udev");
3958d6c0b56eSmrg	if (!mon) {
3959d6c0b56eSmrg		udev_unref(u);
3960d6c0b56eSmrg		return;
3961d6c0b56eSmrg	}
3962d6c0b56eSmrg
3963d6c0b56eSmrg	if (udev_monitor_filter_add_match_subsystem_devtype(mon,
3964d6c0b56eSmrg							    "drm",
3965d6c0b56eSmrg							    "drm_minor") < 0 ||
3966d6c0b56eSmrg	    udev_monitor_enable_receiving(mon) < 0) {
3967d6c0b56eSmrg		udev_monitor_unref(mon);
3968d6c0b56eSmrg		udev_unref(u);
3969d6c0b56eSmrg		return;
3970d6c0b56eSmrg	}
3971d6c0b56eSmrg
3972d6c0b56eSmrg	drmmode->uevent_handler =
3973d6c0b56eSmrg	    xf86AddGeneralHandler(udev_monitor_get_fd(mon),
3974d6c0b56eSmrg				  drmmode_handle_uevents, drmmode);
3975d6c0b56eSmrg
3976d6c0b56eSmrg	drmmode->uevent_monitor = mon;
3977d6c0b56eSmrg#endif
3978d6c0b56eSmrg}
3979d6c0b56eSmrg
3980d6c0b56eSmrgvoid drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode)
3981d6c0b56eSmrg{
3982d6c0b56eSmrg#ifdef HAVE_LIBUDEV
3983d6c0b56eSmrg	if (drmmode->uevent_handler) {
3984d6c0b56eSmrg		struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
3985d6c0b56eSmrg		xf86RemoveGeneralHandler(drmmode->uevent_handler);
3986d6c0b56eSmrg
3987d6c0b56eSmrg		udev_monitor_unref(drmmode->uevent_monitor);
3988d6c0b56eSmrg		udev_unref(u);
3989d6c0b56eSmrg	}
3990d6c0b56eSmrg#endif
3991d6c0b56eSmrg}
3992d6c0b56eSmrg
3993d6c0b56eSmrgBool amdgpu_do_pageflip(ScrnInfoPtr scrn, ClientPtr client,
3994d6c0b56eSmrg			PixmapPtr new_front, uint64_t id, void *data,
399524b90cf4Smrg			xf86CrtcPtr ref_crtc, amdgpu_drm_handler_proc handler,
3996504d986fSmrg			amdgpu_drm_abort_proc abort,
399711bf0794Smrg			enum drmmode_flip_sync flip_sync,
399811bf0794Smrg			uint32_t target_msc)
3999d6c0b56eSmrg{
4000d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
4001d6c0b56eSmrg	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
4002d6c0b56eSmrg	xf86CrtcPtr crtc = NULL;
4003d6c0b56eSmrg	drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private;
400490f2b693Smrg	int crtc_id;
400511bf0794Smrg	uint32_t flip_flags = flip_sync == FLIP_ASYNC ? DRM_MODE_PAGE_FLIP_ASYNC : 0;
4006d6c0b56eSmrg	drmmode_flipdata_ptr flipdata;
400735d5b7c7Smrg	Bool handle_deferred = FALSE;
4008d6c0b56eSmrg	uintptr_t drm_queue_seq = 0;
400935d5b7c7Smrg	struct drmmode_fb *fb;
401035d5b7c7Smrg	int i = 0;
4011d6c0b56eSmrg
401290f2b693Smrg	flipdata = calloc(1, sizeof(*flipdata) + drmmode_crtc->drmmode->count_crtcs *
401335d5b7c7Smrg			  sizeof(flipdata->fb[0]));
4014d6c0b56eSmrg	if (!flipdata) {
4015d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
4016d6c0b56eSmrg			   "flip queue: data alloc failed.\n");
4017d6c0b56eSmrg		goto error;
4018d6c0b56eSmrg	}
4019d6c0b56eSmrg
402035d5b7c7Smrg	fb = amdgpu_pixmap_get_fb(new_front);
402135d5b7c7Smrg	if (!fb) {
402224b90cf4Smrg		ErrorF("Failed to get FB for flip\n");
4023d6c0b56eSmrg		goto error;
402424b90cf4Smrg	}
4025d6c0b56eSmrg
4026d6c0b56eSmrg	/*
4027d6c0b56eSmrg	 * Queue flips on all enabled CRTCs
4028d6c0b56eSmrg	 * Note that if/when we get per-CRTC buffers, we'll have to update this.
4029d6c0b56eSmrg	 * Right now it assumes a single shared fb across all CRTCs, with the
4030d6c0b56eSmrg	 * kernel fixing up the offset of each CRTC as necessary.
4031d6c0b56eSmrg	 *
4032d6c0b56eSmrg	 * Also, flips queued on disabled or incorrectly configured displays
4033d6c0b56eSmrg	 * may never complete; this is a configuration error.
4034d6c0b56eSmrg	 */
4035d6c0b56eSmrg
4036d6c0b56eSmrg	flipdata->event_data = data;
4037d6c0b56eSmrg	flipdata->handler = handler;
4038d6c0b56eSmrg	flipdata->abort = abort;
403924b90cf4Smrg	flipdata->fe_crtc = ref_crtc;
4040d6c0b56eSmrg
4041d6c0b56eSmrg	for (i = 0; i < config->num_crtc; i++) {
4042d6c0b56eSmrg		crtc = config->crtc[i];
404324b90cf4Smrg		drmmode_crtc = crtc->driver_private;
404490f2b693Smrg		crtc_id = drmmode_get_crtc_id(crtc);
4045d6c0b56eSmrg
404624b90cf4Smrg		if (!drmmode_crtc_can_flip(crtc) ||
404724b90cf4Smrg		    (drmmode_crtc->tear_free && crtc != ref_crtc))
4048d6c0b56eSmrg			continue;
4049d6c0b56eSmrg
4050d6c0b56eSmrg		flipdata->flip_count++;
4051d6c0b56eSmrg
4052d6c0b56eSmrg		drm_queue_seq = amdgpu_drm_queue_alloc(crtc, client, id,
4053d6c0b56eSmrg						       flipdata,
4054d6c0b56eSmrg						       drmmode_flip_handler,
405590f2b693Smrg						       drmmode_flip_abort,
405690f2b693Smrg						       TRUE);
4057504d986fSmrg		if (drm_queue_seq == AMDGPU_DRM_QUEUE_ERROR) {
4058d6c0b56eSmrg			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
4059d6c0b56eSmrg				   "Allocating DRM queue event entry failed.\n");
4060d6c0b56eSmrg			goto error;
4061d6c0b56eSmrg		}
4062d6c0b56eSmrg
406324b90cf4Smrg		if (drmmode_crtc->tear_free) {
406424b90cf4Smrg			BoxRec extents = { .x1 = 0, .y1 = 0,
406524b90cf4Smrg					   .x2 = new_front->drawable.width,
406624b90cf4Smrg					   .y2 = new_front->drawable.height };
406724b90cf4Smrg			int scanout_id = drmmode_crtc->scanout_id ^ 1;
406824b90cf4Smrg
406924b90cf4Smrg			if (flip_sync == FLIP_ASYNC) {
407024b90cf4Smrg				if (!drmmode_wait_vblank(crtc,
407124b90cf4Smrg							 DRM_VBLANK_RELATIVE |
407224b90cf4Smrg							 DRM_VBLANK_EVENT,
407324b90cf4Smrg							 0, drm_queue_seq,
407424b90cf4Smrg							 NULL, NULL))
407524b90cf4Smrg					goto flip_error;
407624b90cf4Smrg				goto next;
407724b90cf4Smrg			}
407824b90cf4Smrg
407990f2b693Smrg			drmmode_fb_reference(pAMDGPUEnt->fd, &flipdata->fb[crtc_id],
408035d5b7c7Smrg					     amdgpu_pixmap_get_fb(drmmode_crtc->scanout[scanout_id].pixmap));
408190f2b693Smrg			if (!flipdata->fb[crtc_id]) {
408224b90cf4Smrg				ErrorF("Failed to get FB for TearFree flip\n");
408324b90cf4Smrg				goto error;
408424b90cf4Smrg			}
408524b90cf4Smrg
408624b90cf4Smrg			amdgpu_scanout_do_update(crtc, scanout_id, new_front,
408735d5b7c7Smrg						 extents);
408835d5b7c7Smrg			amdgpu_glamor_flush(crtc->scrn);
408935d5b7c7Smrg
409035d5b7c7Smrg			if (drmmode_crtc->scanout_update_pending) {
409135d5b7c7Smrg				amdgpu_drm_wait_pending_flip(crtc);
409235d5b7c7Smrg				handle_deferred = TRUE;
409335d5b7c7Smrg				amdgpu_drm_abort_entry(drmmode_crtc->scanout_update_pending);
409435d5b7c7Smrg				drmmode_crtc->scanout_update_pending = 0;
409535d5b7c7Smrg			}
409635d5b7c7Smrg		} else {
409790f2b693Smrg			drmmode_fb_reference(pAMDGPUEnt->fd, &flipdata->fb[crtc_id], fb);
409824b90cf4Smrg		}
409924b90cf4Smrg
410024b90cf4Smrg		if (crtc == ref_crtc) {
410111bf0794Smrg			if (drmmode_page_flip_target_absolute(pAMDGPUEnt,
410211bf0794Smrg							      drmmode_crtc,
410390f2b693Smrg							      flipdata->fb[crtc_id]->handle,
410411bf0794Smrg							      flip_flags,
410511bf0794Smrg							      drm_queue_seq,
410611bf0794Smrg							      target_msc) != 0)
410711bf0794Smrg				goto flip_error;
410811bf0794Smrg		} else {
410911bf0794Smrg			if (drmmode_page_flip_target_relative(pAMDGPUEnt,
411011bf0794Smrg							      drmmode_crtc,
411190f2b693Smrg							      flipdata->fb[crtc_id]->handle,
411211bf0794Smrg							      flip_flags,
411311bf0794Smrg							      drm_queue_seq, 0) != 0)
411411bf0794Smrg				goto flip_error;
4115d6c0b56eSmrg		}
411611bf0794Smrg
411724b90cf4Smrg		if (drmmode_crtc->tear_free) {
411824b90cf4Smrg			drmmode_crtc->scanout_id ^= 1;
411924b90cf4Smrg			drmmode_crtc->ignore_damage = TRUE;
412024b90cf4Smrg		}
412124b90cf4Smrg
412235d5b7c7Smrg		drmmode_fb_reference(pAMDGPUEnt->fd, &drmmode_crtc->flip_pending,
412390f2b693Smrg				     flipdata->fb[crtc_id]);
412490f2b693Smrg
412590f2b693Smrg	next:
4126d6c0b56eSmrg		drm_queue_seq = 0;
4127d6c0b56eSmrg	}
4128d6c0b56eSmrg
412935d5b7c7Smrg	if (handle_deferred)
413035d5b7c7Smrg		amdgpu_drm_queue_handle_deferred(ref_crtc);
4131d6c0b56eSmrg	if (flipdata->flip_count > 0)
4132d6c0b56eSmrg		return TRUE;
4133d6c0b56eSmrg
413411bf0794Smrgflip_error:
413511bf0794Smrg	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed: %s\n",
413611bf0794Smrg		   strerror(errno));
413711bf0794Smrg
4138d6c0b56eSmrgerror:
4139d6c0b56eSmrg	if (drm_queue_seq)
4140d6c0b56eSmrg		amdgpu_drm_abort_entry(drm_queue_seq);
4141d6c0b56eSmrg	else if (crtc)
4142d6c0b56eSmrg		drmmode_flip_abort(crtc, flipdata);
414311bf0794Smrg	else {
414411bf0794Smrg		abort(NULL, data);
4145d6c0b56eSmrg		free(flipdata);
414611bf0794Smrg	}
4147d6c0b56eSmrg
4148d6c0b56eSmrg	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
4149d6c0b56eSmrg		   strerror(errno));
415035d5b7c7Smrg	if (handle_deferred)
415135d5b7c7Smrg		amdgpu_drm_queue_handle_deferred(ref_crtc);
4152d6c0b56eSmrg	return FALSE;
4153d6c0b56eSmrg}
4154