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, uint64_t minage) {
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 /* if front-buffer is up-to-date, there's nothing to do */
378 if (minage > 0 && pipe->front && pipe->front->age >= minage)
381 /* find suitable back-buffer */
382 if (!(fb = pipe->back)) {
383 if (!pipe->vtable->target || !(fb = pipe->vtable->target(pipe)))
386 assert(fb == pipe->back);
389 /* if back-buffer is up-to-date, schedule flip */
390 if (minage > 0 && fb->age >= minage) {
391 grdev_display_flip_target(display, target, fb->age);
395 /* we have an out-of-date back-buffer; return for redraw */
403 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
404 grdev_display_cache *cache;
408 assert(!display->modified);
409 assert(display->enabled);
413 cache = container_of(target, grdev_display_cache, target);
416 assert(cache->pipe->tile->display == display);
418 /* reset age of all FB on overflow */
419 if (age < target->fb->age)
420 for (i = 0; i < cache->pipe->max_fbs; ++i)
421 if (cache->pipe->fbs[i])
422 cache->pipe->fbs[i]->age = 0;
424 ((grdev_fb*)target->fb)->age = age;
425 cache->pipe->flip = true;
428 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
429 uint32_t x, y, width, height;
430 grdev_display_target *t;
434 assert(l->cache_w >= c->target.width + c->target.x);
435 assert(l->cache_h >= c->target.height + c->target.y);
441 t->rotate = (t->rotate + l->rotate) & 0x3;
451 case GRDEV_ROTATE_90:
452 t->x = l->cache_h - (height + y);
457 case GRDEV_ROTATE_180:
458 t->x = l->cache_w - (width + x);
459 t->y = l->cache_h - (height + y);
461 case GRDEV_ROTATE_270:
463 t->y = l->cache_w - (width + x);
473 if (l->flip & GRDEV_FLIP_HORIZONTAL)
474 t->x = l->cache_w - (t->width + t->x);
475 if (l->flip & GRDEV_FLIP_VERTICAL)
476 t->y = l->cache_h - (t->height + t->y);
484 static void display_cache_targets(grdev_display *display) {
485 grdev_display_cache *c;
490 /* depth-first with children before parent */
491 for (tile = tile_leftmost(display->tile);
493 tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
494 if (tile->type == GRDEV_TILE_LEAF) {
497 /* We're at a leaf and no parent has been cached, yet.
498 * Copy the pipe information into the target cache and
499 * update our global pipe-caches if required. */
501 assert(tile->leaf.pipe);
502 assert(display->n_pipes + 1 <= display->max_pipes);
505 c = &display->pipes[display->n_pipes++];
510 c->target.width = p->width;
511 c->target.height = p->height;
512 tile->cache_w = p->width;
513 tile->cache_h = p->height;
515 /* all new tiles are incomplete due to geometry changes */
516 c->incomplete = true;
518 display_cache_apply(c, tile);
520 grdev_tile *child, *l;
522 /* We're now at a node with all its children already
523 * computed (depth-first, child before parent). We
524 * first need to know the size of our tile, then we
525 * recurse into all leafs and update their cache. */
530 LIST_FOREACH(children_by_node, child, tile->node.child_list) {
531 if (child->x + child->cache_w > tile->cache_w)
532 tile->cache_w = child->x + child->cache_w;
533 if (child->y + child->cache_h > tile->cache_h)
534 tile->cache_h = child->y + child->cache_h;
537 assert(tile->cache_w > 0);
538 assert(tile->cache_h > 0);
540 TILE_FOREACH(tile, l)
541 if (l->type == GRDEV_TILE_LEAF)
542 display_cache_apply(l->leaf.pipe->cache, tile);
547 static bool display_cache(grdev_display *display) {
555 if (!display->modified)
558 display->modified = false;
559 display->framed = false;
560 display->n_pipes = 0;
564 if (display->n_leafs < 1)
567 TILE_FOREACH(display->tile, tile)
568 if (tile->type == GRDEV_TILE_LEAF)
569 tile->leaf.pipe->cache = NULL;
571 if (display->n_leafs > display->max_pipes) {
572 n = ALIGN_POWER2(display->n_leafs);
578 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
585 display->max_pipes = n;
588 display_cache_targets(display);
589 display->width = display->tile->cache_w;
590 display->height = display->tile->cache_h;
596 log_debug("grdev: %s/%s: cannot cache pipes: %s",
597 display->session->name, display->name, strerror(-r));
605 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
606 assert_return(card, NULL);
607 assert_return(name, NULL);
609 return hashmap_get(card->pipe_map, name);
612 static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
613 grdev_pipe *pipe = userdata;
615 grdev_pipe_frame(pipe);
619 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
622 assert_return(pipe, -EINVAL);
623 assert_return(pipe->vtable, -EINVAL);
624 assert_return(pipe->vtable->free, -EINVAL);
625 assert_return(pipe->card, -EINVAL);
626 assert_return(pipe->card->session, -EINVAL);
627 assert_return(!pipe->cache, -EINVAL);
628 assert_return(pipe->width > 0, -EINVAL);
629 assert_return(pipe->height > 0, -EINVAL);
630 assert_return(pipe->vrefresh > 0, -EINVAL);
631 assert_return(!pipe->enabled, -EINVAL);
632 assert_return(!pipe->running, -EINVAL);
633 assert_return(name, -EINVAL);
635 pipe->name = strdup(name);
640 pipe->fbs = new0(grdev_fb*, n_fbs);
644 pipe->max_fbs = n_fbs;
647 r = grdev_tile_new_leaf(&pipe->tile, pipe);
651 r = sd_event_add_time(pipe->card->session->context->event,
661 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
665 r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
669 card_modified(pipe->card);
673 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
680 assert(pipe->vtable);
681 assert(pipe->vtable->free);
684 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
686 tile_unlink(pipe->tile);
688 assert(!pipe->cache);
691 pipe->vtable->free(pipe);
693 sd_event_source_unref(tmp.vsync_src);
694 grdev_tile_free(tmp.tile);
695 card_modified(tmp.card);
702 static void pipe_enable(grdev_pipe *pipe) {
705 if (!pipe->enabled) {
706 pipe->enabled = true;
707 if (pipe->vtable->enable)
708 pipe->vtable->enable(pipe);
712 static void pipe_disable(grdev_pipe *pipe) {
716 pipe->enabled = false;
717 if (pipe->vtable->disable)
718 pipe->vtable->disable(pipe);
722 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
725 /* grdev_pipe_ready() is used by backends to notify about pipe state
726 * changed. If a pipe is ready, it can be fully used by us (available,
727 * enabled and accessable). Backends can disable pipes at any time
728 * (like for async revocation), but can only enable them from parent
729 * context. Otherwise, we might call user-callbacks recursively. */
731 if (pipe->running == running)
734 pipe->running = running;
736 /* runtime events for unused pipes are not interesting */
737 if (pipe->cache && pipe->enabled) {
738 grdev_display *display = pipe->tile->display;
743 session_frame(display->session, display);
745 pipe->cache->incomplete = true;
749 void grdev_pipe_frame(grdev_pipe *pipe) {
750 grdev_display *display;
754 /* if pipe is unused, ignore any frame events */
755 if (!pipe->cache || !pipe->enabled)
758 display = pipe->tile->display;
761 grdev_pipe_schedule(pipe, 0);
762 session_frame(display->session, display);
765 void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
770 sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
774 r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
778 ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
780 r = sd_event_source_set_time(pipe->vsync_src, ts);
784 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
791 log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s",
792 pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r));
799 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
800 assert_return(session, NULL);
801 assert_return(name, NULL);
803 return hashmap_get(session->card_map, name);
806 int grdev_card_add(grdev_card *card, const char *name) {
809 assert_return(card, -EINVAL);
810 assert_return(card->vtable, -EINVAL);
811 assert_return(card->vtable->free, -EINVAL);
812 assert_return(card->session, -EINVAL);
813 assert_return(name, -EINVAL);
815 card->name = strdup(name);
819 card->pipe_map = hashmap_new(&string_hash_ops);
823 r = hashmap_put(card->session->card_map, card->name, card);
830 grdev_card *grdev_card_free(grdev_card *card) {
836 assert(!card->enabled);
837 assert(card->vtable);
838 assert(card->vtable->free);
841 hashmap_remove_value(card->session->card_map, card->name, card);
844 card->vtable->free(card);
846 assert(hashmap_size(tmp.pipe_map) == 0);
848 hashmap_free(tmp.pipe_map);
854 static void card_modified(grdev_card *card) {
856 assert(card->session->n_pins > 0);
858 card->modified = true;
861 static void grdev_card_enable(grdev_card *card) {
864 if (!card->enabled) {
865 card->enabled = true;
866 if (card->vtable->enable)
867 card->vtable->enable(card);
871 static void grdev_card_disable(grdev_card *card) {
875 card->enabled = false;
876 if (card->vtable->disable)
877 card->vtable->disable(card);
885 static void session_raise(grdev_session *session, grdev_event *event) {
886 session->event_fn(session, session->userdata, event);
889 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
890 grdev_event event = {
891 .type = GRDEV_EVENT_DISPLAY_ADD,
897 session_raise(session, &event);
900 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
901 grdev_event event = {
902 .type = GRDEV_EVENT_DISPLAY_REMOVE,
908 session_raise(session, &event);
911 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
912 grdev_event event = {
913 .type = GRDEV_EVENT_DISPLAY_CHANGE,
919 session_raise(session, &event);
922 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
923 grdev_event event = {
924 .type = GRDEV_EVENT_DISPLAY_FRAME,
930 session_raise(session, &event);
933 static void session_add_card(grdev_session *session, grdev_card *card) {
937 log_debug("grdev: %s: add card '%s'", session->name, card->name);
939 /* Cards are not exposed to users, but managed internally. Cards are
940 * enabled if the session is enabled, and will track that state. The
941 * backend can probe the card at any time, but only if enabled. It
942 * will then add pipes according to hardware state.
943 * That is, the card may create pipes as soon as we enable it here. */
945 if (session->enabled)
946 grdev_card_enable(card);
949 static void session_remove_card(grdev_session *session, grdev_card *card) {
953 log_debug("grdev: %s: remove card '%s'", session->name, card->name);
955 /* As cards are not exposed, it can never be accessed by outside
956 * users and we can simply remove it. Disabling the card does not
957 * necessarily drop all pipes of the card. This is usually deferred
958 * to card destruction (as pipes are cached as long as FDs remain
959 * open). Therefore, the card destruction might cause pipes, and thus
960 * visible displays, to be removed. */
962 grdev_card_disable(card);
963 grdev_card_free(card);
966 static void session_add_display(grdev_session *session, grdev_display *display) {
969 assert(!display->enabled);
971 log_debug("grdev: %s: add display '%s'", session->name, display->name);
973 /* Displays are the main entity for public API users. We create them
974 * independent of card backends and they wrap any underlying display
975 * architecture. Displays are public at all times, thus, may be entered
976 * by outside users at any time. */
978 display->public = true;
979 session_raise_display_add(session, display);
982 static void session_remove_display(grdev_session *session, grdev_display *display) {
986 log_debug("grdev: %s: remove display '%s'", session->name, display->name);
988 /* Displays are public, so we have to be careful when removing them.
989 * We first tell users about their removal, disable them and then drop
990 * them. We now, after the notification, no external access will
991 * happen. Therefore, we can release the tiles afterwards safely. */
993 if (display->public) {
994 display->public = false;
995 session_raise_display_remove(session, display);
998 grdev_display_disable(display);
999 grdev_display_free(display);
1002 static void session_change_display(grdev_session *session, grdev_display *display) {
1008 changed = display_cache(display);
1010 if (display->n_leafs == 0) {
1011 session_remove_display(session, display);
1012 } else if (!display->public) {
1013 session_add_display(session, display);
1014 session_frame(session, display);
1015 } else if (changed) {
1016 session_raise_display_change(session, display);
1017 session_frame(session, display);
1018 } else if (display->framed) {
1019 session_frame(session, display);
1023 static void session_frame(grdev_session *session, grdev_display *display) {
1027 display->framed = false;
1029 if (!display->enabled || !session->enabled)
1032 if (session->n_pins > 0)
1033 display->framed = true;
1035 session_raise_display_frame(session, display);
1038 int grdev_session_new(grdev_session **out,
1039 grdev_context *context,
1042 grdev_event_fn event_fn,
1044 _cleanup_(grdev_session_freep) grdev_session *session = NULL;
1051 assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
1052 assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
1053 assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
1055 session = new0(grdev_session, 1);
1059 session->context = grdev_context_ref(context);
1060 session->custom = flags & GRDEV_SESSION_CUSTOM;
1061 session->managed = flags & GRDEV_SESSION_MANAGED;
1062 session->event_fn = event_fn;
1063 session->userdata = userdata;
1065 session->name = strdup(name);
1069 if (session->managed) {
1070 r = sd_bus_path_encode("/org/freedesktop/login1/session",
1071 session->name, &session->path);
1076 session->card_map = hashmap_new(&string_hash_ops);
1077 if (!session->card_map)
1080 session->display_map = hashmap_new(&string_hash_ops);
1081 if (!session->display_map)
1084 r = hashmap_put(context->session_map, session->name, session);
1093 grdev_session *grdev_session_free(grdev_session *session) {
1099 grdev_session_disable(session);
1101 while ((card = hashmap_first(session->card_map)))
1102 session_remove_card(session, card);
1104 assert(hashmap_size(session->display_map) == 0);
1107 hashmap_remove_value(session->context->session_map, session->name, session);
1109 hashmap_free(session->display_map);
1110 hashmap_free(session->card_map);
1111 session->context = grdev_context_unref(session->context);
1112 free(session->path);
1113 free(session->name);
1119 bool grdev_session_is_enabled(grdev_session *session) {
1120 return session && session->enabled;
1123 void grdev_session_enable(grdev_session *session) {
1129 if (!session->enabled) {
1130 session->enabled = true;
1131 HASHMAP_FOREACH(card, session->card_map, iter)
1132 grdev_card_enable(card);
1136 void grdev_session_disable(grdev_session *session) {
1142 if (session->enabled) {
1143 session->enabled = false;
1144 HASHMAP_FOREACH(card, session->card_map, iter)
1145 grdev_card_disable(card);
1149 void grdev_session_commit(grdev_session *session) {
1155 if (!session->enabled)
1158 HASHMAP_FOREACH(card, session->card_map, iter)
1159 if (card->vtable->commit)
1160 card->vtable->commit(card);
1163 void grdev_session_restore(grdev_session *session) {
1169 if (!session->enabled)
1172 HASHMAP_FOREACH(card, session->card_map, iter)
1173 if (card->vtable->restore)
1174 card->vtable->restore(card);
1177 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1185 devnum = udev_device_get_devnum(ud);
1187 return grdev_session_hotplug_drm(session, ud);
1189 card = grdev_find_drm_card(session, devnum);
1193 r = grdev_drm_card_new(&card, session, ud);
1195 log_debug("grdev: %s: cannot add DRM device for %s: %s",
1196 session->name, udev_device_get_syspath(ud), strerror(-r));
1200 session_add_card(session, card);
1203 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1210 devnum = udev_device_get_devnum(ud);
1212 return grdev_session_hotplug_drm(session, ud);
1214 card = grdev_find_drm_card(session, devnum);
1218 session_remove_card(session, card);
1221 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1222 grdev_card *card = NULL;
1223 struct udev_device *p;
1229 for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1230 devnum = udev_device_get_devnum(ud);
1234 card = grdev_find_drm_card(session, devnum);
1242 grdev_drm_card_hotplug(card, ud);
1245 static void session_configure(grdev_session *session) {
1246 grdev_display *display;
1256 * Whenever backends add or remove pipes, we set session->modified and
1257 * require them to pin the session while modifying it. On release, we
1258 * reconfigure the device and re-assign displays to all modified pipes.
1260 * So far, we configure each pipe as a separate display. We do not
1261 * support user-configuration, nor have we gotten any reports from
1262 * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1263 * we get reports, we keep the logic to a minimum.
1266 /* create new displays for all unconfigured pipes */
1267 HASHMAP_FOREACH(card, session->card_map, i) {
1268 if (!card->modified)
1271 card->modified = false;
1273 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1278 assert(!tile->parent);
1280 display = grdev_find_display(session, pipe->name);
1281 if (display && display->tile) {
1282 log_debug("grdev: %s/%s: occupied display for pipe %s",
1283 session->name, card->name, pipe->name);
1285 } else if (!display) {
1286 r = grdev_display_new(&display, session, pipe->name);
1288 log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
1289 session->name, card->name, pipe->name, strerror(-r));
1294 tile_link(pipe->tile, display->tile);
1298 /* update displays */
1299 HASHMAP_FOREACH(display, session->display_map, i)
1300 session_change_display(session, display);
1303 grdev_session *grdev_session_pin(grdev_session *session) {
1310 grdev_session *grdev_session_unpin(grdev_session *session) {
1314 assert(session->n_pins > 0);
1316 if (--session->n_pins == 0)
1317 session_configure(session);
1326 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1327 _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1329 assert_return(out, -EINVAL);
1330 assert_return(event, -EINVAL);
1332 context = new0(grdev_context, 1);
1337 context->event = sd_event_ref(event);
1340 context->sysbus = sd_bus_ref(sysbus);
1342 context->session_map = hashmap_new(&string_hash_ops);
1343 if (!context->session_map)
1351 static void context_cleanup(grdev_context *context) {
1352 assert(hashmap_size(context->session_map) == 0);
1354 hashmap_free(context->session_map);
1355 context->sysbus = sd_bus_unref(context->sysbus);
1356 context->event = sd_event_unref(context->event);
1360 grdev_context *grdev_context_ref(grdev_context *context) {
1361 assert_return(context, NULL);
1362 assert_return(context->ref > 0, NULL);
1368 grdev_context *grdev_context_unref(grdev_context *context) {
1372 assert_return(context->ref > 0, NULL);
1374 if (--context->ref == 0)
1375 context_cleanup(context);