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->childs_by_node_next) ? : _i->parent)
59 #define TILE_FOREACH_SAFE(_root, _i, _next) \
60 for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->childs_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_childs;
77 LIST_PREPEND(childs_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_childs > 0);
110 --parent->node.n_childs;
111 LIST_REMOVE(childs_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 childs 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_childs < 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(pipe, -EINVAL);
162 assert_return(!pipe->tile, -EINVAL);
168 tile->type = GRDEV_TILE_LEAF;
169 tile->leaf.pipe = pipe;
177 int grdev_tile_new_node(grdev_tile **out) {
178 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
181 assert_return(out, -EINVAL);
187 tile->type = GRDEV_TILE_NODE;
194 grdev_tile *grdev_tile_free(grdev_tile *tile) {
200 switch (tile->type) {
201 case GRDEV_TILE_LEAF:
202 assert(!tile->parent);
203 assert(!tile->display);
204 assert(tile->leaf.pipe);
207 case GRDEV_TILE_NODE:
208 assert(!tile->parent);
209 assert(!tile->display);
210 assert(tile->node.n_childs == 0);
220 grdev_display *grdev_find_display(grdev_session *session, const char *name) {
221 assert_return(session, NULL);
222 assert_return(name, NULL);
224 return hashmap_get(session->display_map, name);
227 int grdev_display_new(grdev_display **out, grdev_session *session, const char *name) {
228 _cleanup_(grdev_display_freep) grdev_display *display = NULL;
234 display = new0(grdev_display, 1);
238 display->session = session;
240 display->name = strdup(name);
244 r = grdev_tile_new_node(&display->tile);
248 display->tile->display = display;
250 r = hashmap_put(session->display_map, display->name, display);
260 grdev_display *grdev_display_free(grdev_display *display) {
264 assert(!display->public);
265 assert(!display->enabled);
266 assert(!display->modified);
267 assert(display->n_leafs == 0);
268 assert(display->n_pipes == 0);
271 hashmap_remove_value(display->session->display_map, display->name, display);
274 display->tile->display = NULL;
275 grdev_tile_free(display->tile);
278 free(display->pipes);
285 bool grdev_display_is_enabled(grdev_display *display) {
286 return display && display->enabled;
289 void grdev_display_enable(grdev_display *display) {
294 if (!display->enabled) {
295 display->enabled = true;
296 TILE_FOREACH(display->tile, t)
297 if (t->type == GRDEV_TILE_LEAF)
298 pipe_enable(t->leaf.pipe);
302 void grdev_display_disable(grdev_display *display) {
307 if (display->enabled) {
308 display->enabled = false;
309 TILE_FOREACH(display->tile, t)
310 if (t->type == GRDEV_TILE_LEAF)
311 pipe_disable(t->leaf.pipe);
315 const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev, uint64_t minage) {
316 grdev_display_cache *cache;
319 assert_return(display, NULL);
320 assert_return(!display->modified, NULL);
321 assert_return(display->enabled, NULL);
324 cache = container_of(prev, grdev_display_cache, target);
327 assert(cache->pipe->tile->display == display);
328 assert(display->pipes >= cache);
330 idx = (cache - display->pipes) / sizeof(*cache) + 1;
335 for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) {
336 grdev_display_target *target;
341 target = &cache->target;
343 if (!pipe->running || !pipe->enabled)
346 /* if front-buffer is up-to-date, there's nothing to do */
347 if (minage > 0 && pipe->front && pipe->front->age >= minage)
350 /* find suitable back-buffer */
351 if (!(fb = pipe->back)) {
352 if (!pipe->vtable->target || !(fb = pipe->vtable->target(pipe)))
356 /* if back-buffer is up-to-date, schedule flip */
357 if (minage > 0 && fb->age >= minage) {
358 grdev_display_flip_target(display, target, fb->age);
362 /* we have an out-of-date back-buffer; return for redraw */
370 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
371 grdev_display_cache *cache;
375 assert(!display->modified);
376 assert(display->enabled);
380 cache = container_of(target, grdev_display_cache, target);
383 assert(cache->pipe->tile->display == display);
385 /* reset age of all FB on overflow */
386 if (age < target->fb->age)
387 for (i = 0; i < cache->pipe->max_fbs; ++i)
388 if (cache->pipe->fbs[i])
389 cache->pipe->fbs[i]->age = 0;
391 ((grdev_fb*)target->fb)->age = age;
392 cache->pipe->flip = true;
395 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
396 uint32_t x, y, width, height;
397 grdev_display_target *t;
401 assert(l->cache_w >= c->target.width + c->target.x);
402 assert(l->cache_h >= c->target.height + c->target.y);
408 t->rotate = (t->rotate + l->rotate) & 0x3;
418 case GRDEV_ROTATE_90:
419 t->x = l->cache_h - (height + y);
424 case GRDEV_ROTATE_180:
425 t->x = l->cache_w - (width + x);
426 t->y = l->cache_h - (height + y);
428 case GRDEV_ROTATE_270:
430 t->y = l->cache_w - (width + x);
440 if (l->flip & GRDEV_FLIP_HORIZONTAL)
441 t->x = l->cache_w - (t->width + t->x);
442 if (l->flip & GRDEV_FLIP_VERTICAL)
443 t->y = l->cache_h - (t->height + t->y);
451 static void display_cache_targets(grdev_display *display) {
452 grdev_display_cache *c;
457 /* depth-first with childs before parent */
458 for (tile = tile_leftmost(display->tile);
460 tile = tile_leftmost(tile->childs_by_node_next) ? : tile->parent) {
461 if (tile->type == GRDEV_TILE_LEAF) {
464 /* We're at a leaf and no parent has been cached, yet.
465 * Copy the pipe information into the target cache and
466 * update our global pipe-caches if required. */
468 assert(tile->leaf.pipe);
469 assert(display->n_pipes + 1 <= display->max_pipes);
472 c = &display->pipes[display->n_pipes++];
477 c->target.width = p->width;
478 c->target.height = p->height;
479 tile->cache_w = p->width;
480 tile->cache_h = p->height;
482 /* all new tiles are incomplete due to geometry changes */
483 c->incomplete = true;
485 display_cache_apply(c, tile);
487 grdev_tile *child, *l;
489 /* We're now at a node with all it's childs already
490 * computed (depth-first, child before parent). We
491 * first need to know the size of our tile, then we
492 * recurse into all leafs and update their cache. */
497 LIST_FOREACH(childs_by_node, child, tile->node.child_list) {
498 if (child->x + child->cache_w > tile->cache_w)
499 tile->cache_w = child->x + child->cache_w;
500 if (child->y + child->cache_h > tile->cache_h)
501 tile->cache_h = child->y + child->cache_h;
504 assert(tile->cache_w > 0);
505 assert(tile->cache_h > 0);
507 TILE_FOREACH(tile, l)
508 if (l->type == GRDEV_TILE_LEAF)
509 display_cache_apply(l->leaf.pipe->cache, tile);
514 static bool display_cache(grdev_display *display) {
522 if (!display->modified)
525 display->modified = false;
526 display->framed = false;
527 display->n_pipes = 0;
531 if (display->n_leafs < 1)
534 TILE_FOREACH(display->tile, tile)
535 if (tile->type == GRDEV_TILE_LEAF)
536 tile->leaf.pipe->cache = NULL;
538 if (display->n_leafs > display->max_pipes) {
539 n = ALIGN_POWER2(display->n_leafs);
545 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
552 display->max_pipes = n;
555 display_cache_targets(display);
561 log_debug("grdev: %s/%s: cannot cache pipes: %s",
562 display->session->name, display->name, strerror(-r));
570 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
571 assert_return(card, NULL);
572 assert_return(name, NULL);
574 return hashmap_get(card->pipe_map, name);
577 static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
578 grdev_pipe *pipe = userdata;
580 grdev_pipe_frame(pipe);
584 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
587 assert_return(pipe, -EINVAL);
588 assert_return(pipe->vtable, -EINVAL);
589 assert_return(pipe->vtable->free, -EINVAL);
590 assert_return(pipe->card, -EINVAL);
591 assert_return(pipe->card->session, -EINVAL);
592 assert_return(!pipe->cache, -EINVAL);
593 assert_return(pipe->width > 0, -EINVAL);
594 assert_return(pipe->height > 0, -EINVAL);
595 assert_return(pipe->vrefresh > 0, -EINVAL);
596 assert_return(!pipe->enabled, -EINVAL);
597 assert_return(!pipe->running, -EINVAL);
598 assert_return(name, -EINVAL);
600 pipe->name = strdup(name);
605 pipe->fbs = new0(grdev_fb*, n_fbs);
609 pipe->max_fbs = n_fbs;
612 r = grdev_tile_new_leaf(&pipe->tile, pipe);
616 r = sd_event_add_time(pipe->card->session->context->event,
626 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
630 r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
634 card_modified(pipe->card);
638 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
645 assert(pipe->vtable);
646 assert(pipe->vtable->free);
649 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
651 tile_unlink(pipe->tile);
653 assert(!pipe->cache);
656 pipe->vtable->free(pipe);
658 sd_event_source_unref(tmp.vsync_src);
659 grdev_tile_free(tmp.tile);
660 card_modified(tmp.card);
667 static void pipe_enable(grdev_pipe *pipe) {
670 if (!pipe->enabled) {
671 pipe->enabled = true;
672 if (pipe->vtable->enable)
673 pipe->vtable->enable(pipe);
677 static void pipe_disable(grdev_pipe *pipe) {
681 pipe->enabled = false;
682 if (pipe->vtable->disable)
683 pipe->vtable->disable(pipe);
687 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
690 /* grdev_pipe_ready() is used by backends to notify about pipe state
691 * changed. If a pipe is ready, it can be fully used by us (available,
692 * enabled and accessable). Backends can disable pipes at any time
693 * (like for async revocation), but can only enable them from parent
694 * context. Otherwise, we might call user-callbacks recursively. */
696 if (pipe->running == running)
699 pipe->running = running;
701 /* runtime events for unused pipes are not interesting */
702 if (pipe->cache && pipe->enabled) {
703 grdev_display *display = pipe->tile->display;
708 session_frame(display->session, display);
710 pipe->cache->incomplete = true;
714 void grdev_pipe_frame(grdev_pipe *pipe) {
715 grdev_display *display;
719 /* if pipe is unused, ignore any frame events */
720 if (!pipe->cache || !pipe->enabled)
723 display = pipe->tile->display;
726 grdev_pipe_schedule(pipe, 0);
727 session_frame(display->session, display);
730 void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
735 sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
739 r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
743 ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
745 r = sd_event_source_set_time(pipe->vsync_src, ts);
749 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
756 log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s",
757 pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r));
764 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
765 assert_return(session, NULL);
766 assert_return(name, NULL);
768 return hashmap_get(session->card_map, name);
771 int grdev_card_add(grdev_card *card, const char *name) {
774 assert_return(card, -EINVAL);
775 assert_return(card->vtable, -EINVAL);
776 assert_return(card->vtable->free, -EINVAL);
777 assert_return(card->session, -EINVAL);
778 assert_return(name, -EINVAL);
780 card->name = strdup(name);
784 card->pipe_map = hashmap_new(&string_hash_ops);
788 r = hashmap_put(card->session->card_map, card->name, card);
795 grdev_card *grdev_card_free(grdev_card *card) {
801 assert(!card->enabled);
802 assert(card->vtable);
803 assert(card->vtable->free);
806 hashmap_remove_value(card->session->card_map, card->name, card);
809 card->vtable->free(card);
811 assert(hashmap_size(tmp.pipe_map) == 0);
813 hashmap_free(tmp.pipe_map);
819 static void card_modified(grdev_card *card) {
821 assert(card->session->n_pins > 0);
823 card->modified = true;
826 static void grdev_card_enable(grdev_card *card) {
829 if (!card->enabled) {
830 card->enabled = true;
831 if (card->vtable->enable)
832 card->vtable->enable(card);
836 static void grdev_card_disable(grdev_card *card) {
840 card->enabled = false;
841 if (card->vtable->disable)
842 card->vtable->disable(card);
850 static void session_raise(grdev_session *session, grdev_event *event) {
851 session->event_fn(session, session->userdata, event);
854 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
855 grdev_event event = {
856 .type = GRDEV_EVENT_DISPLAY_ADD,
862 session_raise(session, &event);
865 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
866 grdev_event event = {
867 .type = GRDEV_EVENT_DISPLAY_REMOVE,
873 session_raise(session, &event);
876 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
877 grdev_event event = {
878 .type = GRDEV_EVENT_DISPLAY_CHANGE,
884 session_raise(session, &event);
887 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
888 grdev_event event = {
889 .type = GRDEV_EVENT_DISPLAY_FRAME,
895 session_raise(session, &event);
898 static void session_add_card(grdev_session *session, grdev_card *card) {
902 log_debug("grdev: %s: add card '%s'", session->name, card->name);
904 /* Cards are not exposed to users, but managed internally. Cards are
905 * enabled if the session is enabled, and will track that state. The
906 * backend can probe the card at any time, but only if enabled. It
907 * will then add pipes according to hardware state.
908 * That is, the card may create pipes as soon as we enable it here. */
910 if (session->enabled)
911 grdev_card_enable(card);
914 static void session_remove_card(grdev_session *session, grdev_card *card) {
918 log_debug("grdev: %s: remove card '%s'", session->name, card->name);
920 /* As cards are not exposed, it can never be accessed by outside
921 * users and we can simply remove it. Disabling the card does not
922 * necessarily drop all pipes of the card. This is usually deferred
923 * to card destruction (as pipes are cached as long as FDs remain
924 * open). Therefore, the card destruction might cause pipes, and thus
925 * visible displays, to be removed. */
927 grdev_card_disable(card);
928 grdev_card_free(card);
931 static void session_add_display(grdev_session *session, grdev_display *display) {
934 assert(!display->enabled);
936 log_debug("grdev: %s: add display '%s'", session->name, display->name);
938 /* Displays are the main entity for public API users. We create them
939 * independent of card backends and they wrap any underlying display
940 * architecture. Displays are public at all times, thus, may be entered
941 * by outside users at any time. */
943 display->public = true;
944 session_raise_display_add(session, display);
947 static void session_remove_display(grdev_session *session, grdev_display *display) {
951 log_debug("grdev: %s: remove display '%s'", session->name, display->name);
953 /* Displays are public, so we have to be careful when removing them.
954 * We first tell users about their removal, disable them and then drop
955 * them. We now, after the notification, no external access will
956 * happen. Therefore, we can release the tiles afterwards safely. */
958 if (display->public) {
959 display->public = false;
960 session_raise_display_remove(session, display);
963 grdev_display_disable(display);
964 grdev_display_free(display);
967 static void session_change_display(grdev_session *session, grdev_display *display) {
973 changed = display_cache(display);
975 if (display->n_leafs == 0) {
976 session_remove_display(session, display);
977 } else if (!display->public) {
978 session_add_display(session, display);
979 session_frame(session, display);
980 } else if (changed) {
981 session_raise_display_change(session, display);
982 session_frame(session, display);
983 } else if (display->framed) {
984 session_frame(session, display);
988 static void session_frame(grdev_session *session, grdev_display *display) {
992 display->framed = false;
994 if (!display->enabled || !session->enabled)
997 if (session->n_pins > 0)
998 display->framed = true;
1000 session_raise_display_frame(session, display);
1003 int grdev_session_new(grdev_session **out,
1004 grdev_context *context,
1007 grdev_event_fn event_fn,
1009 _cleanup_(grdev_session_freep) grdev_session *session = NULL;
1016 assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
1017 assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
1018 assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
1020 session = new0(grdev_session, 1);
1024 session->context = grdev_context_ref(context);
1025 session->custom = flags & GRDEV_SESSION_CUSTOM;
1026 session->managed = flags & GRDEV_SESSION_MANAGED;
1027 session->event_fn = event_fn;
1028 session->userdata = userdata;
1030 session->name = strdup(name);
1034 if (session->managed) {
1035 r = sd_bus_path_encode("/org/freedesktop/login1/session",
1036 session->name, &session->path);
1041 session->card_map = hashmap_new(&string_hash_ops);
1042 if (!session->card_map)
1045 session->display_map = hashmap_new(&string_hash_ops);
1046 if (!session->display_map)
1049 r = hashmap_put(context->session_map, session->name, session);
1058 grdev_session *grdev_session_free(grdev_session *session) {
1064 grdev_session_disable(session);
1066 while ((card = hashmap_first(session->card_map)))
1067 session_remove_card(session, card);
1069 assert(hashmap_size(session->display_map) == 0);
1072 hashmap_remove_value(session->context->session_map, session->name, session);
1074 hashmap_free(session->display_map);
1075 hashmap_free(session->card_map);
1076 session->context = grdev_context_unref(session->context);
1077 free(session->path);
1078 free(session->name);
1084 bool grdev_session_is_enabled(grdev_session *session) {
1085 return session && session->enabled;
1088 void grdev_session_enable(grdev_session *session) {
1094 if (!session->enabled) {
1095 session->enabled = true;
1096 HASHMAP_FOREACH(card, session->card_map, iter)
1097 grdev_card_enable(card);
1101 void grdev_session_disable(grdev_session *session) {
1107 if (session->enabled) {
1108 session->enabled = false;
1109 HASHMAP_FOREACH(card, session->card_map, iter)
1110 grdev_card_disable(card);
1114 void grdev_session_commit(grdev_session *session) {
1120 if (!session->enabled)
1123 HASHMAP_FOREACH(card, session->card_map, iter)
1124 if (card->vtable->commit)
1125 card->vtable->commit(card);
1128 void grdev_session_restore(grdev_session *session) {
1134 if (!session->enabled)
1137 HASHMAP_FOREACH(card, session->card_map, iter)
1138 if (card->vtable->restore)
1139 card->vtable->restore(card);
1142 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1150 devnum = udev_device_get_devnum(ud);
1152 return grdev_session_hotplug_drm(session, ud);
1154 card = grdev_find_drm_card(session, devnum);
1158 r = grdev_drm_card_new(&card, session, ud);
1160 log_debug("grdev: %s: cannot add DRM device for %s: %s",
1161 session->name, udev_device_get_syspath(ud), strerror(-r));
1165 session_add_card(session, card);
1168 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1175 devnum = udev_device_get_devnum(ud);
1177 return grdev_session_hotplug_drm(session, ud);
1179 card = grdev_find_drm_card(session, devnum);
1183 session_remove_card(session, card);
1186 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1187 grdev_card *card = NULL;
1188 struct udev_device *p;
1194 for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1195 devnum = udev_device_get_devnum(ud);
1199 card = grdev_find_drm_card(session, devnum);
1207 grdev_drm_card_hotplug(card, ud);
1210 static void session_configure(grdev_session *session) {
1211 grdev_display *display;
1221 * Whenever backends add or remove pipes, we set session->modified and
1222 * require them to pin the session while modifying it. On release, we
1223 * reconfigure the device and re-assign displays to all modified pipes.
1225 * So far, we configure each pipe as a separate display. We do not
1226 * support user-configuration, nor have we gotten any reports from
1227 * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1228 * we get reports, we keep the logic to a minimum.
1231 /* create new displays for all unconfigured pipes */
1232 HASHMAP_FOREACH(card, session->card_map, i) {
1233 if (!card->modified)
1236 card->modified = false;
1238 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1243 assert(!tile->parent);
1245 display = grdev_find_display(session, pipe->name);
1246 if (display && display->tile) {
1247 log_debug("grdev: %s/%s: occupied display for pipe %s",
1248 session->name, card->name, pipe->name);
1250 } else if (!display) {
1251 r = grdev_display_new(&display, session, pipe->name);
1253 log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
1254 session->name, card->name, pipe->name, strerror(-r));
1259 tile_link(pipe->tile, display->tile);
1263 /* update displays */
1264 HASHMAP_FOREACH(display, session->display_map, i)
1265 session_change_display(session, display);
1268 grdev_session *grdev_session_pin(grdev_session *session) {
1275 grdev_session *grdev_session_unpin(grdev_session *session) {
1279 assert(session->n_pins > 0);
1281 if (--session->n_pins == 0)
1282 session_configure(session);
1291 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1292 _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1294 assert_return(out, -EINVAL);
1295 assert_return(event, -EINVAL);
1297 context = new0(grdev_context, 1);
1302 context->event = sd_event_ref(event);
1305 context->sysbus = sd_bus_ref(sysbus);
1307 context->session_map = hashmap_new(&string_hash_ops);
1308 if (!context->session_map)
1316 static void context_cleanup(grdev_context *context) {
1317 assert(hashmap_size(context->session_map) == 0);
1319 hashmap_free(context->session_map);
1320 context->sysbus = sd_bus_unref(context->sysbus);
1321 context->event = sd_event_unref(context->event);
1325 grdev_context *grdev_context_ref(grdev_context *context) {
1326 assert_return(context, NULL);
1327 assert_return(context->ref > 0, NULL);
1333 grdev_context *grdev_context_unref(grdev_context *context) {
1337 assert_return(context->ref > 0, NULL);
1339 if (--context->ref == 0)
1340 context_cleanup(context);