1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <systemd/sd-bus.h>
27 #include <systemd/sd-event.h>
28 #include <systemd/sd-login.h>
30 #include "grdev-internal.h"
32 #include "login-shared.h"
34 #include "udev-util.h"
37 static void pipe_enable(grdev_pipe *pipe);
38 static void pipe_disable(grdev_pipe *pipe);
39 static void card_modified(grdev_card *card);
40 static void session_frame(grdev_session *session, grdev_display *display);
46 static inline grdev_tile *tile_leftmost(grdev_tile *tile) {
50 while (tile->type == GRDEV_TILE_NODE && tile->node.child_list)
51 tile = tile->node.child_list;
56 #define TILE_FOREACH(_root, _i) \
57 for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->children_by_node_next) ? : _i->parent)
59 #define TILE_FOREACH_SAFE(_root, _i, _next) \
60 for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->children_by_node_next) ? : _i->parent), true); _i = _next)
62 static void tile_link(grdev_tile *tile, grdev_tile *parent) {
63 grdev_display *display;
67 assert(!tile->parent);
68 assert(!tile->display);
70 assert(parent->type == GRDEV_TILE_NODE);
72 display = parent->display;
74 assert(!display || !display->enabled);
76 ++parent->node.n_children;
77 LIST_PREPEND(children_by_node, parent->node.child_list, tile);
78 tile->parent = parent;
81 display->modified = true;
82 TILE_FOREACH(tile, t) {
84 if (t->type == GRDEV_TILE_LEAF) {
87 pipe_enable(t->leaf.pipe);
93 static void tile_unlink(grdev_tile *tile) {
94 grdev_tile *parent, *t;
95 grdev_display *display;
99 display = tile->display;
100 parent = tile->parent;
106 assert(parent->type == GRDEV_TILE_NODE);
107 assert(parent->display == display);
108 assert(parent->node.n_children > 0);
110 --parent->node.n_children;
111 LIST_REMOVE(children_by_node, parent->node.child_list, tile);
115 display->modified = true;
116 TILE_FOREACH(tile, t) {
118 if (t->type == GRDEV_TILE_LEAF) {
120 t->leaf.pipe->cache = NULL;
121 pipe_disable(t->leaf.pipe);
126 /* Tile trees are driven by leafs. Internal nodes have no owner, thus,
127 * we must take care to not leave them around. Therefore, whenever we
128 * unlink any part of a tree, we also destroy the parent, in case it's
130 * Parents are stale if they have no children and either have no display
131 * or if they are intermediate nodes (i.e, they have a parent).
132 * This means, you can easily create trees, but you can never partially
133 * move or destruct them so far. They're always reduced to minimal form
134 * if you cut them. This might change later, but so far we didn't need
135 * partial destruction or the ability to move whole trees. */
137 if (parent->node.n_children < 1 && (parent->parent || !parent->display))
138 grdev_tile_free(parent);
141 static int tile_new(grdev_tile **out) {
142 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
146 tile = new0(grdev_tile, 1);
150 tile->type = (unsigned)-1;
157 int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe) {
158 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
161 assert_return(out, -EINVAL);
162 assert_return(pipe, -EINVAL);
163 assert_return(!pipe->tile, -EINVAL);
169 tile->type = GRDEV_TILE_LEAF;
170 tile->leaf.pipe = pipe;
178 int grdev_tile_new_node(grdev_tile **out) {
179 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
182 assert_return(out, -EINVAL);
188 tile->type = GRDEV_TILE_NODE;
195 grdev_tile *grdev_tile_free(grdev_tile *tile) {
201 switch (tile->type) {
202 case GRDEV_TILE_LEAF:
203 assert(!tile->parent);
204 assert(!tile->display);
205 assert(tile->leaf.pipe);
208 case GRDEV_TILE_NODE:
209 assert(!tile->parent);
210 assert(!tile->display);
211 assert(tile->node.n_children == 0);
221 grdev_display *grdev_find_display(grdev_session *session, const char *name) {
222 assert_return(session, NULL);
223 assert_return(name, NULL);
225 return hashmap_get(session->display_map, name);
228 int grdev_display_new(grdev_display **out, grdev_session *session, const char *name) {
229 _cleanup_(grdev_display_freep) grdev_display *display = NULL;
235 display = new0(grdev_display, 1);
239 display->session = session;
241 display->name = strdup(name);
245 r = grdev_tile_new_node(&display->tile);
249 display->tile->display = display;
251 r = hashmap_put(session->display_map, display->name, display);
261 grdev_display *grdev_display_free(grdev_display *display) {
265 assert(!display->public);
266 assert(!display->enabled);
267 assert(!display->modified);
268 assert(display->n_leafs == 0);
269 assert(display->n_pipes == 0);
272 hashmap_remove_value(display->session->display_map, display->name, display);
275 display->tile->display = NULL;
276 grdev_tile_free(display->tile);
279 free(display->pipes);
286 void grdev_display_set_userdata(grdev_display *display, void *userdata) {
289 display->userdata = userdata;
292 void *grdev_display_get_userdata(grdev_display *display) {
293 assert_return(display, NULL);
295 return display->userdata;
298 const char *grdev_display_get_name(grdev_display *display) {
299 assert_return(display, NULL);
301 return display->name;
304 uint32_t grdev_display_get_width(grdev_display *display) {
305 assert_return(display, 0);
307 return display->width;
310 uint32_t grdev_display_get_height(grdev_display *display) {
311 assert_return(display, 0);
313 return display->height;
316 bool grdev_display_is_enabled(grdev_display *display) {
317 return display && display->enabled;
320 void grdev_display_enable(grdev_display *display) {
325 if (!display->enabled) {
326 display->enabled = true;
327 TILE_FOREACH(display->tile, t)
328 if (t->type == GRDEV_TILE_LEAF)
329 pipe_enable(t->leaf.pipe);
333 void grdev_display_disable(grdev_display *display) {
338 if (display->enabled) {
339 display->enabled = false;
340 TILE_FOREACH(display->tile, t)
341 if (t->type == GRDEV_TILE_LEAF)
342 pipe_disable(t->leaf.pipe);
346 const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev) {
347 grdev_display_cache *cache;
350 assert_return(display, NULL);
351 assert_return(!display->modified, NULL);
352 assert_return(display->enabled, NULL);
355 cache = container_of(prev, grdev_display_cache, target);
358 assert(cache->pipe->tile->display == display);
359 assert(display->pipes >= cache);
361 idx = cache - display->pipes + 1;
366 for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) {
367 grdev_display_target *target;
372 target = &cache->target;
374 if (!pipe->running || !pipe->enabled)
377 /* find suitable back-buffer */
379 if (!pipe->vtable->target)
381 if (!(fb = pipe->vtable->target(pipe)))
384 assert(fb == pipe->back);
387 target->front = pipe->front;
388 target->back = pipe->back;
396 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target) {
397 grdev_display_cache *cache;
400 assert(!display->modified);
401 assert(display->enabled);
404 cache = container_of(target, grdev_display_cache, target);
407 assert(cache->pipe->tile->display == display);
409 cache->pipe->flip = true;
412 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
413 uint32_t x, y, width, height;
414 grdev_display_target *t;
418 assert(l->cache_w >= c->target.width + c->target.x);
419 assert(l->cache_h >= c->target.height + c->target.y);
425 t->rotate = (t->rotate + l->rotate) & 0x3;
435 case GRDEV_ROTATE_90:
436 t->x = l->cache_h - (height + y);
441 case GRDEV_ROTATE_180:
442 t->x = l->cache_w - (width + x);
443 t->y = l->cache_h - (height + y);
445 case GRDEV_ROTATE_270:
447 t->y = l->cache_w - (width + x);
457 if (l->flip & GRDEV_FLIP_HORIZONTAL)
458 t->x = l->cache_w - (t->width + t->x);
459 if (l->flip & GRDEV_FLIP_VERTICAL)
460 t->y = l->cache_h - (t->height + t->y);
468 static void display_cache_targets(grdev_display *display) {
469 grdev_display_cache *c;
474 /* depth-first with children before parent */
475 for (tile = tile_leftmost(display->tile);
477 tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
478 if (tile->type == GRDEV_TILE_LEAF) {
481 /* We're at a leaf and no parent has been cached, yet.
482 * Copy the pipe information into the target cache and
483 * update our global pipe-caches if required. */
485 assert(tile->leaf.pipe);
486 assert(display->n_pipes + 1 <= display->max_pipes);
489 c = &display->pipes[display->n_pipes++];
494 c->target.width = p->width;
495 c->target.height = p->height;
496 tile->cache_w = p->width;
497 tile->cache_h = p->height;
499 /* all new tiles are incomplete due to geometry changes */
500 c->incomplete = true;
502 display_cache_apply(c, tile);
504 grdev_tile *child, *l;
506 /* We're now at a node with all its children already
507 * computed (depth-first, child before parent). We
508 * first need to know the size of our tile, then we
509 * recurse into all leafs and update their cache. */
514 LIST_FOREACH(children_by_node, child, tile->node.child_list) {
515 if (child->x + child->cache_w > tile->cache_w)
516 tile->cache_w = child->x + child->cache_w;
517 if (child->y + child->cache_h > tile->cache_h)
518 tile->cache_h = child->y + child->cache_h;
521 assert(tile->cache_w > 0);
522 assert(tile->cache_h > 0);
524 TILE_FOREACH(tile, l)
525 if (l->type == GRDEV_TILE_LEAF)
526 display_cache_apply(l->leaf.pipe->cache, tile);
531 static bool display_cache(grdev_display *display) {
539 if (!display->modified)
542 display->modified = false;
543 display->framed = false;
544 display->n_pipes = 0;
548 if (display->n_leafs < 1)
551 TILE_FOREACH(display->tile, tile)
552 if (tile->type == GRDEV_TILE_LEAF)
553 tile->leaf.pipe->cache = NULL;
555 if (display->n_leafs > display->max_pipes) {
556 n = ALIGN_POWER2(display->n_leafs);
562 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
569 display->max_pipes = n;
572 display_cache_targets(display);
573 display->width = display->tile->cache_w;
574 display->height = display->tile->cache_h;
580 log_debug_errno(r, "grdev: %s/%s: cannot cache pipes: %m",
581 display->session->name, display->name);
589 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
590 assert_return(card, NULL);
591 assert_return(name, NULL);
593 return hashmap_get(card->pipe_map, name);
596 static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
597 grdev_pipe *pipe = userdata;
599 grdev_pipe_frame(pipe);
603 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
606 assert_return(pipe, -EINVAL);
607 assert_return(pipe->vtable, -EINVAL);
608 assert_return(pipe->vtable->free, -EINVAL);
609 assert_return(pipe->card, -EINVAL);
610 assert_return(pipe->card->session, -EINVAL);
611 assert_return(!pipe->cache, -EINVAL);
612 assert_return(pipe->width > 0, -EINVAL);
613 assert_return(pipe->height > 0, -EINVAL);
614 assert_return(pipe->vrefresh > 0, -EINVAL);
615 assert_return(!pipe->enabled, -EINVAL);
616 assert_return(!pipe->running, -EINVAL);
617 assert_return(name, -EINVAL);
619 pipe->name = strdup(name);
624 pipe->fbs = new0(grdev_fb*, n_fbs);
628 pipe->max_fbs = n_fbs;
631 r = grdev_tile_new_leaf(&pipe->tile, pipe);
635 r = sd_event_add_time(pipe->card->session->context->event,
645 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
649 r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
653 card_modified(pipe->card);
657 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
664 assert(pipe->vtable);
665 assert(pipe->vtable->free);
668 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
670 tile_unlink(pipe->tile);
672 assert(!pipe->cache);
675 pipe->vtable->free(pipe);
677 sd_event_source_unref(tmp.vsync_src);
678 grdev_tile_free(tmp.tile);
679 card_modified(tmp.card);
686 static void pipe_enable(grdev_pipe *pipe) {
689 if (!pipe->enabled) {
690 pipe->enabled = true;
691 if (pipe->vtable->enable)
692 pipe->vtable->enable(pipe);
696 static void pipe_disable(grdev_pipe *pipe) {
700 pipe->enabled = false;
701 if (pipe->vtable->disable)
702 pipe->vtable->disable(pipe);
706 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
709 /* grdev_pipe_ready() is used by backends to notify about pipe state
710 * changed. If a pipe is ready, it can be fully used by us (available,
711 * enabled and accessible). Backends can disable pipes at any time
712 * (like for async revocation), but can only enable them from parent
713 * context. Otherwise, we might call user-callbacks recursively. */
715 if (pipe->running == running)
718 pipe->running = running;
720 /* runtime events for unused pipes are not interesting */
721 if (pipe->cache && pipe->enabled) {
722 grdev_display *display = pipe->tile->display;
727 session_frame(display->session, display);
729 pipe->cache->incomplete = true;
733 void grdev_pipe_frame(grdev_pipe *pipe) {
734 grdev_display *display;
738 /* if pipe is unused, ignore any frame events */
739 if (!pipe->cache || !pipe->enabled)
742 display = pipe->tile->display;
745 grdev_pipe_schedule(pipe, 0);
746 session_frame(display->session, display);
749 void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
754 sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
758 r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
762 ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
764 r = sd_event_source_set_time(pipe->vsync_src, ts);
768 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
775 log_debug_errno(r, "grdev: %s/%s/%s: cannot schedule vsync timer: %m",
776 pipe->card->session->name, pipe->card->name, pipe->name);
783 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
784 assert_return(session, NULL);
785 assert_return(name, NULL);
787 return hashmap_get(session->card_map, name);
790 int grdev_card_add(grdev_card *card, const char *name) {
793 assert_return(card, -EINVAL);
794 assert_return(card->vtable, -EINVAL);
795 assert_return(card->vtable->free, -EINVAL);
796 assert_return(card->session, -EINVAL);
797 assert_return(name, -EINVAL);
799 card->name = strdup(name);
803 card->pipe_map = hashmap_new(&string_hash_ops);
807 r = hashmap_put(card->session->card_map, card->name, card);
814 grdev_card *grdev_card_free(grdev_card *card) {
820 assert(!card->enabled);
821 assert(card->vtable);
822 assert(card->vtable->free);
825 hashmap_remove_value(card->session->card_map, card->name, card);
828 card->vtable->free(card);
830 assert(hashmap_size(tmp.pipe_map) == 0);
832 hashmap_free(tmp.pipe_map);
838 static void card_modified(grdev_card *card) {
840 assert(card->session->n_pins > 0);
842 card->modified = true;
845 static void grdev_card_enable(grdev_card *card) {
848 if (!card->enabled) {
849 card->enabled = true;
850 if (card->vtable->enable)
851 card->vtable->enable(card);
855 static void grdev_card_disable(grdev_card *card) {
859 card->enabled = false;
860 if (card->vtable->disable)
861 card->vtable->disable(card);
869 static void session_raise(grdev_session *session, grdev_event *event) {
870 session->event_fn(session, session->userdata, event);
873 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
874 grdev_event event = {
875 .type = GRDEV_EVENT_DISPLAY_ADD,
881 session_raise(session, &event);
884 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
885 grdev_event event = {
886 .type = GRDEV_EVENT_DISPLAY_REMOVE,
892 session_raise(session, &event);
895 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
896 grdev_event event = {
897 .type = GRDEV_EVENT_DISPLAY_CHANGE,
903 session_raise(session, &event);
906 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
907 grdev_event event = {
908 .type = GRDEV_EVENT_DISPLAY_FRAME,
914 session_raise(session, &event);
917 static void session_add_card(grdev_session *session, grdev_card *card) {
921 log_debug("grdev: %s: add card '%s'", session->name, card->name);
923 /* Cards are not exposed to users, but managed internally. Cards are
924 * enabled if the session is enabled, and will track that state. The
925 * backend can probe the card at any time, but only if enabled. It
926 * will then add pipes according to hardware state.
927 * That is, the card may create pipes as soon as we enable it here. */
929 if (session->enabled)
930 grdev_card_enable(card);
933 static void session_remove_card(grdev_session *session, grdev_card *card) {
937 log_debug("grdev: %s: remove card '%s'", session->name, card->name);
939 /* As cards are not exposed, it can never be accessed by outside
940 * users and we can simply remove it. Disabling the card does not
941 * necessarily drop all pipes of the card. This is usually deferred
942 * to card destruction (as pipes are cached as long as FDs remain
943 * open). Therefore, the card destruction might cause pipes, and thus
944 * visible displays, to be removed. */
946 grdev_card_disable(card);
947 grdev_card_free(card);
950 static void session_add_display(grdev_session *session, grdev_display *display) {
953 assert(!display->enabled);
955 log_debug("grdev: %s: add display '%s'", session->name, display->name);
957 /* Displays are the main entity for public API users. We create them
958 * independent of card backends and they wrap any underlying display
959 * architecture. Displays are public at all times, thus, may be entered
960 * by outside users at any time. */
962 display->public = true;
963 session_raise_display_add(session, display);
966 static void session_remove_display(grdev_session *session, grdev_display *display) {
970 log_debug("grdev: %s: remove display '%s'", session->name, display->name);
972 /* Displays are public, so we have to be careful when removing them.
973 * We first tell users about their removal, disable them and then drop
974 * them. We now, after the notification, no external access will
975 * happen. Therefore, we can release the tiles afterwards safely. */
977 if (display->public) {
978 display->public = false;
979 session_raise_display_remove(session, display);
982 grdev_display_disable(display);
983 grdev_display_free(display);
986 static void session_change_display(grdev_session *session, grdev_display *display) {
992 changed = display_cache(display);
994 if (display->n_leafs == 0) {
995 session_remove_display(session, display);
996 } else if (!display->public) {
997 session_add_display(session, display);
998 session_frame(session, display);
999 } else if (changed) {
1000 session_raise_display_change(session, display);
1001 session_frame(session, display);
1002 } else if (display->framed) {
1003 session_frame(session, display);
1007 static void session_frame(grdev_session *session, grdev_display *display) {
1011 display->framed = false;
1013 if (!display->enabled || !session->enabled)
1016 if (session->n_pins > 0)
1017 display->framed = true;
1019 session_raise_display_frame(session, display);
1022 int grdev_session_new(grdev_session **out,
1023 grdev_context *context,
1026 grdev_event_fn event_fn,
1028 _cleanup_(grdev_session_freep) grdev_session *session = NULL;
1035 assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
1036 assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
1037 assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
1039 session = new0(grdev_session, 1);
1043 session->context = grdev_context_ref(context);
1044 session->custom = flags & GRDEV_SESSION_CUSTOM;
1045 session->managed = flags & GRDEV_SESSION_MANAGED;
1046 session->event_fn = event_fn;
1047 session->userdata = userdata;
1049 session->name = strdup(name);
1053 if (session->managed) {
1054 r = sd_bus_path_encode("/org/freedesktop/login1/session",
1055 session->name, &session->path);
1060 session->card_map = hashmap_new(&string_hash_ops);
1061 if (!session->card_map)
1064 session->display_map = hashmap_new(&string_hash_ops);
1065 if (!session->display_map)
1068 r = hashmap_put(context->session_map, session->name, session);
1077 grdev_session *grdev_session_free(grdev_session *session) {
1083 grdev_session_disable(session);
1085 while ((card = hashmap_first(session->card_map)))
1086 session_remove_card(session, card);
1088 assert(hashmap_size(session->display_map) == 0);
1091 hashmap_remove_value(session->context->session_map, session->name, session);
1093 hashmap_free(session->display_map);
1094 hashmap_free(session->card_map);
1095 session->context = grdev_context_unref(session->context);
1096 free(session->path);
1097 free(session->name);
1103 bool grdev_session_is_enabled(grdev_session *session) {
1104 return session && session->enabled;
1107 void grdev_session_enable(grdev_session *session) {
1113 if (!session->enabled) {
1114 session->enabled = true;
1115 HASHMAP_FOREACH(card, session->card_map, iter)
1116 grdev_card_enable(card);
1120 void grdev_session_disable(grdev_session *session) {
1126 if (session->enabled) {
1127 session->enabled = false;
1128 HASHMAP_FOREACH(card, session->card_map, iter)
1129 grdev_card_disable(card);
1133 void grdev_session_commit(grdev_session *session) {
1139 if (!session->enabled)
1142 HASHMAP_FOREACH(card, session->card_map, iter)
1143 if (card->vtable->commit)
1144 card->vtable->commit(card);
1147 void grdev_session_restore(grdev_session *session) {
1153 if (!session->enabled)
1156 HASHMAP_FOREACH(card, session->card_map, iter)
1157 if (card->vtable->restore)
1158 card->vtable->restore(card);
1161 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1169 devnum = udev_device_get_devnum(ud);
1171 return grdev_session_hotplug_drm(session, ud);
1173 card = grdev_find_drm_card(session, devnum);
1177 r = grdev_drm_card_new(&card, session, ud);
1179 log_debug_errno(r, "grdev: %s: cannot add DRM device for %s: %m",
1180 session->name, udev_device_get_syspath(ud));
1184 session_add_card(session, card);
1187 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1194 devnum = udev_device_get_devnum(ud);
1196 return grdev_session_hotplug_drm(session, ud);
1198 card = grdev_find_drm_card(session, devnum);
1202 session_remove_card(session, card);
1205 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1206 grdev_card *card = NULL;
1207 struct udev_device *p;
1213 for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1214 devnum = udev_device_get_devnum(ud);
1218 card = grdev_find_drm_card(session, devnum);
1226 grdev_drm_card_hotplug(card, ud);
1229 static void session_configure(grdev_session *session) {
1230 grdev_display *display;
1240 * Whenever backends add or remove pipes, we set session->modified and
1241 * require them to pin the session while modifying it. On release, we
1242 * reconfigure the device and re-assign displays to all modified pipes.
1244 * So far, we configure each pipe as a separate display. We do not
1245 * support user-configuration, nor have we gotten any reports from
1246 * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1247 * we get reports, we keep the logic to a minimum.
1250 /* create new displays for all unconfigured pipes */
1251 HASHMAP_FOREACH(card, session->card_map, i) {
1252 if (!card->modified)
1255 card->modified = false;
1257 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1262 assert(!tile->parent);
1264 display = grdev_find_display(session, pipe->name);
1265 if (display && display->tile) {
1266 log_debug("grdev: %s/%s: occupied display for pipe %s",
1267 session->name, card->name, pipe->name);
1269 } else if (!display) {
1270 r = grdev_display_new(&display, session, pipe->name);
1272 log_debug_errno(r, "grdev: %s/%s: cannot create display for pipe %s: %m",
1273 session->name, card->name, pipe->name);
1278 tile_link(pipe->tile, display->tile);
1282 /* update displays */
1283 HASHMAP_FOREACH(display, session->display_map, i)
1284 session_change_display(session, display);
1287 grdev_session *grdev_session_pin(grdev_session *session) {
1294 grdev_session *grdev_session_unpin(grdev_session *session) {
1298 assert(session->n_pins > 0);
1300 if (--session->n_pins == 0)
1301 session_configure(session);
1310 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1311 _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1313 assert_return(out, -EINVAL);
1314 assert_return(event, -EINVAL);
1316 context = new0(grdev_context, 1);
1321 context->event = sd_event_ref(event);
1324 context->sysbus = sd_bus_ref(sysbus);
1326 context->session_map = hashmap_new(&string_hash_ops);
1327 if (!context->session_map)
1335 static void context_cleanup(grdev_context *context) {
1336 assert(hashmap_size(context->session_map) == 0);
1338 hashmap_free(context->session_map);
1339 context->sysbus = sd_bus_unref(context->sysbus);
1340 context->event = sd_event_unref(context->event);
1344 grdev_context *grdev_context_ref(grdev_context *context) {
1345 assert_return(context, NULL);
1346 assert_return(context->ref > 0, NULL);
1352 grdev_context *grdev_context_unref(grdev_context *context) {
1356 assert_return(context->ref > 0, NULL);
1358 if (--context->ref == 0)
1359 context_cleanup(context);