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)))
387 /* if back-buffer is up-to-date, schedule flip */
388 if (minage > 0 && fb->age >= minage) {
389 grdev_display_flip_target(display, target, fb->age);
393 /* we have an out-of-date back-buffer; return for redraw */
401 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
402 grdev_display_cache *cache;
406 assert(!display->modified);
407 assert(display->enabled);
411 cache = container_of(target, grdev_display_cache, target);
414 assert(cache->pipe->tile->display == display);
416 /* reset age of all FB on overflow */
417 if (age < target->fb->age)
418 for (i = 0; i < cache->pipe->max_fbs; ++i)
419 if (cache->pipe->fbs[i])
420 cache->pipe->fbs[i]->age = 0;
422 ((grdev_fb*)target->fb)->age = age;
423 cache->pipe->flip = true;
426 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
427 uint32_t x, y, width, height;
428 grdev_display_target *t;
432 assert(l->cache_w >= c->target.width + c->target.x);
433 assert(l->cache_h >= c->target.height + c->target.y);
439 t->rotate = (t->rotate + l->rotate) & 0x3;
449 case GRDEV_ROTATE_90:
450 t->x = l->cache_h - (height + y);
455 case GRDEV_ROTATE_180:
456 t->x = l->cache_w - (width + x);
457 t->y = l->cache_h - (height + y);
459 case GRDEV_ROTATE_270:
461 t->y = l->cache_w - (width + x);
471 if (l->flip & GRDEV_FLIP_HORIZONTAL)
472 t->x = l->cache_w - (t->width + t->x);
473 if (l->flip & GRDEV_FLIP_VERTICAL)
474 t->y = l->cache_h - (t->height + t->y);
482 static void display_cache_targets(grdev_display *display) {
483 grdev_display_cache *c;
488 /* depth-first with children before parent */
489 for (tile = tile_leftmost(display->tile);
491 tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
492 if (tile->type == GRDEV_TILE_LEAF) {
495 /* We're at a leaf and no parent has been cached, yet.
496 * Copy the pipe information into the target cache and
497 * update our global pipe-caches if required. */
499 assert(tile->leaf.pipe);
500 assert(display->n_pipes + 1 <= display->max_pipes);
503 c = &display->pipes[display->n_pipes++];
508 c->target.width = p->width;
509 c->target.height = p->height;
510 tile->cache_w = p->width;
511 tile->cache_h = p->height;
513 /* all new tiles are incomplete due to geometry changes */
514 c->incomplete = true;
516 display_cache_apply(c, tile);
518 grdev_tile *child, *l;
520 /* We're now at a node with all its children already
521 * computed (depth-first, child before parent). We
522 * first need to know the size of our tile, then we
523 * recurse into all leafs and update their cache. */
528 LIST_FOREACH(children_by_node, child, tile->node.child_list) {
529 if (child->x + child->cache_w > tile->cache_w)
530 tile->cache_w = child->x + child->cache_w;
531 if (child->y + child->cache_h > tile->cache_h)
532 tile->cache_h = child->y + child->cache_h;
535 assert(tile->cache_w > 0);
536 assert(tile->cache_h > 0);
538 TILE_FOREACH(tile, l)
539 if (l->type == GRDEV_TILE_LEAF)
540 display_cache_apply(l->leaf.pipe->cache, tile);
545 static bool display_cache(grdev_display *display) {
553 if (!display->modified)
556 display->modified = false;
557 display->framed = false;
558 display->n_pipes = 0;
562 if (display->n_leafs < 1)
565 TILE_FOREACH(display->tile, tile)
566 if (tile->type == GRDEV_TILE_LEAF)
567 tile->leaf.pipe->cache = NULL;
569 if (display->n_leafs > display->max_pipes) {
570 n = ALIGN_POWER2(display->n_leafs);
576 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
583 display->max_pipes = n;
586 display_cache_targets(display);
587 display->width = display->tile->cache_w;
588 display->height = display->tile->cache_h;
594 log_debug("grdev: %s/%s: cannot cache pipes: %s",
595 display->session->name, display->name, strerror(-r));
603 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
604 assert_return(card, NULL);
605 assert_return(name, NULL);
607 return hashmap_get(card->pipe_map, name);
610 static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
611 grdev_pipe *pipe = userdata;
613 grdev_pipe_frame(pipe);
617 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
620 assert_return(pipe, -EINVAL);
621 assert_return(pipe->vtable, -EINVAL);
622 assert_return(pipe->vtable->free, -EINVAL);
623 assert_return(pipe->card, -EINVAL);
624 assert_return(pipe->card->session, -EINVAL);
625 assert_return(!pipe->cache, -EINVAL);
626 assert_return(pipe->width > 0, -EINVAL);
627 assert_return(pipe->height > 0, -EINVAL);
628 assert_return(pipe->vrefresh > 0, -EINVAL);
629 assert_return(!pipe->enabled, -EINVAL);
630 assert_return(!pipe->running, -EINVAL);
631 assert_return(name, -EINVAL);
633 pipe->name = strdup(name);
638 pipe->fbs = new0(grdev_fb*, n_fbs);
642 pipe->max_fbs = n_fbs;
645 r = grdev_tile_new_leaf(&pipe->tile, pipe);
649 r = sd_event_add_time(pipe->card->session->context->event,
659 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
663 r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
667 card_modified(pipe->card);
671 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
678 assert(pipe->vtable);
679 assert(pipe->vtable->free);
682 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
684 tile_unlink(pipe->tile);
686 assert(!pipe->cache);
689 pipe->vtable->free(pipe);
691 sd_event_source_unref(tmp.vsync_src);
692 grdev_tile_free(tmp.tile);
693 card_modified(tmp.card);
700 static void pipe_enable(grdev_pipe *pipe) {
703 if (!pipe->enabled) {
704 pipe->enabled = true;
705 if (pipe->vtable->enable)
706 pipe->vtable->enable(pipe);
710 static void pipe_disable(grdev_pipe *pipe) {
714 pipe->enabled = false;
715 if (pipe->vtable->disable)
716 pipe->vtable->disable(pipe);
720 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
723 /* grdev_pipe_ready() is used by backends to notify about pipe state
724 * changed. If a pipe is ready, it can be fully used by us (available,
725 * enabled and accessable). Backends can disable pipes at any time
726 * (like for async revocation), but can only enable them from parent
727 * context. Otherwise, we might call user-callbacks recursively. */
729 if (pipe->running == running)
732 pipe->running = running;
734 /* runtime events for unused pipes are not interesting */
735 if (pipe->cache && pipe->enabled) {
736 grdev_display *display = pipe->tile->display;
741 session_frame(display->session, display);
743 pipe->cache->incomplete = true;
747 void grdev_pipe_frame(grdev_pipe *pipe) {
748 grdev_display *display;
752 /* if pipe is unused, ignore any frame events */
753 if (!pipe->cache || !pipe->enabled)
756 display = pipe->tile->display;
759 grdev_pipe_schedule(pipe, 0);
760 session_frame(display->session, display);
763 void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
768 sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
772 r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
776 ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
778 r = sd_event_source_set_time(pipe->vsync_src, ts);
782 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
789 log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s",
790 pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r));
797 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
798 assert_return(session, NULL);
799 assert_return(name, NULL);
801 return hashmap_get(session->card_map, name);
804 int grdev_card_add(grdev_card *card, const char *name) {
807 assert_return(card, -EINVAL);
808 assert_return(card->vtable, -EINVAL);
809 assert_return(card->vtable->free, -EINVAL);
810 assert_return(card->session, -EINVAL);
811 assert_return(name, -EINVAL);
813 card->name = strdup(name);
817 card->pipe_map = hashmap_new(&string_hash_ops);
821 r = hashmap_put(card->session->card_map, card->name, card);
828 grdev_card *grdev_card_free(grdev_card *card) {
834 assert(!card->enabled);
835 assert(card->vtable);
836 assert(card->vtable->free);
839 hashmap_remove_value(card->session->card_map, card->name, card);
842 card->vtable->free(card);
844 assert(hashmap_size(tmp.pipe_map) == 0);
846 hashmap_free(tmp.pipe_map);
852 static void card_modified(grdev_card *card) {
854 assert(card->session->n_pins > 0);
856 card->modified = true;
859 static void grdev_card_enable(grdev_card *card) {
862 if (!card->enabled) {
863 card->enabled = true;
864 if (card->vtable->enable)
865 card->vtable->enable(card);
869 static void grdev_card_disable(grdev_card *card) {
873 card->enabled = false;
874 if (card->vtable->disable)
875 card->vtable->disable(card);
883 static void session_raise(grdev_session *session, grdev_event *event) {
884 session->event_fn(session, session->userdata, event);
887 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
888 grdev_event event = {
889 .type = GRDEV_EVENT_DISPLAY_ADD,
895 session_raise(session, &event);
898 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
899 grdev_event event = {
900 .type = GRDEV_EVENT_DISPLAY_REMOVE,
906 session_raise(session, &event);
909 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
910 grdev_event event = {
911 .type = GRDEV_EVENT_DISPLAY_CHANGE,
917 session_raise(session, &event);
920 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
921 grdev_event event = {
922 .type = GRDEV_EVENT_DISPLAY_FRAME,
928 session_raise(session, &event);
931 static void session_add_card(grdev_session *session, grdev_card *card) {
935 log_debug("grdev: %s: add card '%s'", session->name, card->name);
937 /* Cards are not exposed to users, but managed internally. Cards are
938 * enabled if the session is enabled, and will track that state. The
939 * backend can probe the card at any time, but only if enabled. It
940 * will then add pipes according to hardware state.
941 * That is, the card may create pipes as soon as we enable it here. */
943 if (session->enabled)
944 grdev_card_enable(card);
947 static void session_remove_card(grdev_session *session, grdev_card *card) {
951 log_debug("grdev: %s: remove card '%s'", session->name, card->name);
953 /* As cards are not exposed, it can never be accessed by outside
954 * users and we can simply remove it. Disabling the card does not
955 * necessarily drop all pipes of the card. This is usually deferred
956 * to card destruction (as pipes are cached as long as FDs remain
957 * open). Therefore, the card destruction might cause pipes, and thus
958 * visible displays, to be removed. */
960 grdev_card_disable(card);
961 grdev_card_free(card);
964 static void session_add_display(grdev_session *session, grdev_display *display) {
967 assert(!display->enabled);
969 log_debug("grdev: %s: add display '%s'", session->name, display->name);
971 /* Displays are the main entity for public API users. We create them
972 * independent of card backends and they wrap any underlying display
973 * architecture. Displays are public at all times, thus, may be entered
974 * by outside users at any time. */
976 display->public = true;
977 session_raise_display_add(session, display);
980 static void session_remove_display(grdev_session *session, grdev_display *display) {
984 log_debug("grdev: %s: remove display '%s'", session->name, display->name);
986 /* Displays are public, so we have to be careful when removing them.
987 * We first tell users about their removal, disable them and then drop
988 * them. We now, after the notification, no external access will
989 * happen. Therefore, we can release the tiles afterwards safely. */
991 if (display->public) {
992 display->public = false;
993 session_raise_display_remove(session, display);
996 grdev_display_disable(display);
997 grdev_display_free(display);
1000 static void session_change_display(grdev_session *session, grdev_display *display) {
1006 changed = display_cache(display);
1008 if (display->n_leafs == 0) {
1009 session_remove_display(session, display);
1010 } else if (!display->public) {
1011 session_add_display(session, display);
1012 session_frame(session, display);
1013 } else if (changed) {
1014 session_raise_display_change(session, display);
1015 session_frame(session, display);
1016 } else if (display->framed) {
1017 session_frame(session, display);
1021 static void session_frame(grdev_session *session, grdev_display *display) {
1025 display->framed = false;
1027 if (!display->enabled || !session->enabled)
1030 if (session->n_pins > 0)
1031 display->framed = true;
1033 session_raise_display_frame(session, display);
1036 int grdev_session_new(grdev_session **out,
1037 grdev_context *context,
1040 grdev_event_fn event_fn,
1042 _cleanup_(grdev_session_freep) grdev_session *session = NULL;
1049 assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
1050 assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
1051 assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
1053 session = new0(grdev_session, 1);
1057 session->context = grdev_context_ref(context);
1058 session->custom = flags & GRDEV_SESSION_CUSTOM;
1059 session->managed = flags & GRDEV_SESSION_MANAGED;
1060 session->event_fn = event_fn;
1061 session->userdata = userdata;
1063 session->name = strdup(name);
1067 if (session->managed) {
1068 r = sd_bus_path_encode("/org/freedesktop/login1/session",
1069 session->name, &session->path);
1074 session->card_map = hashmap_new(&string_hash_ops);
1075 if (!session->card_map)
1078 session->display_map = hashmap_new(&string_hash_ops);
1079 if (!session->display_map)
1082 r = hashmap_put(context->session_map, session->name, session);
1091 grdev_session *grdev_session_free(grdev_session *session) {
1097 grdev_session_disable(session);
1099 while ((card = hashmap_first(session->card_map)))
1100 session_remove_card(session, card);
1102 assert(hashmap_size(session->display_map) == 0);
1105 hashmap_remove_value(session->context->session_map, session->name, session);
1107 hashmap_free(session->display_map);
1108 hashmap_free(session->card_map);
1109 session->context = grdev_context_unref(session->context);
1110 free(session->path);
1111 free(session->name);
1117 bool grdev_session_is_enabled(grdev_session *session) {
1118 return session && session->enabled;
1121 void grdev_session_enable(grdev_session *session) {
1127 if (!session->enabled) {
1128 session->enabled = true;
1129 HASHMAP_FOREACH(card, session->card_map, iter)
1130 grdev_card_enable(card);
1134 void grdev_session_disable(grdev_session *session) {
1140 if (session->enabled) {
1141 session->enabled = false;
1142 HASHMAP_FOREACH(card, session->card_map, iter)
1143 grdev_card_disable(card);
1147 void grdev_session_commit(grdev_session *session) {
1153 if (!session->enabled)
1156 HASHMAP_FOREACH(card, session->card_map, iter)
1157 if (card->vtable->commit)
1158 card->vtable->commit(card);
1161 void grdev_session_restore(grdev_session *session) {
1167 if (!session->enabled)
1170 HASHMAP_FOREACH(card, session->card_map, iter)
1171 if (card->vtable->restore)
1172 card->vtable->restore(card);
1175 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1183 devnum = udev_device_get_devnum(ud);
1185 return grdev_session_hotplug_drm(session, ud);
1187 card = grdev_find_drm_card(session, devnum);
1191 r = grdev_drm_card_new(&card, session, ud);
1193 log_debug("grdev: %s: cannot add DRM device for %s: %s",
1194 session->name, udev_device_get_syspath(ud), strerror(-r));
1198 session_add_card(session, card);
1201 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1208 devnum = udev_device_get_devnum(ud);
1210 return grdev_session_hotplug_drm(session, ud);
1212 card = grdev_find_drm_card(session, devnum);
1216 session_remove_card(session, card);
1219 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1220 grdev_card *card = NULL;
1221 struct udev_device *p;
1227 for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1228 devnum = udev_device_get_devnum(ud);
1232 card = grdev_find_drm_card(session, devnum);
1240 grdev_drm_card_hotplug(card, ud);
1243 static void session_configure(grdev_session *session) {
1244 grdev_display *display;
1254 * Whenever backends add or remove pipes, we set session->modified and
1255 * require them to pin the session while modifying it. On release, we
1256 * reconfigure the device and re-assign displays to all modified pipes.
1258 * So far, we configure each pipe as a separate display. We do not
1259 * support user-configuration, nor have we gotten any reports from
1260 * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1261 * we get reports, we keep the logic to a minimum.
1264 /* create new displays for all unconfigured pipes */
1265 HASHMAP_FOREACH(card, session->card_map, i) {
1266 if (!card->modified)
1269 card->modified = false;
1271 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1276 assert(!tile->parent);
1278 display = grdev_find_display(session, pipe->name);
1279 if (display && display->tile) {
1280 log_debug("grdev: %s/%s: occupied display for pipe %s",
1281 session->name, card->name, pipe->name);
1283 } else if (!display) {
1284 r = grdev_display_new(&display, session, pipe->name);
1286 log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
1287 session->name, card->name, pipe->name, strerror(-r));
1292 tile_link(pipe->tile, display->tile);
1296 /* update displays */
1297 HASHMAP_FOREACH(display, session->display_map, i)
1298 session_change_display(session, display);
1301 grdev_session *grdev_session_pin(grdev_session *session) {
1308 grdev_session *grdev_session_unpin(grdev_session *session) {
1312 assert(session->n_pins > 0);
1314 if (--session->n_pins == 0)
1315 session_configure(session);
1324 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1325 _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1327 assert_return(out, -EINVAL);
1328 assert_return(event, -EINVAL);
1330 context = new0(grdev_context, 1);
1335 context->event = sd_event_ref(event);
1338 context->sysbus = sd_bus_ref(sysbus);
1340 context->session_map = hashmap_new(&string_hash_ops);
1341 if (!context->session_map)
1349 static void context_cleanup(grdev_context *context) {
1350 assert(hashmap_size(context->session_map) == 0);
1352 hashmap_free(context->session_map);
1353 context->sysbus = sd_bus_unref(context->sysbus);
1354 context->event = sd_event_unref(context->event);
1358 grdev_context *grdev_context_ref(grdev_context *context) {
1359 assert_return(context, NULL);
1360 assert_return(context->ref > 0, NULL);
1366 grdev_context *grdev_context_unref(grdev_context *context) {
1370 assert_return(context->ref > 0, NULL);
1372 if (--context->ref == 0)
1373 context_cleanup(context);