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/>.
25 #include <systemd/sd-bus.h>
26 #include <systemd/sd-event.h>
28 #include "grdev-internal.h"
30 #include "login-shared.h"
34 static void pipe_enable(grdev_pipe *pipe);
35 static void pipe_disable(grdev_pipe *pipe);
36 static void card_modified(grdev_card *card);
37 static void session_frame(grdev_session *session, grdev_display *display);
43 static inline grdev_tile *tile_leftmost(grdev_tile *tile) {
47 while (tile->type == GRDEV_TILE_NODE && tile->node.child_list)
48 tile = tile->node.child_list;
53 #define TILE_FOREACH(_root, _i) \
54 for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->children_by_node_next) ? : _i->parent)
56 #define TILE_FOREACH_SAFE(_root, _i, _next) \
57 for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->children_by_node_next) ? : _i->parent), true); _i = _next)
59 static void tile_link(grdev_tile *tile, grdev_tile *parent) {
60 grdev_display *display;
64 assert(!tile->parent);
65 assert(!tile->display);
67 assert(parent->type == GRDEV_TILE_NODE);
69 display = parent->display;
71 assert(!display || !display->enabled);
73 ++parent->node.n_children;
74 LIST_PREPEND(children_by_node, parent->node.child_list, tile);
75 tile->parent = parent;
78 display->modified = true;
79 TILE_FOREACH(tile, t) {
81 if (t->type == GRDEV_TILE_LEAF) {
84 pipe_enable(t->leaf.pipe);
90 static void tile_unlink(grdev_tile *tile) {
91 grdev_tile *parent, *t;
92 grdev_display *display;
96 display = tile->display;
97 parent = tile->parent;
103 assert(parent->type == GRDEV_TILE_NODE);
104 assert(parent->display == display);
105 assert(parent->node.n_children > 0);
107 --parent->node.n_children;
108 LIST_REMOVE(children_by_node, parent->node.child_list, tile);
112 display->modified = true;
113 TILE_FOREACH(tile, t) {
115 if (t->type == GRDEV_TILE_LEAF) {
117 t->leaf.pipe->cache = NULL;
118 pipe_disable(t->leaf.pipe);
123 /* Tile trees are driven by leafs. Internal nodes have no owner, thus,
124 * we must take care to not leave them around. Therefore, whenever we
125 * unlink any part of a tree, we also destroy the parent, in case it's
127 * Parents are stale if they have no children and either have no display
128 * or if they are intermediate nodes (i.e, they have a parent).
129 * This means, you can easily create trees, but you can never partially
130 * move or destruct them so far. They're always reduced to minimal form
131 * if you cut them. This might change later, but so far we didn't need
132 * partial destruction or the ability to move whole trees. */
134 if (parent->node.n_children < 1 && (parent->parent || !parent->display))
135 grdev_tile_free(parent);
138 static int tile_new(grdev_tile **out) {
139 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
143 tile = new0(grdev_tile, 1);
147 tile->type = (unsigned)-1;
154 int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe) {
155 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
158 assert_return(out, -EINVAL);
159 assert_return(pipe, -EINVAL);
160 assert_return(!pipe->tile, -EINVAL);
166 tile->type = GRDEV_TILE_LEAF;
167 tile->leaf.pipe = pipe;
175 int grdev_tile_new_node(grdev_tile **out) {
176 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
179 assert_return(out, -EINVAL);
185 tile->type = GRDEV_TILE_NODE;
192 grdev_tile *grdev_tile_free(grdev_tile *tile) {
198 switch (tile->type) {
199 case GRDEV_TILE_LEAF:
200 assert(!tile->parent);
201 assert(!tile->display);
202 assert(tile->leaf.pipe);
205 case GRDEV_TILE_NODE:
206 assert(!tile->parent);
207 assert(!tile->display);
208 assert(tile->node.n_children == 0);
218 grdev_display *grdev_find_display(grdev_session *session, const char *name) {
219 assert_return(session, NULL);
220 assert_return(name, NULL);
222 return hashmap_get(session->display_map, name);
225 int grdev_display_new(grdev_display **out, grdev_session *session, const char *name) {
226 _cleanup_(grdev_display_freep) grdev_display *display = NULL;
232 display = new0(grdev_display, 1);
236 display->session = session;
238 display->name = strdup(name);
242 r = grdev_tile_new_node(&display->tile);
246 display->tile->display = display;
248 r = hashmap_put(session->display_map, display->name, display);
258 grdev_display *grdev_display_free(grdev_display *display) {
262 assert(!display->public);
263 assert(!display->enabled);
264 assert(!display->modified);
265 assert(display->n_leafs == 0);
266 assert(display->n_pipes == 0);
269 hashmap_remove_value(display->session->display_map, display->name, display);
272 display->tile->display = NULL;
273 grdev_tile_free(display->tile);
276 free(display->pipes);
283 void grdev_display_set_userdata(grdev_display *display, void *userdata) {
286 display->userdata = userdata;
289 void *grdev_display_get_userdata(grdev_display *display) {
290 assert_return(display, NULL);
292 return display->userdata;
295 const char *grdev_display_get_name(grdev_display *display) {
296 assert_return(display, NULL);
298 return display->name;
301 uint32_t grdev_display_get_width(grdev_display *display) {
302 assert_return(display, 0);
304 return display->width;
307 uint32_t grdev_display_get_height(grdev_display *display) {
308 assert_return(display, 0);
310 return display->height;
313 bool grdev_display_is_enabled(grdev_display *display) {
314 return display && display->enabled;
317 void grdev_display_enable(grdev_display *display) {
322 if (!display->enabled) {
323 display->enabled = true;
324 TILE_FOREACH(display->tile, t)
325 if (t->type == GRDEV_TILE_LEAF)
326 pipe_enable(t->leaf.pipe);
330 void grdev_display_disable(grdev_display *display) {
335 if (display->enabled) {
336 display->enabled = false;
337 TILE_FOREACH(display->tile, t)
338 if (t->type == GRDEV_TILE_LEAF)
339 pipe_disable(t->leaf.pipe);
343 const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev) {
344 grdev_display_cache *cache;
347 assert_return(display, NULL);
348 assert_return(!display->modified, NULL);
349 assert_return(display->enabled, NULL);
352 cache = container_of(prev, grdev_display_cache, target);
355 assert(cache->pipe->tile->display == display);
356 assert(display->pipes >= cache);
358 idx = cache - display->pipes + 1;
363 for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) {
364 grdev_display_target *target;
369 target = &cache->target;
371 if (!pipe->running || !pipe->enabled)
374 /* find suitable back-buffer */
376 if (!pipe->vtable->target)
378 if (!(fb = pipe->vtable->target(pipe)))
381 assert(fb == pipe->back);
384 target->front = pipe->front;
385 target->back = pipe->back;
393 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target) {
394 grdev_display_cache *cache;
397 assert(!display->modified);
398 assert(display->enabled);
401 cache = container_of(target, grdev_display_cache, target);
404 assert(cache->pipe->tile->display == display);
406 cache->pipe->flip = true;
409 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
410 uint32_t x, y, width, height;
411 grdev_display_target *t;
415 assert(l->cache_w >= c->target.width + c->target.x);
416 assert(l->cache_h >= c->target.height + c->target.y);
422 t->rotate = (t->rotate + l->rotate) & 0x3;
432 case GRDEV_ROTATE_90:
433 t->x = l->cache_h - (height + y);
438 case GRDEV_ROTATE_180:
439 t->x = l->cache_w - (width + x);
440 t->y = l->cache_h - (height + y);
442 case GRDEV_ROTATE_270:
444 t->y = l->cache_w - (width + x);
454 if (l->flip & GRDEV_FLIP_HORIZONTAL)
455 t->x = l->cache_w - (t->width + t->x);
456 if (l->flip & GRDEV_FLIP_VERTICAL)
457 t->y = l->cache_h - (t->height + t->y);
465 static void display_cache_targets(grdev_display *display) {
466 grdev_display_cache *c;
471 /* depth-first with children before parent */
472 for (tile = tile_leftmost(display->tile);
474 tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
475 if (tile->type == GRDEV_TILE_LEAF) {
478 /* We're at a leaf and no parent has been cached, yet.
479 * Copy the pipe information into the target cache and
480 * update our global pipe-caches if required. */
482 assert(tile->leaf.pipe);
483 assert(display->n_pipes + 1 <= display->max_pipes);
486 c = &display->pipes[display->n_pipes++];
491 c->target.width = p->width;
492 c->target.height = p->height;
493 tile->cache_w = p->width;
494 tile->cache_h = p->height;
496 /* all new tiles are incomplete due to geometry changes */
497 c->incomplete = true;
499 display_cache_apply(c, tile);
501 grdev_tile *child, *l;
503 /* We're now at a node with all its children already
504 * computed (depth-first, child before parent). We
505 * first need to know the size of our tile, then we
506 * recurse into all leafs and update their cache. */
511 LIST_FOREACH(children_by_node, child, tile->node.child_list) {
512 if (child->x + child->cache_w > tile->cache_w)
513 tile->cache_w = child->x + child->cache_w;
514 if (child->y + child->cache_h > tile->cache_h)
515 tile->cache_h = child->y + child->cache_h;
518 assert(tile->cache_w > 0);
519 assert(tile->cache_h > 0);
521 TILE_FOREACH(tile, l)
522 if (l->type == GRDEV_TILE_LEAF)
523 display_cache_apply(l->leaf.pipe->cache, tile);
528 static bool display_cache(grdev_display *display) {
536 if (!display->modified)
539 display->modified = false;
540 display->framed = false;
541 display->n_pipes = 0;
545 if (display->n_leafs < 1)
548 TILE_FOREACH(display->tile, tile)
549 if (tile->type == GRDEV_TILE_LEAF)
550 tile->leaf.pipe->cache = NULL;
552 if (display->n_leafs > display->max_pipes) {
553 n = ALIGN_POWER2(display->n_leafs);
559 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
566 display->max_pipes = n;
569 display_cache_targets(display);
570 display->width = display->tile->cache_w;
571 display->height = display->tile->cache_h;
577 log_debug_errno(r, "grdev: %s/%s: cannot cache pipes: %m",
578 display->session->name, display->name);
586 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
587 assert_return(card, NULL);
588 assert_return(name, NULL);
590 return hashmap_get(card->pipe_map, name);
593 static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
594 grdev_pipe *pipe = userdata;
596 grdev_pipe_frame(pipe);
600 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
603 assert_return(pipe, -EINVAL);
604 assert_return(pipe->vtable, -EINVAL);
605 assert_return(pipe->vtable->free, -EINVAL);
606 assert_return(pipe->card, -EINVAL);
607 assert_return(pipe->card->session, -EINVAL);
608 assert_return(!pipe->cache, -EINVAL);
609 assert_return(pipe->width > 0, -EINVAL);
610 assert_return(pipe->height > 0, -EINVAL);
611 assert_return(pipe->vrefresh > 0, -EINVAL);
612 assert_return(!pipe->enabled, -EINVAL);
613 assert_return(!pipe->running, -EINVAL);
614 assert_return(name, -EINVAL);
616 pipe->name = strdup(name);
621 pipe->fbs = new0(grdev_fb*, n_fbs);
625 pipe->max_fbs = n_fbs;
628 r = grdev_tile_new_leaf(&pipe->tile, pipe);
632 r = sd_event_add_time(pipe->card->session->context->event,
642 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
646 r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
650 card_modified(pipe->card);
654 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
661 assert(pipe->vtable);
662 assert(pipe->vtable->free);
665 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
667 tile_unlink(pipe->tile);
669 assert(!pipe->cache);
672 pipe->vtable->free(pipe);
674 sd_event_source_unref(tmp.vsync_src);
675 grdev_tile_free(tmp.tile);
676 card_modified(tmp.card);
683 static void pipe_enable(grdev_pipe *pipe) {
686 if (!pipe->enabled) {
687 pipe->enabled = true;
688 if (pipe->vtable->enable)
689 pipe->vtable->enable(pipe);
693 static void pipe_disable(grdev_pipe *pipe) {
697 pipe->enabled = false;
698 if (pipe->vtable->disable)
699 pipe->vtable->disable(pipe);
703 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
706 /* grdev_pipe_ready() is used by backends to notify about pipe state
707 * changed. If a pipe is ready, it can be fully used by us (available,
708 * enabled and accessible). Backends can disable pipes at any time
709 * (like for async revocation), but can only enable them from parent
710 * context. Otherwise, we might call user-callbacks recursively. */
712 if (pipe->running == running)
715 pipe->running = running;
717 /* runtime events for unused pipes are not interesting */
718 if (pipe->cache && pipe->enabled) {
719 grdev_display *display = pipe->tile->display;
724 session_frame(display->session, display);
726 pipe->cache->incomplete = true;
730 void grdev_pipe_frame(grdev_pipe *pipe) {
731 grdev_display *display;
735 /* if pipe is unused, ignore any frame events */
736 if (!pipe->cache || !pipe->enabled)
739 display = pipe->tile->display;
742 grdev_pipe_schedule(pipe, 0);
743 session_frame(display->session, display);
746 void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
751 sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
755 r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
759 ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
761 r = sd_event_source_set_time(pipe->vsync_src, ts);
765 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
772 log_debug_errno(r, "grdev: %s/%s/%s: cannot schedule vsync timer: %m",
773 pipe->card->session->name, pipe->card->name, pipe->name);
780 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
781 assert_return(session, NULL);
782 assert_return(name, NULL);
784 return hashmap_get(session->card_map, name);
787 int grdev_card_add(grdev_card *card, const char *name) {
790 assert_return(card, -EINVAL);
791 assert_return(card->vtable, -EINVAL);
792 assert_return(card->vtable->free, -EINVAL);
793 assert_return(card->session, -EINVAL);
794 assert_return(name, -EINVAL);
796 card->name = strdup(name);
800 card->pipe_map = hashmap_new(&string_hash_ops);
804 r = hashmap_put(card->session->card_map, card->name, card);
811 grdev_card *grdev_card_free(grdev_card *card) {
817 assert(!card->enabled);
818 assert(card->vtable);
819 assert(card->vtable->free);
822 hashmap_remove_value(card->session->card_map, card->name, card);
825 card->vtable->free(card);
827 assert(hashmap_size(tmp.pipe_map) == 0);
829 hashmap_free(tmp.pipe_map);
835 static void card_modified(grdev_card *card) {
837 assert(card->session->n_pins > 0);
839 card->modified = true;
842 static void grdev_card_enable(grdev_card *card) {
845 if (!card->enabled) {
846 card->enabled = true;
847 if (card->vtable->enable)
848 card->vtable->enable(card);
852 static void grdev_card_disable(grdev_card *card) {
856 card->enabled = false;
857 if (card->vtable->disable)
858 card->vtable->disable(card);
866 static void session_raise(grdev_session *session, grdev_event *event) {
867 session->event_fn(session, session->userdata, event);
870 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
871 grdev_event event = {
872 .type = GRDEV_EVENT_DISPLAY_ADD,
878 session_raise(session, &event);
881 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
882 grdev_event event = {
883 .type = GRDEV_EVENT_DISPLAY_REMOVE,
889 session_raise(session, &event);
892 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
893 grdev_event event = {
894 .type = GRDEV_EVENT_DISPLAY_CHANGE,
900 session_raise(session, &event);
903 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
904 grdev_event event = {
905 .type = GRDEV_EVENT_DISPLAY_FRAME,
911 session_raise(session, &event);
914 static void session_add_card(grdev_session *session, grdev_card *card) {
918 log_debug("grdev: %s: add card '%s'", session->name, card->name);
920 /* Cards are not exposed to users, but managed internally. Cards are
921 * enabled if the session is enabled, and will track that state. The
922 * backend can probe the card at any time, but only if enabled. It
923 * will then add pipes according to hardware state.
924 * That is, the card may create pipes as soon as we enable it here. */
926 if (session->enabled)
927 grdev_card_enable(card);
930 static void session_remove_card(grdev_session *session, grdev_card *card) {
934 log_debug("grdev: %s: remove card '%s'", session->name, card->name);
936 /* As cards are not exposed, it can never be accessed by outside
937 * users and we can simply remove it. Disabling the card does not
938 * necessarily drop all pipes of the card. This is usually deferred
939 * to card destruction (as pipes are cached as long as FDs remain
940 * open). Therefore, the card destruction might cause pipes, and thus
941 * visible displays, to be removed. */
943 grdev_card_disable(card);
944 grdev_card_free(card);
947 static void session_add_display(grdev_session *session, grdev_display *display) {
950 assert(!display->enabled);
952 log_debug("grdev: %s: add display '%s'", session->name, display->name);
954 /* Displays are the main entity for public API users. We create them
955 * independent of card backends and they wrap any underlying display
956 * architecture. Displays are public at all times, thus, may be entered
957 * by outside users at any time. */
959 display->public = true;
960 session_raise_display_add(session, display);
963 static void session_remove_display(grdev_session *session, grdev_display *display) {
967 log_debug("grdev: %s: remove display '%s'", session->name, display->name);
969 /* Displays are public, so we have to be careful when removing them.
970 * We first tell users about their removal, disable them and then drop
971 * them. We now, after the notification, no external access will
972 * happen. Therefore, we can release the tiles afterwards safely. */
974 if (display->public) {
975 display->public = false;
976 session_raise_display_remove(session, display);
979 grdev_display_disable(display);
980 grdev_display_free(display);
983 static void session_change_display(grdev_session *session, grdev_display *display) {
989 changed = display_cache(display);
991 if (display->n_leafs == 0) {
992 session_remove_display(session, display);
993 } else if (!display->public) {
994 session_add_display(session, display);
995 session_frame(session, display);
996 } else if (changed) {
997 session_raise_display_change(session, display);
998 session_frame(session, display);
999 } else if (display->framed) {
1000 session_frame(session, display);
1004 static void session_frame(grdev_session *session, grdev_display *display) {
1008 display->framed = false;
1010 if (!display->enabled || !session->enabled)
1013 if (session->n_pins > 0)
1014 display->framed = true;
1016 session_raise_display_frame(session, display);
1019 int grdev_session_new(grdev_session **out,
1020 grdev_context *context,
1023 grdev_event_fn event_fn,
1025 _cleanup_(grdev_session_freep) grdev_session *session = NULL;
1032 assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
1033 assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
1034 assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
1036 session = new0(grdev_session, 1);
1040 session->context = grdev_context_ref(context);
1041 session->custom = flags & GRDEV_SESSION_CUSTOM;
1042 session->managed = flags & GRDEV_SESSION_MANAGED;
1043 session->event_fn = event_fn;
1044 session->userdata = userdata;
1046 session->name = strdup(name);
1050 if (session->managed) {
1051 r = sd_bus_path_encode("/org/freedesktop/login1/session",
1052 session->name, &session->path);
1057 session->card_map = hashmap_new(&string_hash_ops);
1058 if (!session->card_map)
1061 session->display_map = hashmap_new(&string_hash_ops);
1062 if (!session->display_map)
1065 r = hashmap_put(context->session_map, session->name, session);
1074 grdev_session *grdev_session_free(grdev_session *session) {
1080 grdev_session_disable(session);
1082 while ((card = hashmap_first(session->card_map)))
1083 session_remove_card(session, card);
1085 assert(hashmap_size(session->display_map) == 0);
1088 hashmap_remove_value(session->context->session_map, session->name, session);
1090 hashmap_free(session->display_map);
1091 hashmap_free(session->card_map);
1092 session->context = grdev_context_unref(session->context);
1093 free(session->path);
1094 free(session->name);
1100 bool grdev_session_is_enabled(grdev_session *session) {
1101 return session && session->enabled;
1104 void grdev_session_enable(grdev_session *session) {
1110 if (!session->enabled) {
1111 session->enabled = true;
1112 HASHMAP_FOREACH(card, session->card_map, iter)
1113 grdev_card_enable(card);
1117 void grdev_session_disable(grdev_session *session) {
1123 if (session->enabled) {
1124 session->enabled = false;
1125 HASHMAP_FOREACH(card, session->card_map, iter)
1126 grdev_card_disable(card);
1130 void grdev_session_commit(grdev_session *session) {
1136 if (!session->enabled)
1139 HASHMAP_FOREACH(card, session->card_map, iter)
1140 if (card->vtable->commit)
1141 card->vtable->commit(card);
1144 void grdev_session_restore(grdev_session *session) {
1150 if (!session->enabled)
1153 HASHMAP_FOREACH(card, session->card_map, iter)
1154 if (card->vtable->restore)
1155 card->vtable->restore(card);
1158 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1166 devnum = udev_device_get_devnum(ud);
1168 return grdev_session_hotplug_drm(session, ud);
1170 card = grdev_find_drm_card(session, devnum);
1174 r = grdev_drm_card_new(&card, session, ud);
1176 log_debug_errno(r, "grdev: %s: cannot add DRM device for %s: %m",
1177 session->name, udev_device_get_syspath(ud));
1181 session_add_card(session, card);
1184 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1191 devnum = udev_device_get_devnum(ud);
1193 return grdev_session_hotplug_drm(session, ud);
1195 card = grdev_find_drm_card(session, devnum);
1199 session_remove_card(session, card);
1202 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1203 grdev_card *card = NULL;
1204 struct udev_device *p;
1210 for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1211 devnum = udev_device_get_devnum(ud);
1215 card = grdev_find_drm_card(session, devnum);
1223 grdev_drm_card_hotplug(card, ud);
1226 static void session_configure(grdev_session *session) {
1227 grdev_display *display;
1237 * Whenever backends add or remove pipes, we set session->modified and
1238 * require them to pin the session while modifying it. On release, we
1239 * reconfigure the device and re-assign displays to all modified pipes.
1241 * So far, we configure each pipe as a separate display. We do not
1242 * support user-configuration, nor have we gotten any reports from
1243 * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1244 * we get reports, we keep the logic to a minimum.
1247 /* create new displays for all unconfigured pipes */
1248 HASHMAP_FOREACH(card, session->card_map, i) {
1249 if (!card->modified)
1252 card->modified = false;
1254 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1259 assert(!tile->parent);
1261 display = grdev_find_display(session, pipe->name);
1262 if (display && display->tile) {
1263 log_debug("grdev: %s/%s: occupied display for pipe %s",
1264 session->name, card->name, pipe->name);
1266 } else if (!display) {
1267 r = grdev_display_new(&display, session, pipe->name);
1269 log_debug_errno(r, "grdev: %s/%s: cannot create display for pipe %s: %m",
1270 session->name, card->name, pipe->name);
1275 tile_link(pipe->tile, display->tile);
1279 /* update displays */
1280 HASHMAP_FOREACH(display, session->display_map, i)
1281 session_change_display(session, display);
1284 grdev_session *grdev_session_pin(grdev_session *session) {
1291 grdev_session *grdev_session_unpin(grdev_session *session) {
1295 assert(session->n_pins > 0);
1297 if (--session->n_pins == 0)
1298 session_configure(session);
1307 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1308 _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1310 assert_return(out, -EINVAL);
1311 assert_return(event, -EINVAL);
1313 context = new0(grdev_context, 1);
1318 context->event = sd_event_ref(event);
1321 context->sysbus = sd_bus_ref(sysbus);
1323 context->session_map = hashmap_new(&string_hash_ops);
1324 if (!context->session_map)
1332 static void context_cleanup(grdev_context *context) {
1333 assert(hashmap_size(context->session_map) == 0);
1335 hashmap_free(context->session_map);
1336 context->sysbus = sd_bus_unref(context->sysbus);
1337 context->event = sd_event_unref(context->event);
1341 grdev_context *grdev_context_ref(grdev_context *context) {
1342 assert_return(context, NULL);
1343 assert_return(context->ref > 0, NULL);
1349 grdev_context *grdev_context_unref(grdev_context *context) {
1353 assert_return(context->ref > 0, NULL);
1355 if (--context->ref == 0)
1356 context_cleanup(context);