1From 14a416a3c5959e53d0de2e8cc7b8221a3544be5a 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 15/93] 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 | 22 +- 31 desktop-shell/shell.c | 26 +- 32 include/libweston/libweston.h | 2 + 33 libweston/backend-drm/drm-internal.h | 24 ++ 34 libweston/backend-drm/drm.c | 378 ++++++++++++++++++++++++--- 35 libweston/backend-drm/kms.c | 42 ++- 36 libweston/compositor.c | 42 ++- 37 7 files changed, 477 insertions(+), 59 deletions(-) 38 39diff --git a/compositor/main.c b/compositor/main.c 40index 57a52b6..dcfe85b 100644 41--- a/compositor/main.c 42+++ b/compositor/main.c 43@@ -2326,7 +2326,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@@ -2356,11 +2356,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@@ -2551,6 +2550,21 @@ 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+ 76+ if (head->section) 77+ return true; 78+ 79+ head->section = 80+ drm_config_find_controlling_output_section(wet->config, name); 81+ return !!head->section; 82+} 83+ 84 static void 85 drm_heads_changed(struct wl_listener *listener, void *arg) 86 { 87@@ -2566,6 +2580,8 @@ drm_heads_changed(struct wl_listener *listener, void *arg) 88 * output. 89 */ 90 while ((head = weston_compositor_iterate_heads(compositor, head))) { 91+ drm_head_update_output_section(head); 92+ 93 connected = weston_head_is_connected(head); 94 enabled = weston_head_is_enabled(head); 95 changed = weston_head_is_device_changed(head); 96diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c 97index 79e20ad..89ea491 100644 98--- a/desktop-shell/shell.c 99+++ b/desktop-shell/shell.c 100@@ -3550,6 +3550,9 @@ shell_fade_done_for_output(struct weston_view_animation *animation, void *data) 101 struct shell_output *shell_output = data; 102 struct desktop_shell *shell = shell_output->shell; 103 104+ if (!shell_output->fade.curtain) 105+ return; 106+ 107 shell_output->fade.animation = NULL; 108 switch (shell_output->fade.type) { 109 case FADE_IN: 110@@ -4334,8 +4337,12 @@ shell_output_destroy(struct shell_output *shell_output) 111 shell_output->fade.animation = NULL; 112 } 113 114- if (shell_output->fade.curtain) 115- weston_curtain_destroy(shell_output->fade.curtain); 116+ if (shell_output->fade.curtain) { 117+ struct weston_curtain *curtain = shell_output->fade.curtain; 118+ 119+ shell_output->fade.curtain = NULL; 120+ weston_curtain_destroy(curtain); 121+ } 122 123 if (shell_output->fade.startup_timer) 124 wl_event_source_remove(shell_output->fade.startup_timer); 125@@ -4438,12 +4445,25 @@ handle_output_move_layer(struct desktop_shell *shell, 126 static void 127 handle_output_move(struct wl_listener *listener, void *data) 128 { 129+ struct weston_output *output = data; 130+ struct weston_compositor *compositor = output->compositor; 131 struct desktop_shell *shell; 132 133 shell = container_of(listener, struct desktop_shell, 134 output_move_listener); 135 136- shell_for_each_layer(shell, handle_output_move_layer, data); 137+ if (shell->lock_surface) 138+ shell->lock_surface->committed(shell->lock_surface, 0, 0); 139+ 140+ /* Only move normal layers for non-default output */ 141+ if (output != get_default_output(compositor)) { 142+ shell_for_each_layer(shell, handle_output_move_layer, data); 143+ return; 144+ } 145+ 146+ handle_output_move_layer(shell, &shell->lock_layer, data); 147+ handle_output_move_layer(shell, &shell->background_layer, data); 148+ handle_output_move_layer(shell, &shell->panel_layer, data); 149 } 150 151 static void 152diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h 153index 984d5f3..d35963f 100644 154--- a/include/libweston/libweston.h 155+++ b/include/libweston/libweston.h 156@@ -399,6 +399,8 @@ struct weston_head { 157 158 /** Opaque pointer used by backends to identify heads as theirs */ 159 const void *backend_id; 160+ 161+ struct weston_config_section *section; /**< config section **/ 162 }; 163 164 /** Output properties derived from its color characteristics and profile 165diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h 166index f4b8ed8..ec8bf0e 100644 167--- a/libweston/backend-drm/drm-internal.h 168+++ b/libweston/backend-drm/drm-internal.h 169@@ -110,6 +110,10 @@ 170 171 #define MAX_CLONED_CONNECTORS 4 172 173+/* Min duration between drm outputs update requests, to avoid glith */ 174+#define DRM_MIN_UPDATE_MS 1000 175+ 176+#define DRM_RESIZE_FREEZE_MS 600 177 178 /** 179 * Represents the values of an enum-type KMS property 180@@ -268,6 +272,7 @@ struct drm_device { 181 int fd; 182 char *filename; 183 dev_t devnum; 184+ char *syspath; 185 } drm; 186 187 /* drm_crtc::link */ 188@@ -302,6 +307,10 @@ struct drm_device { 189 int min_height, max_height; 190 }; 191 192+struct drm_head; 193+struct drm_backend; 194+typedef bool (*drm_head_match_t) (struct drm_backend *, struct drm_head *); 195+ 196 struct drm_backend { 197 struct weston_backend base; 198 struct weston_compositor *compositor; 199@@ -327,6 +336,19 @@ struct drm_backend { 200 bool shutting_down; 201 202 struct weston_log_scope *debug; 203+ 204+ struct wl_event_source *hotplug_timer; 205+ bool pending_update; 206+ int64_t last_update_ms; 207+ int64_t last_resize_ms; 208+ int64_t resize_freeze_ms; 209+ 210+ bool single_head; 211+ bool head_fallback; 212+ bool head_fallback_all; 213+ drm_head_match_t *head_matches; 214+ struct drm_head *primary_head; 215+ struct wl_listener output_create_listener; 216 }; 217 218 struct drm_mode { 219@@ -589,6 +611,8 @@ struct drm_output { 220 void (*virtual_destroy)(struct weston_output *base); 221 222 submit_frame_cb virtual_submit_frame; 223+ 224+ bool state_invalid; 225 }; 226 227 void 228diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c 229index 711ed5b..f5ac1b6 100644 230--- a/libweston/backend-drm/drm.c 231+++ b/libweston/backend-drm/drm.c 232@@ -30,6 +30,7 @@ 233 #include "config.h" 234 235 #include <errno.h> 236+#include <stdio.h> 237 #include <stdint.h> 238 #include <stdlib.h> 239 #include <ctype.h> 240@@ -47,6 +48,7 @@ 241 242 #include <libudev.h> 243 244+#include <libweston/config-parser.h> 245 #include <libweston/libweston.h> 246 #include <libweston/backend-drm.h> 247 #include <libweston/weston-log.h> 248@@ -68,6 +70,43 @@ 249 250 static const char default_seat[] = "seat0"; 251 252+static inline bool 253+drm_head_is_external(struct drm_head *head) 254+{ 255+ drmModeConnector *conn = head->connector.conn; 256+ switch (conn->connector_type) { 257+ case DRM_MODE_CONNECTOR_LVDS: 258+ case DRM_MODE_CONNECTOR_eDP: 259+#ifdef DRM_MODE_CONNECTOR_DSI 260+ case DRM_MODE_CONNECTOR_DSI: 261+#endif 262+ return false; 263+ default: 264+ return true; 265+ } 266+}; 267+ 268+static void 269+drm_backend_update_outputs(struct drm_backend *b) 270+{ 271+ struct weston_output *primary; 272+ 273+ if (!b->primary_head) 274+ return; 275+ 276+ primary = b->primary_head->base.output; 277+ if (!primary) 278+ return; 279+ 280+ wl_list_remove(&primary->link); 281+ wl_list_insert(&b->compositor->output_list, &primary->link); 282+ 283+ /* Reflow outputs */ 284+ weston_compositor_reflow_outputs(b->compositor); 285+ 286+ weston_compositor_damage_all(b->compositor); 287+} 288+ 289 static void 290 drm_backend_create_faked_zpos(struct drm_backend *b) 291 { 292@@ -464,18 +503,36 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) 293 struct drm_plane_state *scanout_state; 294 struct drm_pending_state *pending_state; 295 struct drm_device *device; 296+ struct drm_backend *b; 297+ struct timespec now; 298+ int64_t now_ms; 299 300 assert(output); 301 assert(!output->virtual); 302 303 device = output->device; 304 pending_state = device->repaint_data; 305+ b = device->backend; 306 307 if (output->disable_pending || output->destroy_pending) 308 goto err; 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@@ -504,7 +561,7 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) 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@@ -743,6 +800,7 @@ drm_output_apply_mode(struct drm_output *output) 339 * content. 340 */ 341 device->state_invalid = true; 342+ output->state_invalid = true; 343 344 if (b->use_pixman) { 345 drm_output_fini_pixman(output); 346@@ -1322,6 +1380,7 @@ drm_output_attach_head(struct weston_output *output_base, 347 { 348 struct drm_backend *b = to_drm_backend(output_base->compositor); 349 struct drm_device *device = b->drm; 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@@ -1339,6 +1398,7 @@ drm_output_attach_head(struct weston_output *output_base, 355 * will not clear the flag before this output is updated? 356 */ 357 device->state_invalid = true; 358+ output->state_invalid = true; 359 360 weston_output_schedule_repaint(output_base); 361 362@@ -1351,6 +1411,7 @@ drm_output_detach_head(struct weston_output *output_base, 363 { 364 struct drm_backend *b = to_drm_backend(output_base->compositor); 365 struct drm_device *device = b->drm; 366+ struct drm_output *output = to_drm_output(output_base); 367 368 if (!output_base->enabled) 369 return; 370@@ -1359,6 +1420,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 device->state_invalid = true; 374+ output->state_invalid = true; 375 376 weston_output_schedule_repaint(output_base); 377 } 378@@ -1917,6 +1979,8 @@ drm_output_enable(struct weston_output *base) 379 output->base.switch_mode = drm_output_switch_mode; 380 output->base.set_gamma = drm_output_set_gamma; 381 382+ output->state_invalid = true; 383+ 384 weston_log("Output %s (crtc %d) video modes:\n", 385 output->base.name, output->crtc->crtc_id); 386 drm_output_print_modes(output); 387@@ -2278,8 +2342,7 @@ drm_head_create(struct drm_device *device, drmModeConnector *conn, 388 389 head->backlight = backlight_init(drm_device, conn->connector_type); 390 391- if (conn->connector_type == DRM_MODE_CONNECTOR_LVDS || 392- conn->connector_type == DRM_MODE_CONNECTOR_eDP) 393+ if (!drm_head_is_external(head)) 394 weston_head_set_internal(&head->base); 395 396 if (drm_head_read_current_setup(head, device) < 0) { 397@@ -2447,56 +2510,54 @@ drm_backend_add_connector(struct drm_device *device, drmModeConnector *conn, 398 return ret; 399 } 400 401-/** Find all connectors of the fd and create drm_head or drm_writeback objects 402- * (depending on the type of connector they are) for each of them 403- * 404- * These objects are added to the DRM-backend lists of heads and writebacks. 405- * 406- * @param device The DRM device structure 407- * @param drm_device udev device pointer 408- * @param resources The DRM resources, it is taken with drmModeGetResources 409- * @return 0 on success, -1 on failure 410- */ 411-static int 412-drm_backend_discover_connectors(struct drm_device *device, 413- struct udev_device *drm_device, 414- drmModeRes *resources) 415+static bool 416+resources_has_connector(drmModeRes *resources, uint32_t connector_id) 417 { 418- drmModeConnector *conn; 419- int i, ret; 420+ for (int i = 0; i < resources->count_connectors; i++) { 421+ if (resources->connectors[i] == connector_id) 422+ return true; 423+ } 424 425- device->min_width = resources->min_width; 426- device->max_width = resources->max_width; 427- device->min_height = resources->min_height; 428- device->max_height = resources->max_height; 429+ return false; 430+} 431 432- for (i = 0; i < resources->count_connectors; i++) { 433- uint32_t connector_id = resources->connectors[i]; 434+/* based on compositor/main.c#drm_head_prepare_enable() */ 435+static bool 436+drm_head_is_available(struct weston_head *head) 437+{ 438+ struct weston_config_section *section; 439+ char *mode = NULL; 440 441- conn = drmModeGetConnector(device->drm.fd, connector_id); 442- if (!conn) 443- continue; 444+ section = head->section; 445+ if (!section) 446+ return true; 447 448- ret = drm_backend_add_connector(device, conn, drm_device); 449- if (ret < 0) 450- drmModeFreeConnector(conn); 451+ /* skip outputs that are explicitly off, or non-desktop and not 452+ * explicitly enabled. 453+ */ 454+ weston_config_section_get_string(section, "mode", &mode, NULL); 455+ if (mode && strcmp(mode, "off") == 0) { 456+ free(mode); 457+ return false; 458 } 459 460- return 0; 461+ if (!mode && weston_head_is_non_desktop(head)) 462+ return false; 463+ 464+ free(mode); 465+ return true; 466 } 467 468 static bool 469-resources_has_connector(drmModeRes *resources, uint32_t connector_id) 470+drm_head_match_fallback(struct drm_backend *b, struct drm_head *head) 471 { 472- for (int i = 0; i < resources->count_connectors; i++) { 473- if (resources->connectors[i] == connector_id) 474- return true; 475- } 476+ if (b->head_fallback_all) 477+ return true; 478 479- return false; 480+ return b->head_fallback && !b->primary_head; 481 } 482 483-static void 484+static int 485 drm_backend_update_connectors(struct drm_device *device, 486 struct udev_device *drm_device) 487 { 488@@ -2504,17 +2565,24 @@ drm_backend_update_connectors(struct drm_device *device, 489 drmModeRes *resources; 490 drmModeConnector *conn; 491 struct weston_head *base, *base_next; 492- struct drm_head *head; 493+ struct drm_head *head, *old_primary_head; 494 struct drm_writeback *writeback, *writeback_next; 495+ drm_head_match_t *match = b->head_matches; 496+ struct timespec now; 497 uint32_t connector_id; 498 int i, ret; 499 500 resources = drmModeGetResources(device->drm.fd); 501 if (!resources) { 502 weston_log("drmModeGetResources failed\n"); 503- return; 504+ return -1; 505 } 506 507+ device->min_width = resources->min_width; 508+ device->max_width = resources->max_width; 509+ device->min_height = resources->min_height; 510+ device->max_height = resources->max_height; 511+ 512 /* collect new connectors that have appeared, e.g. MST */ 513 for (i = 0; i < resources->count_connectors; i++) { 514 connector_id = resources->connectors[i]; 515@@ -2576,6 +2644,65 @@ drm_backend_update_connectors(struct drm_device *device, 516 } 517 518 drmModeFreeResources(resources); 519+ 520+ old_primary_head = b->primary_head; 521+ b->primary_head = NULL; 522+ 523+ wl_list_for_each_safe(base, base_next, 524+ &b->compositor->head_list, compositor_link) 525+ weston_head_set_connection_status(base, false); 526+ 527+ /* Re-connect matched heads and find primary head */ 528+ while (*match) { 529+ wl_list_for_each_safe(base, base_next, 530+ &b->compositor->head_list, 531+ compositor_link) { 532+ drmModeConnector *conn; 533+ 534+ if (!drm_head_is_available(base)) 535+ continue; 536+ 537+ head = to_drm_head(base); 538+ conn = head->connector.conn; 539+ 540+ if (conn->connection != DRM_MODE_CONNECTED || 541+ !(*match)(b, head)) 542+ continue; 543+ 544+ weston_head_set_connection_status(base, true); 545+ 546+ if (!b->primary_head) { 547+ b->primary_head = head; 548+ 549+ /* Done the single-head match */ 550+ if (b->single_head) 551+ goto match_done; 552+ } 553+ } 554+ 555+ /* Done the fallback match */ 556+ if (*match == drm_head_match_fallback) 557+ goto match_done; 558+ 559+ match++; 560+ 561+ /* Try the fallback match */ 562+ if (!match && !b->primary_head) 563+ *match = drm_head_match_fallback; 564+ } 565+match_done: 566+ 567+ drm_backend_update_outputs(b); 568+ 569+ weston_compositor_read_presentation_clock(b->compositor, &now); 570+ b->last_update_ms = timespec_to_msec(&now); 571+ 572+ /* Assume primary output's size changed */ 573+ if (b->primary_head && old_primary_head && 574+ b->primary_head != old_primary_head) 575+ b->last_resize_ms = b->last_update_ms; 576+ 577+ return 0; 578 } 579 580 static enum wdrm_connector_property 581@@ -2666,6 +2793,50 @@ udev_event_is_conn_prop_change(struct drm_backend *b, 582 return 1; 583 } 584 585+static void 586+udev_hotplug_event(struct drm_device *device, struct udev_device *udev_device) 587+{ 588+ struct drm_backend *b = device->backend; 589+ struct timespec now; 590+ int64_t now_ms, next_ms; 591+ 592+ weston_compositor_read_presentation_clock(b->compositor, &now); 593+ now_ms = timespec_to_msec(&now); 594+ 595+ /* Already have a pending request */ 596+ if (b->pending_update) 597+ return; 598+ 599+ next_ms = b->last_update_ms + DRM_MIN_UPDATE_MS; 600+ if (next_ms <= now_ms) { 601+ /* Long enough to trigger a new request */ 602+ drm_backend_update_connectors(device, udev_device); 603+ } else { 604+ /* Too close to the last request, schedule a new one */ 605+ b->pending_update = true; 606+ wl_event_source_timer_update(b->hotplug_timer, 607+ next_ms - now_ms); 608+ } 609+} 610+ 611+static int 612+hotplug_timer_handler(void *data) 613+{ 614+ struct drm_device *device = data; 615+ struct drm_backend *b = device->backend; 616+ struct udev_device *udev_device; 617+ struct udev *udev; 618+ 619+ udev = udev_monitor_get_udev(b->udev_monitor); 620+ udev_device = udev_device_new_from_syspath(udev, device->drm.syspath); 621+ 622+ drm_backend_update_connectors(device, udev_device); 623+ b->pending_update = false; 624+ 625+ udev_device_unref(udev_device); 626+ return 0; 627+} 628+ 629 static int 630 udev_drm_event(int fd, uint32_t mask, void *data) 631 { 632@@ -2679,7 +2850,7 @@ udev_drm_event(int fd, uint32_t mask, void *data) 633 if (udev_event_is_conn_prop_change(b, event, &conn_id, &prop_id)) 634 drm_backend_update_conn_props(b, conn_id, prop_id); 635 else 636- drm_backend_update_connectors(b->drm, event); 637+ udev_hotplug_event(b->drm, event); 638 } 639 640 udev_device_unref(event); 641@@ -2698,6 +2869,7 @@ drm_destroy(struct weston_compositor *ec) 642 643 udev_input_destroy(&b->input); 644 645+ wl_event_source_remove(b->hotplug_timer); 646 wl_event_source_remove(b->udev_drm_source); 647 wl_event_source_remove(b->drm_source); 648 649@@ -2750,6 +2922,10 @@ session_notify(struct wl_listener *listener, void *data) 650 weston_compositor_wake(compositor); 651 weston_compositor_damage_all(compositor); 652 device->state_invalid = true; 653+ 654+ wl_list_for_each(output, &compositor->output_list, link) 655+ to_drm_output(output)->state_invalid = true; 656+ 657 udev_input_enable(&b->input); 658 } else { 659 weston_log("deactivating session\n"); 660@@ -3106,6 +3282,14 @@ recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time, 661 } 662 #endif 663 664+static void 665+output_create_notify(struct wl_listener *listener, void *data) 666+{ 667+ struct drm_backend *b = container_of(listener, struct drm_backend, 668+ output_create_listener); 669+ 670+ drm_backend_update_outputs(b); 671+} 672 673 static const struct weston_drm_output_api api = { 674 drm_output_set_mode, 675@@ -3114,6 +3298,63 @@ static const struct weston_drm_output_api api = { 676 drm_output_set_max_bpc, 677 }; 678 679+enum drm_head_mode { 680+ DRM_HEAD_MODE_DEFAULT, 681+ DRM_HEAD_MODE_PRIMARY, 682+ DRM_HEAD_MODE_INTERNAL, 683+ DRM_HEAD_MODE_EXTERNAL, 684+ DRM_HEAD_MODE_EXTERNAL_DUAL, 685+}; 686+ 687+static bool 688+drm_head_match_primary(struct drm_backend *b, struct drm_head *head) 689+{ 690+ const char *buf = getenv("WESTON_DRM_PRIMARY"); 691+ return buf && !strcmp(buf, head->base.name); 692+} 693+ 694+static bool 695+drm_head_match_external(struct drm_backend *b, struct drm_head *head) 696+{ 697+ return drm_head_is_external(head); 698+} 699+ 700+static bool 701+drm_head_match_internal(struct drm_backend *b, struct drm_head *head) 702+{ 703+ return !drm_head_is_external(head); 704+} 705+ 706+#define DRM_HEAD_MAX_MATCHES 5 707+static drm_head_match_t drm_head_matches[][DRM_HEAD_MAX_MATCHES] = { 708+ [DRM_HEAD_MODE_DEFAULT] = { 709+ drm_head_match_primary, 710+ drm_head_match_internal, 711+ drm_head_match_external, 712+ NULL, 713+ }, 714+ [DRM_HEAD_MODE_PRIMARY] = { 715+ drm_head_match_primary, 716+ NULL, 717+ }, 718+ [DRM_HEAD_MODE_INTERNAL] = { 719+ drm_head_match_primary, 720+ drm_head_match_internal, 721+ NULL, 722+ }, 723+ [DRM_HEAD_MODE_EXTERNAL] = { 724+ drm_head_match_primary, 725+ drm_head_match_external, 726+ NULL, 727+ }, 728+ [DRM_HEAD_MODE_EXTERNAL_DUAL] = { 729+ drm_head_match_primary, 730+ drm_head_match_external, 731+ drm_head_match_internal, 732+ NULL, 733+ }, 734+}; 735+ 736 static struct drm_backend * 737 drm_backend_create(struct weston_compositor *compositor, 738 struct weston_drm_backend_config *config) 739@@ -3125,7 +3366,9 @@ drm_backend_create(struct weston_compositor *compositor, 740 const char *seat_id = default_seat; 741 const char *session_seat; 742 struct weston_drm_format_array *scanout_formats; 743+ enum drm_head_mode head_mode = DRM_HEAD_MODE_DEFAULT; 744 drmModeRes *res; 745+ char *buf; 746 int ret; 747 748 session_seat = getenv("XDG_SEAT"); 749@@ -3141,6 +3384,48 @@ drm_backend_create(struct weston_compositor *compositor, 750 if (b == NULL) 751 return NULL; 752 753+ buf = getenv("WESTON_DRM_SINGLE_HEAD"); 754+ if (buf && buf[0] == '1') 755+ b->single_head = true; 756+ 757+ buf = getenv("WESTON_DRM_HEAD_FALLBACK"); 758+ if (buf && buf[0] == '1') 759+ b->head_fallback = true; 760+ 761+ buf = getenv("WESTON_DRM_HEAD_FALLBACK_ALL"); 762+ if (buf && buf[0] == '1') 763+ b->head_fallback_all = true; 764+ 765+ buf = getenv("WESTON_DRM_PREFER_EXTERNAL"); 766+ if (buf && buf[0] == '1') { 767+ head_mode = DRM_HEAD_MODE_EXTERNAL; 768+ b->head_fallback = true; 769+ } 770+ 771+ buf = getenv("WESTON_DRM_PREFER_EXTERNAL_DUAL"); 772+ if (buf && buf[0] == '1') 773+ head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL; 774+ 775+ buf = getenv("WESTON_DRM_HEAD_MODE"); 776+ if (buf) { 777+ if (!strcmp(buf, "primary")) 778+ head_mode = DRM_HEAD_MODE_PRIMARY; 779+ else if (!strcmp(buf, "internal")) 780+ head_mode = DRM_HEAD_MODE_INTERNAL; 781+ else if (!strcmp(buf, "external")) 782+ head_mode = DRM_HEAD_MODE_EXTERNAL; 783+ else if (!strcmp(buf, "external-dual")) 784+ head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL; 785+ } 786+ 787+ b->head_matches = drm_head_matches[head_mode]; 788+ 789+ buf = getenv("WESTON_DRM_RESIZE_FREEZE_MS"); 790+ if (buf) 791+ b->resize_freeze_ms = atoi(buf); 792+ else 793+ b->resize_freeze_ms = DRM_RESIZE_FREEZE_MS; 794+ 795 device = zalloc(sizeof *device); 796 if (device == NULL) 797 return NULL; 798@@ -3241,7 +3526,7 @@ drm_backend_create(struct weston_compositor *compositor, 799 } 800 801 wl_list_init(&b->drm->writeback_connector_list); 802- if (drm_backend_discover_connectors(b->drm, drm_device, res) < 0) { 803+ if (drm_backend_update_connectors(b->drm, drm_device) < 0) { 804 weston_log("Failed to create heads for %s\n", b->drm->drm.filename); 805 goto err_udev_input; 806 } 807@@ -3280,6 +3565,10 @@ drm_backend_create(struct weston_compositor *compositor, 808 809 udev_device_unref(drm_device); 810 811+ b->output_create_listener.notify = output_create_notify; 812+ wl_signal_add(&b->compositor->output_created_signal, 813+ &b->output_create_listener); 814+ 815 weston_compositor_add_debug_binding(compositor, KEY_O, 816 planes_binding, b); 817 weston_compositor_add_debug_binding(compositor, KEY_C, 818@@ -3339,6 +3628,9 @@ drm_backend_create(struct weston_compositor *compositor, 819 goto err_udev_monitor; 820 } 821 822+ b->hotplug_timer = 823+ wl_event_loop_add_timer(loop, hotplug_timer_handler, b->drm); 824+ 825 return b; 826 827 err_udev_monitor: 828diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c 829index 735196b..c7dc2f8 100644 830--- a/libweston/backend-drm/kms.c 831+++ b/libweston/backend-drm/kms.c 832@@ -683,6 +683,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state) 833 834 scanout_state = 835 drm_output_state_get_existing_plane(state, scanout_plane); 836+ if (!scanout_state || !scanout_state->fb) 837+ return 0; 838 839 /* The legacy SetCrtc API doesn't allow us to do scaling, and the 840 * legacy PageFlip API doesn't allow us to do clipping either. */ 841@@ -700,7 +702,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state) 842 assert(scanout_state->in_fence_fd == -1); 843 844 mode = to_drm_mode(output->base.current_mode); 845- if (device->state_invalid || 846+ if (output->state_invalid || 847 !scanout_plane->state_cur->fb || 848 scanout_plane->state_cur->fb->strides[0] != 849 scanout_state->fb->strides[0]) { 850@@ -714,6 +716,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state) 851 weston_log("set mode failed: %s\n", strerror(errno)); 852 goto err; 853 } 854+ 855+ output->state_invalid = false; 856 } 857 858 pinfo = scanout_state->fb->format; 859@@ -964,6 +968,11 @@ drm_output_apply_state_atomic(struct drm_output_state *state, 860 *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; 861 } 862 863+ if (output->state_invalid) { 864+ drm_debug(b, "\t\t\t[atomic] output state invalid, modeset OK\n"); 865+ *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; 866+ } 867+ 868 if (state->dpms == WESTON_DPMS_ON) { 869 ret = drm_mode_ensure_blob(device, current_mode); 870 if (ret != 0) 871@@ -1080,6 +1089,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, 872 struct drm_output_state *output_state, *tmp; 873 struct drm_plane *plane; 874 drmModeAtomicReq *req = drmModeAtomicAlloc(); 875+ struct timespec now; 876 uint32_t flags; 877 int ret = 0; 878 879@@ -1214,14 +1224,34 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, 880 goto out; 881 } 882 883+ weston_compositor_read_presentation_clock(b->compositor, &now); 884+ 885 wl_list_for_each_safe(output_state, tmp, &pending_state->output_list, 886- link) 887+ link) { 888+ struct drm_output *output = output_state->output; 889+ struct drm_plane *scanout_plane = output->scanout_plane; 890+ struct drm_plane_state *scanout_state = 891+ drm_output_state_get_existing_plane(output_state, 892+ scanout_plane); 893+ 894+ /* Don't have a new state to apply */ 895+ if (output_state->dpms == WESTON_DPMS_ON && 896+ (!scanout_state || !scanout_state->fb)) 897+ continue; 898+ 899 drm_output_assign_state(output_state, mode); 900+ output->state_invalid = false; 901+ 902+ /* Not gonna receive flip event when dpms off */ 903+ if (output_state->dpms != WESTON_DPMS_ON) 904+ drm_output_update_complete(output, 905+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION, 906+ now.tv_sec, 907+ now.tv_nsec / 1000); 908+ } 909 910 device->state_invalid = false; 911 912- assert(wl_list_empty(&pending_state->output_list)); 913- 914 out: 915 drmModeAtomicFree(req); 916 drm_pending_state_free(pending_state); 917@@ -1321,8 +1351,6 @@ drm_pending_state_apply(struct drm_pending_state *pending_state) 918 919 device->state_invalid = false; 920 921- assert(wl_list_empty(&pending_state->output_list)); 922- 923 drm_pending_state_free(pending_state); 924 925 return 0; 926@@ -1374,8 +1402,6 @@ drm_pending_state_apply_sync(struct drm_pending_state *pending_state) 927 928 device->state_invalid = false; 929 930- assert(wl_list_empty(&pending_state->output_list)); 931- 932 drm_pending_state_free(pending_state); 933 934 return 0; 935diff --git a/libweston/compositor.c b/libweston/compositor.c 936index 9f8282b..2125f1a 100644 937--- a/libweston/compositor.c 938+++ b/libweston/compositor.c 939@@ -160,6 +160,25 @@ weston_paint_node_destroy(struct weston_paint_node *pnode) 940 free(pnode); 941 } 942 943+static struct weston_layer * 944+get_view_layer(struct weston_view *view); 945+ 946+static bool 947+weston_compositor_is_static_layer(struct weston_layer *layer) 948+{ 949+ if (!layer) 950+ return false; 951+ 952+ switch (layer->position) { 953+ case WESTON_LAYER_POSITION_BACKGROUND: 954+ case WESTON_LAYER_POSITION_UI: 955+ case WESTON_LAYER_POSITION_FADE: 956+ return true; 957+ default: 958+ return false; 959+ } 960+} 961+ 962 /** Send wl_output events for mode and scale changes 963 * 964 * \param head Send on all resources bound to this head. 965@@ -1386,6 +1405,22 @@ weston_view_assign_output(struct weston_view *ev) 966 uint32_t max, area, mask; 967 pixman_box32_t *e; 968 969+ /* The static views should bind to the specific output */ 970+ if (weston_compositor_is_static_layer(get_view_layer(ev))) { 971+ struct weston_view *view = ev; 972+ 973+ while (view && !(output = view->output)) 974+ view = view->geometry.parent; 975+ 976+ if (output && !output->destroying) 977+ ev->output_mask = 1u << output->id; 978+ else 979+ weston_view_set_output(ev, NULL); 980+ 981+ weston_surface_assign_output(ev->surface); 982+ return; 983+ } 984+ 985 new_output = NULL; 986 max = 0; 987 mask = 0; 988@@ -3301,6 +3336,7 @@ weston_output_repaint(struct weston_output *output) 989 if (output->dirty) 990 weston_output_update_matrix(output); 991 992+ output->repaint_needed = false; 993 r = output->repaint(output, &output_damage); 994 995 /* Clear painted primary damage */ 996@@ -3309,8 +3345,9 @@ weston_output_repaint(struct weston_output *output) 997 998 pixman_region32_fini(&output_damage); 999 1000- output->repaint_needed = false; 1001- if (r == 0) 1002+ if (output->repaint_needed) 1003+ output->repaint_status = REPAINT_SCHEDULED; 1004+ else if (r == 0) 1005 output->repaint_status = REPAINT_AWAITING_COMPLETION; 1006 1007 weston_compositor_repick(ec); 1008@@ -6911,6 +6948,7 @@ weston_compositor_remove_output(struct weston_output *output) 1009 * Use view_list in case the output did not go through repaint 1010 * after a view came on it, lacking a paint node. Just to be sure. 1011 */ 1012+ weston_compositor_build_view_list(compositor, NULL); 1013 wl_list_for_each(view, &compositor->view_list, link) { 1014 if (view->output_mask & (1u << output->id)) 1015 weston_view_assign_output(view); 1016-- 10172.20.1 1018 1019