17ec681f3Smrg/*
27ec681f3Smrg * Copyright © 2020 Collabora, Ltd.
37ec681f3Smrg * Author: Antonio Caggiano <antonio.caggiano@collabora.com>
47ec681f3Smrg * Author: Rohan Garg <rohan.garg@collabora.com>
57ec681f3Smrg * Author: Robert Beckett <bob.beckett@collabora.com>
67ec681f3Smrg *
77ec681f3Smrg * SPDX-License-Identifier: MIT
87ec681f3Smrg */
97ec681f3Smrg
107ec681f3Smrg#include "pps_device.h"
117ec681f3Smrg
127ec681f3Smrg#include <cassert>
137ec681f3Smrg#include <fcntl.h>
147ec681f3Smrg#include <memory>
157ec681f3Smrg#include <unistd.h>
167ec681f3Smrg#include <xf86drm.h>
177ec681f3Smrg
187ec681f3Smrgnamespace pps
197ec681f3Smrg{
207ec681f3Smrg#define MAX_DRM_DEVICES 64
217ec681f3Smrg
227ec681f3Smrguint32_t DrmDevice::device_count()
237ec681f3Smrg{
247ec681f3Smrg   drmDevicePtr devices[MAX_DRM_DEVICES] = {};
257ec681f3Smrg   int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
267ec681f3Smrg   drmFreeDevices(devices, num_devices);
277ec681f3Smrg   return static_cast<uint32_t>(num_devices);
287ec681f3Smrg}
297ec681f3Smrg
307ec681f3Smrg/// @return The name of a DRM device, empty string in case of error
317ec681f3Smrgstd::string query_drm_name(const int fd)
327ec681f3Smrg{
337ec681f3Smrg   assert(fd && "Failed to query DrmDevice: invalid fd");
347ec681f3Smrg
357ec681f3Smrg   std::string name = "";
367ec681f3Smrg
377ec681f3Smrg   if (drmVersionPtr version = drmGetVersion(fd)) {
387ec681f3Smrg      name = std::string(version->name, version->name_len);
397ec681f3Smrg      drmFreeVersion(version);
407ec681f3Smrg   }
417ec681f3Smrg
427ec681f3Smrg   return name;
437ec681f3Smrg}
447ec681f3Smrg
457ec681f3Smrg/// @return A DRM device, nullopt in case of error
467ec681f3Smrgstd::optional<DrmDevice> create_drm_device(int fd, int32_t gpu_num)
477ec681f3Smrg{
487ec681f3Smrg   if (fd < 0 || gpu_num < 0) {
497ec681f3Smrg      return std::nullopt;
507ec681f3Smrg   }
517ec681f3Smrg
527ec681f3Smrg   // Try getting the name
537ec681f3Smrg   std::string name = query_drm_name(fd);
547ec681f3Smrg   if (name.empty()) {
557ec681f3Smrg      return std::nullopt;
567ec681f3Smrg   }
577ec681f3Smrg
587ec681f3Smrg   auto ret = DrmDevice();
597ec681f3Smrg   ret.fd = fd;
607ec681f3Smrg   ret.gpu_num = gpu_num;
617ec681f3Smrg   ret.name = name;
627ec681f3Smrg   return ret;
637ec681f3Smrg}
647ec681f3Smrg
657ec681f3Smrgstd::vector<DrmDevice> DrmDevice::create_all()
667ec681f3Smrg{
677ec681f3Smrg   std::vector<DrmDevice> ret = {};
687ec681f3Smrg
697ec681f3Smrg   drmDevicePtr devices[MAX_DRM_DEVICES] = {};
707ec681f3Smrg   int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
717ec681f3Smrg   if (num_devices <= 0) {
727ec681f3Smrg      return ret;
737ec681f3Smrg   }
747ec681f3Smrg
757ec681f3Smrg   for (int32_t gpu_num = 0; gpu_num < num_devices; gpu_num++) {
767ec681f3Smrg      drmDevicePtr device = devices[gpu_num];
777ec681f3Smrg      if ((device->available_nodes & (1 << DRM_NODE_RENDER))) {
787ec681f3Smrg         int fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR);
797ec681f3Smrg
807ec681f3Smrg         // If it can create a device, push it into the vector
817ec681f3Smrg         if (auto drm_device = create_drm_device(fd, gpu_num)) {
827ec681f3Smrg            ret.emplace_back(std::move(drm_device.value()));
837ec681f3Smrg         }
847ec681f3Smrg      }
857ec681f3Smrg   }
867ec681f3Smrg
877ec681f3Smrg   drmFreeDevices(devices, num_devices);
887ec681f3Smrg   return ret;
897ec681f3Smrg}
907ec681f3Smrg
917ec681f3Smrgstd::optional<DrmDevice> DrmDevice::create(int32_t gpu_num)
927ec681f3Smrg{
937ec681f3Smrg   std::optional<DrmDevice> ret = std::nullopt;
947ec681f3Smrg
957ec681f3Smrg   if (gpu_num < 0) {
967ec681f3Smrg      return ret;
977ec681f3Smrg   }
987ec681f3Smrg
997ec681f3Smrg   drmDevicePtr devices[MAX_DRM_DEVICES] = {};
1007ec681f3Smrg   int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
1017ec681f3Smrg
1027ec681f3Smrg   if (num_devices > 0 && gpu_num < num_devices) {
1037ec681f3Smrg      drmDevicePtr device = devices[gpu_num];
1047ec681f3Smrg      int fd = open(device->nodes[DRM_NODE_RENDER], O_RDONLY);
1057ec681f3Smrg      ret = create_drm_device(fd, gpu_num);
1067ec681f3Smrg   }
1077ec681f3Smrg
1087ec681f3Smrg   drmFreeDevices(devices, num_devices);
1097ec681f3Smrg   return ret;
1107ec681f3Smrg}
1117ec681f3Smrg
1127ec681f3SmrgDrmDevice::DrmDevice(DrmDevice &&other)
1137ec681f3Smrg   : fd {other.fd}
1147ec681f3Smrg   , gpu_num {other.gpu_num}
1157ec681f3Smrg   , name {std::move(other.name)}
1167ec681f3Smrg{
1177ec681f3Smrg   other.fd = -1;
1187ec681f3Smrg   other.gpu_num = -1;
1197ec681f3Smrg}
1207ec681f3Smrg
1217ec681f3SmrgDrmDevice &DrmDevice::operator=(DrmDevice &&other)
1227ec681f3Smrg{
1237ec681f3Smrg   std::swap(fd, other.fd);
1247ec681f3Smrg   std::swap(gpu_num, other.gpu_num);
1257ec681f3Smrg   std::swap(name, other.name);
1267ec681f3Smrg   return *this;
1277ec681f3Smrg}
1287ec681f3Smrg
1297ec681f3SmrgDrmDevice::~DrmDevice()
1307ec681f3Smrg{
1317ec681f3Smrg   if (fd >= 0) {
1327ec681f3Smrg      close(fd);
1337ec681f3Smrg   }
1347ec681f3Smrg}
1357ec681f3Smrg
1367ec681f3SmrgDrmDevice::operator bool() const
1377ec681f3Smrg{
1387ec681f3Smrg   return !name.empty();
1397ec681f3Smrg}
1407ec681f3Smrg
1417ec681f3Smrg} // namespace pps
142