1From 475bf406ef30aa6c04efc35caf2ff748a4a20df9 Mon Sep 17 00:00:00 2001 2From: Jeffy Chen <jeffy.chen@rock-chips.com> 3Date: Tue, 23 Jun 2020 10:05:48 +0800 4Subject: [PATCH 13/79] backend-drm: Support selecting monitors 5 6Using these environments: 7 8WESTON_DRM_PRIMARY: 9 Specify primary connector. 10WESTON_DRM_SINGLE_HEAD: 11 Force using single connector. 12WESTON_DRM_HEAD_FALLBACK: 13 Fallback to any available connector if none matched. 14WESTON_DRM_HEAD_FALLBACK_ALL: 15 Fallback to all available connector if none matched. 16WESTON_DRM_PREFER_EXTERNAL_DUAL: 17 Prefer external connectors, and also enable internal ones. 18WESTON_DRM_PREFER_EXTERNAL: 19 Prefer external connectors, and disable internal ones if any matched. 20 21WESTON_DRM_HEAD_MODE: 22 default(match primary->internal->external) 23 primary(match primary only) 24 internal(match primary->internal) 25 external(match primary->external) 26 external-dual(match primary->external->internal) 27 28Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com> 29--- 30 compositor/main.c | 26 +- 31 desktop-shell/shell.c | 24 +- 32 include/libweston/libweston.h | 2 + 33 libweston/backend-drm/drm-internal.h | 23 ++ 34 libweston/backend-drm/drm.c | 369 ++++++++++++++++++++++++--- 35 libweston/backend-drm/kms.c | 42 ++- 36 libweston/compositor.c | 42 ++- 37 7 files changed, 471 insertions(+), 57 deletions(-) 38 39diff --git a/compositor/main.c b/compositor/main.c 40index e946b4a..47bf540 100644 41--- a/compositor/main.c 42+++ b/compositor/main.c 43@@ -2067,7 +2067,7 @@ drm_head_prepare_enable(struct wet_compositor *wet, 44 char *output_name = NULL; 45 char *mode = NULL; 46 47- section = drm_config_find_controlling_output_section(wet->config, name); 48+ section = head->section; 49 if (section) { 50 /* skip outputs that are explicitly off, or non-desktop and not 51 * explicitly enabled. The backend turns them off automatically. 52@@ -2097,11 +2097,10 @@ static bool 53 drm_head_should_force_enable(struct wet_compositor *wet, 54 struct weston_head *head) 55 { 56- const char *name = weston_head_get_name(head); 57 struct weston_config_section *section; 58 bool force; 59 60- section = drm_config_find_controlling_output_section(wet->config, name); 61+ section = head->section; 62 if (!section) 63 return false; 64 65@@ -2289,6 +2288,25 @@ drm_head_disable(struct weston_head *head) 66 wet_output_destroy(output); 67 } 68 69+static bool 70+drm_head_update_output_section(struct weston_head *head) 71+{ 72+ struct weston_compositor *compositor = head->compositor; 73+ struct wet_compositor *wet = to_wet_compositor(compositor); 74+ const char *name = weston_head_get_name(head); 75+ struct weston_config_section *section; 76+ 77+ if (head->section) 78+ return true; 79+ 80+ section = drm_config_find_controlling_output_section(wet->config, name); 81+ if (!section) 82+ return false; 83+ 84+ head->section = section; 85+ return true; 86+} 87+ 88 static void 89 drm_heads_changed(struct wl_listener *listener, void *arg) 90 { 91@@ -2304,6 +2322,8 @@ drm_heads_changed(struct wl_listener *listener, void *arg) 92 * output. 93 */ 94 while ((head = weston_compositor_iterate_heads(compositor, head))) { 95+ drm_head_update_output_section(head); 96+ 97 connected = weston_head_is_connected(head); 98 enabled = weston_head_is_enabled(head); 99 changed = weston_head_is_device_changed(head); 100diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c 101index 15c148f..515c57f 100644 102--- a/desktop-shell/shell.c 103+++ b/desktop-shell/shell.c 104@@ -3988,6 +3988,9 @@ shell_fade_done_for_output(struct weston_view_animation *animation, void *data) 105 struct shell_output *shell_output = data; 106 struct desktop_shell *shell = shell_output->shell; 107 108+ if (!shell_output->fade.view) 109+ return; 110+ 111 shell_output->fade.animation = NULL; 112 switch (shell_output->fade.type) { 113 case FADE_IN: 114@@ -4021,6 +4024,7 @@ shell_fade_create_surface_for_output(struct desktop_shell *shell, struct shell_o 115 116 weston_surface_set_size(surface, shell_output->output->width, shell_output->output->height); 117 weston_view_set_position(view, shell_output->output->x, shell_output->output->y); 118+ weston_view_set_output(view, shell_output->output); 119 weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); 120 weston_layer_entry_insert(&compositor->fade_layer.view_list, 121 &view->layer_link); 122@@ -4841,8 +4845,11 @@ shell_output_destroy(struct shell_output *shell_output) 123 } 124 125 if (shell_output->fade.view) { 126+ struct weston_view *view = shell_output->fade.view; 127+ shell_output->fade.view = NULL; 128+ 129 /* destroys the view as well */ 130- weston_surface_destroy(shell_output->fade.view->surface); 131+ weston_surface_destroy(view->surface); 132 } 133 134 if (shell_output->fade.startup_timer) 135@@ -4946,12 +4953,25 @@ handle_output_move_layer(struct desktop_shell *shell, 136 static void 137 handle_output_move(struct wl_listener *listener, void *data) 138 { 139+ struct weston_output *output = data; 140+ struct weston_compositor *compositor = output->compositor; 141 struct desktop_shell *shell; 142 143 shell = container_of(listener, struct desktop_shell, 144 output_move_listener); 145 146- shell_for_each_layer(shell, handle_output_move_layer, data); 147+ if (shell->lock_surface) 148+ shell->lock_surface->committed(shell->lock_surface, 0, 0); 149+ 150+ /* Only move normal layers for non-default output */ 151+ if (output != get_default_output(compositor)) { 152+ shell_for_each_layer(shell, handle_output_move_layer, data); 153+ return; 154+ } 155+ 156+ handle_output_move_layer(shell, &shell->lock_layer, data); 157+ handle_output_move_layer(shell, &shell->background_layer, data); 158+ handle_output_move_layer(shell, &shell->panel_layer, data); 159 } 160 161 static void 162diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h 163index c6ba508..bdecc19 100644 164--- a/include/libweston/libweston.h 165+++ b/include/libweston/libweston.h 166@@ -261,6 +261,8 @@ struct weston_head { 167 168 /** Current content protection status */ 169 enum weston_hdcp_protection current_protection; 170+ 171+ struct weston_config_section *section; /**< config section **/ 172 }; 173 174 /** Content producer for heads 175diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h 176index 4860088..3f42a25 100644 177--- a/libweston/backend-drm/drm-internal.h 178+++ b/libweston/backend-drm/drm-internal.h 179@@ -110,6 +110,10 @@ 180 181 #define MAX_CLONED_CONNECTORS 4 182 183+/* Min duration between drm outputs update requests, to avoid glith */ 184+#define DRM_MIN_UPDATE_MS 1000 185+ 186+#define DRM_RESIZE_FREEZE_MS 600 187 188 /** 189 * Represents the values of an enum-type KMS property 190@@ -249,6 +253,10 @@ enum actions_needed_dmabuf_feedback { 191 ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE = (1 << 1), 192 }; 193 194+struct drm_head; 195+struct drm_backend; 196+typedef bool (*drm_head_match_t) (struct drm_backend *, struct drm_head *); 197+ 198 struct drm_backend { 199 struct weston_backend base; 200 struct weston_compositor *compositor; 201@@ -264,6 +272,7 @@ struct drm_backend { 202 int fd; 203 char *filename; 204 dev_t devnum; 205+ char *syspath; 206 } drm; 207 struct gbm_device *gbm; 208 struct wl_listener session_listener; 209@@ -311,6 +320,18 @@ struct drm_backend { 210 bool fb_modifiers; 211 212 struct weston_log_scope *debug; 213+ 214+ struct wl_event_source *hotplug_timer; 215+ bool pending_update; 216+ int64_t last_update_ms; 217+ int64_t last_resize_ms; 218+ 219+ bool single_head; 220+ bool head_fallback; 221+ bool head_fallback_all; 222+ drm_head_match_t *head_matches; 223+ struct drm_head *primary_head; 224+ struct wl_listener output_create_listener; 225 }; 226 227 struct drm_mode { 228@@ -574,6 +595,8 @@ struct drm_output { 229 bool virtual; 230 231 submit_frame_cb virtual_submit_frame; 232+ 233+ bool state_invalid; 234 }; 235 236 static inline struct drm_head * 237diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c 238index d6a8588..4e00933 100644 239--- a/libweston/backend-drm/drm.c 240+++ b/libweston/backend-drm/drm.c 241@@ -47,6 +47,7 @@ 242 243 #include <libudev.h> 244 245+#include <libweston/config-parser.h> 246 #include <libweston/libweston.h> 247 #include <libweston/backend-drm.h> 248 #include <libweston/weston-log.h> 249@@ -68,6 +69,44 @@ 250 251 static const char default_seat[] = "seat0"; 252 253+static inline bool 254+drm_head_is_external(struct drm_head *head) 255+{ 256+ drmModeConnector *conn = head->connector.conn; 257+ switch (conn->connector_type) { 258+ case DRM_MODE_CONNECTOR_LVDS: 259+ case DRM_MODE_CONNECTOR_eDP: 260+#ifdef DRM_MODE_CONNECTOR_DSI 261+ case DRM_MODE_CONNECTOR_DSI: 262+#endif 263+ return false; 264+ default: 265+ return true; 266+ } 267+}; 268+ 269+static void 270+drm_backend_update_outputs(struct drm_backend *b) 271+{ 272+ struct weston_output *primary; 273+ 274+ if (!b->primary_head) 275+ return; 276+ 277+ primary = b->primary_head->base.output; 278+ if (!primary) 279+ return; 280+ 281+ /* Move primary output to (0,0) */ 282+ wl_list_remove(&primary->link); 283+ wl_list_insert(&b->compositor->output_list, &primary->link); 284+ 285+ /* Reflow outputs */ 286+ weston_compositor_reflow_outputs(b->compositor); 287+ 288+ weston_compositor_damage_all(b->compositor); 289+} 290+ 291 static void 292 drm_backend_create_faked_zpos(struct drm_backend *b) 293 { 294@@ -460,10 +499,13 @@ drm_output_repaint(struct weston_output *output_base, 295 pixman_region32_t *damage, 296 void *repaint_data) 297 { 298+ struct drm_backend *b = to_drm_backend(output_base->compositor); 299 struct drm_pending_state *pending_state = repaint_data; 300 struct drm_output *output = to_drm_output(output_base); 301 struct drm_output_state *state = NULL; 302 struct drm_plane_state *scanout_state; 303+ struct timespec now; 304+ int64_t now_ms; 305 306 assert(!output->virtual); 307 308@@ -472,6 +514,20 @@ drm_output_repaint(struct weston_output *output_base, 309 310 assert(!output->state_last); 311 312+ weston_compositor_read_presentation_clock(b->compositor, &now); 313+ now_ms = timespec_to_msec(&now); 314+ if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS) { 315+ /* Resize fullscreen/maxmium views(not always success) */ 316+ if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS) 317+ wl_signal_emit(&b->compositor->output_resized_signal, 318+ output); 319+ 320+ weston_output_damage(output_base); 321+ weston_output_finish_frame(output_base, NULL, 322+ WP_PRESENTATION_FEEDBACK_INVALID); 323+ return 0; 324+ } 325+ 326 /* If planes have been disabled in the core, we might not have 327 * hit assign_planes at all, so might not have valid output state 328 * here. */ 329@@ -497,7 +553,7 @@ drm_output_repaint(struct weston_output *output_base, 330 331 err: 332 drm_output_state_free(state); 333- return -1; 334+ return 0; 335 } 336 337 /* Determine the type of vblank synchronization to use for the output. 338@@ -718,6 +774,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo 339 * content. 340 */ 341 b->state_invalid = true; 342+ output->state_invalid = true; 343 344 if (b->use_pixman) { 345 drm_output_fini_pixman(output); 346@@ -1271,6 +1328,7 @@ drm_output_attach_head(struct weston_output *output_base, 347 struct weston_head *head_base) 348 { 349 struct drm_backend *b = to_drm_backend(output_base->compositor); 350+ struct drm_output *output = to_drm_output(output_base); 351 352 if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS) 353 return -1; 354@@ -1288,6 +1346,7 @@ drm_output_attach_head(struct weston_output *output_base, 355 * will not clear the flag before this output is updated? 356 */ 357 b->state_invalid = true; 358+ output->state_invalid = true; 359 360 weston_output_schedule_repaint(output_base); 361 362@@ -1299,6 +1358,7 @@ drm_output_detach_head(struct weston_output *output_base, 363 struct weston_head *head_base) 364 { 365 struct drm_backend *b = to_drm_backend(output_base->compositor); 366+ struct drm_output *output = to_drm_output(output_base); 367 368 if (!output_base->enabled) 369 return; 370@@ -1307,6 +1367,7 @@ drm_output_detach_head(struct weston_output *output_base, 371 * be driven. */ 372 /* XXX: Ideally we'd do this per-output, not globally. */ 373 b->state_invalid = true; 374+ output->state_invalid = true; 375 376 weston_output_schedule_repaint(output_base); 377 } 378@@ -1795,6 +1856,7 @@ drm_output_detach_crtc(struct drm_output *output) 379 380 /* Force resetting unused CRTCs */ 381 b->state_invalid = true; 382+ output->state_invalid = true; 383 } 384 385 static int 386@@ -1839,6 +1901,8 @@ drm_output_enable(struct weston_output *base) 387 output->base.switch_mode = drm_output_switch_mode; 388 output->base.set_gamma = drm_output_set_gamma; 389 390+ output->state_invalid = true; 391+ 392 weston_log("Output %s (crtc %d) video modes:\n", 393 output->base.name, output->crtc->crtc_id); 394 drm_output_print_modes(output); 395@@ -2173,8 +2237,7 @@ drm_head_create(struct drm_backend *backend, drmModeConnector *conn, 396 397 head->backlight = backlight_init(drm_device, conn->connector_type); 398 399- if (conn->connector_type == DRM_MODE_CONNECTOR_LVDS || 400- conn->connector_type == DRM_MODE_CONNECTOR_eDP) 401+ if (!drm_head_is_external(head)) 402 weston_head_set_internal(&head->base); 403 404 if (drm_head_read_current_setup(head, backend) < 0) { 405@@ -2335,71 +2398,77 @@ drm_backend_add_connector(struct drm_backend *b, drmModeConnector *conn, 406 return ret; 407 } 408 409-/** Find all connectors of the fd and create drm_head or drm_writeback objects 410- * (depending on the type of connector they are) for each of them 411- * 412- * These objects are added to the DRM-backend lists of heads and writebacks. 413- * 414- * @param b The DRM-backend structure 415- * @param drm_device udev device pointer 416- * @param resources The DRM resources, it is taken with drmModeGetResources 417- * @return 0 on success, -1 on failure 418- */ 419-static int 420-drm_backend_discover_connectors(struct drm_backend *b, struct udev_device *drm_device, 421- drmModeRes *resources) 422+static bool 423+resources_has_connector(drmModeRes *resources, uint32_t connector_id) 424 { 425- drmModeConnector *conn; 426- int i, ret; 427+ for (int i = 0; i < resources->count_connectors; i++) { 428+ if (resources->connectors[i] == connector_id) 429+ return true; 430+ } 431 432- b->min_width = resources->min_width; 433- b->max_width = resources->max_width; 434- b->min_height = resources->min_height; 435- b->max_height = resources->max_height; 436+ return false; 437+} 438 439- for (i = 0; i < resources->count_connectors; i++) { 440- uint32_t connector_id = resources->connectors[i]; 441+/* based on compositor/main.c#drm_head_prepare_enable() */ 442+static bool 443+drm_head_is_available(struct weston_head *head) 444+{ 445+ struct weston_config_section *section; 446+ char *mode = NULL; 447 448- conn = drmModeGetConnector(b->drm.fd, connector_id); 449- if (!conn) 450- continue; 451+ section = head->section; 452+ if (!section) 453+ return true; 454 455- ret = drm_backend_add_connector(b, conn, drm_device); 456- if (ret < 0) 457- drmModeFreeConnector(conn); 458+ /* skip outputs that are explicitly off, or non-desktop and not 459+ * explicitly enabled. 460+ */ 461+ weston_config_section_get_string(section, "mode", &mode, NULL); 462+ if (mode && strcmp(mode, "off") == 0) { 463+ free(mode); 464+ return false; 465 } 466 467- return 0; 468+ if (!mode && weston_head_is_non_desktop(head)) 469+ return false; 470+ 471+ free(mode); 472+ return true; 473 } 474 475 static bool 476-resources_has_connector(drmModeRes *resources, uint32_t connector_id) 477+drm_head_match_fallback(struct drm_backend *b, struct drm_head *head) 478 { 479- for (int i = 0; i < resources->count_connectors; i++) { 480- if (resources->connectors[i] == connector_id) 481- return true; 482- } 483+ if (b->head_fallback_all) 484+ return true; 485 486- return false; 487+ return b->head_fallback && !b->primary_head; 488 } 489 490-static void 491+static int 492 drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_device) 493 { 494 drmModeRes *resources; 495 drmModeConnector *conn; 496 struct weston_head *base, *base_next; 497- struct drm_head *head; 498+ struct drm_head *head, *old_primary_head; 499 struct drm_writeback *writeback, *writeback_next; 500+ drm_head_match_t *match = b->head_matches; 501+ struct timespec now; 502 uint32_t connector_id; 503 int i, ret; 504 505 resources = drmModeGetResources(b->drm.fd); 506 if (!resources) { 507 weston_log("drmModeGetResources failed\n"); 508- return; 509+ return -1; 510 } 511 512+ b->min_width = resources->min_width; 513+ b->max_width = resources->max_width; 514+ b->min_height = resources->min_height; 515+ b->max_height = resources->max_height; 516+ 517 /* collect new connectors that have appeared, e.g. MST */ 518 for (i = 0; i < resources->count_connectors; i++) { 519 connector_id = resources->connectors[i]; 520@@ -2456,6 +2525,65 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev 521 } 522 523 drmModeFreeResources(resources); 524+ 525+ old_primary_head = b->primary_head; 526+ b->primary_head = NULL; 527+ 528+ wl_list_for_each_safe(base, base_next, 529+ &b->compositor->head_list, compositor_link) 530+ weston_head_set_connection_status(base, false); 531+ 532+ /* Re-connect matched heads and find primary head */ 533+ while (*match) { 534+ wl_list_for_each_safe(base, base_next, 535+ &b->compositor->head_list, 536+ compositor_link) { 537+ drmModeConnector *conn; 538+ 539+ if (!drm_head_is_available(base)) 540+ continue; 541+ 542+ head = to_drm_head(base); 543+ conn = head->connector.conn; 544+ 545+ if (conn->connection != DRM_MODE_CONNECTED || 546+ !(*match)(b, head)) 547+ continue; 548+ 549+ weston_head_set_connection_status(base, true); 550+ 551+ if (!b->primary_head) { 552+ b->primary_head = head; 553+ 554+ /* Done the single-head match */ 555+ if (b->single_head) 556+ goto match_done; 557+ } 558+ } 559+ 560+ /* Done the fallback match */ 561+ if (*match == drm_head_match_fallback) 562+ goto match_done; 563+ 564+ match++; 565+ 566+ /* Try the fallback match */ 567+ if (!match && !b->primary_head) 568+ *match = drm_head_match_fallback; 569+ } 570+match_done: 571+ 572+ drm_backend_update_outputs(b); 573+ 574+ weston_compositor_read_presentation_clock(b->compositor, &now); 575+ b->last_update_ms = timespec_to_msec(&now); 576+ 577+ /* Assume primary output's size changed */ 578+ if (b->primary_head && old_primary_head && 579+ b->primary_head != old_primary_head) 580+ b->last_resize_ms = b->last_update_ms; 581+ 582+ return 0; 583 } 584 585 static enum wdrm_connector_property 586@@ -2546,6 +2674,48 @@ udev_event_is_conn_prop_change(struct drm_backend *b, 587 return 1; 588 } 589 590+static void 591+udev_hotplug_event(struct drm_backend *b, struct udev_device *device) 592+{ 593+ struct timespec now; 594+ int64_t now_ms, next_ms; 595+ 596+ weston_compositor_read_presentation_clock(b->compositor, &now); 597+ now_ms = timespec_to_msec(&now); 598+ 599+ /* Already have a pending request */ 600+ if (b->pending_update) 601+ return; 602+ 603+ next_ms = b->last_update_ms + DRM_MIN_UPDATE_MS; 604+ if (next_ms <= now_ms) { 605+ /* Long enough to trigger a new request */ 606+ drm_backend_update_connectors(b, device); 607+ } else { 608+ /* Too close to the last request, schedule a new one */ 609+ b->pending_update = true; 610+ wl_event_source_timer_update(b->hotplug_timer, 611+ next_ms - now_ms); 612+ } 613+} 614+ 615+static int 616+hotplug_timer_handler(void *data) 617+{ 618+ struct drm_backend *b = data; 619+ struct udev_device *device; 620+ struct udev *udev; 621+ 622+ udev = udev_monitor_get_udev(b->udev_monitor); 623+ device = udev_device_new_from_syspath(udev, b->drm.syspath); 624+ 625+ drm_backend_update_connectors(b, device); 626+ b->pending_update = false; 627+ 628+ udev_device_unref(device); 629+ return 0; 630+} 631+ 632 static int 633 udev_drm_event(int fd, uint32_t mask, void *data) 634 { 635@@ -2559,7 +2729,7 @@ udev_drm_event(int fd, uint32_t mask, void *data) 636 if (udev_event_is_conn_prop_change(b, event, &conn_id, &prop_id)) 637 drm_backend_update_conn_props(b, conn_id, prop_id); 638 else 639- drm_backend_update_connectors(b, event); 640+ udev_hotplug_event(b, event); 641 } 642 643 udev_device_unref(event); 644@@ -2577,6 +2747,7 @@ drm_destroy(struct weston_compositor *ec) 645 646 udev_input_destroy(&b->input); 647 648+ wl_event_source_remove(b->hotplug_timer); 649 wl_event_source_remove(b->udev_drm_source); 650 wl_event_source_remove(b->drm_source); 651 652@@ -2627,6 +2798,10 @@ session_notify(struct wl_listener *listener, void *data) 653 weston_compositor_wake(compositor); 654 weston_compositor_damage_all(compositor); 655 b->state_invalid = true; 656+ 657+ wl_list_for_each(output, &compositor->output_list, base.link) 658+ output->state_invalid = true; 659+ 660 udev_input_enable(&b->input); 661 } else { 662 weston_log("deactivating session\n"); 663@@ -2987,6 +3162,14 @@ recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time, 664 } 665 #endif 666 667+static void 668+output_create_notify(struct wl_listener *listener, void *data) 669+{ 670+ struct drm_backend *b = container_of(listener, struct drm_backend, 671+ output_create_listener); 672+ 673+ drm_backend_update_outputs(b); 674+} 675 676 static const struct weston_drm_output_api api = { 677 drm_output_set_mode, 678@@ -2994,6 +3177,63 @@ static const struct weston_drm_output_api api = { 679 drm_output_set_seat, 680 }; 681 682+enum drm_head_mode { 683+ DRM_HEAD_MODE_DEFAULT, 684+ DRM_HEAD_MODE_PRIMARY, 685+ DRM_HEAD_MODE_INTERNAL, 686+ DRM_HEAD_MODE_EXTERNAL, 687+ DRM_HEAD_MODE_EXTERNAL_DUAL, 688+}; 689+ 690+static bool 691+drm_head_match_primary(struct drm_backend *b, struct drm_head *head) 692+{ 693+ const char *buf = getenv("WESTON_DRM_PRIMARY"); 694+ return buf && !strcmp(buf, head->base.name); 695+} 696+ 697+static bool 698+drm_head_match_external(struct drm_backend *b, struct drm_head *head) 699+{ 700+ return drm_head_is_external(head); 701+} 702+ 703+static bool 704+drm_head_match_internal(struct drm_backend *b, struct drm_head *head) 705+{ 706+ return !drm_head_is_external(head); 707+} 708+ 709+#define DRM_HEAD_MAX_MATCHES 5 710+static drm_head_match_t drm_head_matches[][DRM_HEAD_MAX_MATCHES] = { 711+ [DRM_HEAD_MODE_DEFAULT] = { 712+ drm_head_match_primary, 713+ drm_head_match_internal, 714+ drm_head_match_external, 715+ NULL, 716+ }, 717+ [DRM_HEAD_MODE_PRIMARY] = { 718+ drm_head_match_primary, 719+ NULL, 720+ }, 721+ [DRM_HEAD_MODE_INTERNAL] = { 722+ drm_head_match_primary, 723+ drm_head_match_internal, 724+ NULL, 725+ }, 726+ [DRM_HEAD_MODE_EXTERNAL] = { 727+ drm_head_match_primary, 728+ drm_head_match_external, 729+ NULL, 730+ }, 731+ [DRM_HEAD_MODE_EXTERNAL_DUAL] = { 732+ drm_head_match_primary, 733+ drm_head_match_external, 734+ drm_head_match_internal, 735+ NULL, 736+ }, 737+}; 738+ 739 static struct drm_backend * 740 drm_backend_create(struct weston_compositor *compositor, 741 struct weston_drm_backend_config *config) 742@@ -3004,7 +3244,9 @@ drm_backend_create(struct weston_compositor *compositor, 743 const char *seat_id = default_seat; 744 const char *session_seat; 745 struct weston_drm_format_array *scanout_formats; 746+ enum drm_head_mode head_mode = DRM_HEAD_MODE_DEFAULT; 747 drmModeRes *res; 748+ char *buf; 749 int ret; 750 751 session_seat = getenv("XDG_SEAT"); 752@@ -3020,6 +3262,42 @@ drm_backend_create(struct weston_compositor *compositor, 753 if (b == NULL) 754 return NULL; 755 756+ buf = getenv("WESTON_DRM_SINGLE_HEAD"); 757+ if (buf && buf[0] == '1') 758+ b->single_head = true; 759+ 760+ buf = getenv("WESTON_DRM_HEAD_FALLBACK"); 761+ if (buf && buf[0] == '1') 762+ b->head_fallback = true; 763+ 764+ buf = getenv("WESTON_DRM_HEAD_FALLBACK_ALL"); 765+ if (buf && buf[0] == '1') 766+ b->head_fallback_all = true; 767+ 768+ buf = getenv("WESTON_DRM_PREFER_EXTERNAL"); 769+ if (buf && buf[0] == '1') { 770+ head_mode = DRM_HEAD_MODE_EXTERNAL; 771+ b->head_fallback = true; 772+ } 773+ 774+ buf = getenv("WESTON_DRM_PREFER_EXTERNAL_DUAL"); 775+ if (buf && buf[0] == '1') 776+ head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL; 777+ 778+ buf = getenv("WESTON_DRM_HEAD_MODE"); 779+ if (buf) { 780+ if (!strcmp(buf, "primary")) 781+ head_mode = DRM_HEAD_MODE_PRIMARY; 782+ else if (!strcmp(buf, "internal")) 783+ head_mode = DRM_HEAD_MODE_INTERNAL; 784+ else if (!strcmp(buf, "external")) 785+ head_mode = DRM_HEAD_MODE_EXTERNAL; 786+ else if (!strcmp(buf, "external-dual")) 787+ head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL; 788+ } 789+ 790+ b->head_matches = drm_head_matches[head_mode]; 791+ 792 b->state_invalid = true; 793 b->drm.fd = -1; 794 795@@ -3115,7 +3393,7 @@ drm_backend_create(struct weston_compositor *compositor, 796 } 797 798 wl_list_init(&b->writeback_connector_list); 799- if (drm_backend_discover_connectors(b, drm_device, res) < 0) { 800+ if (drm_backend_update_connectors(b, drm_device) < 0) { 801 weston_log("Failed to create heads for %s\n", b->drm.filename); 802 goto err_udev_input; 803 } 804@@ -3154,6 +3432,10 @@ drm_backend_create(struct weston_compositor *compositor, 805 806 udev_device_unref(drm_device); 807 808+ b->output_create_listener.notify = output_create_notify; 809+ wl_signal_add(&b->compositor->output_created_signal, 810+ &b->output_create_listener); 811+ 812 weston_compositor_add_debug_binding(compositor, KEY_O, 813 planes_binding, b); 814 weston_compositor_add_debug_binding(compositor, KEY_C, 815@@ -3215,6 +3497,9 @@ drm_backend_create(struct weston_compositor *compositor, 816 goto err_udev_monitor; 817 } 818 819+ b->hotplug_timer = 820+ wl_event_loop_add_timer(loop, hotplug_timer_handler, b); 821+ 822 return b; 823 824 err_udev_monitor: 825diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c 826index 35113d5..4426af4 100644 827--- a/libweston/backend-drm/kms.c 828+++ b/libweston/backend-drm/kms.c 829@@ -704,6 +704,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state) 830 831 scanout_state = 832 drm_output_state_get_existing_plane(state, scanout_plane); 833+ if (!scanout_state || !scanout_state->fb) 834+ return 0; 835 836 /* The legacy SetCrtc API doesn't allow us to do scaling, and the 837 * legacy PageFlip API doesn't allow us to do clipping either. */ 838@@ -721,7 +723,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state) 839 assert(scanout_state->in_fence_fd == -1); 840 841 mode = to_drm_mode(output->base.current_mode); 842- if (backend->state_invalid || 843+ if (output->state_invalid || 844 !scanout_plane->state_cur->fb || 845 scanout_plane->state_cur->fb->strides[0] != 846 scanout_state->fb->strides[0]) { 847@@ -735,6 +737,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state) 848 weston_log("set mode failed: %s\n", strerror(errno)); 849 goto err; 850 } 851+ 852+ output->state_invalid = false; 853 } 854 855 pinfo = scanout_state->fb->format; 856@@ -945,6 +949,11 @@ drm_output_apply_state_atomic(struct drm_output_state *state, 857 *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; 858 } 859 860+ if (output->state_invalid) { 861+ drm_debug(b, "\t\t\t[atomic] output state invalid, modeset OK\n"); 862+ *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; 863+ } 864+ 865 if (state->dpms == WESTON_DPMS_ON) { 866 ret = drm_mode_ensure_blob(b, current_mode); 867 if (ret != 0) 868@@ -1050,6 +1059,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, 869 struct drm_output_state *output_state, *tmp; 870 struct drm_plane *plane; 871 drmModeAtomicReq *req = drmModeAtomicAlloc(); 872+ struct timespec now; 873 uint32_t flags; 874 int ret = 0; 875 876@@ -1180,14 +1190,34 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, 877 goto out; 878 } 879 880+ weston_compositor_read_presentation_clock(b->compositor, &now); 881+ 882 wl_list_for_each_safe(output_state, tmp, &pending_state->output_list, 883- link) 884+ link) { 885+ struct drm_output *output = output_state->output; 886+ struct drm_plane *scanout_plane = output->scanout_plane; 887+ struct drm_plane_state *scanout_state = 888+ drm_output_state_get_existing_plane(output_state, 889+ scanout_plane); 890+ 891+ /* Don't have a new state to apply */ 892+ if (output_state->dpms == WESTON_DPMS_ON && 893+ (!scanout_state || !scanout_state->fb)) 894+ continue; 895+ 896 drm_output_assign_state(output_state, mode); 897+ output->state_invalid = false; 898+ 899+ /* Not gonna receive flip event when dpms off */ 900+ if (output_state->dpms != WESTON_DPMS_ON) 901+ drm_output_update_complete(output, 902+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION, 903+ now.tv_sec, 904+ now.tv_nsec / 1000); 905+ } 906 907 b->state_invalid = false; 908 909- assert(wl_list_empty(&pending_state->output_list)); 910- 911 out: 912 drmModeAtomicFree(req); 913 drm_pending_state_free(pending_state); 914@@ -1286,8 +1316,6 @@ drm_pending_state_apply(struct drm_pending_state *pending_state) 915 916 b->state_invalid = false; 917 918- assert(wl_list_empty(&pending_state->output_list)); 919- 920 drm_pending_state_free(pending_state); 921 922 return 0; 923@@ -1339,8 +1367,6 @@ drm_pending_state_apply_sync(struct drm_pending_state *pending_state) 924 925 b->state_invalid = false; 926 927- assert(wl_list_empty(&pending_state->output_list)); 928- 929 drm_pending_state_free(pending_state); 930 931 return 0; 932diff --git a/libweston/compositor.c b/libweston/compositor.c 933index e184349..22a8593 100644 934--- a/libweston/compositor.c 935+++ b/libweston/compositor.c 936@@ -157,6 +157,25 @@ weston_paint_node_destroy(struct weston_paint_node *pnode) 937 free(pnode); 938 } 939 940+static struct weston_layer * 941+get_view_layer(struct weston_view *view); 942+ 943+static bool 944+weston_compositor_is_static_layer(struct weston_layer *layer) 945+{ 946+ if (!layer) 947+ return false; 948+ 949+ switch (layer->position) { 950+ case WESTON_LAYER_POSITION_BACKGROUND: 951+ case WESTON_LAYER_POSITION_UI: 952+ case WESTON_LAYER_POSITION_FADE: 953+ return true; 954+ default: 955+ return false; 956+ } 957+} 958+ 959 /** Send wl_output events for mode and scale changes 960 * 961 * \param head Send on all resources bound to this head. 962@@ -1367,6 +1386,22 @@ weston_view_assign_output(struct weston_view *ev) 963 uint32_t max, area, mask; 964 pixman_box32_t *e; 965 966+ /* The static views should bind to the specific output */ 967+ if (weston_compositor_is_static_layer(get_view_layer(ev))) { 968+ struct weston_view *view = ev; 969+ 970+ while (view && !(output = view->output)) 971+ view = view->geometry.parent; 972+ 973+ if (output && !output->destroying) 974+ ev->output_mask |= 1u << output->id; 975+ else 976+ weston_view_set_output(ev, NULL); 977+ 978+ weston_surface_assign_output(ev->surface); 979+ return; 980+ } 981+ 982 new_output = NULL; 983 max = 0; 984 mask = 0; 985@@ -2942,12 +2977,14 @@ weston_output_repaint(struct weston_output *output, void *repaint_data) 986 if (output->dirty) 987 weston_output_update_matrix(output); 988 989+ output->repaint_needed = false; 990 r = output->repaint(output, &output_damage, repaint_data); 991 992 pixman_region32_fini(&output_damage); 993 994- output->repaint_needed = false; 995- if (r == 0) 996+ if (output->repaint_needed) 997+ output->repaint_status = REPAINT_SCHEDULED; 998+ else if (r == 0) 999 output->repaint_status = REPAINT_AWAITING_COMPLETION; 1000 1001 weston_compositor_repick(ec); 1002@@ -6407,6 +6444,7 @@ weston_compositor_remove_output(struct weston_output *output) 1003 * Use view_list in case the output did not go through repaint 1004 * after a view came on it, lacking a paint node. Just to be sure. 1005 */ 1006+ weston_compositor_build_view_list(compositor, NULL); 1007 wl_list_for_each(view, &compositor->view_list, link) { 1008 if (view->output_mask & (1u << output->id)) 1009 weston_view_assign_output(view); 1010-- 10112.20.1 1012 1013