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(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_children == 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 void grdev_display_set_userdata(grdev_display *display, void *userdata) {
288 display->userdata = userdata;
291 void *grdev_display_get_userdata(grdev_display *display) {
292 assert_return(display, NULL);
294 return display->userdata;
297 const char *grdev_display_get_name(grdev_display *display) {
298 assert_return(display, NULL);
300 return display->name;
303 bool grdev_display_is_enabled(grdev_display *display) {
304 return display && display->enabled;
307 void grdev_display_enable(grdev_display *display) {
312 if (!display->enabled) {
313 display->enabled = true;
314 TILE_FOREACH(display->tile, t)
315 if (t->type == GRDEV_TILE_LEAF)
316 pipe_enable(t->leaf.pipe);
320 void grdev_display_disable(grdev_display *display) {
325 if (display->enabled) {
326 display->enabled = false;
327 TILE_FOREACH(display->tile, t)
328 if (t->type == GRDEV_TILE_LEAF)
329 pipe_disable(t->leaf.pipe);
333 const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev, uint64_t minage) {
334 grdev_display_cache *cache;
337 assert_return(display, NULL);
338 assert_return(!display->modified, NULL);
339 assert_return(display->enabled, NULL);
342 cache = container_of(prev, grdev_display_cache, target);
345 assert(cache->pipe->tile->display == display);
346 assert(display->pipes >= cache);
348 idx = (cache - display->pipes) / sizeof(*cache) + 1;
353 for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) {
354 grdev_display_target *target;
359 target = &cache->target;
361 if (!pipe->running || !pipe->enabled)
364 /* if front-buffer is up-to-date, there's nothing to do */
365 if (minage > 0 && pipe->front && pipe->front->age >= minage)
368 /* find suitable back-buffer */
369 if (!(fb = pipe->back)) {
370 if (!pipe->vtable->target || !(fb = pipe->vtable->target(pipe)))
374 /* if back-buffer is up-to-date, schedule flip */
375 if (minage > 0 && fb->age >= minage) {
376 grdev_display_flip_target(display, target, fb->age);
380 /* we have an out-of-date back-buffer; return for redraw */
388 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
389 grdev_display_cache *cache;
393 assert(!display->modified);
394 assert(display->enabled);
398 cache = container_of(target, grdev_display_cache, target);
401 assert(cache->pipe->tile->display == display);
403 /* reset age of all FB on overflow */
404 if (age < target->fb->age)
405 for (i = 0; i < cache->pipe->max_fbs; ++i)
406 if (cache->pipe->fbs[i])
407 cache->pipe->fbs[i]->age = 0;
409 ((grdev_fb*)target->fb)->age = age;
410 cache->pipe->flip = true;
413 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
414 uint32_t x, y, width, height;
415 grdev_display_target *t;
419 assert(l->cache_w >= c->target.width + c->target.x);
420 assert(l->cache_h >= c->target.height + c->target.y);
426 t->rotate = (t->rotate + l->rotate) & 0x3;
436 case GRDEV_ROTATE_90:
437 t->x = l->cache_h - (height + y);
442 case GRDEV_ROTATE_180:
443 t->x = l->cache_w - (width + x);
444 t->y = l->cache_h - (height + y);
446 case GRDEV_ROTATE_270:
448 t->y = l->cache_w - (width + x);
458 if (l->flip & GRDEV_FLIP_HORIZONTAL)
459 t->x = l->cache_w - (t->width + t->x);
460 if (l->flip & GRDEV_FLIP_VERTICAL)
461 t->y = l->cache_h - (t->height + t->y);
469 static void display_cache_targets(grdev_display *display) {
470 grdev_display_cache *c;
475 /* depth-first with children before parent */
476 for (tile = tile_leftmost(display->tile);
478 tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
479 if (tile->type == GRDEV_TILE_LEAF) {
482 /* We're at a leaf and no parent has been cached, yet.
483 * Copy the pipe information into the target cache and
484 * update our global pipe-caches if required. */
486 assert(tile->leaf.pipe);
487 assert(display->n_pipes + 1 <= display->max_pipes);
490 c = &display->pipes[display->n_pipes++];
495 c->target.width = p->width;
496 c->target.height = p->height;
497 tile->cache_w = p->width;
498 tile->cache_h = p->height;
500 /* all new tiles are incomplete due to geometry changes */
501 c->incomplete = true;
503 display_cache_apply(c, tile);
505 grdev_tile *child, *l;
507 /* We're now at a node with all its children already
508 * computed (depth-first, child before parent). We
509 * first need to know the size of our tile, then we
510 * recurse into all leafs and update their cache. */
515 LIST_FOREACH(children_by_node, child, tile->node.child_list) {
516 if (child->x + child->cache_w > tile->cache_w)
517 tile->cache_w = child->x + child->cache_w;
518 if (child->y + child->cache_h > tile->cache_h)
519 tile->cache_h = child->y + child->cache_h;
522 assert(tile->cache_w > 0);
523 assert(tile->cache_h > 0);
525 TILE_FOREACH(tile, l)
526 if (l->type == GRDEV_TILE_LEAF)
527 display_cache_apply(l->leaf.pipe->cache, tile);
532 static bool display_cache(grdev_display *display) {
540 if (!display->modified)
543 display->modified = false;
544 display->framed = false;
545 display->n_pipes = 0;
549 if (display->n_leafs < 1)
552 TILE_FOREACH(display->tile, tile)
553 if (tile->type == GRDEV_TILE_LEAF)
554 tile->leaf.pipe->cache = NULL;
556 if (display->n_leafs > display->max_pipes) {
557 n = ALIGN_POWER2(display->n_leafs);
563 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
570 display->max_pipes = n;
573 display_cache_targets(display);
579 log_debug("grdev: %s/%s: cannot cache pipes: %s",
580 display->session->name, display->name, strerror(-r));
588 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
589 assert_return(card, NULL);
590 assert_return(name, NULL);
592 return hashmap_get(card->pipe_map, name);
595 static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
596 grdev_pipe *pipe = userdata;
598 grdev_pipe_frame(pipe);
602 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
605 assert_return(pipe, -EINVAL);
606 assert_return(pipe->vtable, -EINVAL);
607 assert_return(pipe->vtable->free, -EINVAL);
608 assert_return(pipe->card, -EINVAL);
609 assert_return(pipe->card->session, -EINVAL);
610 assert_return(!pipe->cache, -EINVAL);
611 assert_return(pipe->width > 0, -EINVAL);
612 assert_return(pipe->height > 0, -EINVAL);
613 assert_return(pipe->vrefresh > 0, -EINVAL);
614 assert_return(!pipe->enabled, -EINVAL);
615 assert_return(!pipe->running, -EINVAL);
616 assert_return(name, -EINVAL);
618 pipe->name = strdup(name);
623 pipe->fbs = new0(grdev_fb*, n_fbs);
627 pipe->max_fbs = n_fbs;
630 r = grdev_tile_new_leaf(&pipe->tile, pipe);
634 r = sd_event_add_time(pipe->card->session->context->event,
644 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
648 r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
652 card_modified(pipe->card);
656 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
663 assert(pipe->vtable);
664 assert(pipe->vtable->free);
667 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
669 tile_unlink(pipe->tile);
671 assert(!pipe->cache);
674 pipe->vtable->free(pipe);
676 sd_event_source_unref(tmp.vsync_src);
677 grdev_tile_free(tmp.tile);
678 card_modified(tmp.card);
685 static void pipe_enable(grdev_pipe *pipe) {
688 if (!pipe->enabled) {
689 pipe->enabled = true;
690 if (pipe->vtable->enable)
691 pipe->vtable->enable(pipe);
695 static void pipe_disable(grdev_pipe *pipe) {
699 pipe->enabled = false;
700 if (pipe->vtable->disable)
701 pipe->vtable->disable(pipe);
705 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
708 /* grdev_pipe_ready() is used by backends to notify about pipe state
709 * changed. If a pipe is ready, it can be fully used by us (available,
710 * enabled and accessable). Backends can disable pipes at any time
711 * (like for async revocation), but can only enable them from parent
712 * context. Otherwise, we might call user-callbacks recursively. */
714 if (pipe->running == running)
717 pipe->running = running;
719 /* runtime events for unused pipes are not interesting */
720 if (pipe->cache && pipe->enabled) {
721 grdev_display *display = pipe->tile->display;
726 session_frame(display->session, display);
728 pipe->cache->incomplete = true;
732 void grdev_pipe_frame(grdev_pipe *pipe) {
733 grdev_display *display;
737 /* if pipe is unused, ignore any frame events */
738 if (!pipe->cache || !pipe->enabled)
741 display = pipe->tile->display;
744 grdev_pipe_schedule(pipe, 0);
745 session_frame(display->session, display);
748 void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
753 sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
757 r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
761 ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
763 r = sd_event_source_set_time(pipe->vsync_src, ts);
767 r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
774 log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s",
775 pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r));
782 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
783 assert_return(session, NULL);
784 assert_return(name, NULL);
786 return hashmap_get(session->card_map, name);
789 int grdev_card_add(grdev_card *card, const char *name) {
792 assert_return(card, -EINVAL);
793 assert_return(card->vtable, -EINVAL);
794 assert_return(card->vtable->free, -EINVAL);
795 assert_return(card->session, -EINVAL);
796 assert_return(name, -EINVAL);
798 card->name = strdup(name);
802 card->pipe_map = hashmap_new(&string_hash_ops);
806 r = hashmap_put(card->session->card_map, card->name, card);
813 grdev_card *grdev_card_free(grdev_card *card) {
819 assert(!card->enabled);
820 assert(card->vtable);
821 assert(card->vtable->free);
824 hashmap_remove_value(card->session->card_map, card->name, card);
827 card->vtable->free(card);
829 assert(hashmap_size(tmp.pipe_map) == 0);
831 hashmap_free(tmp.pipe_map);
837 static void card_modified(grdev_card *card) {
839 assert(card->session->n_pins > 0);
841 card->modified = true;
844 static void grdev_card_enable(grdev_card *card) {
847 if (!card->enabled) {
848 card->enabled = true;
849 if (card->vtable->enable)
850 card->vtable->enable(card);
854 static void grdev_card_disable(grdev_card *card) {
858 card->enabled = false;
859 if (card->vtable->disable)
860 card->vtable->disable(card);
868 static void session_raise(grdev_session *session, grdev_event *event) {
869 session->event_fn(session, session->userdata, event);
872 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
873 grdev_event event = {
874 .type = GRDEV_EVENT_DISPLAY_ADD,
880 session_raise(session, &event);
883 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
884 grdev_event event = {
885 .type = GRDEV_EVENT_DISPLAY_REMOVE,
891 session_raise(session, &event);
894 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
895 grdev_event event = {
896 .type = GRDEV_EVENT_DISPLAY_CHANGE,
902 session_raise(session, &event);
905 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
906 grdev_event event = {
907 .type = GRDEV_EVENT_DISPLAY_FRAME,
913 session_raise(session, &event);
916 static void session_add_card(grdev_session *session, grdev_card *card) {
920 log_debug("grdev: %s: add card '%s'", session->name, card->name);
922 /* Cards are not exposed to users, but managed internally. Cards are
923 * enabled if the session is enabled, and will track that state. The
924 * backend can probe the card at any time, but only if enabled. It
925 * will then add pipes according to hardware state.
926 * That is, the card may create pipes as soon as we enable it here. */
928 if (session->enabled)
929 grdev_card_enable(card);
932 static void session_remove_card(grdev_session *session, grdev_card *card) {
936 log_debug("grdev: %s: remove card '%s'", session->name, card->name);
938 /* As cards are not exposed, it can never be accessed by outside
939 * users and we can simply remove it. Disabling the card does not
940 * necessarily drop all pipes of the card. This is usually deferred
941 * to card destruction (as pipes are cached as long as FDs remain
942 * open). Therefore, the card destruction might cause pipes, and thus
943 * visible displays, to be removed. */
945 grdev_card_disable(card);
946 grdev_card_free(card);
949 static void session_add_display(grdev_session *session, grdev_display *display) {
952 assert(!display->enabled);
954 log_debug("grdev: %s: add display '%s'", session->name, display->name);
956 /* Displays are the main entity for public API users. We create them
957 * independent of card backends and they wrap any underlying display
958 * architecture. Displays are public at all times, thus, may be entered
959 * by outside users at any time. */
961 display->public = true;
962 session_raise_display_add(session, display);
965 static void session_remove_display(grdev_session *session, grdev_display *display) {
969 log_debug("grdev: %s: remove display '%s'", session->name, display->name);
971 /* Displays are public, so we have to be careful when removing them.
972 * We first tell users about their removal, disable them and then drop
973 * them. We now, after the notification, no external access will
974 * happen. Therefore, we can release the tiles afterwards safely. */
976 if (display->public) {
977 display->public = false;
978 session_raise_display_remove(session, display);
981 grdev_display_disable(display);
982 grdev_display_free(display);
985 static void session_change_display(grdev_session *session, grdev_display *display) {
991 changed = display_cache(display);
993 if (display->n_leafs == 0) {
994 session_remove_display(session, display);
995 } else if (!display->public) {
996 session_add_display(session, display);
997 session_frame(session, display);
998 } else if (changed) {
999 session_raise_display_change(session, display);
1000 session_frame(session, display);
1001 } else if (display->framed) {
1002 session_frame(session, display);
1006 static void session_frame(grdev_session *session, grdev_display *display) {
1010 display->framed = false;
1012 if (!display->enabled || !session->enabled)
1015 if (session->n_pins > 0)
1016 display->framed = true;
1018 session_raise_display_frame(session, display);
1021 int grdev_session_new(grdev_session **out,
1022 grdev_context *context,
1025 grdev_event_fn event_fn,
1027 _cleanup_(grdev_session_freep) grdev_session *session = NULL;
1034 assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
1035 assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
1036 assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
1038 session = new0(grdev_session, 1);
1042 session->context = grdev_context_ref(context);
1043 session->custom = flags & GRDEV_SESSION_CUSTOM;
1044 session->managed = flags & GRDEV_SESSION_MANAGED;
1045 session->event_fn = event_fn;
1046 session->userdata = userdata;
1048 session->name = strdup(name);
1052 if (session->managed) {
1053 r = sd_bus_path_encode("/org/freedesktop/login1/session",
1054 session->name, &session->path);
1059 session->card_map = hashmap_new(&string_hash_ops);
1060 if (!session->card_map)
1063 session->display_map = hashmap_new(&string_hash_ops);
1064 if (!session->display_map)
1067 r = hashmap_put(context->session_map, session->name, session);
1076 grdev_session *grdev_session_free(grdev_session *session) {
1082 grdev_session_disable(session);
1084 while ((card = hashmap_first(session->card_map)))
1085 session_remove_card(session, card);
1087 assert(hashmap_size(session->display_map) == 0);
1090 hashmap_remove_value(session->context->session_map, session->name, session);
1092 hashmap_free(session->display_map);
1093 hashmap_free(session->card_map);
1094 session->context = grdev_context_unref(session->context);
1095 free(session->path);
1096 free(session->name);
1102 bool grdev_session_is_enabled(grdev_session *session) {
1103 return session && session->enabled;
1106 void grdev_session_enable(grdev_session *session) {
1112 if (!session->enabled) {
1113 session->enabled = true;
1114 HASHMAP_FOREACH(card, session->card_map, iter)
1115 grdev_card_enable(card);
1119 void grdev_session_disable(grdev_session *session) {
1125 if (session->enabled) {
1126 session->enabled = false;
1127 HASHMAP_FOREACH(card, session->card_map, iter)
1128 grdev_card_disable(card);
1132 void grdev_session_commit(grdev_session *session) {
1138 if (!session->enabled)
1141 HASHMAP_FOREACH(card, session->card_map, iter)
1142 if (card->vtable->commit)
1143 card->vtable->commit(card);
1146 void grdev_session_restore(grdev_session *session) {
1152 if (!session->enabled)
1155 HASHMAP_FOREACH(card, session->card_map, iter)
1156 if (card->vtable->restore)
1157 card->vtable->restore(card);
1160 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1168 devnum = udev_device_get_devnum(ud);
1170 return grdev_session_hotplug_drm(session, ud);
1172 card = grdev_find_drm_card(session, devnum);
1176 r = grdev_drm_card_new(&card, session, ud);
1178 log_debug("grdev: %s: cannot add DRM device for %s: %s",
1179 session->name, udev_device_get_syspath(ud), strerror(-r));
1183 session_add_card(session, card);
1186 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1193 devnum = udev_device_get_devnum(ud);
1195 return grdev_session_hotplug_drm(session, ud);
1197 card = grdev_find_drm_card(session, devnum);
1201 session_remove_card(session, card);
1204 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1205 grdev_card *card = NULL;
1206 struct udev_device *p;
1212 for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1213 devnum = udev_device_get_devnum(ud);
1217 card = grdev_find_drm_card(session, devnum);
1225 grdev_drm_card_hotplug(card, ud);
1228 static void session_configure(grdev_session *session) {
1229 grdev_display *display;
1239 * Whenever backends add or remove pipes, we set session->modified and
1240 * require them to pin the session while modifying it. On release, we
1241 * reconfigure the device and re-assign displays to all modified pipes.
1243 * So far, we configure each pipe as a separate display. We do not
1244 * support user-configuration, nor have we gotten any reports from
1245 * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1246 * we get reports, we keep the logic to a minimum.
1249 /* create new displays for all unconfigured pipes */
1250 HASHMAP_FOREACH(card, session->card_map, i) {
1251 if (!card->modified)
1254 card->modified = false;
1256 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1261 assert(!tile->parent);
1263 display = grdev_find_display(session, pipe->name);
1264 if (display && display->tile) {
1265 log_debug("grdev: %s/%s: occupied display for pipe %s",
1266 session->name, card->name, pipe->name);
1268 } else if (!display) {
1269 r = grdev_display_new(&display, session, pipe->name);
1271 log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
1272 session->name, card->name, pipe->name, strerror(-r));
1277 tile_link(pipe->tile, display->tile);
1281 /* update displays */
1282 HASHMAP_FOREACH(display, session->display_map, i)
1283 session_change_display(session, display);
1286 grdev_session *grdev_session_pin(grdev_session *session) {
1293 grdev_session *grdev_session_unpin(grdev_session *session) {
1297 assert(session->n_pins > 0);
1299 if (--session->n_pins == 0)
1300 session_configure(session);
1309 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1310 _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1312 assert_return(out, -EINVAL);
1313 assert_return(event, -EINVAL);
1315 context = new0(grdev_context, 1);
1320 context->event = sd_event_ref(event);
1323 context->sysbus = sd_bus_ref(sysbus);
1325 context->session_map = hashmap_new(&string_hash_ops);
1326 if (!context->session_map)
1334 static void context_cleanup(grdev_context *context) {
1335 assert(hashmap_size(context->session_map) == 0);
1337 hashmap_free(context->session_map);
1338 context->sysbus = sd_bus_unref(context->sysbus);
1339 context->event = sd_event_unref(context->event);
1343 grdev_context *grdev_context_ref(grdev_context *context) {
1344 assert_return(context, NULL);
1345 assert_return(context->ref > 0, NULL);
1351 grdev_context *grdev_context_unref(grdev_context *context) {
1355 assert_return(context->ref > 0, NULL);
1357 if (--context->ref == 0)
1358 context_cleanup(context);