Zbigniew Jędrzejewski-Szmek 62fe94
From f22e0bce3732c1fd005b7a886042394e036bc1b3 Mon Sep 17 00:00:00 2001
Zbigniew Jędrzejewski-Szmek 62fe94
From: David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
Date: Fri, 19 Sep 2014 14:13:06 +0200
Zbigniew Jędrzejewski-Szmek 62fe94
Subject: [PATCH] terminal: add grdev DRM backend
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
The grdev-drm backend manages DRM cards for grdev. Any DRM card with
Zbigniew Jędrzejewski-Szmek 62fe94
DUMB_BUFFER support can be used. So far, our policy is to configure all
Zbigniew Jędrzejewski-Szmek 62fe94
available connectors, but keep pipes inactive as long as users don't
Zbigniew Jędrzejewski-Szmek 62fe94
enable the displays on top.
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
We hard-code double-buffering so far, but can easily support
Zbigniew Jędrzejewski-Szmek 62fe94
single-buffering or n-buffering. We also require XRGB8888 as format as
Zbigniew Jędrzejewski-Szmek 62fe94
this is required to be supported by all DRM drivers and it is what VTs
Zbigniew Jędrzejewski-Szmek 62fe94
use. This allows us to switch from VTs to grdev via page-flips instead of
Zbigniew Jędrzejewski-Szmek 62fe94
deep modesets.
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
There is still a lot room for improvements in this backend, but it works
Zbigniew Jędrzejewski-Szmek 62fe94
smoothly so far so more enhanced features can be added later.
Zbigniew Jędrzejewski-Szmek 62fe94
---
Zbigniew Jędrzejewski-Szmek 62fe94
 Makefile.am                              |    1 +
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/grdev-drm.c      | 2957 ++++++++++++++++++++++++++++++
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/grdev-internal.h |    9 +
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/grdev.c          |   64 +
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/grdev.h          |    5 +
Zbigniew Jędrzejewski-Szmek 62fe94
 5 files changed, 3036 insertions(+)
Zbigniew Jędrzejewski-Szmek 62fe94
 create mode 100644 src/libsystemd-terminal/grdev-drm.c
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/Makefile.am b/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
index 1931c5d96b..be25023c75 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -3008,6 +3008,7 @@ libsystemd_terminal_la_SOURCES = \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/grdev.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/grdev-internal.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/grdev.c \
Zbigniew Jędrzejewski-Szmek 62fe94
+	src/libsystemd-terminal/grdev-drm.c \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/idev.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/idev-internal.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/idev.c \
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
Zbigniew Jędrzejewski-Szmek 62fe94
new file mode 100644
Zbigniew Jędrzejewski-Szmek 62fe94
index 0000000000..3481584fbf
Zbigniew Jędrzejewski-Szmek 62fe94
--- /dev/null
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/grdev-drm.c
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -0,0 +1,2957 @@
Zbigniew Jędrzejewski-Szmek 62fe94
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/***
Zbigniew Jędrzejewski-Szmek 62fe94
+  This file is part of systemd.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is free software; you can redistribute it and/or modify it
Zbigniew Jędrzejewski-Szmek 62fe94
+  under the terms of the GNU Lesser General Public License as published by
Zbigniew Jędrzejewski-Szmek 62fe94
+  the Free Software Foundation; either version 2.1 of the License, or
Zbigniew Jędrzejewski-Szmek 62fe94
+  (at your option) any later version.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is distributed in the hope that it will be useful, but
Zbigniew Jędrzejewski-Szmek 62fe94
+  WITHOUT ANY WARRANTY; without even the implied warranty of
Zbigniew Jędrzejewski-Szmek 62fe94
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Zbigniew Jędrzejewski-Szmek 62fe94
+  Lesser General Public License for more details.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  You should have received a copy of the GNU Lesser General Public License
Zbigniew Jędrzejewski-Szmek 62fe94
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
Zbigniew Jędrzejewski-Szmek 62fe94
+***/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <fcntl.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <libudev.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <sys/ioctl.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <sys/mman.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <sys/types.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-event.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <unistd.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/* Yuck! DRM headers need system headers included first.. but we have to
Zbigniew Jędrzejewski-Szmek 62fe94
+ * include it before shared/missing.h to avoid redefining ioctl bits */
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <drm.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <drm_fourcc.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <drm_mode.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "bus-util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "hashmap.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "grdev.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "grdev-internal.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "macro.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "udev-util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define GRDRM_MAX_TRIES (16)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdrm_object grdrm_object;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdrm_plane grdrm_plane;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdrm_connector grdrm_connector;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdrm_encoder grdrm_encoder;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdrm_crtc grdrm_crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdrm_fb grdrm_fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdrm_pipe grdrm_pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdrm_card grdrm_card;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct unmanaged_card unmanaged_card;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct managed_card managed_card;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Objects
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+enum {
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDRM_TYPE_CRTC,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDRM_TYPE_ENCODER,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDRM_TYPE_CONNECTOR,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDRM_TYPE_PLANE,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDRM_TYPE_CNT
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdrm_object {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t id;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t index;
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int type;
Zbigniew Jędrzejewski-Szmek 62fe94
+        void (*free_fn) (grdrm_object *object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool present : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool assigned : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdrm_plane {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object object;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t used_crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t used_fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t gamma_size;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_crtcs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max_crtcs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t *crtcs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_formats;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max_formats;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t *formats;
Zbigniew Jędrzejewski-Szmek 62fe94
+        } kern;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdrm_connector {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object object;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t type;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t type_id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t used_encoder;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t connection;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t mm_width;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t mm_height;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t subpixel;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_encoders;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max_encoders;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t *encoders;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_modes;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max_modes;
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_modeinfo *modes;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_props;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max_props;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t *prop_ids;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint64_t *prop_values;
Zbigniew Jędrzejewski-Szmek 62fe94
+        } kern;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdrm_encoder {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object object;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t type;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t used_crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_crtcs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max_crtcs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t *crtcs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_clones;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max_clones;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t *clones;
Zbigniew Jędrzejewski-Szmek 62fe94
+        } kern;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdrm_crtc {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object object;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t used_fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t fb_offset_x;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t fb_offset_y;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t gamma_size;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_used_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max_used_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t *used_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                bool mode_set;
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_modeinfo mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+        } kern;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                bool set;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t fb_x;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t fb_y;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t gamma;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t *connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                bool mode_set;
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_modeinfo mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+        } old;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_modeinfo mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t n_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t *connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+        } set;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_pipe *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool applied : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define GRDRM_OBJECT_INIT(_card, _id, _index, _type, _free_fn) ((grdrm_object){ \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .card = (_card), \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .id = (_id), \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .index = (_index), \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .type = (_type), \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .free_fn = (_free_fn), \
Zbigniew Jędrzejewski-Szmek 62fe94
+        })
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdrm_object *grdrm_find_object(grdrm_card *card, uint32_t id);
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_object_add(grdrm_object *object);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdrm_object *grdrm_object_free(grdrm_object *object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(grdrm_object*, grdrm_object_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_plane_new(grdrm_plane **out, grdrm_card *card, uint32_t id, uint32_t index);
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_connector_new(grdrm_connector **out, grdrm_card *card, uint32_t id, uint32_t index);
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_encoder_new(grdrm_encoder **out, grdrm_card *card, uint32_t id, uint32_t index);
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_crtc_new(grdrm_crtc **out, grdrm_card *card, uint32_t id, uint32_t index);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define plane_from_object(_obj) container_of((_obj), grdrm_plane, object)
Zbigniew Jędrzejewski-Szmek 62fe94
+#define connector_from_object(_obj) container_of((_obj), grdrm_connector, object)
Zbigniew Jędrzejewski-Szmek 62fe94
+#define encoder_from_object(_obj) container_of((_obj), grdrm_encoder, object)
Zbigniew Jędrzejewski-Szmek 62fe94
+#define crtc_from_object(_obj) container_of((_obj), grdrm_crtc, object)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Framebuffers
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdrm_fb {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_fb base;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t id;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t handles[4];
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t offsets[4];
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t sizes[4];
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t flipid;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_modeinfo *mode);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdrm_fb *grdrm_fb_free(grdrm_fb *fb);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(grdrm_fb*, grdrm_fb_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define fb_from_base(_fb) container_of((_fb), grdrm_fb, base)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Pipes
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdrm_pipe {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_pipe base;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_crtc *crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t counter;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define grdrm_pipe_from_base(_e) container_of((_e), grdrm_pipe, base)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define GRDRM_PIPE_NAME_MAX (GRDRM_CARD_NAME_MAX + 1 + DECIMAL_STR_MAX(uint32_t))
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static const grdev_pipe_vtable grdrm_pipe_vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_pipe_new(grdrm_pipe **out, grdrm_crtc *crtc, struct drm_mode_modeinfo *mode, size_t n_fbs);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Cards
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdrm_card {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card base;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        int fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source *fd_src;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t n_crtcs;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t n_encoders;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t n_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t n_planes;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t max_ids;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Hashmap *object_map;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool async_hotplug : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool running : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool ready : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool cap_dumb : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool cap_monotonic : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct unmanaged_card {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *devnode;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct managed_card {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_bus_slot *slot_pause_device;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_bus_slot *slot_resume_device;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_bus_slot *slot_take_device;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool requested : 1;             /* TakeDevice() was sent */
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool acquired : 1;              /* TakeDevice() was successful */
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool master : 1;                /* we are DRM-Master */
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define grdrm_card_from_base(_e) container_of((_e), grdrm_card, base)
Zbigniew Jędrzejewski-Szmek 62fe94
+#define unmanaged_card_from_base(_e) \
Zbigniew Jędrzejewski-Szmek 62fe94
+        container_of(grdrm_card_from_base(_e), unmanaged_card, card)
Zbigniew Jędrzejewski-Szmek 62fe94
+#define managed_card_from_base(_e) \
Zbigniew Jędrzejewski-Szmek 62fe94
+        container_of(grdrm_card_from_base(_e), managed_card, card)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define GRDRM_CARD_INIT(_vtable, _session) ((grdrm_card){ \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .base = GRDEV_CARD_INIT((_vtable), (_session)), \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .fd = -1, \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .max_ids = 32, \
Zbigniew Jędrzejewski-Szmek 62fe94
+        })
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define GRDRM_CARD_NAME_MAX (6 + DECIMAL_STR_MAX(unsigned) * 2)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static const grdev_card_vtable unmanaged_card_vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
+static const grdev_card_vtable managed_card_vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_card_open(grdrm_card *card, int dev_fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_close(grdrm_card *card);
Zbigniew Jędrzejewski-Szmek 62fe94
+static bool grdrm_card_async(grdrm_card *card, int r);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The page-flip event of the kernel provides 64bit of arbitrary user-data. As
Zbigniew Jędrzejewski-Szmek 62fe94
+ * drivers tend to drop events on intermediate deep mode-sets or because we
Zbigniew Jędrzejewski-Szmek 62fe94
+ * might receive events during session activation, we try to avoid allocaing
Zbigniew Jędrzejewski-Szmek 62fe94
+ * dynamic data on those events. Instead, we safe the CRTC id plus a 32bit
Zbigniew Jędrzejewski-Szmek 62fe94
+ * counter in there. This way, we only get 32bit counters, not 64bit, but that
Zbigniew Jędrzejewski-Szmek 62fe94
+ * should be more than enough. On the bright side, we no longer care whether we
Zbigniew Jędrzejewski-Szmek 62fe94
+ * lose events. No memory leaks will occur.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Modern DRM drivers might be fixed to no longer leak events, but we want to
Zbigniew Jędrzejewski-Szmek 62fe94
+ * be safe. And associating dynamically allocated data with those events is
Zbigniew Jędrzejewski-Szmek 62fe94
+ * kinda ugly, anyway.
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static uint64_t grdrm_encode_vblank_data(uint32_t id, uint32_t counter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        return id | ((uint64_t)counter << 32);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_decode_vblank_data(uint64_t data, uint32_t *out_id, uint32_t *out_counter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out_id)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out_id = data & 0xffffffffU;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out_counter)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out_counter = (data >> 32) & 0xffffffffU;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static bool grdrm_modes_compatible(const struct drm_mode_modeinfo *a, const struct drm_mode_modeinfo *b) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(a);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(b);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* Test whether both modes are compatible according to our internal
Zbigniew Jędrzejewski-Szmek 62fe94
+         * assumptions on modes. This comparison is highly dependent on how
Zbigniew Jędrzejewski-Szmek 62fe94
+         * we treat modes in grdrm. If we export mode details, we need to
Zbigniew Jędrzejewski-Szmek 62fe94
+         * make this comparison much stricter. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (a->hdisplay != b->hdisplay)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (a->vdisplay != b->vdisplay)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return true;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Objects
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdrm_object *grdrm_find_object(grdrm_card *card, uint32_t id) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(card, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return id > 0 ? hashmap_get(card->object_map, UINT32_TO_PTR(id)) : NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_object_add(grdrm_object *object) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(object->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(object->id > 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(IN_SET(object->type, GRDRM_TYPE_CRTC, GRDRM_TYPE_ENCODER, GRDRM_TYPE_CONNECTOR, GRDRM_TYPE_PLANE));
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(object->free_fn);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (object->index >= 32)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s: object index exceeds 32bit masks: type=%u, index=%" PRIu32,
Zbigniew Jędrzejewski-Szmek 62fe94
+                          object->card->base.name, object->type, object->index);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = hashmap_put(object->card->object_map, UINT32_TO_PTR(object->id), object);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdrm_object *grdrm_object_free(grdrm_object *object) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!object)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(object->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(object->id > 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(IN_SET(object->type, GRDRM_TYPE_CRTC, GRDRM_TYPE_ENCODER, GRDRM_TYPE_CONNECTOR, GRDRM_TYPE_PLANE));
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(object->free_fn);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        hashmap_remove_value(object->card->object_map, UINT32_TO_PTR(object->id), object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        object->free_fn(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Planes
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void plane_free(grdrm_object *object) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_plane *plane = plane_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(plane->kern.formats);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(plane->kern.crtcs);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(plane);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_plane_new(grdrm_plane **out, grdrm_card *card, uint32_t id, uint32_t index) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_plane *plane;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        plane = new0(grdrm_plane, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!plane)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        object = &plane->object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_PLANE, plane_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        plane->kern.max_crtcs = 32;
Zbigniew Jędrzejewski-Szmek 62fe94
+        plane->kern.crtcs = new0(uint32_t, plane->kern.max_crtcs);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!plane->kern.crtcs)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        plane->kern.max_formats = 32;
Zbigniew Jędrzejewski-Szmek 62fe94
+        plane->kern.formats = new0(uint32_t, plane->kern.max_formats);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!plane->kern.formats)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdrm_object_add(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = plane;
Zbigniew Jędrzejewski-Szmek 62fe94
+        object = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_plane_resync(grdrm_plane *plane) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = plane->object.card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t tries;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(plane);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (tries = 0; tries < GRDRM_MAX_TRIES; ++tries) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_get_plane res;
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+                bool resized = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                zero(res);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.plane_id = plane->object.id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.format_type_ptr = PTR_TO_UINT64(plane->kern.formats);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.count_format_types = plane->kern.max_formats;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = ioctl(card->fd, DRM_IOCTL_MODE_GETPLANE, &res;;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r == -ENOENT) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                card->async_hotplug = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: plane %u removed during resync", card->base.name, plane->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: cannot retrieve plane %u: %m", card->base.name, plane->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                plane->kern.n_crtcs = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                memzero(plane->kern.crtcs, sizeof(uint32_t) * plane->kern.max_crtcs);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (object->type != GRDRM_TYPE_CRTC || object->index >= 32)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!(res.possible_crtcs & (1 << object->index)))
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (plane->kern.n_crtcs >= 32) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: possible_crtcs of plane %" PRIu32 " exceeds 32bit mask",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          card->base.name, plane->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        plane->kern.crtcs[plane->kern.n_crtcs++] = object->id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (res.count_format_types > plane->kern.max_formats) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        uint32_t max, *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        max = ALIGN_POWER2(res.count_format_types);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!max || max > UINT16_MAX) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: excessive plane resource limit: %" PRIu32, card->base.name, max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ERANGE;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        t = realloc(plane->kern.formats, sizeof(*t) * max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!t)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        plane->kern.formats = t;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        plane->kern.max_formats = max;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        resized = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (resized)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                plane->kern.n_formats = res.count_format_types;
Zbigniew Jędrzejewski-Szmek 62fe94
+                plane->kern.used_crtc = res.crtc_id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                plane->kern.used_fb = res.fb_id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                plane->kern.gamma_size = res.gamma_size;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (tries >= GRDRM_MAX_TRIES) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s: plane %u not settled for retrieval", card->base.name, plane->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -EFAULT;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Connectors
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void connector_free(grdrm_object *object) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_connector *connector = connector_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(connector->kern.prop_values);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(connector->kern.prop_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(connector->kern.modes);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(connector->kern.encoders);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(connector);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_connector_new(grdrm_connector **out, grdrm_card *card, uint32_t id, uint32_t index) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_connector *connector;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        connector = new0(grdrm_connector, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!connector)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        object = &connector->object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_CONNECTOR, connector_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        connector->kern.max_encoders = 32;
Zbigniew Jędrzejewski-Szmek 62fe94
+        connector->kern.encoders = new0(uint32_t, connector->kern.max_encoders);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!connector->kern.encoders)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        connector->kern.max_modes = 32;
Zbigniew Jędrzejewski-Szmek 62fe94
+        connector->kern.modes = new0(struct drm_mode_modeinfo, connector->kern.max_modes);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!connector->kern.modes)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        connector->kern.max_props = 32;
Zbigniew Jędrzejewski-Szmek 62fe94
+        connector->kern.prop_ids = new0(uint32_t, connector->kern.max_props);
Zbigniew Jędrzejewski-Szmek 62fe94
+        connector->kern.prop_values = new0(uint64_t, connector->kern.max_props);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!connector->kern.prop_ids || !connector->kern.prop_values)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdrm_object_add(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = connector;
Zbigniew Jędrzejewski-Szmek 62fe94
+        object = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_connector_resync(grdrm_connector *connector) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = connector->object.card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t tries;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(connector);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (tries = 0; tries < GRDRM_MAX_TRIES; ++tries) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_get_connector res;
Zbigniew Jędrzejewski-Szmek 62fe94
+                bool resized = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                zero(res);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.connector_id = connector->object.id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.encoders_ptr = PTR_TO_UINT64(connector->kern.encoders);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.props_ptr = PTR_TO_UINT64(connector->kern.prop_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.prop_values_ptr = PTR_TO_UINT64(connector->kern.prop_values);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.count_encoders = connector->kern.max_encoders;
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.count_props = connector->kern.max_props;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* Retrieve modes only if we have none. This avoids expensive
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * EDID reads in the kernel, that can slow down resyncs
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * considerably! */
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (connector->kern.n_modes == 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        res.modes_ptr = PTR_TO_UINT64(connector->kern.modes);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        res.count_modes = connector->kern.max_modes;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = ioctl(card->fd, DRM_IOCTL_MODE_GETCONNECTOR, &res;;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r == -ENOENT) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                card->async_hotplug = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: connector %u removed during resync", card->base.name, connector->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: cannot retrieve connector %u: %m", card->base.name, connector->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (res.count_encoders > connector->kern.max_encoders) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        uint32_t *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        max = ALIGN_POWER2(res.count_encoders);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!max || max > UINT16_MAX) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: excessive connector resource limit: %" PRIu32, card->base.name, max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ERANGE;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        t = realloc(connector->kern.encoders, sizeof(*t) * max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!t)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        connector->kern.encoders = t;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        connector->kern.max_encoders = max;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        resized = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (res.count_modes > connector->kern.max_modes) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        struct drm_mode_modeinfo *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        max = ALIGN_POWER2(res.count_modes);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!max || max > UINT16_MAX) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: excessive connector resource limit: %" PRIu32, card->base.name, max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ERANGE;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        t = realloc(connector->kern.modes, sizeof(*t) * max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!t)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        connector->kern.modes = t;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        connector->kern.max_modes = max;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        resized = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (res.count_props > connector->kern.max_props) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        uint32_t *tids;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        uint64_t *tvals;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        max = ALIGN_POWER2(res.count_props);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!max || max > UINT16_MAX) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: excessive connector resource limit: %" PRIu32, card->base.name, max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ERANGE;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        tids = realloc(connector->kern.prop_ids, sizeof(*tids) * max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!tids)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        connector->kern.prop_ids = tids;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        tvals = realloc(connector->kern.prop_values, sizeof(*tvals) * max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!tvals)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        connector->kern.prop_values = tvals;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        connector->kern.max_props = max;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        resized = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (resized)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.n_encoders = res.count_encoders;
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.n_modes = res.count_modes;
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.n_props = res.count_props;
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.type = res.connector_type;
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.type_id = res.connector_type_id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.used_encoder = res.encoder_id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.connection = res.connection;
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.mm_width = res.mm_width;
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.mm_height = res.mm_height;
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->kern.subpixel = res.subpixel;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (tries >= GRDRM_MAX_TRIES) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s: connector %u not settled for retrieval", card->base.name, connector->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -EFAULT;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Encoders
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void encoder_free(grdrm_object *object) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_encoder *encoder = encoder_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(encoder->kern.clones);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(encoder->kern.crtcs);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(encoder);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_encoder_new(grdrm_encoder **out, grdrm_card *card, uint32_t id, uint32_t index) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_encoder *encoder;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        encoder = new0(grdrm_encoder, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!encoder)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        object = &encoder->object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_ENCODER, encoder_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        encoder->kern.max_crtcs = 32;
Zbigniew Jędrzejewski-Szmek 62fe94
+        encoder->kern.crtcs = new0(uint32_t, encoder->kern.max_crtcs);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!encoder->kern.crtcs)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        encoder->kern.max_clones = 32;
Zbigniew Jędrzejewski-Szmek 62fe94
+        encoder->kern.clones = new0(uint32_t, encoder->kern.max_clones);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!encoder->kern.clones)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdrm_object_add(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = encoder;
Zbigniew Jędrzejewski-Szmek 62fe94
+        object = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_encoder_resync(grdrm_encoder *encoder) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = encoder->object.card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_mode_get_encoder res;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(encoder);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        zero(res);
Zbigniew Jędrzejewski-Szmek 62fe94
+        res.encoder_id = encoder->object.id;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(card->fd, DRM_IOCTL_MODE_GETENCODER, &res;;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r == -ENOENT) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card->async_hotplug = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: encoder %u removed during resync", card->base.name, encoder->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: cannot retrieve encoder %u: %m", card->base.name, encoder->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        encoder->kern.type = res.encoder_type;
Zbigniew Jędrzejewski-Szmek 62fe94
+        encoder->kern.used_crtc = res.crtc_id;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        encoder->kern.n_crtcs = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        memzero(encoder->kern.crtcs, sizeof(uint32_t) * encoder->kern.max_crtcs);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_CRTC || object->index >= 32)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!(res.possible_crtcs & (1 << object->index)))
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (encoder->kern.n_crtcs >= 32) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: possible_crtcs exceeds 32bit mask", card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                encoder->kern.crtcs[encoder->kern.n_crtcs++] = object->id;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        encoder->kern.n_clones = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        memzero(encoder->kern.clones, sizeof(uint32_t) * encoder->kern.max_clones);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_ENCODER || object->index >= 32)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!(res.possible_clones & (1 << object->index)))
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (encoder->kern.n_clones >= 32) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: possible_encoders exceeds 32bit mask", card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                encoder->kern.clones[encoder->kern.n_clones++] = object->id;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Crtcs
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void crtc_free(grdrm_object *object) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_crtc *crtc = crtc_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (crtc->pipe)
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_pipe_free(&crtc->pipe->base);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(crtc->set.connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(crtc->old.connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(crtc->kern.used_connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdrm_crtc_new(grdrm_crtc **out, grdrm_card *card, uint32_t id, uint32_t index) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdrm_object_freep) grdrm_object *object = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_crtc *crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc = new0(grdrm_crtc, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!crtc)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        object = &crtc->object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        *object = GRDRM_OBJECT_INIT(card, id, index, GRDRM_TYPE_CRTC, crtc_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->kern.max_used_connectors = 32;
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->kern.used_connectors = new0(uint32_t, crtc->kern.max_used_connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!crtc->kern.used_connectors)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->old.connectors = new0(uint32_t, crtc->kern.max_used_connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!crtc->old.connectors)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdrm_object_add(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+        object = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_crtc_resync(grdrm_crtc *crtc) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = crtc->object.card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_mode_crtc res = { .crtc_id = crtc->object.id };
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* make sure we can cache any combination later */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (card->n_connectors > crtc->kern.max_used_connectors) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max, *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                max = ALIGN_POWER2(card->n_connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!max)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                t = realloc_multiply(crtc->kern.used_connectors, sizeof(*t), max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!t)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->kern.used_connectors = t;
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->kern.max_used_connectors = max;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!crtc->old.set) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc->old.connectors = calloc(sizeof(*t), max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!crtc->old.connectors)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* GETCRTC doesn't return connectors. We have to read all
Zbigniew Jędrzejewski-Szmek 62fe94
+         * encoder-state and deduce the setup ourselves.. */
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->kern.n_used_connectors = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(card->fd, DRM_IOCTL_MODE_GETCRTC, &res;;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r == -ENOENT) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card->async_hotplug = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: crtc %u removed during resync", card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: cannot retrieve crtc %u: %m", card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->kern.used_fb = res.fb_id;
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->kern.fb_offset_x = res.x;
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->kern.fb_offset_y = res.y;
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->kern.gamma_size = res.gamma_size;
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->kern.mode_set = res.mode_valid;
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->kern.mode = res.mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_crtc_assign(grdrm_crtc *crtc, grdrm_connector *connector) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t n_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!crtc->object.assigned);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!connector || !connector->object.assigned);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* always mark both as assigned; even if assignments cannot be set */
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->object.assigned = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (connector)
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector->object.assigned = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* we will support hw clone mode in the future */
Zbigniew Jędrzejewski-Szmek 62fe94
+        n_connectors = connector ? 1 : 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* bail out if configuration is preserved */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (crtc->set.n_connectors == n_connectors &&
Zbigniew Jędrzejewski-Szmek 62fe94
+            (n_connectors == 0 || crtc->set.connectors[0] == connector->object.id))
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->applied = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        crtc->set.n_connectors = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (n_connectors > crtc->set.max_connectors) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t max, *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                max = ALIGN_POWER2(n_connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!max) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                t = realloc(crtc->set.connectors, sizeof(*t) * max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!t) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->set.connectors = t;
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->set.max_connectors = max;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (connector) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_modeinfo *m, *pref = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t i;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < connector->kern.n_modes; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        m = &connector->kern.modes[i];
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* ignore 3D modes by default */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (m->flags & DRM_MODE_FLAG_3D_MASK)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!pref) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                pref = m;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* use PREFERRED over non-PREFERRED */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if ((pref->type & DRM_MODE_TYPE_PREFERRED) &&
Zbigniew Jędrzejewski-Szmek 62fe94
+                            !(m->type & DRM_MODE_TYPE_PREFERRED))
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* use DRIVER over non-PREFERRED|DRIVER */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if ((pref->type & DRM_MODE_TYPE_DRIVER) &&
Zbigniew Jędrzejewski-Szmek 62fe94
+                            !(m->type & (DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED)))
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* always prefer higher resolution */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (pref->hdisplay > m->hdisplay ||
Zbigniew Jędrzejewski-Szmek 62fe94
+                            (pref->hdisplay == m->hdisplay && pref->vdisplay > m->vdisplay))
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        pref = m;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (pref) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc->set.mode = *pref;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc->set.n_connectors = 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc->set.connectors[0] = connector->object.id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: assigned connector %" PRIu32 " to crtc %" PRIu32 " with mode %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  crtc->object.card->base.name, connector->object.id, crtc->object.id, pref->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: connector %" PRIu32 " to be assigned but has no valid mode",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  crtc->object.card->base.name, connector->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+error:
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdrm: %s: cannot assign crtc %" PRIu32 ": %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                  crtc->object.card->base.name, crtc->object.id, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_crtc_expose(grdrm_crtc *crtc) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_pipe *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_fb *fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t i;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(crtc->object.assigned);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (crtc->set.n_connectors < 1) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (crtc->pipe)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_pipe_free(&crtc->pipe->base);
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->pipe = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe = crtc->pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (pipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (pipe->base.width != crtc->set.mode.hdisplay ||
Zbigniew Jędrzejewski-Szmek 62fe94
+                    pipe->base.height != crtc->set.mode.vdisplay) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_pipe_free(&pipe->base);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc->pipe = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        pipe = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (crtc->pipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->base.front = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->base.back = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < pipe->base.max_fbs; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        fb = fb_from_base(pipe->base.fbs[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (fb->id == crtc->kern.used_fb)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                pipe->base.front = &fb->base;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        else if (!fb->flipid)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                pipe->base.back = &fb->base;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = grdrm_pipe_new(&pipe, crtc, &crtc->set.mode, 2);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: cannot create pipe for crtc %" PRIu32 ": %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  crtc->object.card->base.name, crtc->object.id, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < pipe->base.max_fbs; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = grdrm_fb_new(&fb, crtc->object.card, &crtc->set.mode);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: cannot allocate framebuffer for crtc %" PRIu32 ": %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          crtc->object.card->base.name, crtc->object.id, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                                grdev_pipe_free(&pipe->base);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        pipe->base.fbs[i] = &fb->base;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->base.front = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->base.back = pipe->base.fbs[0];
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->pipe = pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_pipe_ready(&crtc->pipe->base, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_crtc_commit(grdrm_crtc *crtc) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_mode_crtc_page_flip page_flip = { .crtc_id = crtc->object.id };
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = crtc->object.card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_pipe *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_fb **slot;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_fb *fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t cnt;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t i;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(crtc->object.assigned);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe = crtc->pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!pipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* If a crtc is not assigned any connector, we want any
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * previous setup to be cleared, so make sure the CRTC is
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * disabled. Otherwise, there might be content on the CRTC
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * while we run, which is not what we want.
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * If you want to avoid modesets on specific CRTCs, you should
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * still keep their assignment, but never enable the resulting
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * pipe. This way, we wouldn't touch it at all. */
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!crtc->applied) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc->applied = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: cannot shutdown crtc %" PRIu32 ": %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                                grdrm_card_async(card, r);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: crtc %" PRIu32 " applied via shutdown",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* we always fully ignore disabled pipes */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!pipe->base.enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(crtc->set.n_connectors > 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (pipe->base.flip)
Zbigniew Jędrzejewski-Szmek 62fe94
+                slot = &pipe->base.back;
Zbigniew Jędrzejewski-Szmek 62fe94
+        else if (!crtc->applied)
Zbigniew Jędrzejewski-Szmek 62fe94
+                slot = &pipe->base.front;
Zbigniew Jędrzejewski-Szmek 62fe94
+        else
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!*slot)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb = fb_from_base(*slot);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (crtc->applied || grdrm_modes_compatible(&crtc->kern.mode, &crtc->set.mode)) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                cnt = ++pipe->counter ? : ++pipe->counter;
Zbigniew Jędrzejewski-Szmek 62fe94
+                page_flip.fb_id = fb->id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                page_flip.flags = DRM_MODE_PAGE_FLIP_EVENT;
Zbigniew Jędrzejewski-Szmek 62fe94
+                page_flip.user_data = grdrm_encode_vblank_data(crtc->object.id, cnt);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = ioctl(card->fd, DRM_IOCTL_MODE_PAGE_FLIP, &page_flip);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: cannot schedule page-flip on crtc %" PRIu32 ": %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (grdrm_card_async(card, r))
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* fall through to deep modeset */
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!crtc->applied) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: crtc %" PRIu32 " applied via page flip",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc->applied = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        pipe->base.flipping = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        pipe->counter = cnt;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        fb->flipid = cnt;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        *slot = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!pipe->base.back) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                for (i = 0; i < pipe->base.max_fbs; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        if (!pipe->base.fbs[i])
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        fb = fb_from_base(pipe->base.fbs[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        if (&fb->base == pipe->base.front)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        if (fb->flipid)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        pipe->base.back = &fb->base;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                }
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!crtc->applied) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                set_crtc.set_connectors_ptr = PTR_TO_UINT64(crtc->set.connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+                set_crtc.count_connectors = crtc->set.n_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+                set_crtc.fb_id = fb->id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                set_crtc.x = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                set_crtc.y = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                set_crtc.mode_valid = 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+                set_crtc.mode = crtc->set.mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: cannot set crtc %" PRIu32 ": %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdrm_card_async(card, r);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!crtc->applied) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: crtc %" PRIu32 " applied via deep modeset",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc->applied = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                *slot = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->base.front = &fb->base;
Zbigniew Jędrzejewski-Szmek 62fe94
+                fb->flipid = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                ++pipe->counter;
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->base.flipping = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!pipe->base.back) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        for (i = 0; i < pipe->base.max_fbs; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (!pipe->base.fbs[i])
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                                fb = fb_from_base(pipe->base.fbs[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (&fb->base == pipe->base.front)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                                fb->flipid = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                pipe->base.back = &fb->base;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe->base.flip = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_crtc_restore(grdrm_crtc *crtc) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = crtc->object.card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!crtc->old.set)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        set_crtc.set_connectors_ptr = PTR_TO_UINT64(crtc->old.connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+        set_crtc.count_connectors = crtc->old.n_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+        set_crtc.fb_id = crtc->old.fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+        set_crtc.x = crtc->old.fb_x;
Zbigniew Jędrzejewski-Szmek 62fe94
+        set_crtc.y = crtc->old.fb_y;
Zbigniew Jędrzejewski-Szmek 62fe94
+        set_crtc.gamma_size = crtc->old.gamma;
Zbigniew Jędrzejewski-Szmek 62fe94
+        set_crtc.mode_valid = crtc->old.mode_set;
Zbigniew Jędrzejewski-Szmek 62fe94
+        set_crtc.mode = crtc->old.mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s: cannot restore crtc %" PRIu32 ": %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_card_async(card, r);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (crtc->pipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                ++crtc->pipe->counter;
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->pipe->base.front = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->pipe->base.flipping = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdrm: %s: crtc %" PRIu32 " restored", card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_crtc_flip_complete(grdrm_crtc *crtc, uint32_t counter, struct drm_event_vblank *event) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool flipped = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_pipe *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_fb *back = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t i;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(event);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe = crtc->pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!pipe)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* We got a page-flip event. To be safe, we reset all FBs on the same
Zbigniew Jędrzejewski-Szmek 62fe94
+         * pipe that have smaller flipids than the flip we got as we know they
Zbigniew Jędrzejewski-Szmek 62fe94
+         * are executed in order. We need to do this to guarantee
Zbigniew Jędrzejewski-Szmek 62fe94
+         * queue-overflows or other missed events don't cause starvation.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Furthermore, if we find the exact FB this event is for, *and* this
Zbigniew Jędrzejewski-Szmek 62fe94
+         * is the most recent event, we mark it as front FB and raise a
Zbigniew Jędrzejewski-Szmek 62fe94
+         * frame event. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (i = 0; i < pipe->base.max_fbs; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_fb *fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!pipe->base.fbs[i])
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                fb = fb_from_base(pipe->base.fbs[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (counter != 0 && counter == pipe->counter && fb->flipid == counter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        pipe->base.front = &fb->base;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        flipped = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (counter - fb->flipid < UINT16_MAX) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        fb->flipid = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        back = fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else if (fb->flipid == 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        back = fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!pipe->base.back)
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->base.back = &back->base;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (flipped) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->pipe->base.flipping = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_pipe_frame(&pipe->base);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Framebuffers
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_modeinfo *mode) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdrm_fb_freep) grdrm_fb *fb = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_mode_create_dumb create_dumb = { };
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_mode_map_dumb map_dumb = { };
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_mode_fb_cmd2 add_fb = { };
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int i;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(out, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(card, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb = new0(grdrm_fb, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!fb)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* TODO: we should choose a compatible format of the previous CRTC
Zbigniew Jędrzejewski-Szmek 62fe94
+         * setting to allow page-flip to it. Only choose fallback if the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * previous setting was crap (non xrgb32'ish). */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb->card = card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb->base.format = DRM_FORMAT_XRGB8888;
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb->base.width = mode->hdisplay;
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb->base.height = mode->vdisplay;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (i = 0; i < ELEMENTSOF(fb->base.maps); ++i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                fb->base.maps[i] = MAP_FAILED;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        create_dumb.width = fb->base.width;
Zbigniew Jędrzejewski-Szmek 62fe94
+        create_dumb.height = fb->base.height;
Zbigniew Jędrzejewski-Szmek 62fe94
+        create_dumb.bpp = 32;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(card->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s: cannot create dumb buffer %" PRIu32 "x%" PRIu32": %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.name, fb->base.width, fb->base.height);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb->handles[0] = create_dumb.handle;
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb->base.strides[0] = create_dumb.pitch;
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb->sizes[0] = create_dumb.size;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        map_dumb.handle = fb->handles[0];
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(card->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s: cannot map dumb buffer %" PRIu32 "x%" PRIu32": %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.name, fb->base.width, fb->base.height);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb->base.maps[0] = mmap(0, fb->sizes[0], PROT_WRITE, MAP_SHARED, card->fd, map_dumb.offset);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (fb->base.maps[0] == MAP_FAILED) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s: cannot memory-map dumb buffer %" PRIu32 "x%" PRIu32": %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.name, fb->base.width, fb->base.height);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        memzero(fb->base.maps[0], fb->sizes[0]);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        add_fb.width = fb->base.width;
Zbigniew Jędrzejewski-Szmek 62fe94
+        add_fb.height = fb->base.height;
Zbigniew Jędrzejewski-Szmek 62fe94
+        add_fb.pixel_format = fb->base.format;
Zbigniew Jędrzejewski-Szmek 62fe94
+        add_fb.flags = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        memcpy(add_fb.handles, fb->handles, sizeof(fb->handles));
Zbigniew Jędrzejewski-Szmek 62fe94
+        memcpy(add_fb.pitches, fb->base.strides, sizeof(fb->base.strides));
Zbigniew Jędrzejewski-Szmek 62fe94
+        memcpy(add_fb.offsets, fb->offsets, sizeof(fb->offsets));
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(card->fd, DRM_IOCTL_MODE_ADDFB2, &add_fb);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s: cannot add framebuffer %" PRIu32 "x%" PRIu32": %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.name, fb->base.width, fb->base.height);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb->id = add_fb.fb_id;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        *out = fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+        fb = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdrm_fb *grdrm_fb_free(grdrm_fb *fb) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int i;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!fb)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(fb->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (fb->id > 0 && fb->card->fd >= 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                ioctl(fb->card->fd, DRM_IOCTL_MODE_RMFB, fb->id);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (i = 0; i < ELEMENTSOF(fb->handles); ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_destroy_dumb destroy_dumb = { };
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (fb->base.maps[i] != MAP_FAILED)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        munmap(fb->base.maps[i], fb->sizes[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (fb->handles[i] > 0 && fb->card->fd >= 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        destroy_dumb.handle = fb->handles[i];
Zbigniew Jędrzejewski-Szmek 62fe94
+                        ioctl(fb->card->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(fb);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Pipes
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_pipe_name(char *out, grdrm_crtc *crtc) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* @out must be at least of size GRDRM_PIPE_NAME_MAX */
Zbigniew Jędrzejewski-Szmek 62fe94
+        sprintf(out, "%s/%" PRIu32, crtc->object.card->base.name, crtc->object.id);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_pipe_new(grdrm_pipe **out, grdrm_crtc *crtc, struct drm_mode_modeinfo *mode, size_t n_fbs) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_pipe_freep) grdev_pipe *basepipe = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = crtc->object.card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char name[GRDRM_PIPE_NAME_MAX];
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_pipe *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(crtc, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(grdev_is_drm_card(&card->base), -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe = new0(grdrm_pipe, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!pipe)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        basepipe = &pipe->base;
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe->base = GRDEV_PIPE_INIT(&grdrm_pipe_vtable, &card->base);
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe->crtc = crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe->base.width = mode->hdisplay;
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe->base.height = mode->vdisplay;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_pipe_name(name, crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdev_pipe_add(&pipe->base, name, n_fbs);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        basepipe = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_pipe_free(grdev_pipe *basepipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_pipe *pipe = grdrm_pipe_from_base(basepipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t i;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(pipe->crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (i = 0; i < pipe->base.max_fbs; ++i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (pipe->base.fbs[i])
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdrm_fb_free(fb_from_base(pipe->base.fbs[i]));
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static const grdev_pipe_vtable grdrm_pipe_vtable = {
Zbigniew Jędrzejewski-Szmek 62fe94
+        .free                   = grdrm_pipe_free,
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Cards
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_name(char *out, dev_t devnum) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* @out must be at least of size GRDRM_CARD_NAME_MAX */
Zbigniew Jędrzejewski-Szmek 62fe94
+        sprintf(out, "drm/%u:%u", major(devnum), minor(devnum));
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_print(grdrm_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_crtc *crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_encoder *encoder;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_connector *connector;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_plane *plane;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t i;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *p, *buf;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdrm: %s: state dump", card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("  crtcs:");
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_CRTC)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc = crtc_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("    (id: %u index: %d)", object->id, object->index);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (crtc->kern.mode_set)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      mode: %dx%d", crtc->kern.mode.hdisplay, crtc->kern.mode.vdisplay);
Zbigniew Jędrzejewski-Szmek 62fe94
+                else
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      mode: <none>");
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("  encoders:");
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_ENCODER)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                encoder = encoder_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("    (id: %u index: %d)", object->id, object->index);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (encoder->kern.used_crtc)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      crtc: %u", encoder->kern.used_crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+                else
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      crtc: <none>");
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * encoder->kern.n_crtcs + 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (buf) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        buf[0] = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        p = buf;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        for (i = 0; i < encoder->kern.n_crtcs; ++i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                p += sprintf(p, " %" PRIu32, encoder->kern.crtcs[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      possible crtcs:%s", buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        free(buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * encoder->kern.n_clones + 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (buf) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        buf[0] = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        p = buf;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        for (i = 0; i < encoder->kern.n_clones; ++i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                p += sprintf(p, " %" PRIu32, encoder->kern.clones[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      possible clones:%s", buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        free(buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("  connectors:");
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_CONNECTOR)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                connector = connector_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("    (id: %u index: %d)", object->id, object->index);
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("      type: %" PRIu32 "-%" PRIu32 " connection: %" PRIu32 " subpixel: %" PRIu32 " extents: %" PRIu32 "x%" PRIu32,
Zbigniew Jędrzejewski-Szmek 62fe94
+                          connector->kern.type, connector->kern.type_id, connector->kern.connection, connector->kern.subpixel,
Zbigniew Jędrzejewski-Szmek 62fe94
+                          connector->kern.mm_width, connector->kern.mm_height);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (connector->kern.used_encoder)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      encoder: %" PRIu32, connector->kern.used_encoder);
Zbigniew Jędrzejewski-Szmek 62fe94
+                else
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      encoder: <none>");
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * connector->kern.n_encoders + 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (buf) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        buf[0] = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        p = buf;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        for (i = 0; i < connector->kern.n_encoders; ++i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                p += sprintf(p, " %" PRIu32, connector->kern.encoders[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      possible encoders:%s", buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        free(buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < connector->kern.n_modes; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        struct drm_mode_modeinfo *mode = &connector->kern.modes[i];
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      mode: %" PRIu32 "x%" PRIu32, mode->hdisplay, mode->vdisplay);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("  planes:");
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_PLANE)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                plane = plane_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("    (id: %u index: %d)", object->id, object->index);
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("      gamma-size: %" PRIu32, plane->kern.gamma_size);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (plane->kern.used_crtc)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      crtc: %" PRIu32, plane->kern.used_crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+                else
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      crtc: <none>");
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                buf = malloc((DECIMAL_STR_MAX(uint32_t) + 1) * plane->kern.n_crtcs + 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (buf) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        buf[0] = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        p = buf;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        for (i = 0; i < plane->kern.n_crtcs; ++i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                p += sprintf(p, " %" PRIu32, plane->kern.crtcs[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      possible crtcs:%s", buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        free(buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                buf = malloc((DECIMAL_STR_MAX(unsigned int) + 3) * plane->kern.n_formats + 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (buf) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        buf[0] = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        p = buf;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        for (i = 0; i < plane->kern.n_formats; ++i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                p += sprintf(p, " 0x%x", (unsigned int)plane->kern.formats[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("      possible formats:%s", buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        free(buf);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_card_resync(grdrm_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_free_ uint32_t *crtc_ids = NULL, *encoder_ids = NULL, *connector_ids = NULL, *plane_ids = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t allocated = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t tries;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->async_hotplug = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        allocated = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* mark existing objects for possible removal */
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter)
Zbigniew Jędrzejewski-Szmek 62fe94
+                object->present = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (tries = 0; tries < GRDRM_MAX_TRIES; ++tries) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_get_plane_res pres;
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct drm_mode_card_res res;
Zbigniew Jędrzejewski-Szmek 62fe94
+                uint32_t i, max;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (allocated < card->max_ids) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        free(crtc_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        free(encoder_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        free(connector_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        free(plane_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc_ids = new0(uint32_t, card->max_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        encoder_ids = new0(uint32_t, card->max_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        connector_ids = new0(uint32_t, card->max_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        plane_ids = new0(uint32_t, card->max_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!crtc_ids || !encoder_ids || !connector_ids || !plane_ids)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        allocated = card->max_ids;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                zero(res);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.crtc_id_ptr = PTR_TO_UINT64(crtc_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.connector_id_ptr = PTR_TO_UINT64(connector_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.encoder_id_ptr = PTR_TO_UINT64(encoder_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.count_crtcs = allocated;
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.count_encoders = allocated;
Zbigniew Jędrzejewski-Szmek 62fe94
+                res.count_connectors = allocated;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = ioctl(card->fd, DRM_IOCTL_MODE_GETRESOURCES, &res;;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: cannot retrieve drm resources: %m", card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                zero(pres);
Zbigniew Jędrzejewski-Szmek 62fe94
+                pres.plane_id_ptr = PTR_TO_UINT64(plane_ids);
Zbigniew Jędrzejewski-Szmek 62fe94
+                pres.count_planes = allocated;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = ioctl(card->fd, DRM_IOCTL_MODE_GETPLANERESOURCES, &pres;;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s: cannot retrieve drm plane-resources: %m", card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                max = MAX(MAX(res.count_crtcs, res.count_encoders),
Zbigniew Jędrzejewski-Szmek 62fe94
+                          MAX(res.count_connectors, pres.count_planes));
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (max > allocated) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        uint32_t n;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        n = ALIGN_POWER2(max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!n || n > UINT16_MAX) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s: excessive DRM resource limit: %" PRIu32, card->base.name, max);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return -ERANGE;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* retry with resized buffers */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card->max_ids = n;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* mark available objects as present */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < res.count_crtcs; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        object = grdrm_find_object(card, crtc_ids[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (object && object->type == GRDRM_TYPE_CRTC) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                object->present = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                object->index = i;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc_ids[i] = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < res.count_encoders; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        object = grdrm_find_object(card, encoder_ids[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (object && object->type == GRDRM_TYPE_ENCODER) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                object->present = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                object->index = i;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                encoder_ids[i] = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < res.count_connectors; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        object = grdrm_find_object(card, connector_ids[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (object && object->type == GRDRM_TYPE_CONNECTOR) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                object->present = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                object->index = i;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                connector_ids[i] = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < pres.count_planes; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        object = grdrm_find_object(card, plane_ids[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (object && object->type == GRDRM_TYPE_PLANE) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                object->present = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                object->index = i;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                plane_ids[i] = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* drop removed objects */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                HASHMAP_FOREACH(object, card->object_map, iter)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!object->present)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                grdrm_object_free(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* add new objects */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                card->n_crtcs = res.count_crtcs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < res.count_crtcs; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (crtc_ids[i] < 1)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = grdrm_crtc_new(NULL, card, crtc_ids[i], i);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                card->n_encoders = res.count_encoders;
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < res.count_encoders; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (encoder_ids[i] < 1)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = grdrm_encoder_new(NULL, card, encoder_ids[i], i);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                card->n_connectors = res.count_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < res.count_connectors; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (connector_ids[i] < 1)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = grdrm_connector_new(NULL, card, connector_ids[i], i);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                card->n_planes = pres.count_planes;
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < pres.count_planes; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (plane_ids[i] < 1)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = grdrm_plane_new(NULL, card, plane_ids[i], i);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* re-sync objects after object_map is synced */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        switch (object->type) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        case GRDRM_TYPE_CRTC:
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = grdrm_crtc_resync(crtc_from_object(object));
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        case GRDRM_TYPE_ENCODER:
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = grdrm_encoder_resync(encoder_from_object(object));
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        case GRDRM_TYPE_CONNECTOR:
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = grdrm_connector_resync(connector_from_object(object));
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        case GRDRM_TYPE_PLANE:
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = grdrm_plane_resync(plane_from_object(object));
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        default:
Zbigniew Jędrzejewski-Szmek 62fe94
+                                assert_not_reached("grdrm: invalid object type");
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (card->async_hotplug)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* if modeset objects change during sync, start over */
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (card->async_hotplug) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card->async_hotplug = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* cache crtc/connector relationship */
Zbigniew Jędrzejewski-Szmek 62fe94
+                HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdrm_connector *connector;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdrm_encoder *encoder;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdrm_crtc *crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (object->type != GRDRM_TYPE_CONNECTOR)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        connector = connector_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (connector->kern.connection != 1 || connector->kern.used_encoder < 1)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        object = grdrm_find_object(card, connector->kern.used_encoder);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!object || object->type != GRDRM_TYPE_ENCODER)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        encoder = encoder_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (encoder->kern.used_crtc < 1)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        object = grdrm_find_object(card, encoder->kern.used_crtc);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!object || object->type != GRDRM_TYPE_CRTC)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc = crtc_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        assert(crtc->kern.n_used_connectors < crtc->kern.max_used_connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc->kern.used_connectors[crtc->kern.n_used_connectors++] = connector->object.id;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* cache old crtc settings for later restore */
Zbigniew Jędrzejewski-Szmek 62fe94
+                HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdrm_crtc *crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (object->type != GRDRM_TYPE_CRTC)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        crtc = crtc_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* Save data if it is the first time we refresh the CRTC. This data can
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * be used optionally to restore any previous configuration. For
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * instance, it allows us to restore VT configurations after we close
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * our session again. */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!crtc->old.set) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc->old.fb = crtc->kern.used_fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc->old.fb_x = crtc->kern.fb_offset_x;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc->old.fb_y = crtc->kern.fb_offset_y;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc->old.gamma = crtc->kern.gamma_size;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc->old.n_connectors = crtc->kern.n_used_connectors;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (crtc->old.n_connectors)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        memcpy(crtc->old.connectors, crtc->kern.used_connectors, sizeof(uint32_t) * crtc->old.n_connectors);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc->old.mode_set = crtc->kern.mode_set;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc->old.mode = crtc->kern.mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                crtc->old.set = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* everything synced */
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (tries >= GRDRM_MAX_TRIES) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                /*
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * Ugh! We were unable to sync the DRM card state due to heavy
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * hotplugging. This should never happen, so print a debug
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * message and bail out. The next uevent will trigger
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * this again.
Zbigniew Jędrzejewski-Szmek 62fe94
+                 */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s: hotplug-storm when syncing card", card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -EFAULT;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static bool card_configure_crtc(grdrm_crtc *crtc, grdrm_connector *connector) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = crtc->object.card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_encoder *encoder;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t i, j;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (crtc->object.assigned || connector->object.assigned)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (connector->kern.connection != 1)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (i = 0; i < connector->kern.n_encoders; ++i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                object = grdrm_find_object(card, connector->kern.encoders[i]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!object || object->type != GRDRM_TYPE_ENCODER)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                encoder = encoder_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (j = 0; j < encoder->kern.n_crtcs; ++j) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (encoder->kern.crtcs[j] == crtc->object.id) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                grdrm_crtc_assign(crtc, connector);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_configure(grdrm_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Modeset Configuration
Zbigniew Jędrzejewski-Szmek 62fe94
+         * This is where we update our modeset configuration and assign
Zbigniew Jędrzejewski-Szmek 62fe94
+         * connectors to CRTCs. This means, each connector that we want to
Zbigniew Jędrzejewski-Szmek 62fe94
+         * enable needs a CRTC, disabled (or unavailable) connectors are left
Zbigniew Jędrzejewski-Szmek 62fe94
+         * alone in the dark. Once all CRTCs are assigned, the remaining CRTCs
Zbigniew Jędrzejewski-Szmek 62fe94
+         * are disabled.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Sounds trivial, but there're several caveats:
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         *   * Multiple connectors can be driven by the same CRTC. This is
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     known as 'hardware clone mode'. Advantage over software clone
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     mode is that only a single CRTC is needed to drive multiple
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     displays. However, few hardware supports this and it's a huge
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     headache to configure on dynamic demands. Therefore, we only
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     support it if configured statically beforehand.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         *   * CRTCs are not created equal. Some might be much more poweful
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     than others, including more advanced plane support. So far, our
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     CRTC selection is random. You need to supply static
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     configuration if you want special setups. So far, there is no
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     proper way to do advanced CRTC selection on dynamic demands. It
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     is not really clear which demands require what CRTC, so, like
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     everyone else, we do random CRTC selection unless explicitly
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     states otherwise.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         *   * Each Connector has a list of possible encoders that can drive
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     it, and each encoder has a list of possible CRTCs. If this graph
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     is a tree, assignment is trivial. However, if not, we cannot
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     reliably decide on configurations beforehand. The encoder is
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     always selected by the kernel, so we have to actually set a mode
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     to know which encoder is used. There is no way to ask the kernel
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     whether a given configuration is possible. This will change with
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     atomic-modesetting, but until then, we keep our configurations
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     simple and assume they work all just fine. If one fails
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     unexpectedly, we print a warning and disable it.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Configuring a card consists of several steps:
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         *  1) First of all, we apply any user-configuration. If a user wants
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     a fixed configuration, we apply it and preserve it.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     So far, we don't support user configuration files, so this step
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     is skipped.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         *  2) Secondly, we need to apply any quirks from hwdb. Some hardware
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     might only support limited configurations or require special
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     CRTC/Connector mappings. We read this from hwdb and apply it, if
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     present.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     So far, we don't support this as there is no known quirk, so
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     this step is skipped.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         *  3) As deep modesets are expensive, we try to avoid them if
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     possible. Therefore, we read the current configuration from the
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     kernel and try to preserve it, if compatible with our demands.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     If not, we break it and reassign it in a following step.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         *  4) The main step involves configuring all remaining objects. By
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     default, all available connectors are enabled, except for those
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     disabled by user-configuration. We lookup a suitable CRTC for
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     each connector and assign them. As there might be more
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     connectors than CRTCs, we apply some ordering so users can
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     select which connectors are more important right now.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     So far, we only apply the default ordering, more might be added
Zbigniew Jędrzejewski-Szmek 62fe94
+         *     in the future.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_crtc *crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator i, j;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* clear assignments */
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                object->assigned = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* preserve existing configurations */
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_CRTC || object->assigned)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc = crtc_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (crtc->applied) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* If our mode is set, preserve it. If no connector is
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * set, modeset either failed or the pipe is unused. In
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * both cases, leave it alone. It might be tried again
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * below in case there're remaining connectors.
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * Otherwise, try restoring the assignments. If they
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * are no longer valid, leave the pipe untouched. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (crtc->set.n_connectors < 1)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        assert(crtc->set.n_connectors == 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        object = grdrm_find_object(card, crtc->set.connectors[0]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!object || object->type != GRDRM_TYPE_CONNECTOR)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card_configure_crtc(crtc, connector_from_object(object));
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else if (crtc->kern.mode_set && crtc->kern.n_used_connectors != 1) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* If our mode is not set on the pipe, we know the kern
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * information is valid. Try keeping it. If it's not
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * possible, leave the pipe untouched for later
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * assignements. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        object = grdrm_find_object(card, crtc->kern.used_connectors[0]);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!object || object->type != GRDRM_TYPE_CONNECTOR)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card_configure_crtc(crtc, connector_from_object(object));
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* assign remaining objects */
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_CRTC || object->assigned)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc = crtc_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                HASHMAP_FOREACH(object, card->object_map, j) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (object->type != GRDRM_TYPE_CONNECTOR)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (card_configure_crtc(crtc, connector_from_object(object)))
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!crtc->object.assigned)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdrm_crtc_assign(crtc, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* expose configuration */
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_CRTC)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_crtc_expose(crtc_from_object(object));
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_hotplug(grdrm_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!card->ready);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdrm_card_resync(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: cannot re-sync card: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.session->name, card->base.name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session_pin(card->base.session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_print(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_configure(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->ready = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session_unpin(card->base.session);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_event_vblank *vblank;
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_event *event;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t id, counter;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char buf[4096];
Zbigniew Jędrzejewski-Szmek 62fe94
+        ssize_t l, i;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (revents & (EPOLLHUP | EPOLLERR)) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* Immediately close device on HUP; no need to flush pending
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * data.. there're no events we care about here. */
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: HUP", card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_card_close(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (revents & (EPOLLIN)) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                l = read(card->fd, buf, sizeof(buf));
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (l < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (errno == EAGAIN || errno == EINTR)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s/%s: read error: %m", card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdrm_card_close(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else if ((size_t)l < sizeof(*event)) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s/%s: short read of %zd bytes", card->base.session->name, card->base.name, l);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < l; i += event->length) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        event = (void*)&buf[i];
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (i + event->length > l) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdrm: %s/%s: truncated event", card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        switch (event->type) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        case DRM_EVENT_FLIP_COMPLETE:
Zbigniew Jędrzejewski-Szmek 62fe94
+                                vblank = (void*)event;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (event->length < sizeof(*vblank)) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        log_debug("grdrm: %s/%s: truncated vblank event", card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                                grdrm_decode_vblank_data(vblank->user_data, &id, &counter);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                object = grdrm_find_object(card, id);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (!object || object->type != GRDRM_TYPE_CRTC)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        break;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                                grdrm_crtc_flip_complete(crtc_from_object(object), counter, vblank);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_card_add(grdrm_card *card, const char *name) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card->fd < 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->object_map = hashmap_new(&trivial_hash_ops);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!card->object_map)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return grdev_card_add(&card->base, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_destroy(grdrm_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!card->running);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card->fd < 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(hashmap_size(card->object_map) == 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        hashmap_free(card->object_map);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_commit(grdev_card *basecard) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = grdrm_card_from_base(basecard);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!card->ready)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        break;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_CRTC)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_crtc_commit(crtc_from_object(object));
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_restore(grdev_card *basecard) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card *card = grdrm_card_from_base(basecard);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!card->ready)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        break;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_CRTC)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_crtc_restore(crtc_from_object(object));
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_enable(grdrm_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (card->fd < 0 || card->running)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* ignore cards without DUMB_BUFFER capability */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!card->cap_dumb)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card->fd_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdrm: %s/%s: enable", card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->running = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source_set_enabled(card->fd_src, SD_EVENT_ON);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_hotplug(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_disable(grdrm_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (card->fd < 0 || !card->running)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card->fd_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdrm: %s/%s: disable", card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->running = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->ready = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source_set_enabled(card->fd_src, SD_EVENT_OFF);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* stop all pipes */
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(object, card->object_map, iter) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_crtc *crtc;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (object->type != GRDRM_TYPE_CRTC)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc = crtc_from_object(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+                crtc->applied = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (crtc->pipe)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_pipe_ready(&crtc->pipe->base, false);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int grdrm_card_open(grdrm_card *card, int dev_fd) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_session_unpinp) grdev_session *pin = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_close_ int fd = dev_fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct drm_get_cap cap;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, flags;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(dev_fd >= 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card->fd != dev_fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        pin = grdev_session_pin(card->base.session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_close(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdrm: %s/%s: open", card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = fd_nonblock(fd, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = fd_cloexec(fd, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        flags = fcntl(fd, F_GETFL, 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (flags < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if ((flags & O_ACCMODE) != O_RDWR)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -EACCES;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_event_add_io(card->base.session->context->event,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            &card->fd_src,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            fd,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            EPOLLHUP | EPOLLERR | EPOLLIN,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            grdrm_card_io_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source_set_enabled(card->fd_src, SD_EVENT_OFF);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->fd = fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+        fd = -1;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* cache DUMB_BUFFER capability */
Zbigniew Jędrzejewski-Szmek 62fe94
+        cap.capability = DRM_CAP_DUMB_BUFFER;
Zbigniew Jędrzejewski-Szmek 62fe94
+        cap.value = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(card->fd, DRM_IOCTL_GET_CAP, &cap);
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->cap_dumb = r >= 0 && cap.value;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: cannot retrieve DUMB_BUFFER capability: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.session->name, card->base.name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+        else if (!card->cap_dumb)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: DUMB_BUFFER capability not supported",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* cache TIMESTAMP_MONOTONIC capability */
Zbigniew Jędrzejewski-Szmek 62fe94
+        cap.capability = DRM_CAP_TIMESTAMP_MONOTONIC;
Zbigniew Jędrzejewski-Szmek 62fe94
+        cap.value = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(card->fd, DRM_IOCTL_GET_CAP, &cap);
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->cap_monotonic = r >= 0 && cap.value;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: cannot retrieve TIMESTAMP_MONOTONIC capability: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.session->name, card->base.name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+        else if (!card->cap_monotonic)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: TIMESTAMP_MONOTONIC is disabled globally, fix this NOW!",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdrm_card_close(grdrm_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_object *object;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (card->fd < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdrm: %s/%s: close", card->base.session->name, card->base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_disable(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->fd_src = sd_event_source_unref(card->fd_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->fd = safe_close(card->fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session_pin(card->base.session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        while ((object = hashmap_first(card->object_map)))
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_object_free(object);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session_unpin(card->base.session);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static bool grdrm_card_async(grdrm_card *card, int r) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        switch (r) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        case -EACCES:
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* If we get EACCES on runtime DRM calls, we lost DRM-Master
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * (or we did something terribly wrong). Immediately disable
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * the card, so we stop all pipes and wait to be activated
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * again. */
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_card_disable(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case -ENOENT:
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* DRM objects can be hotplugged at any time. If an object is
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * removed that we use, we remember that state so a following
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * call can test for this.
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * Note that we also get a uevent as followup, this will resync
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * the whole device. */
Zbigniew Jędrzejewski-Szmek 62fe94
+                card->async_hotplug = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return !card->ready;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Unmanaged Cards
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The unmanaged DRM card opens the device node for a given DRM device
Zbigniew Jędrzejewski-Szmek 62fe94
+ * directly (/dev/dri/cardX) and thus needs sufficient privileges. It opens
Zbigniew Jędrzejewski-Szmek 62fe94
+ * the device only if we really require it and releases it as soon as we're
Zbigniew Jędrzejewski-Szmek 62fe94
+ * disabled or closed.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The unmanaged element can be used in all situations where you have direct
Zbigniew Jędrzejewski-Szmek 62fe94
+ * access to DRM device nodes. Unlike managed DRM elements, it can be used
Zbigniew Jędrzejewski-Szmek 62fe94
+ * outside of user sessions and in emergency situations where logind is not
Zbigniew Jędrzejewski-Szmek 62fe94
+ * available.
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void unmanaged_card_enable(grdev_card *basecard) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unmanaged_card *cu = unmanaged_card_from_base(basecard);
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (cu->card.fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* try open on activation if it failed during allocation */
Zbigniew Jędrzejewski-Szmek 62fe94
+                fd = open(cu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* not fatal; simply ignore the device */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s/%s: cannot open node %s: %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  basecard->session->name, basecard->name, cu->devnode);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* we might already be DRM-Master by open(); that's fine */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = grdrm_card_open(&cu->card, fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s/%s: cannot open: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  basecard->session->name, basecard->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(cu->card.fd, DRM_IOCTL_SET_MASTER, 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: cannot acquire DRM-Master: %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          basecard->session->name, basecard->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_enable(&cu->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void unmanaged_card_disable(grdev_card *basecard) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unmanaged_card *cu = unmanaged_card_from_base(basecard);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_disable(&cu->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int unmanaged_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_card_freep) grdev_card *basecard = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char name[GRDRM_CARD_NAME_MAX];
Zbigniew Jędrzejewski-Szmek 62fe94
+        unmanaged_card *cu;
Zbigniew Jędrzejewski-Szmek 62fe94
+        const char *devnode;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(session, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(ud, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnode = udev_device_get_devnode(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnum = udev_device_get_devnum(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!devnode || devnum == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENODEV;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_name(name, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cu = new0(unmanaged_card, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!cu)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        basecard = &cu->card.base;
Zbigniew Jędrzejewski-Szmek 62fe94
+        cu->card = GRDRM_CARD_INIT(&unmanaged_card_vtable, session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cu->devnode = strdup(devnode);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!cu->devnode)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdrm_card_add(&cu->card, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* try to open but ignore errors */
Zbigniew Jędrzejewski-Szmek 62fe94
+        fd = open(cu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* not fatal; allow uaccess based control on activation */
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: cannot open node %s: %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          basecard->session->name, basecard->name, cu->devnode);
Zbigniew Jędrzejewski-Szmek 62fe94
+        } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* We might get DRM-Master implicitly on open(); drop it immediately
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * so we acquire it only once we're actually enabled. */
Zbigniew Jędrzejewski-Szmek 62fe94
+                ioctl(fd, DRM_IOCTL_DROP_MASTER, 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = grdrm_card_open(&cu->card, fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s/%s: cannot open: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  basecard->session->name, basecard->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = basecard;
Zbigniew Jędrzejewski-Szmek 62fe94
+        basecard = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void unmanaged_card_free(grdev_card *basecard) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unmanaged_card *cu = unmanaged_card_from_base(basecard);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!basecard->enabled);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_close(&cu->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_destroy(&cu->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(cu->devnode);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(cu);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static const grdev_card_vtable unmanaged_card_vtable = {
Zbigniew Jędrzejewski-Szmek 62fe94
+        .free                   = unmanaged_card_free,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .enable                 = unmanaged_card_enable,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .disable                = unmanaged_card_disable,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .commit                 = grdrm_card_commit,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .restore                = grdrm_card_restore,
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Managed Cards
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The managed DRM card uses systemd-logind to acquire DRM devices. This
Zbigniew Jędrzejewski-Szmek 62fe94
+ * means, we do not open the device node /dev/dri/cardX directly. Instead,
Zbigniew Jędrzejewski-Szmek 62fe94
+ * logind passes us a file-descriptor whenever our session is activated. Thus,
Zbigniew Jędrzejewski-Szmek 62fe94
+ * we don't need access to the device node directly.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Furthermore, whenever the session is put asleep, logind revokes the
Zbigniew Jędrzejewski-Szmek 62fe94
+ * file-descriptor so we loose access to the device.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Managed DRM cards should be preferred over unmanaged DRM cards whenever
Zbigniew Jędrzejewski-Szmek 62fe94
+ * you run inside a user session with exclusive device access.
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void managed_card_enable(grdev_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_card *cm = managed_card_from_base(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* If the device is manually re-enabled, we try to resume our card
Zbigniew Jędrzejewski-Szmek 62fe94
+         * management. Note that we have no control over DRM-Master and the fd,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * so we have to take over the state from the last logind event. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (cm->master)
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_card_enable(&cm->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void managed_card_disable(grdev_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_card *cm = managed_card_from_base(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* If the device is manually disabled, we keep the FD but put our card
Zbigniew Jędrzejewski-Szmek 62fe94
+         * management asleep. This way, we can wake up at any time, but don't
Zbigniew Jędrzejewski-Szmek 62fe94
+         * touch the device while asleep. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_disable(&cm->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_card_pause_device_fn(sd_bus *bus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        sd_bus_message *signal,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        void *userdata,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        sd_bus_error *ret_error) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_card *cm = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session *session = cm->card.base.session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t major, minor;
Zbigniew Jędrzejewski-Szmek 62fe94
+        const char *mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * We get PauseDevice() signals from logind whenever a device we
Zbigniew Jędrzejewski-Szmek 62fe94
+         * requested was, or is about to be, paused. Arguments are major/minor
Zbigniew Jędrzejewski-Szmek 62fe94
+         * number of the device and the mode of the operation.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * In case the event is not about our device, we ignore it. Otherwise,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * we treat it as asynchronous DRM-DROP-MASTER. Note that we might have
Zbigniew Jędrzejewski-Szmek 62fe94
+         * already handled an EACCES error from a modeset ioctl, in which case
Zbigniew Jędrzejewski-Szmek 62fe94
+         * we already disabled the device.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         * @mode can be one of the following:
Zbigniew Jędrzejewski-Szmek 62fe94
+         *   "pause": The device is about to be paused. We must react
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            immediately and respond with PauseDeviceComplete(). Once
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            we replied, logind will pause the device. Note that
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            logind might apply any kind of timeout and force pause
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            the device if we don't respond in a timely manner. In
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            this case, we will receive a second PauseDevice event
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            with @mode set to "force" (or similar).
Zbigniew Jędrzejewski-Szmek 62fe94
+         *   "force": The device was disabled forecfully by logind. DRM-Master
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            was already dropped. This is just an asynchronous
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            notification so we can put the device asleep (in case
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            we didn't already notice the dropped DRM-Master).
Zbigniew Jędrzejewski-Szmek 62fe94
+         *    "gone": This is like "force" but is sent if the device was
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            paused due to a device-removal event.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         * We always handle PauseDevice signals as "force" as we properly
Zbigniew Jędrzejewski-Szmek 62fe94
+         * support asynchronously dropping DRM-Master, anyway. But in case
Zbigniew Jędrzejewski-Szmek 62fe94
+         * logind sent mode "pause", we also call PauseDeviceComplete() to
Zbigniew Jędrzejewski-Szmek 62fe94
+         * immediately acknowledge the request.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_read(signal, "uus", &major, &minor, &mode);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: erroneous PauseDevice signal",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          session->name, cm->card.base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* not our device? */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (makedev(major, minor) != cm->devnum)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->master = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_disable(&cm->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (streq(mode, "pause")) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /*
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * Sending PauseDeviceComplete() is racy if logind triggers the
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * timeout. That is, if we take too long and logind pauses the
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * device by sending a forced PauseDevice, our
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * PauseDeviceComplete call will be stray. That's fine, though.
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * logind ignores such stray calls. Only if logind also sent a
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * further PauseDevice() signal, it might match our call
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * incorrectly to the newer PauseDevice(). That's fine, too, as
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * we handle that event asynchronously, anyway. Therefore,
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * whatever happens, we're fine. Yay!
Zbigniew Jędrzejewski-Szmek 62fe94
+                 */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = sd_bus_message_new_method_call(session->context->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   &m,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   "org.freedesktop.login1",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   session->path,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   "org.freedesktop.login1.Session",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   "PauseDeviceComplete");
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r >= 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = sd_bus_message_append(m, "uu", major, minor);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r >= 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = sd_bus_send(session->context->sysbus, m, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s/%s: cannot send PauseDeviceComplete: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  session->name, cm->card.base.name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_card_resume_device_fn(sd_bus *bus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                         sd_bus_message *signal,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                         void *userdata,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                         sd_bus_error *ret_error) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_card *cm = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session *session = cm->card.base.session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t major, minor;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * We get ResumeDevice signals whenever logind resumed a previously
Zbigniew Jędrzejewski-Szmek 62fe94
+         * paused device. The arguments contain the major/minor number of the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * related device and a new file-descriptor for the freshly opened
Zbigniew Jędrzejewski-Szmek 62fe94
+         * device-node.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * If the signal is not about our device, we simply ignore it.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Otherwise, we immediately resume the device. Note that we drop the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * new file-descriptor as we already have one from TakeDevice(). logind
Zbigniew Jędrzejewski-Szmek 62fe94
+         * preserves the file-context across pause/resume for DRM but only
Zbigniew Jędrzejewski-Szmek 62fe94
+         * drops/acquires DRM-Master accordingly. This way, our context (like
Zbigniew Jędrzejewski-Szmek 62fe94
+         * DRM-FBs and BOs) is preserved.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd;;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: erroneous ResumeDevice signal",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          session->name, cm->card.base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* not our device? */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (makedev(major, minor) != cm->devnum)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (cm->card.fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* This shouldn't happen. We should already own an FD from
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * TakeDevice(). However, lets be safe and use this FD in case
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * we really don't have one. There is no harm in doing this
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * and our code works fine this way. */
Zbigniew Jędrzejewski-Szmek 62fe94
+                fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s/%s: cannot duplicate fd: %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  session->name, cm->card.base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = grdrm_card_open(&cm->card, fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("grdrm: %s/%s: cannot open: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  session->name, cm->card.base.name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->master = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (cm->card.base.enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_card_enable(&cm->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_card_setup_bus(managed_card *cm) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session *session = cm->card.base.session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_free_ char *match = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        match = strjoin("type='signal',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "sender='org.freedesktop.login1',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "interface='org.freedesktop.login1.Session',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "member='PauseDevice',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "path='", session->path, "'",
Zbigniew Jędrzejewski-Szmek 62fe94
+                        NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!match)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_add_match(session->context->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             &cm->slot_pause_device,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             match,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             managed_card_pause_device_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             cm);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(match);
Zbigniew Jędrzejewski-Szmek 62fe94
+        match = strjoin("type='signal',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "sender='org.freedesktop.login1',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "interface='org.freedesktop.login1.Session',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "member='ResumeDevice',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "path='", session->path, "'",
Zbigniew Jędrzejewski-Szmek 62fe94
+                        NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!match)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_add_match(session->context->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             &cm->slot_resume_device,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             match,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             managed_card_resume_device_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             cm);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_card_take_device_fn(sd_bus *bus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                       sd_bus_message *reply,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                       void *userdata,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                       sd_bus_error *ret_error) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_card *cm = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session *session = cm->card.base.session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, paused, fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->slot_take_device = sd_bus_slot_unref(cm->slot_take_device);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (sd_bus_message_is_method_error(reply, NULL)) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                const sd_bus_error *error = sd_bus_message_get_error(reply);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: TakeDevice failed: %s: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          session->name, cm->card.base.name, error->name, error->message);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->acquired = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_read(reply, "hb", &fd, &paused);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: erroneous TakeDevice reply",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          session->name, cm->card.base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: cannot duplicate fd: %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          session->name, cm->card.base.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdrm_card_open(&cm->card, fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: cannot open: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          session->name, cm->card.base.name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!paused && cm->card.base.enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdrm_card_enable(&cm->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void managed_card_take_device(managed_card *cm) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session *session = cm->card.base.session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_new_method_call(session->context->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           &m,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "org.freedesktop.login1",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           session->path,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "org.freedesktop.login1.Session",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "TakeDevice");
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_append(m, "uu", major(cm->devnum), minor(cm->devnum));
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_call_async(session->context->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              &cm->slot_take_device,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              m,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              managed_card_take_device_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              cm,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->requested = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+error:
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdrm: %s/%s: cannot send TakeDevice request: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                  session->name, cm->card.base.name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void managed_card_release_device(managed_card *cm) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session *session = cm->card.base.session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * If TakeDevice() is pending or was successful, make sure to
Zbigniew Jędrzejewski-Szmek 62fe94
+         * release the device again. We don't care for return-values,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * so send it without waiting or callbacks.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * If a failed TakeDevice() is pending, but someone else took
Zbigniew Jędrzejewski-Szmek 62fe94
+         * the device on the same bus-connection, we might incorrectly
Zbigniew Jędrzejewski-Szmek 62fe94
+         * release their device. This is an unlikely race, though.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Furthermore, you really shouldn't have two users of the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * controller-API on the same session, on the same devices, *AND* on
Zbigniew Jędrzejewski-Szmek 62fe94
+         * the same bus-connection. So we don't care for that race..
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_close(&cm->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->requested = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!cm->acquired && !cm->slot_take_device)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->slot_take_device = sd_bus_slot_unref(cm->slot_take_device);
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->acquired = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_new_method_call(session->context->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           &m,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "org.freedesktop.login1",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           session->path,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "org.freedesktop.login1.Session",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "ReleaseDevice");
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r >= 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = sd_bus_message_append(m, "uu", major(cm->devnum), minor(cm->devnum));
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r >= 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = sd_bus_send(session->context->sysbus, m, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0 && r != -ENOTCONN)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdrm: %s/%s: cannot send ReleaseDevice: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          session->name, cm->card.base.name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_card_freep) grdev_card *basecard = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char name[GRDRM_CARD_NAME_MAX];
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_card *cm;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(session, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(session->managed, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(session->context->sysbus, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(ud, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnum = udev_device_get_devnum(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (devnum == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENODEV;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_name(name, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm = new0(managed_card, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!cm)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        basecard = &cm->card.base;
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->card = GRDRM_CARD_INIT(&managed_card_vtable, session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->devnum = devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = managed_card_setup_bus(cm);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdrm_card_add(&cm->card, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_card_take_device(cm);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = basecard;
Zbigniew Jędrzejewski-Szmek 62fe94
+        basecard = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void managed_card_free(grdev_card *basecard) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_card *cm = managed_card_from_base(basecard);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!basecard->enabled);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_card_release_device(cm);
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->slot_resume_device = sd_bus_slot_unref(cm->slot_resume_device);
Zbigniew Jędrzejewski-Szmek 62fe94
+        cm->slot_pause_device = sd_bus_slot_unref(cm->slot_pause_device);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_card_destroy(&cm->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(cm);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static const grdev_card_vtable managed_card_vtable = {
Zbigniew Jędrzejewski-Szmek 62fe94
+        .free                   = managed_card_free,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .enable                 = managed_card_enable,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .disable                = managed_card_disable,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .commit                 = grdrm_card_commit,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .restore                = grdrm_card_restore,
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Generic Constructor
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Instead of relying on the caller to choose between managed and unmanaged
Zbigniew Jędrzejewski-Szmek 62fe94
+ * DRM devices, the grdev_drm_new() constructor does that for you (by
Zbigniew Jędrzejewski-Szmek 62fe94
+ * looking at session->managed).
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+bool grdev_is_drm_card(grdev_card *basecard) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        return basecard && (basecard->vtable == &unmanaged_card_vtable ||
Zbigniew Jędrzejewski-Szmek 62fe94
+                            basecard->vtable == &managed_card_vtable);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_card *grdev_find_drm_card(grdev_session *session, dev_t devnum) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        char name[GRDRM_CARD_NAME_MAX];
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(session, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(devnum != 0, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdrm_name(name, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+        return grdev_find_card(session, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_drm_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(session, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(ud, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return session->managed ? managed_card_new(out, session, ud) : unmanaged_card_new(out, session, ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/grdev-internal.h b/src/libsystemd-terminal/grdev-internal.h
Zbigniew Jędrzejewski-Szmek 62fe94
index 7e69c49b63..0064f0be02 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/src/libsystemd-terminal/grdev-internal.h
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/grdev-internal.h
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -22,6 +22,7 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 #pragma once
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <libudev.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -40,6 +41,14 @@ typedef struct grdev_card_vtable        grdev_card_vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
 typedef struct grdev_card               grdev_card;
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 /*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * DRM cards
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+bool grdev_is_drm_card(grdev_card *card);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_card *grdev_find_drm_card(grdev_session *session, dev_t devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_drm_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
  * Displays
Zbigniew Jędrzejewski-Szmek 62fe94
  */
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
Zbigniew Jędrzejewski-Szmek 62fe94
index ab1c407ecb..1e02a6799c 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/src/libsystemd-terminal/grdev.c
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/grdev.c
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -20,6 +20,7 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 ***/
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <libudev.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -30,6 +31,7 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 #include "hashmap.h"
Zbigniew Jędrzejewski-Szmek 62fe94
 #include "login-shared.h"
Zbigniew Jędrzejewski-Szmek 62fe94
 #include "macro.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "udev-util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
 #include "util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 static void pipe_enable(grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -1083,6 +1085,68 @@ void grdev_session_restore(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
                         card->vtable->restore(card);
Zbigniew Jędrzejewski-Szmek 62fe94
 }
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnum = udev_device_get_devnum(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (devnum == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card = grdev_find_drm_card(session, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (card)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdev_drm_card_new(&card, session, ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdev: %s: cannot add DRM device for %s: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          session->name, udev_device_get_syspath(ud), strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session_add_card(session, card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnum = udev_device_get_devnum(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (devnum == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card = grdev_find_drm_card(session, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!card)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session_remove_card(session, card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnum = udev_device_get_devnum(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (devnum == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card = grdev_find_drm_card(session, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!card)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* TODO: hotplug card */
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
 static void session_configure(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
         grdev_display *display;
Zbigniew Jędrzejewski-Szmek 62fe94
         grdev_tile *tile;
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/grdev.h b/src/libsystemd-terminal/grdev.h
Zbigniew Jędrzejewski-Szmek 62fe94
index 2645b12113..9924a257b6 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/src/libsystemd-terminal/grdev.h
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/grdev.h
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -55,6 +55,7 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <drm_fourcc.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <libudev.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -171,6 +172,10 @@ void grdev_session_disable(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
 void grdev_session_commit(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
 void grdev_session_restore(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_add_drm(grdev_session *session, struct udev_device *ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
 /*
Zbigniew Jędrzejewski-Szmek 62fe94
  * Contexts
Zbigniew Jędrzejewski-Szmek 62fe94
  */