Skip to content

Commit

Permalink
platforms: Add atomic-kms platform
Browse files Browse the repository at this point in the history
This is initially a copy of the display half of `gbm-kms`, quickly ported to use
only the atomic KMS APIs.

It shall be further developed to usefully use the atomic APIs to fix various TODOs,
and provide support for extra performance features
  • Loading branch information
RAOF committed Aug 8, 2024
1 parent 9beaf4e commit 8bf1aca
Show file tree
Hide file tree
Showing 40 changed files with 4,964 additions and 20 deletions.
13 changes: 8 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -161,18 +161,18 @@ pkg_check_modules(WAYLAND_EGLSTREAM wayland-eglstream IMPORTED_TARGET)
if (WAYLAND_EGLSTREAM_FOUND)
set(
MIR_PLATFORM
gbm-kms;x11;eglstream-kms;wayland
gbm-kms;atomic-kms;x11;eglstream-kms;wayland
CACHE
STRING
"a list of graphics backends to build (options are 'gbm-kms', 'x11', 'eglstream-kms', or 'wayland')"
"a list of graphics backends to build (options are 'gbm-kms', 'atomic-kms', 'x11', 'eglstream-kms', or 'wayland')"
)
else()
set(
MIR_PLATFORM
gbm-kms;x11;wayland
gbm-kms;atomic-kms;x11;wayland
CACHE
STRING
"a list of graphics backends to build (options are 'gbm-kms', 'x11', 'eglstream-kms', or 'wayland')"
"a list of graphics backends to build (options are 'gbm-kms', 'atomic-kms', 'x11', 'eglstream-kms', or 'wayland')"
)
endif()

Expand Down Expand Up @@ -209,6 +209,9 @@ foreach(platform IN LISTS MIR_PLATFORM)
if (platform STREQUAL "gbm-kms")
set(MIR_BUILD_PLATFORM_GBM_KMS TRUE)
endif()
if (platform STREQUAL "atomic-kms")
set(MIR_BUILD_PLATFORM_ATOMIC_KMS TRUE)
endif()
if (platform STREQUAL "x11")
set(MIR_BUILD_PLATFORM_X11 TRUE)
endif()
Expand Down Expand Up @@ -263,7 +266,7 @@ if (HAVE_SYS_SDT_H)
add_compile_definitions(LTTNG_UST_HAVE_SDT_INTEGRATION)
endif()

if (MIR_BUILD_PLATFORM_GBM_KMS)
if (MIR_BUILD_PLATFORM_GBM_KMS OR MIR_BUILD_PLATFORM_ATOMIC_KMS)
pkg_check_modules(GBM REQUIRED IMPORTED_TARGET gbm>=11.0.0)
endif()

Expand Down
4 changes: 4 additions & 0 deletions src/platforms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ if (MIR_BUILD_PLATFORM_GBM_KMS)
add_subdirectory(gbm-kms/)
endif()

if (MIR_BUILD_PLATFORM_ATOMIC_KMS)
add_subdirectory(atomic-kms/)
endif()

if (MIR_BUILD_PLATFORM_X11)
add_subdirectory(x11/)
endif()
Expand Down
1 change: 1 addition & 0 deletions src/platforms/atomic-kms/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(server/)
34 changes: 34 additions & 0 deletions src/platforms/atomic-kms/include/gbm_format_conversions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright © Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2 or 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef MIR_GRAPHICS_GBM_GBM_FORMAT_CONVERSIONS_H_
#define MIR_GRAPHICS_GBM_GBM_FORMAT_CONVERSIONS_H_
#include <mir_toolkit/common.h>
#include <stdint.h>
#include <limits>
namespace mir
{
namespace graphics
{
namespace atomic
{
enum : uint32_t { invalid_atomic_format = std::numeric_limits<uint32_t>::max() };
}
}
}
#endif /* MIR_GRAPHICS_GBM_GBM_FORMAT_CONVERSIONS_H_ */
24 changes: 24 additions & 0 deletions src/platforms/atomic-kms/server/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
add_subdirectory(kms/)

add_library(
mirsharedatomickmscommon-static STATIC

display_helpers.cpp
gbm_display_allocator.h
gbm_display_allocator.cpp
)

target_include_directories(
mirsharedatomickmscommon-static
PUBLIC
${server_common_include_dirs}
${CMAKE_CURRENT_BINARY_DIR}
)

target_link_libraries(
mirsharedatomickmscommon-static

server_platform_common
kms_utils
mirplatform
)
241 changes: 241 additions & 0 deletions src/platforms/atomic-kms/server/display_helpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*
* Copyright © Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2 or 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "display_helpers.h"
#include "one_shot_device_observer.h"

#include "kms-utils/drm_mode_resources.h"
#include "mir/graphics/egl_error.h"
#include "kms/quirks.h"

#include "mir/udev/wrapper.h"
#include "mir/console_services.h"

#include <sys/sysmacros.h>

#define MIR_LOG_COMPONENT "atomic-kms"
#include "mir/log.h"

#include <boost/exception/errinfo_errno.hpp>
#include <boost/throw_exception.hpp>

#include <cstring>
#include <stdexcept>
#include <xf86drm.h>
#include <fcntl.h>
#include <vector>
#include <boost/exception/diagnostic_information.hpp>

namespace mg = mir::graphics;
namespace mga = mir::graphics::atomic;
namespace mgc = mir::graphics::common;
namespace mgmh = mir::graphics::atomic::helpers;

/*************
* DRMHelper *
*************/

std::vector<std::shared_ptr<mgmh::DRMHelper>>
mgmh::DRMHelper::open_all_devices(
std::shared_ptr<mir::udev::Context> const& udev,
mir::ConsoleServices& console,
mga::Quirks const& quirks)
{
int error = ENODEV; //Default error is "there are no DRM devices"

mir::udev::Enumerator devices(udev);
devices.match_subsystem("drm");
devices.match_sysname("card[0-9]");

devices.scan_devices();

std::vector<std::shared_ptr<DRMHelper>> opened_devices;

for(auto& device : devices)
{
if (quirks.should_skip(device))
{
mir::log_info("Ignoring device %s due to specified quirk", device.devnode());
continue;
}

mir::Fd tmp_fd;
std::unique_ptr<mir::Device> device_handle;
try
{
device_handle = console.acquire_device(
major(device.devnum()), minor(device.devnum()),
std::make_unique<mgc::OneShotDeviceObserver>(tmp_fd))
.get();
}
catch (std::exception const& error)
{
mir::log_warning(
"Failed to open DRM device node %s: %s",
device.devnode(),
boost::diagnostic_information(error).c_str());
continue;
}

// Paranoia is always justified when dealing with hardware interfaces…
if (tmp_fd == mir::Fd::invalid)
{
mir::log_critical(
"Opening the DRM device %s succeeded, but provided an invalid descriptor!",
device.devnode());
mir::log_critical(
"This is probably a logic error in Mir, please report to https://github.com/MirServer/mir/issues");
continue;
}

// Check that the drm device is usable by setting the interface version we use (1.4)
drmSetVersion sv;
sv.drm_di_major = 1;
sv.drm_di_minor = 4;
sv.drm_dd_major = -1; /* Don't care */
sv.drm_dd_minor = -1; /* Don't care */

if ((error = -drmSetInterfaceVersion(tmp_fd, &sv)))
{
mir::log_warning(
"Failed to set DRM interface version on device %s: %i (%s)",
device.devnode(),
error,
strerror(error));
continue;
}

auto busid = std::unique_ptr<char, decltype(&drmFreeBusid)>{
drmGetBusid(tmp_fd), &drmFreeBusid
};

if (!busid)
{
mir::log_warning(
"Failed to query BusID for device %s; cannot check if KMS is available",
device.devnode());
}
else
{
switch (auto err = -drmCheckModesettingSupported(busid.get()))
{
case 0: break;

case ENOSYS:
if (quirks.require_modesetting_support(device))
{
mir::log_info("Ignoring non-KMS DRM device %s", device.devnode());
error = ENOSYS;
continue;
}

[[fallthrough]];
case EINVAL:
mir::log_warning(
"Failed to detect whether device %s supports KMS, but continuing anyway",
device.devnode());
break;

default:
mir::log_warning("Unexpected error from drmCheckModesettingSupported(): %s (%i), but continuing anyway",
strerror(err), err);
mir::log_warning("Please file a bug at https://github.com/MirServer/mir/issues containing this message");
}
}

// Can't use make_shared with the private constructor.
opened_devices.push_back(
std::shared_ptr<DRMHelper>{
new DRMHelper{
std::move(tmp_fd),
std::move(device_handle)}});
mir::log_info("Using DRM device %s", device.devnode());
}

if (opened_devices.size() == 0)
{
BOOST_THROW_EXCEPTION((
std::system_error{error, std::system_category(), "Error opening DRM device"}));
}

return opened_devices;
}

std::unique_ptr<mgmh::DRMHelper> mgmh::DRMHelper::open_any_render_node(
std::shared_ptr<mir::udev::Context> const& udev)
{
mir::Fd tmp_fd;
int error = ENODEV; //Default error is "there are no DRM devices"

mir::udev::Enumerator devices(udev);
devices.match_subsystem("drm");
devices.match_sysname("renderD[0-9]*");

devices.scan_devices();

for(auto& device : devices)
{
// If directly opening the DRM device is good enough for X it's good enough for us!
tmp_fd = mir::Fd{open(device.devnode(), O_RDWR | O_CLOEXEC)};
if (tmp_fd < 0)
{
error = errno;
continue;
}

break;
}

if (tmp_fd < 0)
{
BOOST_THROW_EXCEPTION((
std::system_error{
error,
std::system_category(),
"Error opening DRM device"}));
}

return std::unique_ptr<mgmh::DRMHelper>{
new mgmh::DRMHelper{std::move(tmp_fd), nullptr}};
}

mgmh::DRMHelper::DRMHelper(mir::Fd&& fd, std::unique_ptr<mir::Device> device)
: fd{std::move(fd)},
device_handle{std::move(device)}
{
}

mgmh::DRMHelper::~DRMHelper()
{
}

/*************
* GBMHelper *
*************/

mgmh::GBMHelper::GBMHelper(mir::Fd const& drm_fd)
: device{gbm_create_device(drm_fd)}
{
if (!device)
BOOST_THROW_EXCEPTION(
std::runtime_error("Failed to create GBM device"));
}

mgmh::GBMHelper::~GBMHelper()
{
if (device)
gbm_device_destroy(device);
}
Loading

0 comments on commit 8bf1aca

Please sign in to comment.