101e04c3fSmrg/**************************************************************************
201e04c3fSmrg *
301e04c3fSmrg * Copyright 2015, 2018 Collabora
401e04c3fSmrg * All Rights Reserved.
501e04c3fSmrg *
601e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
701e04c3fSmrg * copy of this software and associated documentation files (the
801e04c3fSmrg * "Software"), to deal in the Software without restriction, including
901e04c3fSmrg * without limitation the rights to use, copy, modify, merge, publish,
1001e04c3fSmrg * distribute, sub license, and/or sell copies of the Software, and to
1101e04c3fSmrg * permit persons to whom the Software is furnished to do so, subject to
1201e04c3fSmrg * the following conditions:
1301e04c3fSmrg *
1401e04c3fSmrg * The above copyright notice and this permission notice (including the
1501e04c3fSmrg * next paragraph) shall be included in all copies or substantial portions
1601e04c3fSmrg * of the Software.
1701e04c3fSmrg *
1801e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1901e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2001e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2101e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2201e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2301e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2401e04c3fSmrg * DEALINGS IN THE SOFTWARE.
2501e04c3fSmrg *
2601e04c3fSmrg **************************************************************************/
2701e04c3fSmrg
2801e04c3fSmrg#ifdef HAVE_LIBDRM
2901e04c3fSmrg#include <xf86drm.h>
3001e04c3fSmrg#endif
317ec681f3Smrg#include "util/compiler.h"
3201e04c3fSmrg#include "util/macros.h"
3301e04c3fSmrg
3401e04c3fSmrg#include "eglcurrent.h"
3501e04c3fSmrg#include "egldevice.h"
3601e04c3fSmrg#include "egllog.h"
3701e04c3fSmrg#include "eglglobals.h"
3801e04c3fSmrg#include "egltypedefs.h"
3901e04c3fSmrg
4001e04c3fSmrg
4101e04c3fSmrgstruct _egl_device {
4201e04c3fSmrg   _EGLDevice *Next;
4301e04c3fSmrg
4401e04c3fSmrg   const char *extensions;
4501e04c3fSmrg
4601e04c3fSmrg   EGLBoolean MESA_device_software;
4701e04c3fSmrg   EGLBoolean EXT_device_drm;
487ec681f3Smrg   EGLBoolean EXT_device_drm_render_node;
4901e04c3fSmrg
5001e04c3fSmrg#ifdef HAVE_LIBDRM
5101e04c3fSmrg   drmDevicePtr device;
5201e04c3fSmrg#endif
5301e04c3fSmrg};
5401e04c3fSmrg
5501e04c3fSmrgvoid
5601e04c3fSmrg_eglFiniDevice(void)
5701e04c3fSmrg{
5801e04c3fSmrg   _EGLDevice *dev_list, *dev;
5901e04c3fSmrg
6001e04c3fSmrg   /* atexit function is called with global mutex locked */
6101e04c3fSmrg
6201e04c3fSmrg   dev_list = _eglGlobal.DeviceList;
6301e04c3fSmrg
6401e04c3fSmrg   /* The first device is static allocated SW device */
6501e04c3fSmrg   assert(dev_list);
6601e04c3fSmrg   assert(_eglDeviceSupports(dev_list, _EGL_DEVICE_SOFTWARE));
6701e04c3fSmrg   dev_list = dev_list->Next;
6801e04c3fSmrg
6901e04c3fSmrg   while (dev_list) {
7001e04c3fSmrg      /* pop list head */
7101e04c3fSmrg      dev = dev_list;
7201e04c3fSmrg      dev_list = dev_list->Next;
7301e04c3fSmrg
7401e04c3fSmrg#ifdef HAVE_LIBDRM
7501e04c3fSmrg      assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
7601e04c3fSmrg      drmFreeDevice(&dev->device);
7701e04c3fSmrg#endif
7801e04c3fSmrg      free(dev);
7901e04c3fSmrg   }
8001e04c3fSmrg
8101e04c3fSmrg   _eglGlobal.DeviceList = NULL;
8201e04c3fSmrg}
8301e04c3fSmrg
8401e04c3fSmrgEGLBoolean
8501e04c3fSmrg_eglCheckDeviceHandle(EGLDeviceEXT device)
8601e04c3fSmrg{
8701e04c3fSmrg   _EGLDevice *cur;
8801e04c3fSmrg
8901e04c3fSmrg   mtx_lock(_eglGlobal.Mutex);
9001e04c3fSmrg   cur = _eglGlobal.DeviceList;
9101e04c3fSmrg   while (cur) {
9201e04c3fSmrg      if (cur == (_EGLDevice *) device)
9301e04c3fSmrg         break;
9401e04c3fSmrg      cur = cur->Next;
9501e04c3fSmrg   }
9601e04c3fSmrg   mtx_unlock(_eglGlobal.Mutex);
9701e04c3fSmrg   return (cur != NULL);
9801e04c3fSmrg}
9901e04c3fSmrg
10001e04c3fSmrg_EGLDevice _eglSoftwareDevice = {
1017ec681f3Smrg   /* TODO: EGL_EXT_device_drm support for KMS + llvmpipe */
1027ec681f3Smrg   .extensions = "EGL_MESA_device_software EGL_EXT_device_drm_render_node",
10301e04c3fSmrg   .MESA_device_software = EGL_TRUE,
1047ec681f3Smrg   .EXT_device_drm_render_node = EGL_TRUE,
10501e04c3fSmrg};
10601e04c3fSmrg
10701e04c3fSmrg#ifdef HAVE_LIBDRM
10801e04c3fSmrg/*
10901e04c3fSmrg * Negative value on error, zero if newly added, one if already in list.
11001e04c3fSmrg */
11101e04c3fSmrgstatic int
11201e04c3fSmrg_eglAddDRMDevice(drmDevicePtr device, _EGLDevice **out_dev)
11301e04c3fSmrg{
11401e04c3fSmrg   _EGLDevice *dev;
11501e04c3fSmrg
1167ec681f3Smrg   if ((device->available_nodes & (1 << DRM_NODE_PRIMARY |
1177ec681f3Smrg                                   1 << DRM_NODE_RENDER)) == 0)
11801e04c3fSmrg      return -1;
11901e04c3fSmrg
12001e04c3fSmrg   dev = _eglGlobal.DeviceList;
12101e04c3fSmrg
12201e04c3fSmrg   /* The first device is always software */
12301e04c3fSmrg   assert(dev);
12401e04c3fSmrg   assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
12501e04c3fSmrg
12601e04c3fSmrg   while (dev->Next) {
12701e04c3fSmrg      dev = dev->Next;
12801e04c3fSmrg
12901e04c3fSmrg      assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
13001e04c3fSmrg      if (drmDevicesEqual(device, dev->device) != 0) {
13101e04c3fSmrg         if (out_dev)
13201e04c3fSmrg            *out_dev = dev;
13301e04c3fSmrg         return 1;
13401e04c3fSmrg      }
13501e04c3fSmrg   }
13601e04c3fSmrg
13701e04c3fSmrg   dev->Next = calloc(1, sizeof(_EGLDevice));
13801e04c3fSmrg   if (!dev->Next) {
13901e04c3fSmrg      if (out_dev)
14001e04c3fSmrg         *out_dev = NULL;
14101e04c3fSmrg      return -1;
14201e04c3fSmrg   }
14301e04c3fSmrg
14401e04c3fSmrg   dev = dev->Next;
14501e04c3fSmrg   dev->extensions = "EGL_EXT_device_drm";
14601e04c3fSmrg   dev->EXT_device_drm = EGL_TRUE;
14701e04c3fSmrg   dev->device = device;
14801e04c3fSmrg
1497ec681f3Smrg   /* TODO: EGL_EXT_device_drm_render_node support for kmsro + renderonly */
1507ec681f3Smrg   if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
1517ec681f3Smrg      dev->extensions = "EGL_EXT_device_drm EGL_EXT_device_drm_render_node";
1527ec681f3Smrg      dev->EXT_device_drm_render_node = EGL_TRUE;
1537ec681f3Smrg   }
1547ec681f3Smrg
15501e04c3fSmrg   if (out_dev)
15601e04c3fSmrg      *out_dev = dev;
15701e04c3fSmrg
15801e04c3fSmrg   return 0;
15901e04c3fSmrg}
16001e04c3fSmrg#endif
16101e04c3fSmrg
16201e04c3fSmrg/* Adds a device in DeviceList, if needed for the given fd.
16301e04c3fSmrg *
16401e04c3fSmrg * If a software device, the fd is ignored.
16501e04c3fSmrg */
16601e04c3fSmrg_EGLDevice *
16701e04c3fSmrg_eglAddDevice(int fd, bool software)
16801e04c3fSmrg{
16901e04c3fSmrg   _EGLDevice *dev;
17001e04c3fSmrg
17101e04c3fSmrg   mtx_lock(_eglGlobal.Mutex);
17201e04c3fSmrg   dev = _eglGlobal.DeviceList;
17301e04c3fSmrg
17401e04c3fSmrg   /* The first device is always software */
17501e04c3fSmrg   assert(dev);
17601e04c3fSmrg   assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
17701e04c3fSmrg   if (software)
17801e04c3fSmrg      goto out;
17901e04c3fSmrg
18001e04c3fSmrg#ifdef HAVE_LIBDRM
18101e04c3fSmrg   drmDevicePtr device;
18201e04c3fSmrg
18301e04c3fSmrg   if (drmGetDevice2(fd, 0, &device) != 0) {
18401e04c3fSmrg      dev = NULL;
18501e04c3fSmrg      goto out;
18601e04c3fSmrg   }
18701e04c3fSmrg
18801e04c3fSmrg   /* Device is not added - error or already present */
18901e04c3fSmrg   if (_eglAddDRMDevice(device, &dev) != 0)
19001e04c3fSmrg      drmFreeDevice(&device);
19101e04c3fSmrg#else
19201e04c3fSmrg   _eglLog(_EGL_FATAL, "Driver bug: Built without libdrm, yet looking for HW device");
19301e04c3fSmrg   dev = NULL;
19401e04c3fSmrg#endif
19501e04c3fSmrg
19601e04c3fSmrgout:
19701e04c3fSmrg   mtx_unlock(_eglGlobal.Mutex);
19801e04c3fSmrg   return dev;
19901e04c3fSmrg}
20001e04c3fSmrg
20101e04c3fSmrgEGLBoolean
20201e04c3fSmrg_eglDeviceSupports(_EGLDevice *dev, _EGLDeviceExtension ext)
20301e04c3fSmrg{
20401e04c3fSmrg   switch (ext) {
20501e04c3fSmrg   case _EGL_DEVICE_SOFTWARE:
20601e04c3fSmrg      return dev->MESA_device_software;
20701e04c3fSmrg   case _EGL_DEVICE_DRM:
20801e04c3fSmrg      return dev->EXT_device_drm;
2097ec681f3Smrg   case _EGL_DEVICE_DRM_RENDER_NODE:
2107ec681f3Smrg      return dev->EXT_device_drm_render_node;
21101e04c3fSmrg   default:
21201e04c3fSmrg      assert(0);
21301e04c3fSmrg      return EGL_FALSE;
21401e04c3fSmrg   };
21501e04c3fSmrg}
21601e04c3fSmrg
2177ec681f3Smrg/* Ideally we'll have an extension which passes the render node,
2187ec681f3Smrg * instead of the card one + magic.
2197ec681f3Smrg *
2207ec681f3Smrg * Then we can move this in _eglQueryDeviceStringEXT below. Until then
2217ec681f3Smrg * keep it separate.
2227ec681f3Smrg */
2237ec681f3Smrgconst char *
2247ec681f3Smrg_eglGetDRMDeviceRenderNode(_EGLDevice *dev)
2257ec681f3Smrg{
2267ec681f3Smrg#ifdef HAVE_LIBDRM
2277ec681f3Smrg   return dev->device->nodes[DRM_NODE_RENDER];
2287ec681f3Smrg#else
2297ec681f3Smrg   return NULL;
2307ec681f3Smrg#endif
2317ec681f3Smrg}
2327ec681f3Smrg
23301e04c3fSmrgEGLBoolean
23401e04c3fSmrg_eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute,
23501e04c3fSmrg                         EGLAttrib *value)
23601e04c3fSmrg{
23701e04c3fSmrg   switch (attribute) {
23801e04c3fSmrg   default:
2397ec681f3Smrg      _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceAttribEXT");
24001e04c3fSmrg      return EGL_FALSE;
24101e04c3fSmrg   }
24201e04c3fSmrg}
24301e04c3fSmrg
24401e04c3fSmrgconst char *
24501e04c3fSmrg_eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name)
24601e04c3fSmrg{
24701e04c3fSmrg   switch (name) {
24801e04c3fSmrg   case EGL_EXTENSIONS:
24901e04c3fSmrg      return dev->extensions;
25001e04c3fSmrg   case EGL_DRM_DEVICE_FILE_EXT:
2517ec681f3Smrg      if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM))
2527ec681f3Smrg         break;
2537ec681f3Smrg#ifdef HAVE_LIBDRM
2547ec681f3Smrg      return dev->device->nodes[DRM_NODE_PRIMARY];
2557ec681f3Smrg#else
2567ec681f3Smrg      /* This should never happen: we don't yet support EGL_DEVICE_DRM for the
2577ec681f3Smrg       * software device, and physical devices are only exposed when libdrm is
2587ec681f3Smrg       * available. */
2597ec681f3Smrg      assert(0);
2607ec681f3Smrg      break;
26101e04c3fSmrg#endif
2627ec681f3Smrg   case EGL_DRM_RENDER_NODE_FILE_EXT:
2637ec681f3Smrg      if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM_RENDER_NODE))
2647ec681f3Smrg         break;
2657ec681f3Smrg#ifdef HAVE_LIBDRM
2667ec681f3Smrg      return dev->device ? dev->device->nodes[DRM_NODE_RENDER] : NULL;
2677ec681f3Smrg#else
2687ec681f3Smrg      /* Physical devices are only exposed when libdrm is available. */
2697ec681f3Smrg      assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
27001e04c3fSmrg      return NULL;
2717ec681f3Smrg#endif
2727ec681f3Smrg   }
2737ec681f3Smrg   _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT");
2747ec681f3Smrg   return NULL;
27501e04c3fSmrg}
27601e04c3fSmrg
27701e04c3fSmrg/* Do a fresh lookup for devices.
27801e04c3fSmrg *
27901e04c3fSmrg * Walks through the DeviceList, discarding no longer available ones
28001e04c3fSmrg * and adding new ones as applicable.
28101e04c3fSmrg *
28201e04c3fSmrg * Must be called with the global lock held.
28301e04c3fSmrg */
28401e04c3fSmrgstatic int
28501e04c3fSmrg_eglRefreshDeviceList(void)
28601e04c3fSmrg{
2877ec681f3Smrg   ASSERTED _EGLDevice *dev;
28801e04c3fSmrg   int count = 0;
28901e04c3fSmrg
29001e04c3fSmrg   dev = _eglGlobal.DeviceList;
29101e04c3fSmrg
29201e04c3fSmrg   /* The first device is always software */
29301e04c3fSmrg   assert(dev);
29401e04c3fSmrg   assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
29501e04c3fSmrg   count++;
29601e04c3fSmrg
29701e04c3fSmrg#ifdef HAVE_LIBDRM
29801e04c3fSmrg   drmDevicePtr devices[64];
29901e04c3fSmrg   int num_devs, ret;
30001e04c3fSmrg
30101e04c3fSmrg   num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
30201e04c3fSmrg   for (int i = 0; i < num_devs; i++) {
3037ec681f3Smrg      if (!(devices[i]->available_nodes & (1 << DRM_NODE_RENDER)))
3047ec681f3Smrg         continue;
3057ec681f3Smrg
30601e04c3fSmrg      ret = _eglAddDRMDevice(devices[i], NULL);
30701e04c3fSmrg
30801e04c3fSmrg      /* Device is not added - error or already present */
30901e04c3fSmrg      if (ret != 0)
31001e04c3fSmrg         drmFreeDevice(&devices[i]);
31101e04c3fSmrg
31201e04c3fSmrg      if (ret >= 0)
31301e04c3fSmrg         count++;
31401e04c3fSmrg   }
31501e04c3fSmrg#endif
31601e04c3fSmrg
31701e04c3fSmrg   return count;
31801e04c3fSmrg}
31901e04c3fSmrg
32001e04c3fSmrgEGLBoolean
32101e04c3fSmrg_eglQueryDevicesEXT(EGLint max_devices,
32201e04c3fSmrg                    _EGLDevice **devices,
32301e04c3fSmrg                    EGLint *num_devices)
32401e04c3fSmrg{
32501e04c3fSmrg   _EGLDevice *dev, *devs;
32601e04c3fSmrg   int i = 0, num_devs;
32701e04c3fSmrg
32801e04c3fSmrg   if ((devices && max_devices <= 0) || !num_devices)
32901e04c3fSmrg      return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT");
33001e04c3fSmrg
33101e04c3fSmrg   mtx_lock(_eglGlobal.Mutex);
33201e04c3fSmrg
33301e04c3fSmrg   num_devs = _eglRefreshDeviceList();
33401e04c3fSmrg   devs = _eglGlobal.DeviceList;
33501e04c3fSmrg
33601e04c3fSmrg   /* bail early if we only care about the count */
33701e04c3fSmrg   if (!devices) {
33801e04c3fSmrg      *num_devices = num_devs;
33901e04c3fSmrg      goto out;
34001e04c3fSmrg   }
34101e04c3fSmrg
3427ec681f3Smrg   /* Push the first device (the software one) to the end of the list.
3437ec681f3Smrg    * Sending it to the user only if they've requested the full list.
3447ec681f3Smrg    *
3457ec681f3Smrg    * By default, the user is likely to pick the first device so having the
3467ec681f3Smrg    * software (aka least performant) one is not a good idea.
3477ec681f3Smrg    */
34801e04c3fSmrg   *num_devices = MIN2(num_devs, max_devices);
34901e04c3fSmrg
3507ec681f3Smrg   for (i = 0, dev = devs->Next; dev && i < max_devices; i++) {
35101e04c3fSmrg      devices[i] = dev;
35201e04c3fSmrg      dev = dev->Next;
35301e04c3fSmrg   }
35401e04c3fSmrg
3557ec681f3Smrg   /* User requested the full device list, add the sofware device. */
3567ec681f3Smrg   if (max_devices >= num_devs) {
3577ec681f3Smrg      assert(_eglDeviceSupports(devs, _EGL_DEVICE_SOFTWARE));
3587ec681f3Smrg      devices[num_devs - 1] = devs;
3597ec681f3Smrg   }
3607ec681f3Smrg
36101e04c3fSmrgout:
36201e04c3fSmrg   mtx_unlock(_eglGlobal.Mutex);
36301e04c3fSmrg
36401e04c3fSmrg   return EGL_TRUE;
36501e04c3fSmrg}
366