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 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
580 assert_return(pipe, -EINVAL);
581 assert_return(pipe->vtable, -EINVAL);
582 assert_return(pipe->vtable->free, -EINVAL);
583 assert_return(pipe->card, -EINVAL);
584 assert_return(pipe->card->session, -EINVAL);
585 assert_return(!pipe->cache, -EINVAL);
586 assert_return(pipe->width > 0, -EINVAL);
587 assert_return(pipe->height > 0, -EINVAL);
588 assert_return(!pipe->enabled, -EINVAL);
589 assert_return(!pipe->running, -EINVAL);
590 assert_return(name, -EINVAL);
592 pipe->name = strdup(name);
597 pipe->fbs = new0(grdev_fb*, n_fbs);
601 pipe->max_fbs = n_fbs;
604 r = grdev_tile_new_leaf(&pipe->tile, pipe);
608 r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
612 card_modified(pipe->card);
616 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
623 assert(pipe->vtable);
624 assert(pipe->vtable->free);
627 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
629 tile_unlink(pipe->tile);
631 assert(!pipe->cache);
634 pipe->vtable->free(pipe);
636 grdev_tile_free(tmp.tile);
637 card_modified(tmp.card);
644 static void pipe_enable(grdev_pipe *pipe) {
647 if (!pipe->enabled) {
648 pipe->enabled = true;
649 if (pipe->vtable->enable)
650 pipe->vtable->enable(pipe);
654 static void pipe_disable(grdev_pipe *pipe) {
658 pipe->enabled = false;
659 if (pipe->vtable->disable)
660 pipe->vtable->disable(pipe);
664 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
667 /* grdev_pipe_ready() is used by backends to notify about pipe state
668 * changed. If a pipe is ready, it can be fully used by us (available,
669 * enabled and accessable). Backends can disable pipes at any time
670 * (like for async revocation), but can only enable them from parent
671 * context. Otherwise, we might call user-callbacks recursively. */
673 if (pipe->running == running)
676 pipe->running = running;
678 /* runtime events for unused pipes are not interesting */
680 grdev_display *display = pipe->tile->display;
686 session_frame(display->session, display);
688 pipe->cache->incomplete = true;
693 void grdev_pipe_frame(grdev_pipe *pipe) {
694 grdev_display *display;
698 /* if pipe is unused, ignore any frame events */
702 display = pipe->tile->display;
706 session_frame(display->session, display);
713 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
714 assert_return(session, NULL);
715 assert_return(name, NULL);
717 return hashmap_get(session->card_map, name);
720 int grdev_card_add(grdev_card *card, const char *name) {
723 assert_return(card, -EINVAL);
724 assert_return(card->vtable, -EINVAL);
725 assert_return(card->vtable->free, -EINVAL);
726 assert_return(card->session, -EINVAL);
727 assert_return(name, -EINVAL);
729 card->name = strdup(name);
733 card->pipe_map = hashmap_new(&string_hash_ops);
737 r = hashmap_put(card->session->card_map, card->name, card);
744 grdev_card *grdev_card_free(grdev_card *card) {
750 assert(!card->enabled);
751 assert(card->vtable);
752 assert(card->vtable->free);
755 hashmap_remove_value(card->session->card_map, card->name, card);
758 card->vtable->free(card);
760 assert(hashmap_size(tmp.pipe_map) == 0);
762 hashmap_free(tmp.pipe_map);
768 static void card_modified(grdev_card *card) {
770 assert(card->session->n_pins > 0);
772 card->modified = true;
775 static void grdev_card_enable(grdev_card *card) {
778 if (!card->enabled) {
779 card->enabled = true;
780 if (card->vtable->enable)
781 card->vtable->enable(card);
785 static void grdev_card_disable(grdev_card *card) {
789 card->enabled = false;
790 if (card->vtable->disable)
791 card->vtable->disable(card);
799 static void session_raise(grdev_session *session, grdev_event *event) {
800 session->event_fn(session, session->userdata, event);
803 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
804 grdev_event event = {
805 .type = GRDEV_EVENT_DISPLAY_ADD,
811 session_raise(session, &event);
814 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
815 grdev_event event = {
816 .type = GRDEV_EVENT_DISPLAY_REMOVE,
822 session_raise(session, &event);
825 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
826 grdev_event event = {
827 .type = GRDEV_EVENT_DISPLAY_CHANGE,
833 session_raise(session, &event);
836 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
837 grdev_event event = {
838 .type = GRDEV_EVENT_DISPLAY_FRAME,
844 session_raise(session, &event);
847 static void session_add_card(grdev_session *session, grdev_card *card) {
851 log_debug("grdev: %s: add card '%s'", session->name, card->name);
853 /* Cards are not exposed to users, but managed internally. Cards are
854 * enabled if the session is enabled, and will track that state. The
855 * backend can probe the card at any time, but only if enabled. It
856 * will then add pipes according to hardware state.
857 * That is, the card may create pipes as soon as we enable it here. */
859 if (session->enabled)
860 grdev_card_enable(card);
863 static void session_remove_card(grdev_session *session, grdev_card *card) {
867 log_debug("grdev: %s: remove card '%s'", session->name, card->name);
869 /* As cards are not exposed, it can never be accessed by outside
870 * users and we can simply remove it. Disabling the card does not
871 * necessarily drop all pipes of the card. This is usually deferred
872 * to card destruction (as pipes are cached as long as FDs remain
873 * open). Therefore, the card destruction might cause pipes, and thus
874 * visible displays, to be removed. */
876 grdev_card_disable(card);
877 grdev_card_free(card);
880 static void session_add_display(grdev_session *session, grdev_display *display) {
883 assert(!display->enabled);
885 log_debug("grdev: %s: add display '%s'", session->name, display->name);
887 /* Displays are the main entity for public API users. We create them
888 * independent of card backends and they wrap any underlying display
889 * architecture. Displays are public at all times, thus, may be entered
890 * by outside users at any time. */
892 display->public = true;
893 session_raise_display_add(session, display);
896 static void session_remove_display(grdev_session *session, grdev_display *display) {
900 log_debug("grdev: %s: remove display '%s'", session->name, display->name);
902 /* Displays are public, so we have to be careful when removing them.
903 * We first tell users about their removal, disable them and then drop
904 * them. We now, after the notification, no external access will
905 * happen. Therefore, we can release the tiles afterwards safely. */
907 if (display->public) {
908 display->public = false;
909 session_raise_display_remove(session, display);
912 grdev_display_disable(display);
913 grdev_display_free(display);
916 static void session_change_display(grdev_session *session, grdev_display *display) {
922 changed = display_cache(display);
924 if (display->n_leafs == 0) {
925 session_remove_display(session, display);
926 } else if (!display->public) {
927 session_add_display(session, display);
928 session_frame(session, display);
929 } else if (changed) {
930 session_raise_display_change(session, display);
931 session_frame(session, display);
932 } else if (display->framed) {
933 session_frame(session, display);
937 static void session_frame(grdev_session *session, grdev_display *display) {
941 display->framed = false;
943 if (!display->enabled || !session->enabled)
946 if (session->n_pins > 0)
947 display->framed = true;
949 session_raise_display_frame(session, display);
952 int grdev_session_new(grdev_session **out,
953 grdev_context *context,
956 grdev_event_fn event_fn,
958 _cleanup_(grdev_session_freep) grdev_session *session = NULL;
965 assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
966 assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
967 assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
969 session = new0(grdev_session, 1);
973 session->context = grdev_context_ref(context);
974 session->custom = flags & GRDEV_SESSION_CUSTOM;
975 session->managed = flags & GRDEV_SESSION_MANAGED;
976 session->event_fn = event_fn;
977 session->userdata = userdata;
979 session->name = strdup(name);
983 if (session->managed) {
984 r = sd_bus_path_encode("/org/freedesktop/login1/session",
985 session->name, &session->path);
990 session->card_map = hashmap_new(&string_hash_ops);
991 if (!session->card_map)
994 session->display_map = hashmap_new(&string_hash_ops);
995 if (!session->display_map)
998 r = hashmap_put(context->session_map, session->name, session);
1007 grdev_session *grdev_session_free(grdev_session *session) {
1013 grdev_session_disable(session);
1015 while ((card = hashmap_first(session->card_map)))
1016 session_remove_card(session, card);
1018 assert(hashmap_size(session->display_map) == 0);
1021 hashmap_remove_value(session->context->session_map, session->name, session);
1023 hashmap_free(session->display_map);
1024 hashmap_free(session->card_map);
1025 session->context = grdev_context_unref(session->context);
1026 free(session->path);
1027 free(session->name);
1033 bool grdev_session_is_enabled(grdev_session *session) {
1034 return session && session->enabled;
1037 void grdev_session_enable(grdev_session *session) {
1043 if (!session->enabled) {
1044 session->enabled = true;
1045 HASHMAP_FOREACH(card, session->card_map, iter)
1046 grdev_card_enable(card);
1050 void grdev_session_disable(grdev_session *session) {
1056 if (session->enabled) {
1057 session->enabled = false;
1058 HASHMAP_FOREACH(card, session->card_map, iter)
1059 grdev_card_disable(card);
1063 void grdev_session_commit(grdev_session *session) {
1069 if (!session->enabled)
1072 HASHMAP_FOREACH(card, session->card_map, iter)
1073 if (card->vtable->commit)
1074 card->vtable->commit(card);
1077 void grdev_session_restore(grdev_session *session) {
1083 if (!session->enabled)
1086 HASHMAP_FOREACH(card, session->card_map, iter)
1087 if (card->vtable->restore)
1088 card->vtable->restore(card);
1091 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1099 devnum = udev_device_get_devnum(ud);
1101 return grdev_session_hotplug_drm(session, ud);
1103 card = grdev_find_drm_card(session, devnum);
1107 r = grdev_drm_card_new(&card, session, ud);
1109 log_debug("grdev: %s: cannot add DRM device for %s: %s",
1110 session->name, udev_device_get_syspath(ud), strerror(-r));
1114 session_add_card(session, card);
1117 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1124 devnum = udev_device_get_devnum(ud);
1126 return grdev_session_hotplug_drm(session, ud);
1128 card = grdev_find_drm_card(session, devnum);
1132 session_remove_card(session, card);
1135 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1136 grdev_card *card = NULL;
1137 struct udev_device *p;
1143 for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1144 devnum = udev_device_get_devnum(ud);
1148 card = grdev_find_drm_card(session, devnum);
1156 grdev_drm_card_hotplug(card, ud);
1159 static void session_configure(grdev_session *session) {
1160 grdev_display *display;
1170 * Whenever backends add or remove pipes, we set session->modified and
1171 * require them to pin the session while modifying it. On release, we
1172 * reconfigure the device and re-assign displays to all modified pipes.
1174 * So far, we configure each pipe as a separate display. We do not
1175 * support user-configuration, nor have we gotten any reports from
1176 * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1177 * we get reports, we keep the logic to a minimum.
1180 /* create new displays for all unconfigured pipes */
1181 HASHMAP_FOREACH(card, session->card_map, i) {
1182 if (!card->modified)
1185 card->modified = false;
1187 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1192 assert(!tile->parent);
1194 display = grdev_find_display(session, pipe->name);
1195 if (display && display->tile) {
1196 log_debug("grdev: %s/%s: occupied display for pipe %s",
1197 session->name, card->name, pipe->name);
1199 } else if (!display) {
1200 r = grdev_display_new(&display, session, pipe->name);
1202 log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
1203 session->name, card->name, pipe->name, strerror(-r));
1208 tile_link(pipe->tile, display->tile);
1212 /* update displays */
1213 HASHMAP_FOREACH(display, session->display_map, i)
1214 session_change_display(session, display);
1217 grdev_session *grdev_session_pin(grdev_session *session) {
1224 grdev_session *grdev_session_unpin(grdev_session *session) {
1228 assert(session->n_pins > 0);
1230 if (--session->n_pins == 0)
1231 session_configure(session);
1240 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1241 _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1243 assert_return(out, -EINVAL);
1244 assert_return(event, -EINVAL);
1246 context = new0(grdev_context, 1);
1251 context->event = sd_event_ref(event);
1254 context->sysbus = sd_bus_ref(sysbus);
1256 context->session_map = hashmap_new(&string_hash_ops);
1257 if (!context->session_map)
1265 static void context_cleanup(grdev_context *context) {
1266 assert(hashmap_size(context->session_map) == 0);
1268 hashmap_free(context->session_map);
1269 context->sysbus = sd_bus_unref(context->sysbus);
1270 context->event = sd_event_unref(context->event);
1274 grdev_context *grdev_context_ref(grdev_context *context) {
1275 assert_return(context, NULL);
1276 assert_return(context->ref > 0, NULL);
1282 grdev_context *grdev_context_unref(grdev_context *context) {
1286 assert_return(context->ref > 0, NULL);
1288 if (--context->ref == 0)
1289 context_cleanup(context);