10d16fef4Smrg/*
20d16fef4Smrg * Copyright © 2013-2014 Intel Corporation
30d16fef4Smrg * Copyright © 2015 Advanced Micro Devices, Inc.
40d16fef4Smrg *
50d16fef4Smrg * Permission to use, copy, modify, distribute, and sell this software and its
60d16fef4Smrg * documentation for any purpose is hereby granted without fee, provided that
70d16fef4Smrg * the above copyright notice appear in all copies and that both that copyright
80d16fef4Smrg * notice and this permission notice appear in supporting documentation, and
90d16fef4Smrg * that the name of the copyright holders not be used in advertising or
100d16fef4Smrg * publicity pertaining to distribution of the software without specific,
110d16fef4Smrg * written prior permission.  The copyright holders make no representations
120d16fef4Smrg * about the suitability of this software for any purpose.  It is provided "as
130d16fef4Smrg * is" without express or implied warranty.
140d16fef4Smrg *
150d16fef4Smrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
160d16fef4Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
170d16fef4Smrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
180d16fef4Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
190d16fef4Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
200d16fef4Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
210d16fef4Smrg * OF THIS SOFTWARE.
220d16fef4Smrg */
230d16fef4Smrg
240d16fef4Smrg
250d16fef4Smrg#ifdef HAVE_CONFIG_H
260d16fef4Smrg#include "config.h"
270d16fef4Smrg#endif
280d16fef4Smrg
290d16fef4Smrg#include "radeon.h"
300d16fef4Smrg
310d16fef4Smrg#ifdef HAVE_DRI3_H
320d16fef4Smrg
330d16fef4Smrg#include "radeon_bo_gem.h"
340d16fef4Smrg#include "radeon_glamor.h"
350d16fef4Smrg#include "dri3.h"
360d16fef4Smrg
370d16fef4Smrg#include <sys/types.h>
380d16fef4Smrg#include <sys/stat.h>
390d16fef4Smrg#include <fcntl.h>
40446f62d6Smrg#include <gbm.h>
410d16fef4Smrg#include <errno.h>
420d16fef4Smrg#include <libgen.h>
430d16fef4Smrg
443ed65abbSmrgstatic int open_master_node(ScreenPtr screen, int *out)
450d16fef4Smrg{
460d16fef4Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
478bf5c682Smrg	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
480d16fef4Smrg	RADEONInfoPtr info = RADEONPTR(scrn);
490d16fef4Smrg	drm_magic_t magic;
500d16fef4Smrg	int fd;
510d16fef4Smrg
520d16fef4Smrg	fd = open(info->dri2.device_name, O_RDWR | O_CLOEXEC);
530d16fef4Smrg	if (fd < 0)
540d16fef4Smrg		return BadAlloc;
550d16fef4Smrg
560d16fef4Smrg	/* Before FD passing in the X protocol with DRI3 (and increased
570d16fef4Smrg	 * security of rendering with per-process address spaces on the
580d16fef4Smrg	 * GPU), the kernel had to come up with a way to have the server
590d16fef4Smrg	 * decide which clients got to access the GPU, which was done by
600d16fef4Smrg	 * each client getting a unique (magic) number from the kernel,
610d16fef4Smrg	 * passing it to the server, and the server then telling the
620d16fef4Smrg	 * kernel which clients were authenticated for using the device.
630d16fef4Smrg	 *
640d16fef4Smrg	 * Now that we have FD passing, the server can just set up the
650d16fef4Smrg	 * authentication on its own and hand the prepared FD off to the
660d16fef4Smrg	 * client.
670d16fef4Smrg	 */
680d16fef4Smrg	if (drmGetMagic(fd, &magic) < 0) {
690d16fef4Smrg		if (errno == EACCES) {
700d16fef4Smrg			/* Assume that we're on a render node, and the fd is
710d16fef4Smrg			 * already as authenticated as it should be.
720d16fef4Smrg			 */
730d16fef4Smrg			*out = fd;
740d16fef4Smrg			return Success;
750d16fef4Smrg		} else {
760d16fef4Smrg			close(fd);
770d16fef4Smrg			return BadMatch;
780d16fef4Smrg		}
790d16fef4Smrg	}
800d16fef4Smrg
818bf5c682Smrg	if (drmAuthMagic(pRADEONEnt->fd, magic) < 0) {
820d16fef4Smrg		close(fd);
830d16fef4Smrg		return BadMatch;
840d16fef4Smrg	}
850d16fef4Smrg
860d16fef4Smrg	*out = fd;
870d16fef4Smrg	return Success;
880d16fef4Smrg}
890d16fef4Smrg
903ed65abbSmrgstatic int open_render_node(ScreenPtr screen, int *out)
913ed65abbSmrg{
923ed65abbSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
933ed65abbSmrg	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
943ed65abbSmrg	int fd;
953ed65abbSmrg
963ed65abbSmrg	fd = open(pRADEONEnt->render_node, O_RDWR | O_CLOEXEC);
973ed65abbSmrg	if (fd < 0)
983ed65abbSmrg		return BadAlloc;
993ed65abbSmrg
1003ed65abbSmrg	*out = fd;
1013ed65abbSmrg	return Success;
1023ed65abbSmrg}
1033ed65abbSmrg
1043ed65abbSmrgstatic int
1053ed65abbSmrgradeon_dri3_open(ScreenPtr screen, RRProviderPtr provider, int *out)
1063ed65abbSmrg{
1073ed65abbSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
1083ed65abbSmrg	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
1093ed65abbSmrg	int ret = BadAlloc;
1103ed65abbSmrg
1113ed65abbSmrg	if (pRADEONEnt->render_node)
1123ed65abbSmrg		ret = open_render_node(screen, out);
1133ed65abbSmrg
1143ed65abbSmrg	if (ret != Success)
1153ed65abbSmrg		ret = open_master_node(screen, out);
1163ed65abbSmrg
1173ed65abbSmrg	return ret;
1183ed65abbSmrg}
1193ed65abbSmrg
1200d16fef4Smrg#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
1210d16fef4Smrg
1220d16fef4Smrgstatic int
1230d16fef4Smrgradeon_dri3_open_client(ClientPtr client, ScreenPtr screen,
1240d16fef4Smrg			RRProviderPtr provider, int *out)
1250d16fef4Smrg{
1260d16fef4Smrg	const char *cmdname = GetClientCmdName(client);
1270d16fef4Smrg	Bool is_ssh = FALSE;
1280d16fef4Smrg
1290d16fef4Smrg	/* If the executable name is "ssh", assume that this client connection
1300d16fef4Smrg	 * is forwarded from another host via SSH
1310d16fef4Smrg	 */
1320d16fef4Smrg	if (cmdname) {
1330d16fef4Smrg		char *cmd = strdup(cmdname);
1340d16fef4Smrg
1350d16fef4Smrg		/* Cut off any colon and whatever comes after it, see
1360d16fef4Smrg		 * https://lists.freedesktop.org/archives/xorg-devel/2015-December/048164.html
1370d16fef4Smrg		 */
1380d16fef4Smrg		cmd = strtok(cmd, ":");
1390d16fef4Smrg
1400d16fef4Smrg		is_ssh = strcmp(basename(cmd), "ssh") == 0;
1410d16fef4Smrg		free(cmd);
1420d16fef4Smrg	}
1430d16fef4Smrg
1440d16fef4Smrg	if (!is_ssh)
1450d16fef4Smrg		return radeon_dri3_open(screen, provider, out);
1460d16fef4Smrg
1470d16fef4Smrg	return BadAccess;
1480d16fef4Smrg}
1490d16fef4Smrg
1500d16fef4Smrg#endif /* DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0) */
1510d16fef4Smrg
1520d16fef4Smrgstatic PixmapPtr radeon_dri3_pixmap_from_fd(ScreenPtr screen,
1530d16fef4Smrg					    int fd,
1540d16fef4Smrg					    CARD16 width,
1550d16fef4Smrg					    CARD16 height,
1560d16fef4Smrg					    CARD16 stride,
1570d16fef4Smrg					    CARD8 depth,
1580d16fef4Smrg					    CARD8 bpp)
1590d16fef4Smrg{
1600d16fef4Smrg	PixmapPtr pixmap;
1610d16fef4Smrg
1620d16fef4Smrg#ifdef USE_GLAMOR
1630d16fef4Smrg	/* Avoid generating a GEM flink name if possible */
1640d16fef4Smrg	if (RADEONPTR(xf86ScreenToScrn(screen))->use_glamor) {
1650d16fef4Smrg		pixmap = glamor_pixmap_from_fd(screen, fd, width, height,
1660d16fef4Smrg					       stride, depth, bpp);
1670d16fef4Smrg		if (pixmap) {
1680d16fef4Smrg			struct radeon_pixmap *priv =
1690d16fef4Smrg				calloc(1, sizeof(struct radeon_pixmap));
1700d16fef4Smrg
1710d16fef4Smrg			if (priv) {
1720d16fef4Smrg				radeon_set_pixmap_private(pixmap, priv);
17339413783Smrg				pixmap->usage_hint |= RADEON_CREATE_PIXMAP_DRI2;
1740d16fef4Smrg				return pixmap;
1750d16fef4Smrg			}
1760d16fef4Smrg
1770d16fef4Smrg			screen->DestroyPixmap(pixmap);
1787314432eSmrg			return NULL;
1790d16fef4Smrg		}
1800d16fef4Smrg	}
1810d16fef4Smrg#endif
1820d16fef4Smrg
1830d16fef4Smrg	if (depth < 8)
1840d16fef4Smrg		return NULL;
1850d16fef4Smrg
1860d16fef4Smrg	switch (bpp) {
1870d16fef4Smrg	case 8:
1880d16fef4Smrg	case 16:
1890d16fef4Smrg	case 32:
1900d16fef4Smrg		break;
1910d16fef4Smrg	default:
1920d16fef4Smrg		return NULL;
1930d16fef4Smrg	}
1940d16fef4Smrg
1950d16fef4Smrg	pixmap = screen->CreatePixmap(screen, 0, 0, depth, RADEON_CREATE_PIXMAP_DRI2);
1960d16fef4Smrg	if (!pixmap)
1970d16fef4Smrg		return NULL;
1980d16fef4Smrg
1990d16fef4Smrg	if (!screen->ModifyPixmapHeader(pixmap, width, height, 0, bpp, stride,
2000d16fef4Smrg					NULL))
2010d16fef4Smrg		goto free_pixmap;
2020d16fef4Smrg
2030d16fef4Smrg	if (screen->SetSharedPixmapBacking(pixmap, (void*)(intptr_t)fd))
2040d16fef4Smrg		return pixmap;
2050d16fef4Smrg
2060d16fef4Smrgfree_pixmap:
2070d16fef4Smrg	fbDestroyPixmap(pixmap);
2080d16fef4Smrg	return NULL;
2090d16fef4Smrg}
2100d16fef4Smrg
2110d16fef4Smrgstatic int radeon_dri3_fd_from_pixmap(ScreenPtr screen,
2120d16fef4Smrg				      PixmapPtr pixmap,
2130d16fef4Smrg				      CARD16 *stride,
2140d16fef4Smrg				      CARD32 *size)
2150d16fef4Smrg{
216446f62d6Smrg	struct radeon_buffer *bo;
2170d16fef4Smrg	int fd;
2180d16fef4Smrg#ifdef USE_GLAMOR
21939413783Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
22039413783Smrg	RADEONInfoPtr info = RADEONPTR(scrn);
2210d16fef4Smrg
222446f62d6Smrg	if (info->use_glamor) {
2230d2a5547Smrg		int ret = glamor_fd_from_pixmap(screen, pixmap, stride, size);
224446f62d6Smrg
2250d2a5547Smrg		/* Any pending drawing operations need to be flushed to the
2260d2a5547Smrg		 * kernel driver before the client starts using the pixmap
2270d2a5547Smrg		 * storage for direct rendering.
228446f62d6Smrg		 */
2290d2a5547Smrg		if (ret >= 0)
230446f62d6Smrg			radeon_cs_flush_indirect(scrn);
231446f62d6Smrg
232446f62d6Smrg		return ret;
233446f62d6Smrg	}
2340d16fef4Smrg#endif
2350d16fef4Smrg
236446f62d6Smrg	bo = radeon_get_pixmap_bo(pixmap);
23739413783Smrg	if (!bo) {
2380d16fef4Smrg		exaMoveInPixmap(pixmap);
239446f62d6Smrg		bo = radeon_get_pixmap_bo(pixmap);
2400d16fef4Smrg		if (!bo)
2410d16fef4Smrg			return -1;
2420d16fef4Smrg	}
2430d16fef4Smrg
2440d16fef4Smrg	if (pixmap->devKind > UINT16_MAX)
2450d16fef4Smrg		return -1;
2460d16fef4Smrg
247446f62d6Smrg	if (radeon_gem_prime_share_bo(bo->bo.radeon, &fd) < 0)
2480d16fef4Smrg		return -1;
2490d16fef4Smrg
2500d16fef4Smrg	*stride = pixmap->devKind;
251446f62d6Smrg	*size = bo->bo.radeon->size;
2520d16fef4Smrg	return fd;
2530d16fef4Smrg}
2540d16fef4Smrg
2550d16fef4Smrgstatic dri3_screen_info_rec radeon_dri3_screen_info = {
2560d16fef4Smrg#if DRI3_SCREEN_INFO_VERSION >= 1 && XORG_VERSION_CURRENT <= XORG_VERSION_NUMERIC(1,18,99,1,0)
2570d16fef4Smrg	.version = 1,
2580d16fef4Smrg	.open_client = radeon_dri3_open_client,
2590d16fef4Smrg#else
2600d16fef4Smrg	.version = 0,
2610d16fef4Smrg	.open = radeon_dri3_open,
2620d16fef4Smrg#endif
2630d16fef4Smrg	.pixmap_from_fd = radeon_dri3_pixmap_from_fd,
2640d16fef4Smrg	.fd_from_pixmap = radeon_dri3_fd_from_pixmap
2650d16fef4Smrg};
2660d16fef4Smrg
2670d16fef4SmrgBool
2680d16fef4Smrgradeon_dri3_screen_init(ScreenPtr screen)
2690d16fef4Smrg{
2700d16fef4Smrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
2713ed65abbSmrg	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
2723ed65abbSmrg
2733ed65abbSmrg	pRADEONEnt->render_node = drmGetRenderDeviceNameFromFd(pRADEONEnt->fd);
2740d16fef4Smrg
2750d16fef4Smrg	if (!dri3_screen_init(screen, &radeon_dri3_screen_info)) {
2760d16fef4Smrg		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
2770d16fef4Smrg			   "dri3_screen_init failed\n");
2780d16fef4Smrg		return FALSE;
2790d16fef4Smrg	}
2800d16fef4Smrg
2810d16fef4Smrg	return TRUE;
2820d16fef4Smrg}
2830d16fef4Smrg
2840d16fef4Smrg#else /* !HAVE_DRI3_H */
2850d16fef4Smrg
2860d16fef4SmrgBool
2870d16fef4Smrgradeon_dri3_screen_init(ScreenPtr screen)
2880d16fef4Smrg{
2890d16fef4Smrg	xf86DrvMsg(xf86ScreenToScrn(screen)->scrnIndex, X_INFO,
2900d16fef4Smrg		   "Can't initialize DRI3 because dri3.h not available at "
2910d16fef4Smrg		   "build time\n");
2920d16fef4Smrg
2930d16fef4Smrg	return FALSE;
2940d16fef4Smrg}
2950d16fef4Smrg
2960d16fef4Smrg#endif
297