1d6c0b56eSmrg/*
2d6c0b56eSmrg * Copyright © 2013-2014 Intel Corporation
3d6c0b56eSmrg * Copyright © 2015 Advanced Micro Devices, Inc.
4d6c0b56eSmrg *
5d6c0b56eSmrg * Permission to use, copy, modify, distribute, and sell this software and its
6d6c0b56eSmrg * documentation for any purpose is hereby granted without fee, provided that
7d6c0b56eSmrg * the above copyright notice appear in all copies and that both that copyright
8d6c0b56eSmrg * notice and this permission notice appear in supporting documentation, and
9d6c0b56eSmrg * that the name of the copyright holders not be used in advertising or
10d6c0b56eSmrg * publicity pertaining to distribution of the software without specific,
11d6c0b56eSmrg * written prior permission.  The copyright holders make no representations
12d6c0b56eSmrg * about the suitability of this software for any purpose.  It is provided "as
13d6c0b56eSmrg * is" without express or implied warranty.
14d6c0b56eSmrg *
15d6c0b56eSmrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16d6c0b56eSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17d6c0b56eSmrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18d6c0b56eSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19d6c0b56eSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20d6c0b56eSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21d6c0b56eSmrg * OF THIS SOFTWARE.
22d6c0b56eSmrg */
23d6c0b56eSmrg
24d6c0b56eSmrg
25d6c0b56eSmrg#ifdef HAVE_CONFIG_H
26d6c0b56eSmrg#include "config.h"
27d6c0b56eSmrg#endif
28d6c0b56eSmrg
29d6c0b56eSmrg#include "amdgpu_drv.h"
30d6c0b56eSmrg
31d6c0b56eSmrg#ifdef HAVE_DRI3_H
32d6c0b56eSmrg
33d6c0b56eSmrg#include "amdgpu_glamor.h"
34d6c0b56eSmrg#include "amdgpu_pixmap.h"
35d6c0b56eSmrg#include "dri3.h"
36d6c0b56eSmrg
37d6c0b56eSmrg#include <sys/types.h>
38d6c0b56eSmrg#include <sys/stat.h>
39d6c0b56eSmrg#include <fcntl.h>
4090f2b693Smrg#include <gbm.h>
41d6c0b56eSmrg#include <errno.h>
42d6c0b56eSmrg#include <libgen.h>
43d6c0b56eSmrg
4446845023Smrgstatic int open_card_node(ScreenPtr screen, int *out)
45d6c0b56eSmrg{
46d6c0b56eSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
47d6c0b56eSmrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
48d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
49d6c0b56eSmrg	drm_magic_t magic;
50d6c0b56eSmrg	int fd;
51d6c0b56eSmrg
52d6c0b56eSmrg	fd = open(info->dri2.device_name, O_RDWR | O_CLOEXEC);
53d6c0b56eSmrg	if (fd < 0)
54d6c0b56eSmrg		return BadAlloc;
55d6c0b56eSmrg
56d6c0b56eSmrg	/* Before FD passing in the X protocol with DRI3 (and increased
57d6c0b56eSmrg	 * security of rendering with per-process address spaces on the
58d6c0b56eSmrg	 * GPU), the kernel had to come up with a way to have the server
59d6c0b56eSmrg	 * decide which clients got to access the GPU, which was done by
60d6c0b56eSmrg	 * each client getting a unique (magic) number from the kernel,
61d6c0b56eSmrg	 * passing it to the server, and the server then telling the
62d6c0b56eSmrg	 * kernel which clients were authenticated for using the device.
63d6c0b56eSmrg	 *
64d6c0b56eSmrg	 * Now that we have FD passing, the server can just set up the
65d6c0b56eSmrg	 * authentication on its own and hand the prepared FD off to the
66d6c0b56eSmrg	 * client.
67d6c0b56eSmrg	 */
68d6c0b56eSmrg	if (drmGetMagic(fd, &magic) < 0) {
69d6c0b56eSmrg		if (errno == EACCES) {
70d6c0b56eSmrg			/* Assume that we're on a render node, and the fd is
71d6c0b56eSmrg			 * already as authenticated as it should be.
72d6c0b56eSmrg			 */
73d6c0b56eSmrg			*out = fd;
74d6c0b56eSmrg			return Success;
75d6c0b56eSmrg		} else {
76d6c0b56eSmrg			close(fd);
77d6c0b56eSmrg			return BadMatch;
78d6c0b56eSmrg		}
79d6c0b56eSmrg	}
80d6c0b56eSmrg
81d6c0b56eSmrg	if (drmAuthMagic(pAMDGPUEnt->fd, magic) < 0) {
82d6c0b56eSmrg		close(fd);
83d6c0b56eSmrg		return BadMatch;
84d6c0b56eSmrg	}
85d6c0b56eSmrg
86d6c0b56eSmrg	*out = fd;
87d6c0b56eSmrg	return Success;
88d6c0b56eSmrg}
89d6c0b56eSmrg
9011bf0794Smrgstatic int open_render_node(ScreenPtr screen, int *out)
9111bf0794Smrg{
9211bf0794Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
9311bf0794Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
9411bf0794Smrg	int fd;
9511bf0794Smrg
9611bf0794Smrg	fd = open(pAMDGPUEnt->render_node, O_RDWR | O_CLOEXEC);
9711bf0794Smrg	if (fd < 0)
9811bf0794Smrg		return BadAlloc;
9911bf0794Smrg
10011bf0794Smrg	*out = fd;
10111bf0794Smrg	return Success;
10211bf0794Smrg}
10311bf0794Smrg
10411bf0794Smrgstatic int
10511bf0794Smrgamdgpu_dri3_open(ScreenPtr screen, RRProviderPtr provider, int *out)
10611bf0794Smrg{
10711bf0794Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
10811bf0794Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
10911bf0794Smrg	int ret = BadAlloc;
11011bf0794Smrg
11111bf0794Smrg	if (pAMDGPUEnt->render_node)
11211bf0794Smrg		ret = open_render_node(screen, out);
11311bf0794Smrg
11411bf0794Smrg	if (ret != Success)
11546845023Smrg		ret = open_card_node(screen, out);
11611bf0794Smrg
11711bf0794Smrg	return ret;
11811bf0794Smrg}
11911bf0794Smrg
120d6c0b56eSmrg#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
121d6c0b56eSmrg
122d6c0b56eSmrgstatic int
123d6c0b56eSmrgamdgpu_dri3_open_client(ClientPtr client, ScreenPtr screen,
124d6c0b56eSmrg			RRProviderPtr provider, int *out)
125d6c0b56eSmrg{
126d6c0b56eSmrg	const char *cmdname = GetClientCmdName(client);
127d6c0b56eSmrg	Bool is_ssh = FALSE;
128d6c0b56eSmrg
129d6c0b56eSmrg	/* If the executable name is "ssh", assume that this client connection
130d6c0b56eSmrg	 * is forwarded from another host via SSH
131d6c0b56eSmrg	 */
132d6c0b56eSmrg	if (cmdname) {
133d6c0b56eSmrg		char *cmd = strdup(cmdname);
134d6c0b56eSmrg
135d6c0b56eSmrg		/* Cut off any colon and whatever comes after it, see
136d6c0b56eSmrg		 * https://lists.freedesktop.org/archives/xorg-devel/2015-December/048164.html
137d6c0b56eSmrg		 */
138d6c0b56eSmrg		cmd = strtok(cmd, ":");
139d6c0b56eSmrg
140d6c0b56eSmrg		is_ssh = strcmp(basename(cmd), "ssh") == 0;
141d6c0b56eSmrg		free(cmd);
142d6c0b56eSmrg	}
143d6c0b56eSmrg
144d6c0b56eSmrg	if (!is_ssh)
145d6c0b56eSmrg		return amdgpu_dri3_open(screen, provider, out);
146d6c0b56eSmrg
147d6c0b56eSmrg	return BadAccess;
148d6c0b56eSmrg}
149d6c0b56eSmrg
150d6c0b56eSmrg#endif /* DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0) */
151d6c0b56eSmrg
152d6c0b56eSmrgstatic PixmapPtr amdgpu_dri3_pixmap_from_fd(ScreenPtr screen,
153d6c0b56eSmrg					    int fd,
154d6c0b56eSmrg					    CARD16 width,
155d6c0b56eSmrg					    CARD16 height,
156d6c0b56eSmrg					    CARD16 stride,
157d6c0b56eSmrg					    CARD8 depth,
158d6c0b56eSmrg					    CARD8 bpp)
159d6c0b56eSmrg{
160d6c0b56eSmrg	PixmapPtr pixmap;
161d6c0b56eSmrg
162d6c0b56eSmrg#ifdef USE_GLAMOR
163d6c0b56eSmrg	/* Avoid generating a GEM flink name if possible */
164d6c0b56eSmrg	if (AMDGPUPTR(xf86ScreenToScrn(screen))->use_glamor) {
165d6c0b56eSmrg		pixmap = glamor_pixmap_from_fd(screen, fd, width, height,
166d6c0b56eSmrg					       stride, depth, bpp);
167d6c0b56eSmrg		if (pixmap) {
168d6c0b56eSmrg			struct amdgpu_pixmap *priv = calloc(1, sizeof(*priv));
169d6c0b56eSmrg
170504d986fSmrg			if (priv) {
171504d986fSmrg				amdgpu_set_pixmap_private(pixmap, priv);
17235d5b7c7Smrg				pixmap->usage_hint |= AMDGPU_CREATE_PIXMAP_DRI2;
173504d986fSmrg				return pixmap;
174504d986fSmrg			}
175504d986fSmrg
176504d986fSmrg			screen->DestroyPixmap(pixmap);
177504d986fSmrg			return NULL;
178d6c0b56eSmrg		}
179d6c0b56eSmrg	}
180d6c0b56eSmrg#endif
181d6c0b56eSmrg
182d6c0b56eSmrg	if (depth < 8)
183d6c0b56eSmrg		return NULL;
184d6c0b56eSmrg
185d6c0b56eSmrg	switch (bpp) {
186d6c0b56eSmrg	case 8:
187d6c0b56eSmrg	case 16:
188d6c0b56eSmrg	case 32:
189d6c0b56eSmrg		break;
190d6c0b56eSmrg	default:
191d6c0b56eSmrg		return NULL;
192d6c0b56eSmrg	}
193d6c0b56eSmrg
194d6c0b56eSmrg	pixmap = screen->CreatePixmap(screen, 0, 0, depth,
195d6c0b56eSmrg				      AMDGPU_CREATE_PIXMAP_DRI2);
196d6c0b56eSmrg	if (!pixmap)
197d6c0b56eSmrg		return NULL;
198d6c0b56eSmrg
199d6c0b56eSmrg	if (!screen->ModifyPixmapHeader(pixmap, width, height, 0, bpp, stride,
200d6c0b56eSmrg					NULL))
201d6c0b56eSmrg		goto free_pixmap;
202d6c0b56eSmrg
203d6c0b56eSmrg	if (screen->SetSharedPixmapBacking(pixmap, (void*)(intptr_t)fd))
204d6c0b56eSmrg		return pixmap;
205d6c0b56eSmrg
206d6c0b56eSmrgfree_pixmap:
207d6c0b56eSmrg	fbDestroyPixmap(pixmap);
208d6c0b56eSmrg	return NULL;
209d6c0b56eSmrg}
210d6c0b56eSmrg
211d6c0b56eSmrgstatic int amdgpu_dri3_fd_from_pixmap(ScreenPtr screen,
212d6c0b56eSmrg				      PixmapPtr pixmap,
213d6c0b56eSmrg				      CARD16 *stride,
214d6c0b56eSmrg				      CARD32 *size)
215d6c0b56eSmrg{
216d6c0b56eSmrg	struct amdgpu_buffer *bo;
217d6c0b56eSmrg	struct amdgpu_bo_info bo_info;
218d6c0b56eSmrg	uint32_t fd;
219d6c0b56eSmrg#ifdef USE_GLAMOR
220d6c0b56eSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
221d6c0b56eSmrg	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
222d6c0b56eSmrg
22390f2b693Smrg	if (info->use_glamor) {
22477d6d1ecSmrg		int ret = glamor_fd_from_pixmap(screen, pixmap, stride, size);
22590f2b693Smrg
22677d6d1ecSmrg		/* Any pending drawing operations need to be flushed to the
22777d6d1ecSmrg		 * kernel driver before the client starts using the pixmap
22877d6d1ecSmrg		 * storage for direct rendering.
22990f2b693Smrg		 */
23077d6d1ecSmrg		if (ret >= 0)
23190f2b693Smrg			amdgpu_glamor_flush(scrn);
23290f2b693Smrg
23390f2b693Smrg		return ret;
23490f2b693Smrg	}
235d6c0b56eSmrg#endif
236d6c0b56eSmrg
237d6c0b56eSmrg	bo = amdgpu_get_pixmap_bo(pixmap);
238d6c0b56eSmrg	if (!bo)
239d6c0b56eSmrg		return -1;
240d6c0b56eSmrg
241d6c0b56eSmrg	if (pixmap->devKind > UINT16_MAX)
242d6c0b56eSmrg		return -1;
243d6c0b56eSmrg
244d6c0b56eSmrg	if (amdgpu_bo_query_info(bo->bo.amdgpu, &bo_info) != 0)
245d6c0b56eSmrg		return -1;
246d6c0b56eSmrg
247d6c0b56eSmrg	if (amdgpu_bo_export(bo->bo.amdgpu, amdgpu_bo_handle_type_dma_buf_fd,
248d6c0b56eSmrg			     &fd) != 0)
249d6c0b56eSmrg		return -1;
250d6c0b56eSmrg
251d6c0b56eSmrg	*stride = pixmap->devKind;
252d6c0b56eSmrg	*size = bo_info.alloc_size;
253d6c0b56eSmrg	return fd;
254d6c0b56eSmrg}
255d6c0b56eSmrg
256d6c0b56eSmrgstatic dri3_screen_info_rec amdgpu_dri3_screen_info = {
257d6c0b56eSmrg#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
258d6c0b56eSmrg	.version = 1,
259d6c0b56eSmrg	.open_client = amdgpu_dri3_open_client,
260d6c0b56eSmrg#else
261d6c0b56eSmrg	.version = 0,
262d6c0b56eSmrg	.open = amdgpu_dri3_open,
263d6c0b56eSmrg#endif
264d6c0b56eSmrg	.pixmap_from_fd = amdgpu_dri3_pixmap_from_fd,
265d6c0b56eSmrg	.fd_from_pixmap = amdgpu_dri3_fd_from_pixmap
266d6c0b56eSmrg};
267d6c0b56eSmrg
268d6c0b56eSmrgBool
269d6c0b56eSmrgamdgpu_dri3_screen_init(ScreenPtr screen)
270d6c0b56eSmrg{
271d6c0b56eSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
27211bf0794Smrg	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
27311bf0794Smrg
27411bf0794Smrg	pAMDGPUEnt->render_node = drmGetRenderDeviceNameFromFd(pAMDGPUEnt->fd);
275d6c0b56eSmrg
276d6c0b56eSmrg	if (!dri3_screen_init(screen, &amdgpu_dri3_screen_info)) {
277d6c0b56eSmrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
278d6c0b56eSmrg			   "dri3_screen_init failed\n");
279d6c0b56eSmrg		return FALSE;
280d6c0b56eSmrg	}
281d6c0b56eSmrg
282d6c0b56eSmrg	return TRUE;
283d6c0b56eSmrg}
284d6c0b56eSmrg
285d6c0b56eSmrg#else /* !HAVE_DRI3_H */
286d6c0b56eSmrg
287d6c0b56eSmrgBool
288d6c0b56eSmrgamdgpu_dri3_screen_init(ScreenPtr screen)
289d6c0b56eSmrg{
290d6c0b56eSmrg	xf86DrvMsg(xf86ScreenToScrn(screen)->scrnIndex, X_INFO,
291d6c0b56eSmrg		   "Can't initialize DRI3 because dri3.h not available at "
292d6c0b56eSmrg		   "build time\n");
293d6c0b56eSmrg
294d6c0b56eSmrg	return FALSE;
295d6c0b56eSmrg}
296d6c0b56eSmrg
297d6c0b56eSmrg#endif
298