Introduces a “same-as” configuration option for each output, which bypasses the rest of the output configuration (mode, scale, transform and seat) and instead makes it a clone of the specified output.
This is implemented by splitting the drm_output struct into the per-connector drm_output and the per-weston_output drm_logical_output, with the latter containing one or more of the former in a wl_list. I tested it on both i915 and etnaviv platforms, with various external monitors and configurations, as well as hotplugging. Signed-off-by: Emmanuel Gil Peyrot <emmanuel.pey...@collabora.com> --- src/compositor-drm.c | 794 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 477 insertions(+), 317 deletions(-) diff --git a/src/compositor-drm.c b/src/compositor-drm.c index 1edcaab..a0ed45f 100644 --- a/src/compositor-drm.c +++ b/src/compositor-drm.c @@ -157,8 +157,12 @@ struct drm_edid { char serial_number[13]; }; +struct drm_logical_output; + struct drm_output { - struct weston_output base; + struct wl_list link; + struct drm_logical_output *base; + char *name; uint32_t crtc_id; int pipe; @@ -166,13 +170,21 @@ struct drm_output { drmModeCrtcPtr original_crtc; struct drm_edid edid; drmModePropertyPtr dpms_prop; - uint32_t gbm_format; - enum dpms_enum dpms; + struct backlight *backlight; int vblank_pending; int page_flip_pending; int destroy_pending; +}; + +struct drm_logical_output { + struct weston_output base; + struct wl_list output_list; + + int page_flip_refcount; + uint32_t gbm_format; + enum dpms_enum dpms; struct gbm_surface *gbm_surface; struct gbm_bo *gbm_cursor_bo[2]; @@ -181,7 +193,6 @@ struct drm_output { struct weston_view *cursor_view; int current_cursor; struct drm_fb *current, *next; - struct backlight *backlight; struct drm_fb *dumb[2]; pixman_image_t *image[2]; @@ -229,7 +240,7 @@ static struct gl_renderer_interface *gl_renderer; static const char default_seat[] = "seat0"; static void -drm_output_set_cursor(struct drm_output *output); +drm_output_set_cursor(struct drm_logical_output *output); static void drm_output_update_msc(struct weston_output *output_base, unsigned int seq); @@ -237,7 +248,7 @@ drm_output_update_msc(struct weston_output *output_base, unsigned int seq); static int drm_sprite_crtc_supported(struct drm_output *output, uint32_t supported) { - struct weston_compositor *ec = output->base.compositor; + struct weston_compositor *ec = output->base->base.compositor; struct drm_backend *b =(struct drm_backend *)ec->backend; int crtc; @@ -423,7 +434,7 @@ drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer) } static void -drm_output_release_fb(struct drm_output *output, struct drm_fb *fb) +drm_output_release_fb(struct drm_logical_output *output, struct drm_fb *fb) { if (!fb) return; @@ -441,7 +452,7 @@ drm_output_release_fb(struct drm_output *output, struct drm_fb *fb) } static uint32_t -drm_output_check_scanout_format(struct drm_output *output, +drm_output_check_scanout_format(struct drm_logical_output *output, struct weston_surface *es, struct gbm_bo *bo) { uint32_t format; @@ -471,7 +482,7 @@ drm_output_check_scanout_format(struct drm_output *output, } static struct weston_plane * -drm_output_prepare_scanout_view(struct drm_output *output, +drm_output_prepare_scanout_view(struct drm_logical_output *output, struct weston_view *ev) { struct weston_output *output_base = &output->base; @@ -519,7 +530,8 @@ drm_output_prepare_scanout_view(struct drm_output *output, } static void -drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage) +drm_output_render_gl(struct drm_logical_output *output, + pixman_region32_t *damage) { struct drm_backend *b = (struct drm_backend *)output->base.compositor->backend; @@ -543,7 +555,8 @@ drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage) } static void -drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage) +drm_output_render_pixman(struct drm_logical_output *output, + pixman_region32_t *damage) { struct weston_compositor *ec = output->base.compositor; pixman_region32_t total_damage, previous_damage; @@ -569,7 +582,7 @@ drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage) } static void -drm_output_render(struct drm_output *output, pixman_region32_t *damage) +drm_output_render(struct drm_logical_output *output, pixman_region32_t *damage) { struct weston_compositor *c = output->base.compositor; struct drm_backend *b = (struct drm_backend *)c->backend; @@ -634,98 +647,117 @@ static int drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) { - struct drm_output *output = (struct drm_output *) output_base; + struct drm_logical_output *logical_output = + (struct drm_logical_output *)output_base; + struct drm_output *output; struct drm_backend *backend = (struct drm_backend *)output_base->compositor->backend; struct drm_sprite *s; struct drm_mode *mode; int ret = 0; - if (output->destroy_pending) - return -1; + wl_list_for_each(output, &logical_output->output_list, link) { + if (output->destroy_pending) + return -1; + } - if (!output->next) - drm_output_render(output, damage); - if (!output->next) + if (!logical_output->next) + drm_output_render(logical_output, damage); + if (!logical_output->next) return -1; mode = container_of(output_base->current_mode, struct drm_mode, base); - if (!output->current || - output->current->stride != output->next->stride) { - ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, - output->next->fb_id, 0, 0, - &output->connector_id, 1, - &mode->mode_info); - if (ret) { - weston_log("set mode failed: %m\n"); - goto err_pageflip; + if (!logical_output->current || + logical_output->current->stride != logical_output->next->stride) { + wl_list_for_each(output, &logical_output->output_list, link) { + ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, + logical_output->next->fb_id, 0, 0, + &output->connector_id, 1, + &mode->mode_info); + if (ret) { + weston_log("set mode failed: %m\n"); + goto err_pageflip; + } } output_base->set_dpms(output_base, WESTON_DPMS_ON); } - if (drmModePageFlip(backend->drm.fd, output->crtc_id, - output->next->fb_id, - DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { - weston_log("queueing pageflip failed: %m\n"); - goto err_pageflip; - } + wl_list_for_each(output, &logical_output->output_list, link) { + if (output->page_flip_pending) + continue; + if (drmModePageFlip(backend->drm.fd, output->crtc_id, + logical_output->next->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { + weston_log("queueing pageflip failed: %m\n"); + goto err_pageflip; + } - output->page_flip_pending = 1; + output->page_flip_pending = 1; + ++logical_output->page_flip_refcount; + } if (!backend->cursors_are_broken) - drm_output_set_cursor(output); + drm_output_set_cursor(logical_output); - /* - * Now, update all the sprite surfaces - */ - wl_list_for_each(s, &backend->sprite_list, link) { - uint32_t flags = 0, fb_id = 0; - drmVBlank vbl = { - .request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT, - .request.sequence = 1, - }; + /* Sprite support is disabled on clone mode for now. */ + if (wl_list_length(&logical_output->output_list) == 1) { + /* Fetches the only output present from the list */ + wl_list_for_each(output, &logical_output->output_list, link); - if ((!s->current && !s->next) || - !drm_sprite_crtc_supported(output, s->possible_crtcs)) - continue; + /* + * Now, update all the sprite surfaces + */ + wl_list_for_each(s, &backend->sprite_list, link) { + uint32_t flags = 0, fb_id = 0; + drmVBlank vbl = { + .request.type = DRM_VBLANK_RELATIVE | + DRM_VBLANK_EVENT, + .request.sequence = 1, + }; + + if ((!s->current && !s->next) || + !drm_sprite_crtc_supported(output, + s->possible_crtcs)) + continue; - if (s->next && !backend->sprites_hidden) - fb_id = s->next->fb_id; + if (s->next && !backend->sprites_hidden) + fb_id = s->next->fb_id; - ret = drmModeSetPlane(backend->drm.fd, s->plane_id, - output->crtc_id, fb_id, flags, - s->dest_x, s->dest_y, - s->dest_w, s->dest_h, - s->src_x, s->src_y, - s->src_w, s->src_h); - if (ret) - weston_log("setplane failed: %d: %s\n", - ret, strerror(errno)); + ret = drmModeSetPlane(backend->drm.fd, s->plane_id, + output->crtc_id, fb_id, flags, + s->dest_x, s->dest_y, + s->dest_w, s->dest_h, + s->src_x, s->src_y, + s->src_w, s->src_h); + if (ret) + weston_log("setplane failed: %d: %s\n", + ret, strerror(errno)); - vbl.request.type |= drm_waitvblank_pipe(output); + vbl.request.type |= drm_waitvblank_pipe(output); - /* - * Queue a vblank signal so we know when the surface - * becomes active on the display or has been replaced. - */ - vbl.request.signal = (unsigned long)s; - ret = drmWaitVBlank(backend->drm.fd, &vbl); - if (ret) { - weston_log("vblank event request failed: %d: %s\n", - ret, strerror(errno)); - } + /* + * Queue a vblank signal so we know when the surface + * becomes active on the display or has been replaced. + */ + vbl.request.signal = (unsigned long)s; + ret = drmWaitVBlank(backend->drm.fd, &vbl); + if (ret) { + weston_log("vblank event request failed: %d:" + " %s\n", ret, strerror(errno)); + } - s->output = output; - output->vblank_pending = 1; + s->output = output; + output->vblank_pending = 1; + } } return 0; err_pageflip: - output->cursor_view = NULL; - if (output->next) { - drm_output_release_fb(output, output->next); - output->next = NULL; + logical_output->cursor_view = NULL; + if (logical_output->next) { + drm_output_release_fb(logical_output, logical_output->next); + logical_output->next = NULL; } return -1; @@ -734,13 +766,16 @@ err_pageflip: static void drm_output_start_repaint_loop(struct weston_output *output_base) { - struct drm_output *output = (struct drm_output *) output_base; + struct drm_logical_output *logical_output = + (struct drm_logical_output *)output_base; + struct drm_output *output; struct drm_backend *backend = (struct drm_backend *) output_base->compositor->backend; uint32_t fb_id; struct timespec ts, tnow; struct timespec vbl2now; int64_t refresh_nsec; + long last_sec = 0, last_usec = 0; int ret; drmVBlank vbl = { .request.type = DRM_VBLANK_RELATIVE, @@ -748,20 +783,32 @@ drm_output_start_repaint_loop(struct weston_output *output_base) .request.signal = 0, }; - if (output->destroy_pending) - return; + wl_list_for_each(output, &logical_output->output_list, link) { + if (output->destroy_pending) + return; + } - if (!output->current) { + if (!logical_output->current) { /* We can't page flip if there's no mode set */ goto finish_frame; } /* Try to get current msc and timestamp via instant query */ - vbl.request.type |= drm_waitvblank_pipe(output); - ret = drmWaitVBlank(backend->drm.fd, &vbl); + wl_list_for_each(output, &logical_output->output_list, link) { + vbl.request.type |= drm_waitvblank_pipe(output); + ret = drmWaitVBlank(backend->drm.fd, &vbl); + if (ret != 0) + continue; + if (last_sec < vbl.reply.tval_sec || + (last_sec == vbl.reply.tval_sec && + last_usec < vbl.reply.tval_usec)) { + last_sec = vbl.reply.tval_sec; + last_usec = vbl.reply.tval_usec; + } + } /* Error ret or zero timestamp means failure to get valid timestamp */ - if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) { + if (last_sec > 0 || last_usec > 0) { ts.tv_sec = vbl.reply.tval_sec; ts.tv_nsec = vbl.reply.tval_usec * 1000; @@ -785,12 +832,15 @@ drm_output_start_repaint_loop(struct weston_output *output_base) /* Immediate query didn't provide valid timestamp. * Use pageflip fallback. */ - fb_id = output->current->fb_id; + fb_id = logical_output->current->fb_id; - if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id, - DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { - weston_log("queueing pageflip failed: %m\n"); - goto finish_frame; + wl_list_for_each(output, &logical_output->output_list, link) { + if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id, + DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { + weston_log("queueing pageflip failed: %m\n"); + goto finish_frame; + } + ++logical_output->page_flip_refcount; } return; @@ -819,21 +869,22 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, { struct drm_sprite *s = (struct drm_sprite *)data; struct drm_output *output = s->output; + struct drm_logical_output *logical_output = output->base; struct timespec ts; uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; - drm_output_update_msc(&output->base, frame); + drm_output_update_msc(&logical_output->base, frame); output->vblank_pending = 0; - drm_output_release_fb(output, s->current); + drm_output_release_fb(logical_output, s->current); s->current = s->next; s->next = NULL; if (!output->page_flip_pending) { ts.tv_sec = sec; ts.tv_nsec = usec * 1000; - weston_output_finish_frame(&output->base, &ts, flags); + weston_output_finish_frame(&logical_output->base, &ts, flags); } } @@ -845,35 +896,37 @@ page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { struct drm_output *output = (struct drm_output *) data; + struct drm_logical_output *logical_output = output->base; struct timespec ts; uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC | WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; - drm_output_update_msc(&output->base, frame); + drm_output_update_msc(&logical_output->base, frame); /* We don't set page_flip_pending on start_repaint_loop, in that case * we just want to page flip to the current buffer to get an accurate * timestamp */ - if (output->page_flip_pending) { - drm_output_release_fb(output, output->current); - output->current = output->next; - output->next = NULL; + if (output->page_flip_pending && + --logical_output->page_flip_refcount == 0) { + drm_output_release_fb(logical_output, logical_output->current); + logical_output->current = logical_output->next; + logical_output->next = NULL; } output->page_flip_pending = 0; if (output->destroy_pending) - drm_output_destroy(&output->base); + drm_output_destroy(&logical_output->base); else if (!output->vblank_pending) { ts.tv_sec = sec; ts.tv_nsec = usec * 1000; - weston_output_finish_frame(&output->base, &ts, flags); + weston_output_finish_frame(&logical_output->base, &ts, flags); /* We can't call this from frame_notify, because the output's * repaint needed flag is cleared just after that */ - if (output->recorder) - weston_output_schedule_repaint(&output->base); + if (logical_output->recorder) + weston_output_schedule_repaint(&logical_output->base); } } @@ -914,14 +967,15 @@ drm_view_transform_supported(struct weston_view *ev) } static struct weston_plane * -drm_output_prepare_overlay_view(struct drm_output *output, +drm_output_prepare_overlay_view(struct drm_logical_output *logical_output, struct weston_view *ev) { - struct weston_output *output_base = &output->base; + struct weston_output *output_base = &logical_output->base; struct weston_compositor *ec = output_base->compositor; struct drm_backend *b = (struct drm_backend *)ec->backend; struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport; struct wl_resource *buffer_resource; + struct drm_output *output; struct drm_sprite *s; struct linux_dmabuf_buffer *dmabuf; int found = 0; @@ -959,8 +1013,14 @@ drm_output_prepare_overlay_view(struct drm_output *output, if (!drm_view_transform_supported(ev)) return NULL; + if (wl_list_length(&logical_output->output_list) != 1) + return NULL; + /* Fetches the only output present from the list */ + wl_list_for_each(output, &logical_output->output_list, link); + wl_list_for_each(s, &b->sprite_list, link) { - if (!drm_sprite_crtc_supported(output, s->possible_crtcs)) + if (!drm_sprite_crtc_supported(output, + s->possible_crtcs)) continue; if (!s->next) { @@ -1088,7 +1148,7 @@ drm_output_prepare_overlay_view(struct drm_output *output, } static struct weston_plane * -drm_output_prepare_cursor_view(struct drm_output *output, +drm_output_prepare_cursor_view(struct drm_logical_output *output, struct weston_view *ev) { struct drm_backend *b = @@ -1167,39 +1227,46 @@ cursor_bo_update(struct drm_backend *b, struct gbm_bo *bo, } static void -drm_output_set_cursor(struct drm_output *output) +drm_output_set_cursor(struct drm_logical_output *logical_output) { - struct weston_view *ev = output->cursor_view; + struct drm_output *output; + struct weston_plane *cursor_plane = &logical_output->cursor_plane; + struct weston_view *ev = logical_output->cursor_view; struct weston_buffer *buffer; struct drm_backend *b = - (struct drm_backend *) output->base.compositor->backend; + (struct drm_backend *) logical_output->base.compositor->backend; EGLint handle; struct gbm_bo *bo; float x, y; - output->cursor_view = NULL; + logical_output->cursor_view = NULL; if (ev == NULL) { - drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); - output->cursor_plane.x = INT32_MIN; - output->cursor_plane.y = INT32_MIN; + wl_list_for_each(output, &logical_output->output_list, link) + drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); + cursor_plane->x = INT32_MIN; + cursor_plane->y = INT32_MIN; return; } buffer = ev->surface->buffer_ref.buffer; if (buffer && - pixman_region32_not_empty(&output->cursor_plane.damage)) { - pixman_region32_fini(&output->cursor_plane.damage); - pixman_region32_init(&output->cursor_plane.damage); - output->current_cursor ^= 1; - bo = output->gbm_cursor_bo[output->current_cursor]; + pixman_region32_not_empty(&cursor_plane->damage)) { + pixman_region32_fini(&cursor_plane->damage); + pixman_region32_init(&cursor_plane->damage); + logical_output->current_cursor ^= 1; + bo = logical_output->gbm_cursor_bo[ + logical_output->current_cursor]; cursor_bo_update(b, bo, ev); handle = gbm_bo_get_handle(bo).s32; - if (drmModeSetCursor(b->drm.fd, output->crtc_id, handle, - b->cursor_width, b->cursor_height)) { - weston_log("failed to set cursor: %m\n"); - b->cursors_are_broken = 1; + wl_list_for_each(output, &logical_output->output_list, link) { + if (drmModeSetCursor(b->drm.fd, output->crtc_id, handle, + b->cursor_width, + b->cursor_height)) { + weston_log("failed to set cursor: %m\n"); + b->cursors_are_broken = 1; + } } } @@ -1208,17 +1275,20 @@ drm_output_set_cursor(struct drm_output *output) /* From global to output space, output transform is guaranteed to be * NORMAL by drm_output_prepare_cursor_view(). */ - x = (x - output->base.x) * output->base.current_scale; - y = (y - output->base.y) * output->base.current_scale; - - if (output->cursor_plane.x != x || output->cursor_plane.y != y) { - if (drmModeMoveCursor(b->drm.fd, output->crtc_id, x, y)) { - weston_log("failed to move cursor: %m\n"); - b->cursors_are_broken = 1; + x = (x - logical_output->base.x) * logical_output->base.current_scale; + y = (y - logical_output->base.y) * logical_output->base.current_scale; + + if (cursor_plane->x != x || cursor_plane->y != y) { + wl_list_for_each(output, &logical_output->output_list, link) { + if (drmModeMoveCursor(b->drm.fd, output->crtc_id, + x, y)) { + weston_log("failed to move cursor: %m\n"); + b->cursors_are_broken = 1; + } } - output->cursor_plane.x = x; - output->cursor_plane.y = y; + cursor_plane->x = x; + cursor_plane->y = y; } } @@ -1227,7 +1297,8 @@ drm_assign_planes(struct weston_output *output_base) { struct drm_backend *b = (struct drm_backend *)output_base->compositor->backend; - struct drm_output *output = (struct drm_output *)output_base; + struct drm_logical_output *output = + (struct drm_logical_output *)output_base; struct weston_view *ev, *next; pixman_region32_t overlap, surface_overlap; struct weston_plane *primary, *next_plane; @@ -1307,52 +1378,58 @@ drm_assign_planes(struct weston_output *output_base) } static void -drm_output_fini_pixman(struct drm_output *output); +drm_output_fini_pixman(struct drm_logical_output *output); static void drm_output_destroy(struct weston_output *output_base) { - struct drm_output *output = (struct drm_output *) output_base; + struct drm_logical_output *logical_output = + (struct drm_logical_output *)output_base; + struct drm_output *output; struct drm_backend *b = (struct drm_backend *)output_base->compositor->backend; - drmModeCrtcPtr origcrtc = output->original_crtc; + drmModeCrtcPtr origcrtc; - if (output->page_flip_pending) { - output->destroy_pending = 1; - weston_log("destroy output while page flip pending\n"); - return; - } + wl_list_for_each(output, &logical_output->output_list, link) { + if (output->page_flip_pending) { + output->destroy_pending = 1; + weston_log("destroy output while page flip pending\n"); + return; + } - if (output->backlight) - backlight_destroy(output->backlight); + if (output->backlight) + backlight_destroy(output->backlight); - drmModeFreeProperty(output->dpms_prop); + drmModeFreeProperty(output->dpms_prop); - /* Turn off hardware cursor */ - drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); + /* Turn off hardware cursor */ + drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); - /* Restore original CRTC state */ - drmModeSetCrtc(b->drm.fd, origcrtc->crtc_id, origcrtc->buffer_id, - origcrtc->x, origcrtc->y, - &output->connector_id, 1, &origcrtc->mode); - drmModeFreeCrtc(origcrtc); + /* Restore original CRTC state */ + origcrtc = output->original_crtc; + drmModeSetCrtc(b->drm.fd, origcrtc->crtc_id, + origcrtc->buffer_id, + origcrtc->x, origcrtc->y, + &output->connector_id, 1, &origcrtc->mode); + drmModeFreeCrtc(origcrtc); - b->crtc_allocator &= ~(1 << output->crtc_id); - b->connector_allocator &= ~(1 << output->connector_id); + b->crtc_allocator &= ~(1 << output->crtc_id); + b->connector_allocator &= ~(1 << output->connector_id); + } if (b->use_pixman) { - drm_output_fini_pixman(output); + drm_output_fini_pixman(logical_output); } else { gl_renderer->output_destroy(output_base); - gbm_surface_destroy(output->gbm_surface); + gbm_surface_destroy(logical_output->gbm_surface); } - weston_plane_release(&output->fb_plane); - weston_plane_release(&output->cursor_plane); + weston_plane_release(&logical_output->fb_plane); + weston_plane_release(&logical_output->cursor_plane); - weston_output_destroy(&output->base); + weston_output_destroy(output_base); - free(output); + free(logical_output); } /** @@ -1367,7 +1444,7 @@ drm_output_destroy(struct weston_output *output_base) * @returns Pointer to a mode from the output's mode list */ static struct drm_mode * -choose_mode (struct weston_output *output_base, struct weston_mode *target_mode) +choose_mode(struct weston_output *output_base, struct weston_mode *target_mode) { struct drm_mode *tmp_mode = NULL, *mode; @@ -1392,14 +1469,17 @@ choose_mode (struct weston_output *output_base, struct weston_mode *target_mode) } static int -drm_output_init_egl(struct drm_output *output, struct drm_backend *b); +drm_output_init_egl(struct drm_logical_output *output, struct drm_backend *b); static int -drm_output_init_pixman(struct drm_output *output, struct drm_backend *b); +drm_output_init_pixman(struct drm_logical_output *output, + struct drm_backend *b); static int -drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode) +drm_output_switch_mode(struct weston_output *output_base, + struct weston_mode *mode) { - struct drm_output *output; + struct drm_logical_output *output = + (struct drm_logical_output *)output_base; struct drm_mode *drm_mode; struct drm_backend *b; @@ -1414,8 +1494,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo } b = (struct drm_backend *)output_base->compositor->backend; - output = (struct drm_output *)output_base; - drm_mode = choose_mode(&output->base, mode); + drm_mode = choose_mode(output_base, mode); if (!drm_mode) { weston_log("%s, invalid resolution:%dx%d\n", __func__, mode->width, mode->height); @@ -1444,7 +1523,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo return -1; } } else { - gl_renderer->output_destroy(&output->base); + gl_renderer->output_destroy(output_base); gbm_surface_destroy(output->gbm_surface); if (drm_output_init_egl(output, b) < 0) { @@ -1704,21 +1783,25 @@ drm_get_backlight(struct drm_output *output) static void drm_set_backlight(struct weston_output *output_base, uint32_t value) { - struct drm_output *output = (struct drm_output *) output_base; + struct drm_logical_output *logical_output = + (struct drm_logical_output *)output_base; + struct drm_output *output; long max_brightness, new_brightness; - if (!output->backlight) - return; + wl_list_for_each(output, &logical_output->output_list, link) { + if (!output->backlight) + return; - if (value > 255) - return; + if (value > 255) + return; - max_brightness = backlight_get_max_brightness(output->backlight); + max_brightness = backlight_get_max_brightness(output->backlight); - /* get denormalized value */ - new_brightness = (value * max_brightness) / 255; + /* get denormalized value */ + new_brightness = (value * max_brightness) / 255; - backlight_set_brightness(output->backlight, new_brightness); + backlight_set_brightness(output->backlight, new_brightness); + } } static drmModePropertyPtr @@ -1744,23 +1827,28 @@ drm_get_prop(int fd, drmModeConnectorPtr connector, const char *name) static void drm_set_dpms(struct weston_output *output_base, enum dpms_enum level) { - struct drm_output *output = (struct drm_output *) output_base; + struct drm_logical_output *logical_output = + (struct drm_logical_output *)output_base; struct weston_compositor *ec = output_base->compositor; struct drm_backend *b = (struct drm_backend *)ec->backend; + struct drm_output *output; int ret; - if (!output->dpms_prop) - return; + wl_list_for_each(output, &logical_output->output_list, link) { + if (!output->dpms_prop) + continue; - ret = drmModeConnectorSetProperty(b->drm.fd, output->connector_id, - output->dpms_prop->prop_id, level); - if (ret) { - weston_log("DRM: DPMS: failed property set for %s\n", - output_base->name); - return; + ret = drmModeConnectorSetProperty(b->drm.fd, + output->connector_id, + output->dpms_prop->prop_id, + level); + if (ret) { + weston_log("DRM: DPMS: failed property set for %s\n", + output->name); + continue; + } } - - output->dpms = level; + logical_output->dpms = level; } static const char * const connector_type_names[] = { @@ -1831,7 +1919,7 @@ find_crtc_for_connector(struct drm_backend *b, /* Init output state that depends on gl or gbm */ static int -drm_output_init_egl(struct drm_output *output, struct drm_backend *b) +drm_output_init_egl(struct drm_logical_output *output, struct drm_backend *b) { EGLint format[2] = { output->gbm_format, @@ -1883,7 +1971,8 @@ drm_output_init_egl(struct drm_output *output, struct drm_backend *b) } static int -drm_output_init_pixman(struct drm_output *output, struct drm_backend *b) +drm_output_init_pixman(struct drm_logical_output *output, + struct drm_backend *b) { int w = output->base.current_mode->width; int h = output->base.current_mode->height; @@ -1927,7 +2016,7 @@ err: } static void -drm_output_fini_pixman(struct drm_output *output) +drm_output_fini_pixman(struct drm_logical_output *output) { unsigned int i; @@ -2073,11 +2162,11 @@ find_and_parse_output_edid(struct drm_backend *b, output->edid.monitor_name, output->edid.serial_number); if (output->edid.pnp_id[0] != '\0') - output->base.make = output->edid.pnp_id; + output->base->base.make = output->edid.pnp_id; if (output->edid.monitor_name[0] != '\0') - output->base.model = output->edid.monitor_name; + output->base->base.model = output->edid.monitor_name; if (output->edid.serial_number[0] != '\0') - output->base.serial_number = output->edid.serial_number; + output->base->base.serial_number = output->edid.serial_number; } drmModeFreePropertyBlob(edid_blob); } @@ -2302,6 +2391,7 @@ create_output_for_connector(struct drm_backend *b, drmModeConnector *connector, int x, int y, struct udev_device *drm_device) { + struct drm_logical_output *logical_output = NULL; struct drm_output *output; struct drm_mode *drm_mode, *next, *current; struct weston_output *output_base; @@ -2312,6 +2402,7 @@ create_output_for_connector(struct drm_backend *b, char *s, *name; enum output_config config; uint32_t transform; + bool is_clone = false; i = find_crtc_for_connector(b, resources, connector); if (i < 0) { @@ -2321,20 +2412,32 @@ create_output_for_connector(struct drm_backend *b, name = make_connector_name(connector); - output = zalloc(sizeof *output); - if (output == NULL) - return -1; - - output_base = &output->base; - output_base->subpixel = drm_subpixel_to_wayland(connector->subpixel); - output_base->name = name; - output_base->make = "unknown"; - output_base->model = "unknown"; - output_base->serial_number = "unknown"; - wl_list_init(&output_base->mode_list); - section = weston_config_get_section(b->compositor->config, "output", "name", name); + + weston_config_section_get_string(section, "same-as", &s, ""); + if (strcmp(s, "") != 0) { + struct drm_logical_output *temporary_logical_output = NULL; + wl_list_for_each(temporary_logical_output, + &b->compositor->output_list, base.link) { + if (strcmp(temporary_logical_output->base.name, s) == 0) { + logical_output = temporary_logical_output; + break; + } + } + if (logical_output == NULL) + return -1; + is_clone = true; + } else { + logical_output = zalloc(sizeof *logical_output); + if (logical_output == NULL) + return -1; + is_clone = false; + } + free(s); + + output_base = &logical_output->base; + weston_config_section_get_string(section, "mode", &s, "preferred"); if (strcmp(s, "off") == 0) config = OUTPUT_CONFIG_OFF; @@ -2352,21 +2455,42 @@ create_output_for_connector(struct drm_backend *b, } free(s); - weston_config_section_get_int(section, "scale", &scale, 1); - weston_config_section_get_string(section, "transform", &s, "normal"); - if (weston_parse_transform(s, &transform) < 0) - weston_log("Invalid transform \"%s\" for output %s\n", s, name); + if (!is_clone) { + output_base->subpixel = + drm_subpixel_to_wayland(connector->subpixel); + output_base->name = name; + output_base->make = "unknown"; + output_base->model = "unknown"; + output_base->serial_number = "unknown"; + wl_list_init(&output_base->mode_list); - free(s); + weston_config_section_get_int(section, "scale", &scale, 1); + weston_config_section_get_string(section, "transform", &s, + "normal"); + if (weston_parse_transform(s, &transform) < 0) + weston_log("Invalid transform \"%s\" for output %s\n", + s, name); - if (get_gbm_format_from_section(section, - b->gbm_format, - &output->gbm_format) == -1) - output->gbm_format = b->gbm_format; + free(s); - weston_config_section_get_string(section, "seat", &s, ""); - setup_output_seat_constraint(b, output_base, s); - free(s); + if (get_gbm_format_from_section(section, + b->gbm_format, + &logical_output->gbm_format) == -1) + logical_output->gbm_format = b->gbm_format; + + weston_config_section_get_string(section, "seat", &s, ""); + setup_output_seat_constraint(b, output_base, s); + free(s); + + wl_list_init(&logical_output->output_list); + } + + output = zalloc(sizeof *output); + if (output == NULL) + return -1; + + wl_list_insert(&logical_output->output_list, &output->link); + output->base = logical_output; output->crtc_id = resources->crtcs[i]; output->pipe = i; @@ -2377,15 +2501,6 @@ create_output_for_connector(struct drm_backend *b, output->original_crtc = drmModeGetCrtc(b->drm.fd, output->crtc_id); output->dpms_prop = drm_get_prop(b->drm.fd, connector, "DPMS"); - if (connector_get_current_mode(connector, b->drm.fd, &crtc_mode) < 0) - goto err_free; - - for (i = 0; i < connector->count_modes; i++) { - drm_mode = drm_output_add_mode(output_base, &connector->modes[i]); - if (!drm_mode) - goto err_free; - } - if (config == OUTPUT_CONFIG_OFF) { weston_log("Disabling output %s\n", name); drmModeSetCrtc(b->drm.fd, output->crtc_id, @@ -2393,62 +2508,80 @@ create_output_for_connector(struct drm_backend *b, goto err_free; } - current = drm_output_choose_initial_mode(output_base, config, - width, height, - &crtc_mode, &modeline); - if (!current) - goto err_free; - output_base->current_mode = ¤t->base; - output_base->current_mode->flags |= WL_OUTPUT_MODE_CURRENT; + find_and_parse_output_edid(b, output, connector); + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + output_base->connection_internal = 1; - weston_output_init(output_base, b->compositor, x, y, - connector->mmWidth, connector->mmHeight, - transform, scale); + if (!is_clone) { + if (connector_get_current_mode(connector, b->drm.fd, &crtc_mode) < 0) + goto err_free; - if (b->use_pixman) { - if (drm_output_init_pixman(output, b) < 0) { - weston_log("Failed to init output pixman state\n"); + for (i = 0; i < connector->count_modes; i++) { + drm_mode = drm_output_add_mode(output_base, + &connector->modes[i]); + if (!drm_mode) + goto err_free; + } + + current = drm_output_choose_initial_mode(output_base, config, + width, height, + &crtc_mode, + &modeline); + if (!current) + goto err_free; + output_base->current_mode = ¤t->base; + output_base->current_mode->flags |= WL_OUTPUT_MODE_CURRENT; + + weston_output_init(output_base, b->compositor, x, y, + connector->mmWidth, connector->mmHeight, + transform, scale); + + if (b->use_pixman) { + if (drm_output_init_pixman(logical_output, b) < 0) { + weston_log("Failed to init output pixman state\n"); + goto err_output; + } + } else if (drm_output_init_egl(logical_output, b) < 0) { + weston_log("Failed to init output gl state\n"); goto err_output; } - } else if (drm_output_init_egl(output, b) < 0) { - weston_log("Failed to init output gl state\n"); - goto err_output; - } - - output->backlight = backlight_init(drm_device, - connector->connector_type); - if (output->backlight) { - weston_log("Initialized backlight, device %s\n", - output->backlight->path); - output_base->set_backlight = drm_set_backlight; - output_base->backlight_current = drm_get_backlight(output); - } else { - weston_log("Failed to initialize backlight\n"); - } - weston_compositor_add_output(b->compositor, output_base); + output->backlight = backlight_init(drm_device, + connector->connector_type); + if (output->backlight) { + weston_log("Initialized backlight, device %s\n", + output->backlight->path); + output_base->set_backlight = drm_set_backlight; + output_base->backlight_current = + drm_get_backlight(output); + } else { + weston_log("Failed to initialize backlight\n"); + } - find_and_parse_output_edid(b, output, connector); - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) - output_base->connection_internal = 1; + weston_compositor_add_output(b->compositor, output_base); - output_base->start_repaint_loop = drm_output_start_repaint_loop; - output_base->repaint = drm_output_repaint; - output_base->destroy = drm_output_destroy; - output_base->assign_planes = drm_assign_planes; - output_base->set_dpms = drm_set_dpms; - output_base->switch_mode = drm_output_switch_mode; + output_base->start_repaint_loop = drm_output_start_repaint_loop; + output_base->repaint = drm_output_repaint; + output_base->destroy = drm_output_destroy; + output_base->assign_planes = drm_assign_planes; + output_base->set_dpms = drm_set_dpms; + output_base->switch_mode = drm_output_switch_mode; - output_base->gamma_size = output->original_crtc->gamma_size; - output_base->set_gamma = drm_output_set_gamma; + output_base->gamma_size = output->original_crtc->gamma_size; + output_base->set_gamma = drm_output_set_gamma; - weston_plane_init(&output->cursor_plane, b->compositor, - INT32_MIN, INT32_MIN); - weston_plane_init(&output->fb_plane, b->compositor, 0, 0); + weston_plane_init(&logical_output->cursor_plane, b->compositor, + INT32_MIN, INT32_MIN); + weston_plane_init(&logical_output->fb_plane, b->compositor, + 0, 0); - weston_compositor_stack_plane(b->compositor, &output->cursor_plane, NULL); - weston_compositor_stack_plane(b->compositor, &output->fb_plane, - &b->compositor->primary_plane); + weston_compositor_stack_plane(b->compositor, + &logical_output->cursor_plane, + NULL); + weston_compositor_stack_plane(b->compositor, + &logical_output->fb_plane, + &b->compositor->primary_plane); + } weston_log("Output %s, (connector %d, crtc %d)\n", name, output->connector_id, output->crtc_id); @@ -2472,16 +2605,20 @@ err_output: weston_output_destroy(output_base); err_free: wl_list_for_each_safe(drm_mode, next, &output_base->mode_list, - base.link) { + base.link) { wl_list_remove(&drm_mode->base.link); free(drm_mode); } + wl_list_remove(&output->link); drmModeFreeCrtc(output->original_crtc); b->crtc_allocator &= ~(1 << output->crtc_id); b->connector_allocator &= ~(1 << output->connector_id); free(output); + if (!is_clone) + free(logical_output); + return -1; } @@ -2537,20 +2674,27 @@ static void destroy_sprites(struct drm_backend *backend) { struct drm_sprite *sprite, *next; + struct drm_logical_output *logical_output; struct drm_output *output; - output = container_of(backend->compositor->output_list.next, - struct drm_output, base.link); + logical_output = container_of(backend->compositor->output_list.next, + struct drm_logical_output, base.link); - wl_list_for_each_safe(sprite, next, &backend->sprite_list, link) { - drmModeSetPlane(backend->drm.fd, - sprite->plane_id, - output->crtc_id, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0); - drm_output_release_fb(output, sprite->current); - drm_output_release_fb(output, sprite->next); - weston_plane_release(&sprite->plane); - free(sprite); + /* Sprite support is disabled on clone mode for now. */ + if (wl_list_length(&logical_output->output_list) == 1) { + /* Fetches the only output present from the list */ + wl_list_for_each(output, &logical_output->output_list, link); + + wl_list_for_each_safe(sprite, next, &backend->sprite_list, link) { + drmModeSetPlane(backend->drm.fd, + sprite->plane_id, + output->crtc_id, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + drm_output_release_fb(logical_output, sprite->current); + drm_output_release_fb(logical_output, sprite->next); + weston_plane_release(&sprite->plane); + free(sprite); + } } } @@ -2623,7 +2767,8 @@ update_outputs(struct drm_backend *b, struct udev_device *drm_device) { drmModeConnector *connector; drmModeRes *resources; - struct drm_output *output, *next; + struct drm_logical_output *logical_output, *next_logical_output; + struct drm_output *output, *next_output; int x = 0, y = 0; uint32_t connected = 0, disconnects = 0; int i; @@ -2672,13 +2817,17 @@ update_outputs(struct drm_backend *b, struct udev_device *drm_device) disconnects = b->connector_allocator & ~connected; if (disconnects) { - wl_list_for_each_safe(output, next, &b->compositor->output_list, - base.link) { - if (disconnects & (1 << output->connector_id)) { - disconnects &= ~(1 << output->connector_id); - weston_log("connector %d disconnected\n", - output->connector_id); - drm_output_destroy(&output->base); + wl_list_for_each_safe(logical_output, next_logical_output, + &b->compositor->output_list, base.link) { + wl_list_for_each_safe(output, next_output, &logical_output->output_list, link) { + if (disconnects & (1 << output->connector_id)) { + disconnects &= ~(1 << output->connector_id); + weston_log("connector %d disconnected\n", + output->connector_id); + wl_list_remove(&output->link); + if (wl_list_empty(&logical_output->output_list)) + drm_output_destroy(&logical_output->base); + } } } } @@ -2754,31 +2903,34 @@ drm_destroy(struct weston_compositor *ec) static void drm_backend_set_modes(struct drm_backend *backend) { + struct drm_logical_output *logical_output; struct drm_output *output; struct drm_mode *drm_mode; int ret; - wl_list_for_each(output, &backend->compositor->output_list, base.link) { - if (!output->current) { + wl_list_for_each(logical_output, &backend->compositor->output_list, base.link) { + if (!logical_output->current) { /* If something that would cause the output to * switch mode happened while in another vt, we * might not have a current drm_fb. In that case, * schedule a repaint and let drm_output_repaint * handle setting the mode. */ - weston_output_schedule_repaint(&output->base); + weston_output_schedule_repaint(&logical_output->base); continue; } - drm_mode = (struct drm_mode *) output->base.current_mode; - ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, - output->current->fb_id, 0, 0, - &output->connector_id, 1, - &drm_mode->mode_info); - if (ret < 0) { - weston_log( - "failed to set mode %dx%d for output at %d,%d: %m\n", - drm_mode->base.width, drm_mode->base.height, - output->base.x, output->base.y); + drm_mode = (struct drm_mode *)logical_output->base.current_mode; + wl_list_for_each(output, &logical_output->output_list, link) { + ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, + logical_output->current->fb_id, 0, 0, + &output->connector_id, 1, + &drm_mode->mode_info); + if (ret < 0) { + weston_log( + "failed to set mode %dx%d for output at %d,%d: %m\n", + drm_mode->base.width, drm_mode->base.height, + logical_output->base.x, logical_output->base.y); + } } } } @@ -2789,6 +2941,7 @@ session_notify(struct wl_listener *listener, void *data) struct weston_compositor *compositor = data; struct drm_backend *b = (struct drm_backend *)compositor->backend; struct drm_sprite *sprite; + struct drm_logical_output *logical_output; struct drm_output *output; if (compositor->session_active) { @@ -2812,19 +2965,26 @@ session_notify(struct wl_listener *listener, void *data) * back, we schedule a repaint, which will process * pending frame callbacks. */ - wl_list_for_each(output, &compositor->output_list, base.link) { - output->base.repaint_needed = 0; - drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); + wl_list_for_each(logical_output, &compositor->output_list, base.link) { + logical_output->base.repaint_needed = 0; + wl_list_for_each(output, &logical_output->output_list, link) + drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0); } - output = container_of(compositor->output_list.next, - struct drm_output, base.link); + logical_output = container_of(compositor->output_list.next, + struct drm_logical_output, base.link); - wl_list_for_each(sprite, &b->sprite_list, link) - drmModeSetPlane(b->drm.fd, - sprite->plane_id, - output->crtc_id, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0); + /* Sprite support is disabled on clone mode for now. */ + if (wl_list_length(&logical_output->output_list) == 1) { + /* Fetches the only output present from the list */ + wl_list_for_each(output, &logical_output->output_list, link); + + wl_list_for_each(sprite, &b->sprite_list, link) + drmModeSetPlane(b->drm.fd, + sprite->plane_id, + output->crtc_id, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + } }; } @@ -2907,7 +3067,7 @@ planes_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, #ifdef BUILD_VAAPI_RECORDER static void -recorder_destroy(struct drm_output *output) +recorder_destroy(struct drm_logical_output *output) { vaapi_recorder_destroy(output->recorder); output->recorder = NULL; @@ -2921,11 +3081,11 @@ recorder_destroy(struct drm_output *output) static void recorder_frame_notify(struct wl_listener *listener, void *data) { - struct drm_output *output; + struct drm_logical_output *output; struct drm_backend *b; int fd, ret; - output = container_of(listener, struct drm_output, + output = container_of(listener, struct drm_logical_output, recorder_frame_listener); b = (struct drm_backend *)output->base.compositor->backend; @@ -2970,11 +3130,11 @@ recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, void *data) { struct drm_backend *b = data; - struct drm_output *output; + struct drm_logical_output *output; int width, height; output = container_of(b->compositor->output_list.next, - struct drm_output, base.link); + struct drm_logical_output, base.link); if (!output->recorder) { if (output->gbm_format != GBM_FORMAT_XRGB8888) { @@ -3018,7 +3178,7 @@ recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, static void switch_to_gl_renderer(struct drm_backend *b) { - struct drm_output *output; + struct drm_logical_output *output; bool dmabuf_support_inited; if (!b->use_pixman) -- 2.8.2 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel