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) {
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 /* find suitable back-buffer */
379 if (!pipe->vtable->target)
381 if (!(fb = pipe->vtable->target(pipe)))
384 assert(fb == pipe->back);
387 target->front = pipe->front;
388 target->back = pipe->back;
396 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
397 grdev_display_cache *cache;
401 assert(!display->modified);
402 assert(display->enabled);
404 assert(target->back);
406 cache = container_of(target, grdev_display_cache, target);
409 assert(cache->pipe->tile->display == display);
411 /* reset age of all FB on overflow */
412 if (age < target->back->age)
413 for (i = 0; i < cache->pipe->max_fbs; ++i)
414 if (cache->pipe->fbs[i])
415 cache->pipe->fbs[i]->age = 0;
417 ((grdev_fb*)target->back)->age = age;
418 cache->pipe->flip = true;
421 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
422 uint32_t x, y, width, height;
423 grdev_display_target *t;
427 assert(l->cache_w >= c->target.width + c->target.x);
428 assert(l->cache_h >= c->target.height + c->target.y);
434 t->rotate = (t->rotate + l->rotate) & 0x3;
444 case GRDEV_ROTATE_90:
445 t->x = l->cache_h - (height + y);
450 case GRDEV_ROTATE_180:
451 t->x = l->cache_w - (width + x);
452 t->y = l->cache_h - (height + y);
454 case GRDEV_ROTATE_270:
456 t->y = l->cache_w - (width + x);
466 if (l->flip & GRDEV_FLIP_HORIZONTAL)
467 t->x = l->cache_w - (t->width + t->x);
468 if (l->flip & GRDEV_FLIP_VERTICAL)
469 t->y = l->cache_h - (t->height + t->y);
477 static void display_cache_targets(grdev_display *display) {
478 grdev_display_cache *c;
483 /* depth-first with children before parent */
484 for (tile = tile_leftmost(display->tile);
486 tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
487 if (tile->type == GRDEV_TILE_LEAF) {
490 /* We're at a leaf and no parent has been cached, yet.
491 * Copy the pipe information into the target cache and
492 * update our global pipe-caches if required. */
494 assert(tile->leaf.pipe);
495 assert(display->n_pipes + 1 <= display->max_pipes);
498 c = &display->pipes[display->n_pipes++];
503 c->target.width = p->width;
504 c->target.height = p->height;
505 tile->cache_w = p->width;
506 tile->cache_h = p->height;
508 /* all new tiles are incomplete due to geometry changes */
509 c->incomplete = true;
511 display_cache_apply(c, tile);
513 grdev_tile *child, *l;
515 /* We're now at a node with all its children already
516 * computed (depth-first, child before parent). We
517 * first need to know the size of our tile, then we
518 * recurse into all leafs and update their cache. */
523 LIST_FOREACH(children_by_node, child, tile->node.child_list) {
524 if (child->x + child->cache_w > tile->cache_w)
525 tile->cache_w = child->x + child->cache_w;
526 if (child->y + child->cache_h > tile->cache_h)
527 tile->cache_h = child->y + child->cache_h;
530 assert(tile->cache_w > 0);
531 assert(tile->cache_h > 0);
533 TILE_FOREACH(tile, l)
534 if (l->type == GRDEV_TILE_LEAF)
535 display_cache_apply(l->leaf.pipe->cache, tile);
540 static bool display_cache(grdev_display *display) {
548 if (!display->modified)
551 display->modified = false;
552 display->framed = false;
553 display->n_pipes = 0;
557 if (display->n_leafs < 1)
560 TILE_FOREACH(display->tile, tile)
561 if (tile->type == GRDEV_TILE_LEAF)
562 tile->leaf.pipe->cache = NULL;
564 if (display->n_leafs > display->max_pipes) {
565 n = ALIGN_POWER2(display->n_leafs);
571 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
578 display->max_pipes = n;
581 display_cache_targets(display);
582 display->width = display->tile->cache_w;
583 display->height = display->tile->cache_h;
589 log_debug("grdev: %s/%s: cannot cache pipes: %s",
590 display->session->name, display->name, strerror(-r));
598 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
599 assert_return(card, NULL);
600 assert_return(name, NULL);
602 return hashmap_get(card->pipe_map, name);
605 static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
606 grdev_pipe *pipe = userdata;
608 grdev_pipe_frame(pipe);
612 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
615 assert_return(pipe, -EINVAL);
616 assert_return(pipe->vtable, -EINVAL);
617 assert_return(pipe->vtable->free, -EINVAL);
618 assert_return(pipe->card, -EINVAL);
619 assert_return(pipe->card->session, -EINVAL);
620 assert_return(!pipe->cache, -EINVAL);
621 assert_return(pipe->width > 0, -EINVAL);
622 assert_return(pipe->height > 0, -EINVAL);
623 assert_return(pipe->vrefresh > 0, -EINVAL);
624 assert_return(!pipe->enabled, -EINVAL);
625 assert_return(!pipe->running, -EINVAL);
626 assert_return(name, -EINVAL);
628 pipe->name = strdup(name);
633 pipe->fbs = new0(grdev_fb*, n_fbs);
637 pipe->max_fbs = n_fbs;
640 r = grdev_tile_new_leaf(&pipe->tile, pipe);
644 r = sd_event_add_time(pipe->card->session->context->event,
654 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
658 r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
662 card_modified(pipe->card);
666 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
673 assert(pipe->vtable);
674 assert(pipe->vtable->free);
677 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
679 tile_unlink(pipe->tile);
681 assert(!pipe->cache);
684 pipe->vtable->free(pipe);
686 sd_event_source_unref(tmp.vsync_src);
687 grdev_tile_free(tmp.tile);
688 card_modified(tmp.card);
695 static void pipe_enable(grdev_pipe *pipe) {
698 if (!pipe->enabled) {
699 pipe->enabled = true;
700 if (pipe->vtable->enable)
701 pipe->vtable->enable(pipe);
705 static void pipe_disable(grdev_pipe *pipe) {
709 pipe->enabled = false;
710 if (pipe->vtable->disable)
711 pipe->vtable->disable(pipe);
715 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
718 /* grdev_pipe_ready() is used by backends to notify about pipe state
719 * changed. If a pipe is ready, it can be fully used by us (available,
720 * enabled and accessable). Backends can disable pipes at any time
721 * (like for async revocation), but can only enable them from parent
722 * context. Otherwise, we might call user-callbacks recursively. */
724 if (pipe->running == running)
727 pipe->running = running;
729 /* runtime events for unused pipes are not interesting */
730 if (pipe->cache && pipe->enabled) {
731 grdev_display *display = pipe->tile->display;
736 session_frame(display->session, display);
738 pipe->cache->incomplete = true;
742 void grdev_pipe_frame(grdev_pipe *pipe) {
743 grdev_display *display;
747 /* if pipe is unused, ignore any frame events */
748 if (!pipe->cache || !pipe->enabled)
751 display = pipe->tile->display;
754 grdev_pipe_schedule(pipe, 0);
755 session_frame(display->session, display);
758 void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
763 sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
767 r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
771 ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
773 r = sd_event_source_set_time(pipe->vsync_src, ts);
777 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
784 log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s",
785 pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r));
792 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
793 assert_return(session, NULL);
794 assert_return(name, NULL);
796 return hashmap_get(session->card_map, name);
799 int grdev_card_add(grdev_card *card, const char *name) {
802 assert_return(card, -EINVAL);
803 assert_return(card->vtable, -EINVAL);
804 assert_return(card->vtable->free, -EINVAL);
805 assert_return(card->session, -EINVAL);
806 assert_return(name, -EINVAL);
808 card->name = strdup(name);
812 card->pipe_map = hashmap_new(&string_hash_ops);
816 r = hashmap_put(card->session->card_map, card->name, card);
823 grdev_card *grdev_card_free(grdev_card *card) {
829 assert(!card->enabled);
830 assert(card->vtable);
831 assert(card->vtable->free);
834 hashmap_remove_value(card->session->card_map, card->name, card);
837 card->vtable->free(card);
839 assert(hashmap_size(tmp.pipe_map) == 0);
841 hashmap_free(tmp.pipe_map);
847 static void card_modified(grdev_card *card) {
849 assert(card->session->n_pins > 0);
851 card->modified = true;
854 static void grdev_card_enable(grdev_card *card) {
857 if (!card->enabled) {
858 card->enabled = true;
859 if (card->vtable->enable)
860 card->vtable->enable(card);
864 static void grdev_card_disable(grdev_card *card) {
868 card->enabled = false;
869 if (card->vtable->disable)
870 card->vtable->disable(card);
878 static void session_raise(grdev_session *session, grdev_event *event) {
879 session->event_fn(session, session->userdata, event);
882 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
883 grdev_event event = {
884 .type = GRDEV_EVENT_DISPLAY_ADD,
890 session_raise(session, &event);
893 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
894 grdev_event event = {
895 .type = GRDEV_EVENT_DISPLAY_REMOVE,
901 session_raise(session, &event);
904 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
905 grdev_event event = {
906 .type = GRDEV_EVENT_DISPLAY_CHANGE,
912 session_raise(session, &event);
915 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
916 grdev_event event = {
917 .type = GRDEV_EVENT_DISPLAY_FRAME,
923 session_raise(session, &event);
926 static void session_add_card(grdev_session *session, grdev_card *card) {
930 log_debug("grdev: %s: add card '%s'", session->name, card->name);
932 /* Cards are not exposed to users, but managed internally. Cards are
933 * enabled if the session is enabled, and will track that state. The
934 * backend can probe the card at any time, but only if enabled. It
935 * will then add pipes according to hardware state.
936 * That is, the card may create pipes as soon as we enable it here. */
938 if (session->enabled)
939 grdev_card_enable(card);
942 static void session_remove_card(grdev_session *session, grdev_card *card) {
946 log_debug("grdev: %s: remove card '%s'", session->name, card->name);
948 /* As cards are not exposed, it can never be accessed by outside
949 * users and we can simply remove it. Disabling the card does not
950 * necessarily drop all pipes of the card. This is usually deferred
951 * to card destruction (as pipes are cached as long as FDs remain
952 * open). Therefore, the card destruction might cause pipes, and thus
953 * visible displays, to be removed. */
955 grdev_card_disable(card);
956 grdev_card_free(card);
959 static void session_add_display(grdev_session *session, grdev_display *display) {
962 assert(!display->enabled);
964 log_debug("grdev: %s: add display '%s'", session->name, display->name);
966 /* Displays are the main entity for public API users. We create them
967 * independent of card backends and they wrap any underlying display
968 * architecture. Displays are public at all times, thus, may be entered
969 * by outside users at any time. */
971 display->public = true;
972 session_raise_display_add(session, display);
975 static void session_remove_display(grdev_session *session, grdev_display *display) {
979 log_debug("grdev: %s: remove display '%s'", session->name, display->name);
981 /* Displays are public, so we have to be careful when removing them.
982 * We first tell users about their removal, disable them and then drop
983 * them. We now, after the notification, no external access will
984 * happen. Therefore, we can release the tiles afterwards safely. */
986 if (display->public) {
987 display->public = false;
988 session_raise_display_remove(session, display);
991 grdev_display_disable(display);
992 grdev_display_free(display);
995 static void session_change_display(grdev_session *session, grdev_display *display) {
1001 changed = display_cache(display);
1003 if (display->n_leafs == 0) {
1004 session_remove_display(session, display);
1005 } else if (!display->public) {
1006 session_add_display(session, display);
1007 session_frame(session, display);
1008 } else if (changed) {
1009 session_raise_display_change(session, display);
1010 session_frame(session, display);
1011 } else if (display->framed) {
1012 session_frame(session, display);
1016 static void session_frame(grdev_session *session, grdev_display *display) {
1020 display->framed = false;
1022 if (!display->enabled || !session->enabled)
1025 if (session->n_pins > 0)
1026 display->framed = true;
1028 session_raise_display_frame(session, display);
1031 int grdev_session_new(grdev_session **out,
1032 grdev_context *context,
1035 grdev_event_fn event_fn,
1037 _cleanup_(grdev_session_freep) grdev_session *session = NULL;
1044 assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
1045 assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
1046 assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
1048 session = new0(grdev_session, 1);
1052 session->context = grdev_context_ref(context);
1053 session->custom = flags & GRDEV_SESSION_CUSTOM;
1054 session->managed = flags & GRDEV_SESSION_MANAGED;
1055 session->event_fn = event_fn;
1056 session->userdata = userdata;
1058 session->name = strdup(name);
1062 if (session->managed) {
1063 r = sd_bus_path_encode("/org/freedesktop/login1/session",
1064 session->name, &session->path);
1069 session->card_map = hashmap_new(&string_hash_ops);
1070 if (!session->card_map)
1073 session->display_map = hashmap_new(&string_hash_ops);
1074 if (!session->display_map)
1077 r = hashmap_put(context->session_map, session->name, session);
1086 grdev_session *grdev_session_free(grdev_session *session) {
1092 grdev_session_disable(session);
1094 while ((card = hashmap_first(session->card_map)))
1095 session_remove_card(session, card);
1097 assert(hashmap_size(session->display_map) == 0);
1100 hashmap_remove_value(session->context->session_map, session->name, session);
1102 hashmap_free(session->display_map);
1103 hashmap_free(session->card_map);
1104 session->context = grdev_context_unref(session->context);
1105 free(session->path);
1106 free(session->name);
1112 bool grdev_session_is_enabled(grdev_session *session) {
1113 return session && session->enabled;
1116 void grdev_session_enable(grdev_session *session) {
1122 if (!session->enabled) {
1123 session->enabled = true;
1124 HASHMAP_FOREACH(card, session->card_map, iter)
1125 grdev_card_enable(card);
1129 void grdev_session_disable(grdev_session *session) {
1135 if (session->enabled) {
1136 session->enabled = false;
1137 HASHMAP_FOREACH(card, session->card_map, iter)
1138 grdev_card_disable(card);
1142 void grdev_session_commit(grdev_session *session) {
1148 if (!session->enabled)
1151 HASHMAP_FOREACH(card, session->card_map, iter)
1152 if (card->vtable->commit)
1153 card->vtable->commit(card);
1156 void grdev_session_restore(grdev_session *session) {
1162 if (!session->enabled)
1165 HASHMAP_FOREACH(card, session->card_map, iter)
1166 if (card->vtable->restore)
1167 card->vtable->restore(card);
1170 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1178 devnum = udev_device_get_devnum(ud);
1180 return grdev_session_hotplug_drm(session, ud);
1182 card = grdev_find_drm_card(session, devnum);
1186 r = grdev_drm_card_new(&card, session, ud);
1188 log_debug("grdev: %s: cannot add DRM device for %s: %s",
1189 session->name, udev_device_get_syspath(ud), strerror(-r));
1193 session_add_card(session, card);
1196 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1203 devnum = udev_device_get_devnum(ud);
1205 return grdev_session_hotplug_drm(session, ud);
1207 card = grdev_find_drm_card(session, devnum);
1211 session_remove_card(session, card);
1214 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1215 grdev_card *card = NULL;
1216 struct udev_device *p;
1222 for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1223 devnum = udev_device_get_devnum(ud);
1227 card = grdev_find_drm_card(session, devnum);
1235 grdev_drm_card_hotplug(card, ud);
1238 static void session_configure(grdev_session *session) {
1239 grdev_display *display;
1249 * Whenever backends add or remove pipes, we set session->modified and
1250 * require them to pin the session while modifying it. On release, we
1251 * reconfigure the device and re-assign displays to all modified pipes.
1253 * So far, we configure each pipe as a separate display. We do not
1254 * support user-configuration, nor have we gotten any reports from
1255 * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1256 * we get reports, we keep the logic to a minimum.
1259 /* create new displays for all unconfigured pipes */
1260 HASHMAP_FOREACH(card, session->card_map, i) {
1261 if (!card->modified)
1264 card->modified = false;
1266 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1271 assert(!tile->parent);
1273 display = grdev_find_display(session, pipe->name);
1274 if (display && display->tile) {
1275 log_debug("grdev: %s/%s: occupied display for pipe %s",
1276 session->name, card->name, pipe->name);
1278 } else if (!display) {
1279 r = grdev_display_new(&display, session, pipe->name);
1281 log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
1282 session->name, card->name, pipe->name, strerror(-r));
1287 tile_link(pipe->tile, display->tile);
1291 /* update displays */
1292 HASHMAP_FOREACH(display, session->display_map, i)
1293 session_change_display(session, display);
1296 grdev_session *grdev_session_pin(grdev_session *session) {
1303 grdev_session *grdev_session_unpin(grdev_session *session) {
1307 assert(session->n_pins > 0);
1309 if (--session->n_pins == 0)
1310 session_configure(session);
1319 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1320 _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1322 assert_return(out, -EINVAL);
1323 assert_return(event, -EINVAL);
1325 context = new0(grdev_context, 1);
1330 context->event = sd_event_ref(event);
1333 context->sysbus = sd_bus_ref(sysbus);
1335 context->session_map = hashmap_new(&string_hash_ops);
1336 if (!context->session_map)
1344 static void context_cleanup(grdev_context *context) {
1345 assert(hashmap_size(context->session_map) == 0);
1347 hashmap_free(context->session_map);
1348 context->sysbus = sd_bus_unref(context->sysbus);
1349 context->event = sd_event_unref(context->event);
1353 grdev_context *grdev_context_ref(grdev_context *context) {
1354 assert_return(context, NULL);
1355 assert_return(context->ref > 0, NULL);
1361 grdev_context *grdev_context_unref(grdev_context *context) {
1365 assert_return(context->ref > 0, NULL);
1367 if (--context->ref == 0)
1368 context_cleanup(context);