/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 Intel Corporation
+ * Copyright © 2017, 2018 Collabora, Ltd.
+ * Copyright © 2017, 2018 General Electric Company
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2
#endif
+#ifndef DRM_CLIENT_CAP_ASPECT_RATIO
+#define DRM_CLIENT_CAP_ASPECT_RATIO 4
+#endif
+
#ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8
#endif
#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
#endif
+#define MAX_CLONED_CONNECTORS 4
+
+/**
+ * aspect ratio info taken from the drmModeModeInfo flag bits 19-22,
+ * which should be used to fill the aspect ratio field in weston_mode.
+ */
+#define DRM_MODE_FLAG_PIC_AR_BITS_POS 19
+#ifndef DRM_MODE_FLAG_PIC_AR_MASK
+#define DRM_MODE_FLAG_PIC_AR_MASK (0xF << DRM_MODE_FLAG_PIC_AR_BITS_POS)
+#endif
+
/**
* Represents the values of an enum-type KMS property
*/
*/
int min_width, max_width;
int min_height, max_height;
- int no_addfb2;
struct wl_list plane_list;
int sprites_are_broken;
bool state_invalid;
- /* Connector and CRTC IDs not used by any enabled output. */
- struct wl_array unused_connectors;
+ /* CRTC IDs not used by any enabled output. */
struct wl_array unused_crtcs;
int cursors_are_broken;
bool atomic_modeset;
int use_pixman;
+ bool use_pixman_shadow;
struct udev_input input;
uint32_t pageflip_timeout;
bool shutting_down;
+
+ bool aspect_ratio_supported;
};
struct drm_mode {
uint32_t formats[];
};
-struct drm_output {
- struct weston_output base;
- drmModeConnector *connector;
+struct drm_head {
+ struct weston_head base;
+ struct drm_backend *backend;
- uint32_t crtc_id; /* object ID to pass to DRM functions */
- int pipe; /* index of CRTC in resource array / bitmasks */
+ drmModeConnector *connector;
uint32_t connector_id;
struct drm_edid edid;
/* Holds the properties for the connector */
struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT];
- /* Holds the properties for the CRTC */
- struct drm_property_info props_crtc[WDRM_CRTC__COUNT];
struct backlight *backlight;
+ drmModeModeInfo inherited_mode; /**< Original mode on the connector */
+ uint32_t inherited_crtc_id; /**< Original CRTC assignment */
+};
+
+struct drm_output {
+ struct weston_output base;
+
+ uint32_t crtc_id; /* object ID to pass to DRM functions */
+ int pipe; /* index of CRTC in resource array / bitmasks */
+
+ /* Holds the properties for the CRTC */
+ struct drm_property_info props_crtc[WDRM_CRTC__COUNT];
+
int vblank_pending;
int page_flip_pending;
int atomic_complete_pending;
struct wl_event_source *pageflip_timer;
};
+static const char *const aspect_ratio_as_string[] = {
+ [WESTON_MODE_PIC_AR_NONE] = "",
+ [WESTON_MODE_PIC_AR_4_3] = " 4:3",
+ [WESTON_MODE_PIC_AR_16_9] = " 16:9",
+ [WESTON_MODE_PIC_AR_64_27] = " 64:27",
+ [WESTON_MODE_PIC_AR_256_135] = " 256:135",
+};
+
static struct gl_renderer_interface *gl_renderer;
static const char default_seat[] = "seat0";
}
}
+static inline struct drm_head *
+to_drm_head(struct weston_head *base)
+{
+ return container_of(base, struct drm_head, base);
+}
+
static inline struct drm_output *
to_drm_output(struct weston_output *base)
{
*/
static uint64_t
drm_property_get_value(struct drm_property_info *info,
- drmModeObjectPropertiesPtr props,
+ const drmModeObjectProperties *props,
uint64_t def)
{
unsigned int i;
/**
* Free DRM property information
*
- * Frees all memory associated with a DRM property info array.
+ * Frees all memory associated with a DRM property info array and zeroes
+ * it out, leaving it usable for a further drm_property_info_update() or
+ * drm_property_info_free().
*
* @param info DRM property info array
* @param num_props Number of entries in array to free
for (i = 0; i < num_props; i++)
free(info[i].enum_values);
+
+ memset(info, 0, sizeof(*info) * num_props);
}
static void
return output;
}
- wl_list_for_each(output, &b->compositor->pending_output_list,
- base.link) {
- if (output->crtc_id == crtc_id)
- return output;
- }
-
return NULL;
}
-static struct drm_output *
-drm_output_find_by_connector(struct drm_backend *b, uint32_t connector_id)
+static struct drm_head *
+drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id)
{
- struct drm_output *output;
+ struct weston_head *base;
+ struct drm_head *head;
- wl_list_for_each(output, &b->compositor->output_list, base.link) {
- if (output->connector_id == connector_id)
- return output;
- }
-
- wl_list_for_each(output, &b->compositor->pending_output_list,
- base.link) {
- if (output->connector_id == connector_id)
- return output;
+ wl_list_for_each(base,
+ &backend->compositor->head_list, compositor_link) {
+ head = to_drm_head(base);
+ if (head->connector_id == connector_id)
+ return head;
}
return NULL;
struct drm_mode_create_dumb create_arg;
struct drm_mode_destroy_dumb destroy_arg;
struct drm_mode_map_dumb map_arg;
+ uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
fb = zalloc(sizeof *fb);
if (!fb)
fb->height = height;
fb->fd = b->drm.fd;
- ret = -1;
-
- if (!b->no_addfb2) {
- uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
-
- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(b->drm.fd, width, height,
- fb->format->format,
- handles, pitches, offsets,
- &fb->fb_id, 0);
- if (ret) {
- weston_log("addfb2 failed: %m\n");
- b->no_addfb2 = 1;
- }
- }
+ handles[0] = fb->handle;
+ pitches[0] = fb->stride;
+ offsets[0] = 0;
+ ret = drmModeAddFB2(b->drm.fd, width, height, fb->format->format,
+ handles, pitches, offsets, &fb->fb_id, 0);
if (ret) {
ret = drmModeAddFB(b->drm.fd, width, height,
fb->format->depth, fb->format->bpp,
static struct drm_fb *
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
- uint32_t format, enum drm_fb_type type)
+ bool is_opaque, enum drm_fb_type type)
{
struct drm_fb *fb = gbm_bo_get_user_data(bo);
uint32_t handles[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
fb->height = gbm_bo_get_height(bo);
fb->stride = gbm_bo_get_stride(bo);
fb->handle = gbm_bo_get_handle(bo).u32;
- fb->format = pixel_format_get_info(format);
+ fb->format = pixel_format_get_info(gbm_bo_get_format(bo));
fb->size = fb->stride * fb->height;
fb->fd = backend->drm.fd;
if (!fb->format) {
weston_log("couldn't look up format 0x%lx\n",
- (unsigned long) format);
+ (unsigned long) gbm_bo_get_format(bo));
goto err_free;
}
+ /* We can scanout an ARGB buffer if the surface's opaque region covers
+ * the whole output, but we have to use XRGB as the KMS format code. */
+ if (is_opaque)
+ fb->format = pixel_format_get_opaque_substitute(fb->format);
+
if (backend->min_width > fb->width ||
fb->width > backend->max_width ||
backend->min_height > fb->height ||
goto err_free;
}
- ret = -1;
-
- if (format && !backend->no_addfb2) {
- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(backend->drm.fd, fb->width, fb->height,
- format, handles, pitches, offsets,
- &fb->fb_id, 0);
- if (ret) {
- weston_log("addfb2 failed: %m\n");
- backend->no_addfb2 = 1;
- backend->sprites_are_broken = 1;
- }
- }
+ handles[0] = fb->handle;
+ pitches[0] = fb->stride;
+ offsets[0] = 0;
+ ret = drmModeAddFB2(backend->drm.fd, fb->width, fb->height,
+ fb->format->format, handles, pitches, offsets,
+ &fb->fb_id, 0);
if (ret && fb->format->depth && fb->format->bpp)
ret = drmModeAddFB(backend->drm.fd, fb->width, fb->height,
fb->format->depth, fb->format->bpp,
}
/**
+ * Given a weston_view, fill the drm_plane_state's co-ordinates to display on
+ * a given plane.
+ */
+static void
+drm_plane_state_coords_for_view(struct drm_plane_state *state,
+ struct weston_view *ev)
+{
+ struct drm_output *output = state->output;
+ struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
+ pixman_region32_t dest_rect, src_rect;
+ pixman_box32_t *box, tbox;
+ float sxf1, syf1, sxf2, syf2;
+
+ /* Update the base weston_plane co-ordinates. */
+ box = pixman_region32_extents(&ev->transform.boundingbox);
+ state->plane->base.x = box->x1;
+ state->plane->base.y = box->y1;
+
+ /* First calculate the destination co-ordinates by taking the
+ * area of the view which is visible on this output, performing any
+ * transforms to account for output rotation and scale as necessary. */
+ pixman_region32_init(&dest_rect);
+ pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox,
+ &output->base.region);
+ pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y);
+ box = pixman_region32_extents(&dest_rect);
+ tbox = weston_transformed_rect(output->base.width,
+ output->base.height,
+ output->base.transform,
+ output->base.current_scale,
+ *box);
+ state->dest_x = tbox.x1;
+ state->dest_y = tbox.y1;
+ state->dest_w = tbox.x2 - tbox.x1;
+ state->dest_h = tbox.y2 - tbox.y1;
+ pixman_region32_fini(&dest_rect);
+
+ /* Now calculate the source rectangle, by finding the extents of the
+ * view, and working backwards to source co-ordinates. */
+ pixman_region32_init(&src_rect);
+ pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
+ &output->base.region);
+ box = pixman_region32_extents(&src_rect);
+ weston_view_from_global_float(ev, box->x1, box->y1, &sxf1, &syf1);
+ weston_surface_to_buffer_float(ev->surface, sxf1, syf1, &sxf1, &syf1);
+ weston_view_from_global_float(ev, box->x2, box->y2, &sxf2, &syf2);
+ weston_surface_to_buffer_float(ev->surface, sxf2, syf2, &sxf2, &syf2);
+ pixman_region32_fini(&src_rect);
+
+ /* Buffer transforms may mean that x2 is to the left of x1, and/or that
+ * y2 is above y1. */
+ if (sxf2 < sxf1) {
+ double tmp = sxf1;
+ sxf1 = sxf2;
+ sxf2 = tmp;
+ }
+ if (syf2 < syf1) {
+ double tmp = syf1;
+ syf1 = syf2;
+ syf2 = tmp;
+ }
+
+ /* Shift from S23.8 wl_fixed to U16.16 KMS fixed-point encoding. */
+ state->src_x = wl_fixed_from_double(sxf1) << 8;
+ state->src_y = wl_fixed_from_double(syf1) << 8;
+ state->src_w = wl_fixed_from_double(sxf2 - sxf1) << 8;
+ state->src_h = wl_fixed_from_double(syf2 - syf1) << 8;
+
+ /* Clamp our source co-ordinates to surface bounds; it's possible
+ * for intermediate translations to give us slightly incorrect
+ * co-ordinates if we have, for example, multiple zooming
+ * transformations. View bounding boxes are also explicitly rounded
+ * greedily. */
+ if (state->src_x < 0)
+ state->src_x = 0;
+ if (state->src_y < 0)
+ state->src_y = 0;
+ if (state->src_w > (uint32_t) ((buffer->width << 16) - state->src_x))
+ state->src_w = (buffer->width << 16) - state->src_x;
+ if (state->src_h > (uint32_t) ((buffer->height << 16) - state->src_y))
+ state->src_h = (buffer->height << 16) - state->src_y;
+}
+
+/**
* Return a plane state from a drm_output_state.
*/
static struct drm_plane_state *
(ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
}
-static uint32_t
-drm_output_check_scanout_format(struct drm_output *output,
- struct weston_surface *es, struct gbm_bo *bo)
+static bool
+drm_view_is_opaque(struct weston_view *ev)
{
- uint32_t format;
pixman_region32_t r;
+ bool ret = false;
- format = gbm_bo_get_format(bo);
-
- if (format == GBM_FORMAT_ARGB8888) {
- /* We can scanout an ARGB buffer if the surface's
- * opaque region covers the whole output, but we have
- * to use XRGB as the KMS format code. */
- pixman_region32_init_rect(&r, 0, 0,
- output->base.width,
- output->base.height);
- pixman_region32_subtract(&r, &r, &es->opaque);
-
- if (!pixman_region32_not_empty(&r))
- format = GBM_FORMAT_XRGB8888;
+ pixman_region32_init_rect(&r, 0, 0,
+ ev->surface->width,
+ ev->surface->height);
+ pixman_region32_subtract(&r, &r, &ev->surface->opaque);
- pixman_region32_fini(&r);
- }
+ if (!pixman_region32_not_empty(&r))
+ ret = true;
- if (output->gbm_format == format)
- return format;
+ pixman_region32_fini(&r);
- return 0;
+ return ret;
}
static struct weston_plane *
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
struct gbm_bo *bo;
- uint32_t format;
/* Don't import buffers which span multiple outputs. */
if (ev->output_mask != (1u << output->base.id))
if (!bo)
return NULL;
- format = drm_output_check_scanout_format(output, ev->surface, bo);
- if (format == 0) {
+ state->fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev),
+ BUFFER_CLIENT);
+ if (!state->fb) {
drm_plane_state_put_back(state);
gbm_bo_destroy(bo);
return NULL;
}
- state->fb = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
- if (!state->fb) {
+ /* Can't change formats with just a pageflip */
+ if (state->fb->format->format != output->gbm_format) {
+ /* No need to destroy the GBM BO here, as it's now owned
+ * by the FB. */
drm_plane_state_put_back(state);
- gbm_bo_destroy(bo);
return NULL;
}
return NULL;
}
- ret = drm_fb_get_from_bo(bo, b, output->gbm_format, BUFFER_GBM_SURFACE);
+ /* The renderer always produces an opaque image. */
+ ret = drm_fb_get_from_bo(bo, b, true, BUFFER_GBM_SURFACE);
if (!ret) {
weston_log("failed to get drm_fb for bo\n");
gbm_surface_release_buffer(output->gbm_surface, bo);
{
struct drm_output *output = state->output;
struct weston_compositor *ec = output->base.compositor;
- pixman_region32_t total_damage, previous_damage;
-
- pixman_region32_init(&total_damage);
- pixman_region32_init(&previous_damage);
-
- pixman_region32_copy(&previous_damage, damage);
-
- pixman_region32_union(&total_damage, damage, &output->previous_damage);
- pixman_region32_copy(&output->previous_damage, &previous_damage);
output->current_image ^= 1;
pixman_renderer_output_set_buffer(&output->base,
output->image[output->current_image]);
+ pixman_renderer_output_set_hw_extra_damage(&output->base,
+ &output->previous_damage);
- ec->renderer->repaint_output(&output->base, &total_damage);
+ ec->renderer->repaint_output(&output->base, damage);
- pixman_region32_fini(&total_damage);
- pixman_region32_fini(&previous_damage);
+ pixman_region32_copy(&output->previous_damage, damage);
return drm_fb_ref(output->dumb[output->current_image]);
}
struct drm_output *output = state->output;
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_plane *scanout_plane = output->scanout_plane;
- struct drm_property_info *dpms_prop =
- &output->props_conn[WDRM_CONNECTOR_DPMS];
+ struct drm_property_info *dpms_prop;
struct drm_plane_state *scanout_state;
struct drm_plane_state *ps;
struct drm_plane *p;
struct drm_mode *mode;
+ struct drm_head *head;
+ uint32_t connectors[MAX_CLONED_CONNECTORS];
+ int n_conn = 0;
struct timespec now;
int ret = 0;
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ assert(n_conn < MAX_CLONED_CONNECTORS);
+ connectors[n_conn++] = head->connector_id;
+ }
+
/* If disable_planes is set then assign_planes() wasn't
* called for this render, so we could still have a stale
* cursor plane set up.
*/
if (output->base.disable_planes) {
output->cursor_view = NULL;
- output->cursor_plane->base.x = INT32_MIN;
- output->cursor_plane->base.y = INT32_MIN;
+ if (output->cursor_plane) {
+ output->cursor_plane->base.x = INT32_MIN;
+ output->cursor_plane->base.y = INT32_MIN;
+ }
}
if (state->dpms != WESTON_DPMS_ON) {
}
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, 0, 0, 0,
- &output->connector_id, 0, NULL);
+ NULL, 0, NULL);
if (ret)
weston_log("drmModeSetCrtc failed disabling: %m\n");
ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
scanout_state->fb->fb_id,
0, 0,
- &output->connector_id, 1,
+ connectors, n_conn,
&mode->mode_info);
if (ret) {
weston_log("set mode failed: %m\n");
}
}
- if (dpms_prop->prop_id && state->dpms != output->state_cur->dpms) {
- ret = drmModeConnectorSetProperty(backend->drm.fd,
- output->connector_id,
- dpms_prop->prop_id,
- state->dpms);
- if (ret) {
- weston_log("DRM: DPMS: failed property set for %s\n",
- output->base.name);
+ if (state->dpms != output->state_cur->dpms) {
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ dpms_prop = &head->props_conn[WDRM_CONNECTOR_DPMS];
+ if (dpms_prop->prop_id == 0)
+ continue;
+
+ ret = drmModeConnectorSetProperty(backend->drm.fd,
+ head->connector_id,
+ dpms_prop->prop_id,
+ state->dpms);
+ if (ret) {
+ weston_log("DRM: DPMS: failed property set for %s\n",
+ head->base.name);
+ }
}
}
}
static int
-connector_add_prop(drmModeAtomicReq *req, struct drm_output *output,
+connector_add_prop(drmModeAtomicReq *req, struct drm_head *head,
enum wdrm_connector_property prop, uint64_t val)
{
- struct drm_property_info *info = &output->props_conn[prop];
+ struct drm_property_info *info = &head->props_conn[prop];
int ret;
if (info->prop_id == 0)
return -1;
- ret = drmModeAtomicAddProperty(req, output->connector_id,
+ ret = drmModeAtomicAddProperty(req, head->connector_id,
info->prop_id, val);
return (ret <= 0) ? -1 : 0;
}
struct drm_backend *backend = to_drm_backend(output->base.compositor);
struct drm_plane_state *plane_state;
struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
+ struct drm_head *head;
int ret = 0;
if (state->dpms != output->state_cur->dpms)
ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID,
current_mode->blob_id);
ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 1);
- ret |= connector_add_prop(req, output, WDRM_CONNECTOR_CRTC_ID,
- output->crtc_id);
+
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID,
+ output->crtc_id);
+ }
} else {
ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0);
ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 0);
- ret |= connector_add_prop(req, output, WDRM_CONNECTOR_CRTC_ID,
- 0);
+
+ wl_list_for_each(head, &output->base.head_list, base.output_link)
+ ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, 0);
}
if (ret != 0) {
return -1;
if (b->state_invalid) {
+ struct weston_head *head_base;
+ struct drm_head *head;
uint32_t *unused;
int err;
/* If we need to reset all our state (e.g. because we've
* just started, or just been VT-switched in), explicitly
* disable all the CRTCs and connectors we aren't using. */
- wl_array_for_each(unused, &b->unused_connectors) {
- struct drm_property_info infos[WDRM_CONNECTOR__COUNT];
+ wl_list_for_each(head_base,
+ &b->compositor->head_list, compositor_link) {
struct drm_property_info *info;
- drmModeObjectProperties *props;
-
- memset(infos, 0, sizeof(infos));
- props = drmModeObjectGetProperties(b->drm.fd,
- *unused,
- DRM_MODE_OBJECT_CONNECTOR);
- if (!props) {
- ret = -1;
+ if (weston_head_is_enabled(head_base))
continue;
- }
- drm_property_info_populate(b, connector_props, infos,
- WDRM_CONNECTOR__COUNT,
- props);
- drmModeFreeObjectProperties(props);
+ head = to_drm_head(head_base);
- info = &infos[WDRM_CONNECTOR_CRTC_ID];
- err = drmModeAtomicAddProperty(req, *unused,
+ info = &head->props_conn[WDRM_CONNECTOR_CRTC_ID];
+ err = drmModeAtomicAddProperty(req, head->connector_id,
info->prop_id, 0);
if (err <= 0)
ret = -1;
- info = &infos[WDRM_CONNECTOR_DPMS];
+ info = &head->props_conn[WDRM_CONNECTOR_DPMS];
if (info->prop_id > 0)
- err = drmModeAtomicAddProperty(req, *unused,
+ err = drmModeAtomicAddProperty(req, head->connector_id,
info->prop_id,
DRM_MODE_DPMS_OFF);
if (err <= 0)
ret = -1;
-
- drm_property_info_free(infos, WDRM_CONNECTOR__COUNT);
}
wl_array_for_each(unused, &b->unused_crtcs) {
}
#endif
-static uint32_t
-drm_output_check_plane_format(struct drm_plane *p,
- struct weston_view *ev, struct gbm_bo *bo)
-{
- uint32_t i, format;
-
- format = gbm_bo_get_format(bo);
-
- if (format == GBM_FORMAT_ARGB8888) {
- pixman_region32_t r;
-
- pixman_region32_init_rect(&r, 0, 0,
- ev->surface->width,
- ev->surface->height);
- pixman_region32_subtract(&r, &r, &ev->surface->opaque);
-
- if (!pixman_region32_not_empty(&r))
- format = GBM_FORMAT_XRGB8888;
-
- pixman_region32_fini(&r);
- }
-
- for (i = 0; i < p->count_formats; i++)
- if (p->formats[i] == format)
- return format;
-
- return 0;
-}
-
static struct weston_plane *
drm_output_prepare_overlay_view(struct drm_output_state *output_state,
struct weston_view *ev)
{
struct drm_output *output = output_state->output;
struct weston_compositor *ec = output->base.compositor;
- struct drm_backend *b = to_drm_backend(ec);
struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
+ struct drm_backend *b = to_drm_backend(ec);
struct wl_resource *buffer_resource;
struct drm_plane *p;
struct drm_plane_state *state = NULL;
struct linux_dmabuf_buffer *dmabuf;
- struct gbm_bo *bo;
- pixman_region32_t dest_rect, src_rect;
- pixman_box32_t *box, tbox;
- uint32_t format;
- wl_fixed_t sx1, sy1, sx2, sy2;
+ struct gbm_bo *bo = NULL;
+ unsigned int i;
if (b->sprites_are_broken)
return NULL;
if (dmabuf->attributes.n_planes != 1 ||
dmabuf->attributes.offset[0] != 0 ||
dmabuf->attributes.flags)
- return NULL;
+ goto err;
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
GBM_BO_USE_SCANOUT);
if (!bo)
goto err;
- format = drm_output_check_plane_format(p, ev, bo);
- if (format == 0)
+ state->fb = drm_fb_get_from_bo(bo, b, drm_view_is_opaque(ev),
+ BUFFER_CLIENT);
+ if (!state->fb)
goto err;
+ bo = NULL;
- state->fb = drm_fb_get_from_bo(bo, b, format, BUFFER_CLIENT);
- if (!state->fb)
+ /* Check whether the format is supported */
+ for (i = 0; i < p->count_formats; i++)
+ if (p->formats[i] == state->fb->format->format)
+ break;
+ if (i == p->count_formats)
goto err;
drm_fb_set_buffer(state->fb, ev->surface->buffer_ref.buffer);
state->output = output;
-
- box = pixman_region32_extents(&ev->transform.boundingbox);
- p->base.x = box->x1;
- p->base.y = box->y1;
-
- /*
- * Calculate the source & dest rects properly based on actual
- * position (note the caller has called weston_view_update_transform()
- * for us already).
- */
- pixman_region32_init(&dest_rect);
- pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox,
- &output->base.region);
- pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y);
- box = pixman_region32_extents(&dest_rect);
- tbox = weston_transformed_rect(output->base.width,
- output->base.height,
- output->base.transform,
- output->base.current_scale,
- *box);
- state->dest_x = tbox.x1;
- state->dest_y = tbox.y1;
- state->dest_w = tbox.x2 - tbox.x1;
- state->dest_h = tbox.y2 - tbox.y1;
- pixman_region32_fini(&dest_rect);
-
- pixman_region32_init(&src_rect);
- pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
- &output->base.region);
- box = pixman_region32_extents(&src_rect);
-
- weston_view_from_global_fixed(ev,
- wl_fixed_from_int(box->x1),
- wl_fixed_from_int(box->y1),
- &sx1, &sy1);
- weston_view_from_global_fixed(ev,
- wl_fixed_from_int(box->x2),
- wl_fixed_from_int(box->y2),
- &sx2, &sy2);
-
- if (sx1 < 0)
- sx1 = 0;
- if (sy1 < 0)
- sy1 = 0;
- if (sx2 > wl_fixed_from_int(ev->surface->width))
- sx2 = wl_fixed_from_int(ev->surface->width);
- if (sy2 > wl_fixed_from_int(ev->surface->height))
- sy2 = wl_fixed_from_int(ev->surface->height);
-
- tbox.x1 = sx1;
- tbox.y1 = sy1;
- tbox.x2 = sx2;
- tbox.y2 = sy2;
-
- tbox = weston_transformed_rect(wl_fixed_from_int(ev->surface->width),
- wl_fixed_from_int(ev->surface->height),
- viewport->buffer.transform,
- viewport->buffer.scale,
- tbox);
-
- state->src_x = tbox.x1 << 8;
- state->src_y = tbox.y1 << 8;
- state->src_w = (tbox.x2 - tbox.x1) << 8;
- state->src_h = (tbox.y2 - tbox.y1) << 8;
- pixman_region32_fini(&src_rect);
+ drm_plane_state_coords_for_view(state, ev);
return &p->base;
err:
- drm_plane_state_put_back(state);
+ /* Destroy the BO as we've allocated it, but it won't yet
+ * be deallocated by the state. */
if (bo)
gbm_bo_destroy(bo);
+ drm_plane_state_put_back(state);
return NULL;
}
drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
}
+/*
+ * Get the aspect-ratio from drmModeModeInfo mode flags.
+ *
+ * @param drm_mode_flags- flags from drmModeModeInfo structure.
+ * @returns aspect-ratio as encoded in enum 'weston_mode_aspect_ratio'.
+ */
+static enum weston_mode_aspect_ratio
+drm_to_weston_mode_aspect_ratio(uint32_t drm_mode_flags)
+{
+ return (drm_mode_flags & DRM_MODE_FLAG_PIC_AR_MASK) >>
+ DRM_MODE_FLAG_PIC_AR_BITS_POS;
+}
+
+static const char *
+aspect_ratio_to_string(enum weston_mode_aspect_ratio ratio)
+{
+ if (ratio < 0 || ratio >= ARRAY_LENGTH(aspect_ratio_as_string) ||
+ !aspect_ratio_as_string[ratio])
+ return " (unknown aspect ratio)";
+
+ return aspect_ratio_as_string[ratio];
+}
+
static void
drm_assign_planes(struct weston_output *output_base, void *repaint_data)
{
struct drm_output *output = to_drm_output(output_base);
struct drm_output_state *state;
struct drm_plane_state *plane_state;
- struct weston_view *ev, *next;
- pixman_region32_t overlap, surface_overlap;
+ struct weston_view *ev;
+ pixman_region32_t surface_overlap, renderer_region;
struct weston_plane *primary, *next_plane;
- bool picked_scanout = false;
assert(!output->state_last);
state = drm_output_state_duplicate(output->state_cur,
* the client buffer can be used directly for the sprite surface
* as we do for flipping full screen surfaces.
*/
- pixman_region32_init(&overlap);
+ pixman_region32_init(&renderer_region);
primary = &output_base->compositor->primary_plane;
- wl_list_for_each_safe(ev, next, &output_base->compositor->view_list, link) {
+ wl_list_for_each(ev, &output_base->compositor->view_list, link) {
struct weston_surface *es = ev->surface;
/* Test whether this buffer can ever go into a plane:
es->keep_buffer = false;
pixman_region32_init(&surface_overlap);
- pixman_region32_intersect(&surface_overlap, &overlap,
+ pixman_region32_intersect(&surface_overlap, &renderer_region,
&ev->transform.boundingbox);
next_plane = NULL;
- if (pixman_region32_not_empty(&surface_overlap) || picked_scanout)
+ if (pixman_region32_not_empty(&surface_overlap))
next_plane = primary;
if (next_plane == NULL)
next_plane = drm_output_prepare_cursor_view(state, ev);
- /* If a higher-stacked view already got assigned to scanout, it's incorrect to
- * assign a subsequent (lower-stacked) view to scanout.
- */
- if (next_plane == NULL) {
+ if (next_plane == NULL)
next_plane = drm_output_prepare_scanout_view(state, ev);
- if (next_plane)
- picked_scanout = true;
- }
if (next_plane == NULL)
next_plane = drm_output_prepare_overlay_view(state, ev);
weston_view_move_to_plane(ev, next_plane);
if (next_plane == primary)
- pixman_region32_union(&overlap, &overlap,
+ pixman_region32_union(&renderer_region,
+ &renderer_region,
&ev->transform.boundingbox);
if (next_plane == primary ||
pixman_region32_fini(&surface_overlap);
}
- pixman_region32_fini(&overlap);
+ pixman_region32_fini(&renderer_region);
/* We rely on output->cursor_view being both an accurate reflection of
* the cursor plane's state, but also being maintained across repaints
static struct drm_mode *
choose_mode (struct drm_output *output, struct weston_mode *target_mode)
{
- struct drm_mode *tmp_mode = NULL, *mode;
+ struct drm_mode *tmp_mode = NULL, *mode_fall_back = NULL, *mode;
+ enum weston_mode_aspect_ratio src_aspect = WESTON_MODE_PIC_AR_NONE;
+ enum weston_mode_aspect_ratio target_aspect = WESTON_MODE_PIC_AR_NONE;
+ struct drm_backend *b;
+ b = to_drm_backend(output->base.compositor);
+ target_aspect = target_mode->aspect_ratio;
+ src_aspect = output->base.current_mode->aspect_ratio;
if (output->base.current_mode->width == target_mode->width &&
output->base.current_mode->height == target_mode->height &&
(output->base.current_mode->refresh == target_mode->refresh ||
- target_mode->refresh == 0))
- return to_drm_mode(output->base.current_mode);
+ target_mode->refresh == 0)) {
+ if (!b->aspect_ratio_supported || src_aspect == target_aspect)
+ return to_drm_mode(output->base.current_mode);
+ }
wl_list_for_each(mode, &output->base.mode_list, base.link) {
+
+ src_aspect = mode->base.aspect_ratio;
if (mode->mode_info.hdisplay == target_mode->width &&
mode->mode_info.vdisplay == target_mode->height) {
if (mode->base.refresh == target_mode->refresh ||
target_mode->refresh == 0) {
- return mode;
- } else if (!tmp_mode)
+ if (!b->aspect_ratio_supported ||
+ src_aspect == target_aspect)
+ return mode;
+ else if (!mode_fall_back)
+ mode_fall_back = mode;
+ } else if (!tmp_mode) {
tmp_mode = mode;
+ }
}
}
+ if (mode_fall_back)
+ return mode_fall_back;
+
return tmp_mode;
}
weston_log("DRM: %s atomic modesetting\n",
b->atomic_modeset ? "supports" : "does not support");
+ ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
+ b->aspect_ratio_supported = (ret == 0);
+ weston_log("DRM: %s picture aspect ratio\n",
+ b->aspect_ratio_supported ? "supports" : "does not support");
+
return 0;
}
/* On some platforms, primary/cursor planes can roam
* between different CRTCs, so make sure we don't claim the
* same plane for two outputs. */
- wl_list_for_each(tmp, &b->compositor->pending_output_list,
- base.link) {
- if (tmp->cursor_plane == plane ||
- tmp->scanout_plane == plane) {
- found_elsewhere = true;
- break;
- }
- }
wl_list_for_each(tmp, &b->compositor->output_list,
base.link) {
if (tmp->cursor_plane == plane ||
drm_plane_destroy(plane);
}
+static uint32_t
+drm_refresh_rate_mHz(const drmModeModeInfo *info)
+{
+ uint64_t refresh;
+
+ /* Calculate higher precision (mHz) refresh rate */
+ refresh = (info->clock * 1000000LL / info->htotal +
+ info->vtotal / 2) / info->vtotal;
+
+ if (info->flags & DRM_MODE_FLAG_INTERLACE)
+ refresh *= 2;
+ if (info->flags & DRM_MODE_FLAG_DBLSCAN)
+ refresh /= 2;
+ if (info->vscan > 1)
+ refresh /= info->vscan;
+
+ return refresh;
+}
+
/**
* Add a mode to output's mode list
*
drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
{
struct drm_mode *mode;
- uint64_t refresh;
mode = malloc(sizeof *mode);
if (mode == NULL)
mode->base.width = info->hdisplay;
mode->base.height = info->vdisplay;
- /* Calculate higher precision (mHz) refresh rate */
- refresh = (info->clock * 1000000LL / info->htotal +
- info->vtotal / 2) / info->vtotal;
-
- if (info->flags & DRM_MODE_FLAG_INTERLACE)
- refresh *= 2;
- if (info->flags & DRM_MODE_FLAG_DBLSCAN)
- refresh /= 2;
- if (info->vscan > 1)
- refresh /= info->vscan;
-
- mode->base.refresh = refresh;
+ mode->base.refresh = drm_refresh_rate_mHz(info);
mode->mode_info = *info;
mode->blob_id = 0;
if (info->type & DRM_MODE_TYPE_PREFERRED)
mode->base.flags |= WL_OUTPUT_MODE_PREFERRED;
+ mode->base.aspect_ratio = drm_to_weston_mode_aspect_ratio(info->flags);
+
wl_list_insert(output->base.mode_list.prev, &mode->base.link);
return mode;
free(mode);
}
+/** Destroy a list of drm_modes
+ *
+ * @param backend The backend for releasing mode property blobs.
+ * @param mode_list The list linked by drm_mode::base.link.
+ */
+static void
+drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list)
+{
+ struct drm_mode *mode, *next;
+
+ wl_list_for_each_safe(mode, next, mode_list, base.link)
+ drm_output_destroy_mode(backend, mode);
+}
+
static int
drm_subpixel_to_wayland(int drm_value)
{
/* returns a value between 0-255 range, where higher is brighter */
static uint32_t
-drm_get_backlight(struct drm_output *output)
+drm_get_backlight(struct drm_head *head)
{
long brightness, max_brightness, norm;
- brightness = backlight_get_brightness(output->backlight);
- max_brightness = backlight_get_max_brightness(output->backlight);
+ brightness = backlight_get_brightness(head->backlight);
+ max_brightness = backlight_get_max_brightness(head->backlight);
/* convert it on a scale of 0 to 255 */
norm = (brightness * 255)/(max_brightness);
drm_set_backlight(struct weston_output *output_base, uint32_t value)
{
struct drm_output *output = to_drm_output(output_base);
+ struct drm_head *head;
long max_brightness, new_brightness;
- if (!output->backlight)
- return;
-
if (value > 255)
return;
- max_brightness = backlight_get_max_brightness(output->backlight);
+ wl_list_for_each(head, &output->base.head_list, base.output_link) {
+ if (!head->backlight)
+ return;
- /* get denormalized value */
- new_brightness = (value * max_brightness) / 255;
+ max_brightness = backlight_get_max_brightness(head->backlight);
- backlight_set_brightness(output->backlight, new_brightness);
-}
+ /* get denormalized value */
+ new_brightness = (value * max_brightness) / 255;
+
+ backlight_set_brightness(head->backlight, new_brightness);
+ }
+}
+
+static void
+drm_output_init_backlight(struct drm_output *output)
+{
+ struct weston_head *base;
+ struct drm_head *head;
+
+ output->base.set_backlight = NULL;
+
+ wl_list_for_each(base, &output->base.head_list, output_link) {
+ head = to_drm_head(base);
+
+ if (head->backlight) {
+ weston_log("Initialized backlight for head '%s', device %s\n",
+ head->base.name, head->backlight->path);
+
+ if (!output->base.set_backlight) {
+ output->base.set_backlight = drm_set_backlight;
+ output->base.backlight_current =
+ drm_get_backlight(head);
+ }
+ }
+ }
+
+ if (!output->base.set_backlight) {
+ weston_log("No backlight control for output '%s'\n",
+ output->base.name);
+ }
+}
/**
* Power output on or off
return name;
}
-static int
-find_crtc_for_connector(struct drm_backend *b,
- drmModeRes *resources, drmModeConnector *connector)
-{
- drmModeEncoder *encoder;
- int i, j;
- int ret = -1;
-
- for (j = 0; j < connector->count_encoders; j++) {
- uint32_t possible_crtcs, encoder_id, crtc_id;
-
- encoder = drmModeGetEncoder(b->drm.fd, connector->encoders[j]);
- if (encoder == NULL) {
- weston_log("Failed to get encoder.\n");
- continue;
- }
- encoder_id = encoder->encoder_id;
- possible_crtcs = encoder->possible_crtcs;
- crtc_id = encoder->crtc_id;
- drmModeFreeEncoder(encoder);
-
- for (i = 0; i < resources->count_crtcs; i++) {
- if (!(possible_crtcs & (1 << i)))
- continue;
-
- if (drm_output_find_by_crtc(b, resources->crtcs[i]))
- continue;
-
- /* Try to preserve the existing
- * CRTC -> encoder -> connector routing; it makes
- * initialisation faster, and also since we have a
- * very dumb picking algorithm, may preserve a better
- * choice. */
- if (!connector->encoder_id ||
- (encoder_id == connector->encoder_id &&
- crtc_id == resources->crtcs[i]))
- return i;
-
- ret = i;
- }
- }
-
- return ret;
-}
-
static void drm_output_fini_cursor_egl(struct drm_output *output)
{
unsigned int i;
goto err;
output->gbm_cursor_fb[i] =
- drm_fb_get_from_bo(bo, b, GBM_FORMAT_ARGB8888,
- BUFFER_CURSOR);
+ drm_fb_get_from_bo(bo, b, false, BUFFER_CURSOR);
if (!output->gbm_cursor_fb[i]) {
gbm_bo_destroy(bo);
goto err;
uint32_t format = output->gbm_format;
uint32_t pixman_format;
unsigned int i;
+ uint32_t flags = 0;
switch (format) {
case GBM_FORMAT_XRGB8888:
goto err;
}
- if (pixman_renderer_output_create(&output->base) < 0)
- goto err;
+ if (b->use_pixman_shadow)
+ flags |= PIXMAN_RENDERER_OUTPUT_USE_SHADOW;
+
+ if (pixman_renderer_output_create(&output->base, flags) < 0)
+ goto err;
+
+ weston_log("DRM: output %s %s shadow framebuffer.\n", output->base.name,
+ b->use_pixman_shadow ? "uses" : "does not use");
pixman_region32_init_rect(&output->previous_damage,
output->base.x, output->base.y, output->base.width, output->base.height);
/** Parse monitor make, model and serial from EDID
*
- * \param b The backend instance.
- * \param output The output whose \c drm_edid to fill in.
+ * \param head The head whose \c drm_edid to fill in.
* \param props The DRM connector properties to get the EDID from.
* \param make[out] The monitor make (PNP ID).
* \param model[out] The monitor model (name).
* Each of \c *make, \c *model and \c *serial_number are set only if the
* information is found in the EDID. The pointers they are set to must not
* be free()'d explicitly, instead they get implicitly freed when the
- * \c drm_output is destroyed.
+ * \c drm_head is destroyed.
*/
static void
-find_and_parse_output_edid(struct drm_backend *b, struct drm_output *output,
+find_and_parse_output_edid(struct drm_head *head,
drmModeObjectPropertiesPtr props,
const char **make,
const char **model,
int rc;
blob_id =
- drm_property_get_value(&output->props_conn[WDRM_CONNECTOR_EDID],
+ drm_property_get_value(&head->props_conn[WDRM_CONNECTOR_EDID],
props, 0);
if (!blob_id)
return;
- edid_blob = drmModeGetPropertyBlob(b->drm.fd, blob_id);
+ edid_blob = drmModeGetPropertyBlob(head->backend->drm.fd, blob_id);
if (!edid_blob)
return;
- rc = edid_parse(&output->edid,
+ rc = edid_parse(&head->edid,
edid_blob->data,
edid_blob->length);
if (!rc) {
- weston_log("EDID data '%s', '%s', '%s'\n",
- output->edid.pnp_id,
- output->edid.monitor_name,
- output->edid.serial_number);
- if (output->edid.pnp_id[0] != '\0')
- *make = output->edid.pnp_id;
- if (output->edid.monitor_name[0] != '\0')
- *model = output->edid.monitor_name;
- if (output->edid.serial_number[0] != '\0')
- *serial_number = output->edid.serial_number;
+ if (head->edid.pnp_id[0] != '\0')
+ *make = head->edid.pnp_id;
+ if (head->edid.monitor_name[0] != '\0')
+ *model = head->edid.monitor_name;
+ if (head->edid.serial_number[0] != '\0')
+ *serial_number = head->edid.serial_number;
}
drmModeFreePropertyBlob(edid_blob);
}
char vsync[16];
float fclock;
+ memset(mode, 0, sizeof *mode);
+
mode->type = DRM_MODE_TYPE_USERDEF;
mode->hskew = 0;
mode->vscan = 0;
return -1;
mode->clock = fclock * 1000;
- if (strcmp(hsync, "+hsync") == 0)
+ if (strcasecmp(hsync, "+hsync") == 0)
mode->flags |= DRM_MODE_FLAG_PHSYNC;
- else if (strcmp(hsync, "-hsync") == 0)
+ else if (strcasecmp(hsync, "-hsync") == 0)
mode->flags |= DRM_MODE_FLAG_NHSYNC;
else
return -1;
- if (strcmp(vsync, "+vsync") == 0)
+ if (strcasecmp(vsync, "+vsync") == 0)
mode->flags |= DRM_MODE_FLAG_PVSYNC;
- else if (strcmp(vsync, "-vsync") == 0)
+ else if (strcasecmp(vsync, "-vsync") == 0)
mode->flags |= DRM_MODE_FLAG_NVSYNC;
else
return -1;
}
static int
+drm_output_attach_head(struct weston_output *output_base,
+ struct weston_head *head_base)
+{
+ struct drm_backend *b = to_drm_backend(output_base->compositor);
+
+ if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS)
+ return -1;
+
+ if (!output_base->enabled)
+ return 0;
+
+ /* XXX: ensure the configuration will work.
+ * This is actually impossible without major infrastructure
+ * work. */
+
+ /* Need to go through modeset to add connectors. */
+ /* XXX: Ideally we'd do this per-output, not globally. */
+ /* XXX: Doing it globally, what guarantees another output's update
+ * will not clear the flag before this output is updated?
+ */
+ b->state_invalid = true;
+
+ weston_output_schedule_repaint(output_base);
+
+ return 0;
+}
+
+static void
+drm_output_detach_head(struct weston_output *output_base,
+ struct weston_head *head_base)
+{
+ struct drm_backend *b = to_drm_backend(output_base->compositor);
+
+ if (!output_base->enabled)
+ return;
+
+ /* Need to go through modeset to drop connectors that should no longer
+ * be driven. */
+ /* XXX: Ideally we'd do this per-output, not globally. */
+ b->state_invalid = true;
+
+ weston_output_schedule_repaint(output_base);
+}
+
+static int
parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
{
int ret = 0;
return ret;
}
+static uint32_t
+u32distance(uint32_t a, uint32_t b)
+{
+ if (a < b)
+ return b - a;
+ else
+ return a - b;
+}
+
+/** Choose equivalent mode
+ *
+ * If the two modes are not equivalent, return NULL.
+ * Otherwise return the mode that is more likely to work in place of both.
+ *
+ * None of the fuzzy matching criteria in this function have any justification.
+ *
+ * typedef struct _drmModeModeInfo {
+ * uint32_t clock;
+ * uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew;
+ * uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;
+ *
+ * uint32_t vrefresh;
+ *
+ * uint32_t flags;
+ * uint32_t type;
+ * char name[DRM_DISPLAY_MODE_LEN];
+ * } drmModeModeInfo, *drmModeModeInfoPtr;
+ */
+static const drmModeModeInfo *
+drm_mode_pick_equivalent(const drmModeModeInfo *a, const drmModeModeInfo *b)
+{
+ uint32_t refresh_a, refresh_b;
+
+ if (a->hdisplay != b->hdisplay || a->vdisplay != b->vdisplay)
+ return NULL;
+
+ if (a->flags != b->flags)
+ return NULL;
+
+ /* kHz */
+ if (u32distance(a->clock, b->clock) > 500)
+ return NULL;
+
+ refresh_a = drm_refresh_rate_mHz(a);
+ refresh_b = drm_refresh_rate_mHz(b);
+ if (u32distance(refresh_a, refresh_b) > 50)
+ return NULL;
+
+ if ((a->type ^ b->type) & DRM_MODE_TYPE_PREFERRED) {
+ if (a->type & DRM_MODE_TYPE_PREFERRED)
+ return a;
+ else
+ return b;
+ }
+
+ return a;
+}
+
+/* If the given mode info is not already in the list, add it.
+ * If it is in the list, either keep the existing or replace it,
+ * depending on which one is "better".
+ */
+static int
+drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
+{
+ struct weston_mode *base;
+ struct drm_mode *mode;
+ struct drm_backend *backend;
+ const drmModeModeInfo *chosen = NULL;
+
+ assert(info);
+
+ wl_list_for_each(base, &output->base.mode_list, link) {
+ mode = to_drm_mode(base);
+ chosen = drm_mode_pick_equivalent(&mode->mode_info, info);
+ if (chosen)
+ break;
+ }
+
+ if (chosen == info) {
+ backend = to_drm_backend(output->base.compositor);
+ drm_output_destroy_mode(backend, mode);
+ chosen = NULL;
+ }
+
+ if (!chosen) {
+ mode = drm_output_add_mode(output, info);
+ if (!mode)
+ return -1;
+ }
+ /* else { the equivalent mode is already in the list } */
+
+ return 0;
+}
+
+/** Rewrite the output's mode list
+ *
+ * @param output The output.
+ * @return 0 on success, -1 on failure.
+ *
+ * Destroy all existing modes in the list, and reconstruct a new list from
+ * scratch, based on the currently attached heads.
+ *
+ * On failure the output's mode list may contain some modes.
+ */
+static int
+drm_output_update_modelist_from_heads(struct drm_output *output)
+{
+ struct drm_backend *backend = to_drm_backend(output->base.compositor);
+ struct weston_head *head_base;
+ struct drm_head *head;
+ int i;
+ int ret;
+
+ assert(!output->base.enabled);
+
+ drm_mode_list_destroy(backend, &output->base.mode_list);
+
+ wl_list_for_each(head_base, &output->base.head_list, output_link) {
+ head = to_drm_head(head_base);
+ for (i = 0; i < head->connector->count_modes; i++) {
+ ret = drm_output_try_add_mode(output,
+ &head->connector->modes[i]);
+ if (ret < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
/**
* Choose suitable mode for an output
*
struct drm_mode *preferred = NULL;
struct drm_mode *current = NULL;
struct drm_mode *configured = NULL;
+ struct drm_mode *config_fall_back = NULL;
struct drm_mode *best = NULL;
struct drm_mode *drm_mode;
drmModeModeInfo drm_modeline;
int32_t width = 0;
int32_t height = 0;
uint32_t refresh = 0;
+ uint32_t aspect_width = 0;
+ uint32_t aspect_height = 0;
+ enum weston_mode_aspect_ratio aspect_ratio = WESTON_MODE_PIC_AR_NONE;
int n;
if (mode == WESTON_DRM_BACKEND_OUTPUT_PREFERRED && modeline) {
- n = sscanf(modeline, "%dx%d@%d", &width, &height, &refresh);
- if (n != 2 && n != 3) {
+ n = sscanf(modeline, "%dx%d@%d %u:%u", &width, &height,
+ &refresh, &aspect_width, &aspect_height);
+ if (backend->aspect_ratio_supported && n == 5) {
+ if (aspect_width == 4 && aspect_height == 3)
+ aspect_ratio = WESTON_MODE_PIC_AR_4_3;
+ else if (aspect_width == 16 && aspect_height == 9)
+ aspect_ratio = WESTON_MODE_PIC_AR_16_9;
+ else if (aspect_width == 64 && aspect_height == 27)
+ aspect_ratio = WESTON_MODE_PIC_AR_64_27;
+ else if (aspect_width == 256 && aspect_height == 135)
+ aspect_ratio = WESTON_MODE_PIC_AR_256_135;
+ else
+ weston_log("Invalid modeline \"%s\" for output %s\n",
+ modeline, output->base.name);
+ }
+ if (n != 2 && n != 3 && n != 5) {
width = -1;
if (parse_modeline(modeline, &drm_modeline) == 0) {
wl_list_for_each_reverse(drm_mode, &output->base.mode_list, base.link) {
if (width == drm_mode->base.width &&
height == drm_mode->base.height &&
- (refresh == 0 || refresh == drm_mode->mode_info.vrefresh))
- configured = drm_mode;
+ (refresh == 0 || refresh == drm_mode->mode_info.vrefresh)) {
+ if (!backend->aspect_ratio_supported ||
+ aspect_ratio == drm_mode->base.aspect_ratio)
+ configured = drm_mode;
+ else
+ config_fall_back = drm_mode;
+ }
if (memcmp(current_mode, &drm_mode->mode_info,
sizeof *current_mode) == 0)
if (configured)
return configured;
+ if (config_fall_back)
+ return config_fall_back;
+
if (preferred)
return preferred;
}
static int
-connector_get_current_mode(drmModeConnector *connector, int drm_fd,
- drmModeModeInfo *mode)
+drm_head_read_current_setup(struct drm_head *head, struct drm_backend *backend)
{
+ int drm_fd = backend->drm.fd;
drmModeEncoder *encoder;
drmModeCrtc *crtc;
/* Get the current mode on the crtc that's currently driving
* this connector. */
- encoder = drmModeGetEncoder(drm_fd, connector->encoder_id);
- memset(mode, 0, sizeof *mode);
+ encoder = drmModeGetEncoder(drm_fd, head->connector->encoder_id);
if (encoder != NULL) {
+ head->inherited_crtc_id = encoder->crtc_id;
+
crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id);
drmModeFreeEncoder(encoder);
+
if (crtc == NULL)
return -1;
if (crtc->mode_valid)
- *mode = crtc->mode;
+ head->inherited_mode = crtc->mode;
drmModeFreeCrtc(crtc);
}
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
+ struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
struct drm_mode *current;
- drmModeModeInfo crtc_mode;
- if (connector_get_current_mode(output->connector, b->drm.fd, &crtc_mode) < 0)
+ if (drm_output_update_modelist_from_heads(output) < 0)
return -1;
- current = drm_output_choose_initial_mode(b, output, mode, modeline, &crtc_mode);
+ current = drm_output_choose_initial_mode(b, output, mode, modeline,
+ &head->inherited_mode);
if (!current)
return -1;
return 0;
}
+static uint32_t
+drm_head_get_possible_crtcs_mask(struct drm_head *head)
+{
+ uint32_t possible_crtcs = 0;
+ drmModeEncoder *encoder;
+ int i;
+
+ for (i = 0; i < head->connector->count_encoders; i++) {
+ encoder = drmModeGetEncoder(head->backend->drm.fd,
+ head->connector->encoders[i]);
+ if (!encoder)
+ continue;
+
+ possible_crtcs |= encoder->possible_crtcs;
+ drmModeFreeEncoder(encoder);
+ }
+
+ return possible_crtcs;
+}
+
+static int
+drm_crtc_get_index(drmModeRes *resources, uint32_t crtc_id)
+{
+ int i;
+
+ for (i = 0; i < resources->count_crtcs; i++) {
+ if (resources->crtcs[i] == crtc_id)
+ return i;
+ }
+
+ assert(0 && "unknown crtc id");
+ return -1;
+}
+
+/** Pick a CRTC that might be able to drive all attached connectors
+ *
+ * @param output The output whose attached heads to include.
+ * @param resources The DRM KMS resources.
+ * @return CRTC index, or -1 on failure or not found.
+ */
+static int
+drm_output_pick_crtc(struct drm_output *output, drmModeRes *resources)
+{
+ struct drm_backend *backend;
+ struct weston_head *base;
+ struct drm_head *head;
+ uint32_t possible_crtcs = 0xffffffff;
+ int existing_crtc[32];
+ unsigned j, n = 0;
+ uint32_t crtc_id;
+ int best_crtc_index = -1;
+ int fallback_crtc_index = -1;
+ int i;
+ bool match;
+
+ backend = to_drm_backend(output->base.compositor);
+
+ /* This algorithm ignores drmModeEncoder::possible_clones restriction,
+ * because it is more often set wrong than not in the kernel. */
+
+ /* Accumulate a mask of possible crtcs and find existing routings. */
+ wl_list_for_each(base, &output->base.head_list, output_link) {
+ head = to_drm_head(base);
+
+ possible_crtcs &= drm_head_get_possible_crtcs_mask(head);
+
+ crtc_id = head->inherited_crtc_id;
+ if (crtc_id > 0 && n < ARRAY_LENGTH(existing_crtc))
+ existing_crtc[n++] = drm_crtc_get_index(resources,
+ crtc_id);
+ }
+
+ /* Find a crtc that could drive each connector individually at least,
+ * and prefer existing routings. */
+ for (i = 0; i < resources->count_crtcs; i++) {
+ crtc_id = resources->crtcs[i];
+
+ /* Could the crtc not drive each connector? */
+ if (!(possible_crtcs & (1 << i)))
+ continue;
+
+ /* Is the crtc already in use? */
+ if (drm_output_find_by_crtc(backend, crtc_id))
+ continue;
+
+ /* Try to preserve the existing CRTC -> connector routing;
+ * it makes initialisation faster, and also since we have a
+ * very dumb picking algorithm, may preserve a better
+ * choice. */
+ for (j = 0; j < n; j++) {
+ if (existing_crtc[j] == i)
+ return i;
+ }
+
+ /* Check if any other head had existing routing to this CRTC.
+ * If they did, this is not the best CRTC as it might be needed
+ * for another output we haven't enabled yet. */
+ match = false;
+ wl_list_for_each(base, &backend->compositor->head_list,
+ compositor_link) {
+ head = to_drm_head(base);
+
+ if (head->base.output == &output->base)
+ continue;
+
+ if (weston_head_is_enabled(&head->base))
+ continue;
+
+ if (head->inherited_crtc_id == crtc_id) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ best_crtc_index = i;
+
+ fallback_crtc_index = i;
+ }
+
+ if (best_crtc_index != -1)
+ return best_crtc_index;
+
+ if (fallback_crtc_index != -1)
+ return fallback_crtc_index;
+
+ /* Likely possible_crtcs was empty due to asking for clones,
+ * but since the DRM documentation says the kernel lies, let's
+ * pick one crtc anyway. Trial and error is the only way to
+ * be sure if something doesn't work. */
+
+ /* First pick any existing assignment. */
+ for (j = 0; j < n; j++) {
+ crtc_id = resources->crtcs[existing_crtc[j]];
+ if (!drm_output_find_by_crtc(backend, crtc_id))
+ return existing_crtc[j];
+ }
+
+ /* Otherwise pick any available crtc. */
+ for (i = 0; i < resources->count_crtcs; i++) {
+ crtc_id = resources->crtcs[i];
+
+ if (!drm_output_find_by_crtc(backend, crtc_id))
+ return i;
+ }
+
+ return -1;
+}
+
/** Allocate a CRTC for the output
*
* @param output The output with no allocated CRTC.
* @param resources DRM KMS resources.
- * @param connector The DRM KMS connector data.
* @return 0 on success, -1 on failure.
*
- * Finds a free CRTC that can drive the given connector, reserves the CRTC
+ * Finds a free CRTC that might drive the attached connectors, reserves the CRTC
* for the output, and loads the CRTC properties.
*
* Populates the cursor and scanout planes.
* On failure, the output remains without a CRTC.
*/
static int
-drm_output_init_crtc(struct drm_output *output,
- drmModeRes *resources, drmModeConnector *connector)
+drm_output_init_crtc(struct drm_output *output, drmModeRes *resources)
{
struct drm_backend *b = to_drm_backend(output->base.compositor);
drmModeObjectPropertiesPtr props;
assert(output->crtc_id == 0);
- i = find_crtc_for_connector(b, resources, connector);
+ i = drm_output_pick_crtc(output, resources);
if (i < 0) {
- weston_log("No usable crtc/encoder pair for connector.\n");
+ weston_log("Output '%s': No available CRTCs.\n",
+ output->base.name);
return -1;
}
drm_output_find_special_plane(b, output,
WDRM_PLANE_TYPE_CURSOR);
+ wl_array_remove_uint32(&b->unused_crtcs, output->crtc_id);
+
return 0;
err_crtc:
drm_output_fini_crtc(struct drm_output *output)
{
struct drm_backend *b = to_drm_backend(output->base.compositor);
+ uint32_t *unused;
if (!b->universal_planes && !b->shutting_down) {
/* With universal planes, the 'special' planes are allocated at
}
drm_property_info_free(output->props_crtc, WDRM_CRTC__COUNT);
+
+ assert(output->crtc_id != 0);
+
+ unused = wl_array_add(&b->unused_crtcs, sizeof(*unused));
+ *unused = output->crtc_id;
+
+ /* Force resetting unused CRTCs */
+ b->state_invalid = true;
+
output->crtc_id = 0;
output->cursor_plane = NULL;
output->scanout_plane = NULL;
}
+static void
+drm_output_print_modes(struct drm_output *output)
+{
+ struct weston_mode *m;
+ struct drm_mode *dm;
+ const char *aspect_ratio;
+
+ wl_list_for_each(m, &output->base.mode_list, link) {
+ dm = to_drm_mode(m);
+
+ aspect_ratio = aspect_ratio_to_string(m->aspect_ratio);
+ weston_log_continue(STAMP_SPACE "%dx%d@%.1f%s%s%s, %.1f MHz\n",
+ m->width, m->height, m->refresh / 1000.0,
+ aspect_ratio,
+ m->flags & WL_OUTPUT_MODE_PREFERRED ?
+ ", preferred" : "",
+ m->flags & WL_OUTPUT_MODE_CURRENT ?
+ ", current" : "",
+ dm->mode_info.clock / 1000.0);
+ }
+}
+
static int
drm_output_enable(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
- struct weston_mode *m;
+ drmModeRes *resources;
+ int ret;
+
+ resources = drmModeGetResources(b->drm.fd);
+ if (!resources) {
+ weston_log("drmModeGetResources failed\n");
+ return -1;
+ }
+ ret = drm_output_init_crtc(output, resources);
+ drmModeFreeResources(resources);
+ if (ret < 0)
+ return -1;
+
+ if (drm_output_init_gamma_size(output) < 0)
+ goto err;
if (b->pageflip_timeout)
drm_output_pageflip_timer_create(output);
goto err;
}
- 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");
- }
+ drm_output_init_backlight(output);
output->base.start_repaint_loop = drm_output_start_repaint_loop;
output->base.repaint = drm_output_repaint;
&output->scanout_plane->base,
&b->compositor->primary_plane);
- wl_array_remove_uint32(&b->unused_connectors, output->connector_id);
- wl_array_remove_uint32(&b->unused_crtcs, output->crtc_id);
-
- weston_log("Output %s, (connector %d, crtc %d)\n",
- output->base.name, output->connector_id, output->crtc_id);
- wl_list_for_each(m, &output->base.mode_list, link)
- weston_log_continue(STAMP_SPACE "mode %dx%d@%.1f%s%s%s\n",
- m->width, m->height, m->refresh / 1000.0,
- m->flags & WL_OUTPUT_MODE_PREFERRED ?
- ", preferred" : "",
- m->flags & WL_OUTPUT_MODE_CURRENT ?
- ", current" : "",
- output->connector->count_modes == 0 ?
- ", built-in" : "");
+ weston_log("Output %s (crtc %d) video modes:\n",
+ output->base.name, output->crtc_id);
+ drm_output_print_modes(output);
return 0;
err:
+ drm_output_fini_crtc(output);
+
return -1;
}
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
- uint32_t *unused;
if (b->use_pixman)
drm_output_fini_pixman(output);
}
}
- unused = wl_array_add(&b->unused_connectors, sizeof(*unused));
- *unused = output->connector_id;
- unused = wl_array_add(&b->unused_crtcs, sizeof(*unused));
- *unused = output->crtc_id;
-
- /* Force programming unused connectors and crtcs. */
- b->state_invalid = true;
+ drm_output_fini_crtc(output);
}
static void
+drm_head_destroy(struct drm_head *head);
+
+static void
drm_output_destroy(struct weston_output *base)
{
struct drm_output *output = to_drm_output(base);
struct drm_backend *b = to_drm_backend(base->compositor);
- struct drm_mode *drm_mode, *next;
if (output->page_flip_pending || output->vblank_pending ||
output->atomic_complete_pending) {
if (output->base.enabled)
drm_output_deinit(&output->base);
- wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
- base.link)
- drm_output_destroy_mode(b, drm_mode);
+ drm_mode_list_destroy(b, &output->base.mode_list);
if (output->pageflip_timer)
wl_event_source_remove(output->pageflip_timer);
weston_output_release(&output->base);
- drm_output_fini_crtc(output);
-
- drm_property_info_free(output->props_conn, WDRM_CONNECTOR__COUNT);
- drmModeFreeConnector(output->connector);
-
- if (output->backlight)
- backlight_destroy(output->backlight);
-
assert(!output->state_last);
drm_output_state_free(output->state_cur);
/**
* Update the list of unused connectors and CRTCs
*
- * This keeps the unused_connectors and unused_crtcs arrays up to date.
+ * This keeps the unused_crtc arrays up to date.
*
* @param b Weston backend structure
* @param resources DRM resources for this device
{
int i;
- wl_array_release(&b->unused_connectors);
- wl_array_init(&b->unused_connectors);
-
- for (i = 0; i < resources->count_connectors; i++) {
- struct drm_output *output;
- uint32_t *connector_id;
-
- output = drm_output_find_by_connector(b, resources->connectors[i]);
- if (output && output->base.enabled)
- continue;
-
- connector_id = wl_array_add(&b->unused_connectors,
- sizeof(*connector_id));
- *connector_id = resources->connectors[i];
- }
-
wl_array_release(&b->unused_crtcs);
wl_array_init(&b->unused_crtcs);
}
}
-/**
- * Create a Weston output structure
+/** Replace connector data and monitor information
*
- * Given a DRM connector, create a matching drm_output structure and add it
- * to Weston's output list. It also takes ownership of the connector, which
- * is released when output is destroyed.
+ * @param head The head to update.
+ * @param connector The connector data to be owned by the head, must match
+ * the head's connector ID.
+ * @return 0 on success, -1 on failure.
*
- * @param b Weston backend structure
- * @param resources DRM resources for this device
- * @param connector DRM connector to use for this new output
- * @param drm_device udev device pointer
- * @returns 0 on success, or -1 on failure
+ * Takes ownership of @c connector on success, not on failure.
+ *
+ * May schedule a heads changed call.
*/
static int
-create_output_for_connector(struct drm_backend *b,
- drmModeRes *resources,
- drmModeConnector *connector,
- struct udev_device *drm_device)
+drm_head_assign_connector_info(struct drm_head *head,
+ drmModeConnector *connector)
{
- struct drm_output *output;
- drmModeObjectPropertiesPtr props;
- struct drm_mode *drm_mode;
- char *name;
+ drmModeObjectProperties *props;
const char *make = "unknown";
const char *model = "unknown";
const char *serial_number = "unknown";
- int i;
- output = zalloc(sizeof *output);
- if (output == NULL)
- goto err_init;
+ assert(connector);
+ assert(head->connector_id == connector->connector_id);
+
+ props = drmModeObjectGetProperties(head->backend->drm.fd,
+ head->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!props) {
+ weston_log("Error: failed to get connector '%s' properties\n",
+ head->base.name);
+ return -1;
+ }
+
+ if (head->connector)
+ drmModeFreeConnector(head->connector);
+ head->connector = connector;
+
+ drm_property_info_populate(head->backend, connector_props,
+ head->props_conn,
+ WDRM_CONNECTOR__COUNT, props);
+ find_and_parse_output_edid(head, props, &make, &model, &serial_number);
+ weston_head_set_monitor_strings(&head->base, make, model, serial_number);
+ weston_head_set_subpixel(&head->base,
+ drm_subpixel_to_wayland(head->connector->subpixel));
+
+ weston_head_set_physical_size(&head->base, head->connector->mmWidth,
+ head->connector->mmHeight);
+
+ drmModeFreeObjectProperties(props);
+
+ /* Unknown connection status is assumed disconnected. */
+ weston_head_set_connection_status(&head->base,
+ head->connector->connection == DRM_MODE_CONNECTED);
+
+ return 0;
+}
+
+static void
+drm_head_log_info(struct drm_head *head, const char *msg)
+{
+ if (head->base.connected) {
+ weston_log("DRM: head '%s' %s, connector %d is connected, "
+ "EDID make '%s', model '%s', serial '%s'\n",
+ head->base.name, msg, head->connector_id,
+ head->base.make, head->base.model,
+ head->base.serial_number ?: "");
+ } else {
+ weston_log("DRM: head '%s' %s, connector %d is disconnected.\n",
+ head->base.name, msg, head->connector_id);
+ }
+}
+
+/** Update connector and monitor information
+ *
+ * @param head The head to update.
+ *
+ * Re-reads the DRM property lists for the connector and updates monitor
+ * information and connection status. This may schedule a heads changed call
+ * to the user.
+ */
+static void
+drm_head_update_info(struct drm_head *head)
+{
+ drmModeConnector *connector;
+
+ connector = drmModeGetConnector(head->backend->drm.fd,
+ head->connector_id);
+ if (!connector) {
+ weston_log("DRM: getting connector info for '%s' failed.\n",
+ head->base.name);
+ return;
+ }
+
+ if (drm_head_assign_connector_info(head, connector) < 0)
+ drmModeFreeConnector(connector);
- output->connector = connector;
- output->connector_id = connector->connector_id;
+ if (head->base.device_changed)
+ drm_head_log_info(head, "updated");
+}
+
+/**
+ * Create a Weston head for a connector
+ *
+ * Given a DRM connector, create a matching drm_head structure and add it
+ * to Weston's head list.
+ *
+ * @param b Weston backend structure
+ * @param connector_id DRM connector ID for the head
+ * @param drm_device udev device pointer
+ * @returns The new head, or NULL on failure.
+ */
+static struct drm_head *
+drm_head_create(struct drm_backend *backend, uint32_t connector_id,
+ struct udev_device *drm_device)
+{
+ struct drm_head *head;
+ drmModeConnector *connector;
+ char *name;
- output->backlight = backlight_init(drm_device,
- connector->connector_type);
+ head = zalloc(sizeof *head);
+ if (!head)
+ return NULL;
+
+ connector = drmModeGetConnector(backend->drm.fd, connector_id);
+ if (!connector)
+ goto err_alloc;
name = make_connector_name(connector);
- weston_output_init(&output->base, b->compositor, name);
+ if (!name)
+ goto err_alloc;
+
+ weston_head_init(&head->base, name);
free(name);
- output->base.enable = drm_output_enable;
- output->base.destroy = drm_output_destroy;
- output->base.disable = drm_output_disable;
+ head->connector_id = connector_id;
+ head->backend = backend;
- output->destroy_pending = 0;
- output->disable_pending = 0;
+ head->backlight = backlight_init(drm_device, connector->connector_type);
- if (drm_output_init_crtc(output, resources, connector) < 0)
- goto err_output;
+ if (drm_head_assign_connector_info(head, connector) < 0)
+ goto err_init;
- props = drmModeObjectGetProperties(b->drm.fd, connector->connector_id,
- DRM_MODE_OBJECT_CONNECTOR);
- if (!props) {
- weston_log("failed to get connector properties\n");
- goto err_output;
+ if (head->connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
+ head->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+ weston_head_set_internal(&head->base);
+
+ if (drm_head_read_current_setup(head, backend) < 0) {
+ weston_log("Failed to retrieve current mode from connector %d.\n",
+ head->connector_id);
+ /* Not fatal. */
}
- drm_property_info_populate(b, connector_props, output->props_conn,
- WDRM_CONNECTOR__COUNT, props);
- find_and_parse_output_edid(b, output, props,
- &make, &model, &serial_number);
- output->base.make = (char *)make;
- output->base.model = (char *)model;
- output->base.serial_number = (char *)serial_number;
- output->base.subpixel = drm_subpixel_to_wayland(output->connector->subpixel);
- drmModeFreeObjectProperties(props);
+ weston_compositor_add_head(backend->compositor, &head->base);
+ drm_head_log_info(head, "found");
- if (output->connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
- output->connector->connector_type == DRM_MODE_CONNECTOR_eDP)
- output->base.connection_internal = true;
+ return head;
- if (drm_output_init_gamma_size(output) < 0)
- goto err_output;
+err_init:
+ weston_head_release(&head->base);
- output->state_cur = drm_output_state_alloc(output, NULL);
+err_alloc:
+ if (connector)
+ drmModeFreeConnector(connector);
- output->base.mm_width = output->connector->mmWidth;
- output->base.mm_height = output->connector->mmHeight;
+ free(head);
- for (i = 0; i < output->connector->count_modes; i++) {
- drm_mode = drm_output_add_mode(output, &output->connector->modes[i]);
- if (!drm_mode) {
- weston_log("failed to add mode\n");
- goto err_output;
- }
- }
+ return NULL;
+}
- weston_compositor_add_pending_output(&output->base, b->compositor);
+static void
+drm_head_destroy(struct drm_head *head)
+{
+ weston_head_release(&head->base);
- return 0;
+ drm_property_info_free(head->props_conn, WDRM_CONNECTOR__COUNT);
+ drmModeFreeConnector(head->connector);
-err_output:
- drm_output_destroy(&output->base);
- return -1;
- /* no fallthrough! */
+ if (head->backlight)
+ backlight_destroy(head->backlight);
-err_init:
- drmModeFreeConnector(connector);
- return -1;
+ free(head);
+}
+
+/**
+ * Create a Weston output structure
+ *
+ * Create an "empty" drm_output. This is the implementation of
+ * weston_backend::create_output.
+ *
+ * Creating an output is usually followed by drm_output_attach_head()
+ * and drm_output_enable() to make use of it.
+ *
+ * @param compositor The compositor instance.
+ * @param name Name for the new output.
+ * @returns The output, or NULL on failure.
+ */
+static struct weston_output *
+drm_output_create(struct weston_compositor *compositor, const char *name)
+{
+ struct drm_backend *b = to_drm_backend(compositor);
+ struct drm_output *output;
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return NULL;
+
+ weston_output_init(&output->base, compositor, name);
+
+ output->base.enable = drm_output_enable;
+ output->base.destroy = drm_output_destroy;
+ output->base.disable = drm_output_disable;
+ output->base.attach_head = drm_output_attach_head;
+ output->base.detach_head = drm_output_detach_head;
+
+ output->destroy_pending = 0;
+ output->disable_pending = 0;
+
+ output->state_cur = drm_output_state_alloc(output, NULL);
+
+ weston_compositor_add_pending_output(&output->base, b->compositor);
+
+ return &output->base;
}
static int
-create_outputs(struct drm_backend *b, struct udev_device *drm_device)
+drm_backend_create_heads(struct drm_backend *b, struct udev_device *drm_device)
{
- drmModeConnector *connector;
+ struct drm_head *head;
drmModeRes *resources;
int i;
b->max_height = resources->max_height;
for (i = 0; i < resources->count_connectors; i++) {
- int ret;
-
- connector = drmModeGetConnector(b->drm.fd,
- resources->connectors[i]);
- if (connector == NULL)
- continue;
+ uint32_t connector_id = resources->connectors[i];
- if (connector->connection == DRM_MODE_CONNECTED) {
- ret = create_output_for_connector(b, resources,
- connector, drm_device);
- if (ret < 0)
- weston_log("failed to create new connector\n");
- } else {
- drmModeFreeConnector(connector);
+ head = drm_head_create(b, connector_id, drm_device);
+ if (!head) {
+ weston_log("DRM: failed to create head for connector %d.\n",
+ connector_id);
}
}
drm_backend_update_unused_outputs(b, resources);
- if (wl_list_empty(&b->compositor->output_list) &&
- wl_list_empty(&b->compositor->pending_output_list))
- weston_log("No currently active connector found.\n");
-
drmModeFreeResources(resources);
return 0;
}
static void
-update_outputs(struct drm_backend *b, struct udev_device *drm_device)
+drm_backend_update_heads(struct drm_backend *b, struct udev_device *drm_device)
{
- drmModeConnector *connector;
drmModeRes *resources;
- struct drm_output *output, *next;
- uint32_t *connected;
+ struct weston_head *base, *next;
+ struct drm_head *head;
int i;
resources = drmModeGetResources(b->drm.fd);
return;
}
- connected = calloc(resources->count_connectors, sizeof(uint32_t));
- if (!connected) {
- drmModeFreeResources(resources);
- return;
- }
-
- /* collect new connects */
+ /* collect new connectors that have appeared, e.g. MST */
for (i = 0; i < resources->count_connectors; i++) {
uint32_t connector_id = resources->connectors[i];
- connector = drmModeGetConnector(b->drm.fd, connector_id);
- if (connector == NULL)
- continue;
-
- if (connector->connection != DRM_MODE_CONNECTED) {
- drmModeFreeConnector(connector);
- continue;
- }
-
- connected[i] = connector_id;
-
- if (drm_output_find_by_connector(b, connector_id)) {
- drmModeFreeConnector(connector);
- continue;
+ head = drm_head_find_by_connector(b, connector_id);
+ if (head) {
+ drm_head_update_info(head);
+ } else {
+ head = drm_head_create(b, connector_id, drm_device);
+ if (!head)
+ weston_log("DRM: failed to create head for hot-added connector %d.\n",
+ connector_id);
}
-
- create_output_for_connector(b, resources,
- connector, drm_device);
- weston_log("connector %d connected\n", connector_id);
}
- wl_list_for_each_safe(output, next, &b->compositor->output_list,
- base.link) {
- bool disconnected = true;
-
- for (i = 0; i < resources->count_connectors; i++) {
- if (connected[i] == output->connector_id) {
- disconnected = false;
- break;
- }
- }
+ /* Remove connectors that have disappeared. */
+ wl_list_for_each_safe(base, next,
+ &b->compositor->head_list, compositor_link) {
+ bool removed = true;
- if (!disconnected)
- continue;
-
- weston_log("connector %d disconnected\n", output->connector_id);
- drm_output_destroy(&output->base);
- }
-
- wl_list_for_each_safe(output, next, &b->compositor->pending_output_list,
- base.link) {
- bool disconnected = true;
+ head = to_drm_head(base);
for (i = 0; i < resources->count_connectors; i++) {
- if (connected[i] == output->connector_id) {
- disconnected = false;
+ if (resources->connectors[i] == head->connector_id) {
+ removed = false;
break;
}
}
- if (!disconnected)
+ if (!removed)
continue;
- weston_log("connector %d disconnected\n", output->connector_id);
- drm_output_destroy(&output->base);
+ weston_log("DRM: head '%s' (connector %d) disappeared.\n",
+ head->base.name, head->connector_id);
+ drm_head_destroy(head);
}
drm_backend_update_unused_outputs(b, resources);
- free(connected);
drmModeFreeResources(resources);
}
event = udev_monitor_receive_device(b->udev_monitor);
if (udev_event_is_hotplug(b, event))
- update_outputs(b, event);
+ drm_backend_update_heads(b, event);
udev_device_unref(event);
drm_destroy(struct weston_compositor *ec)
{
struct drm_backend *b = to_drm_backend(ec);
+ struct weston_head *base, *next;
udev_input_destroy(&b->input);
weston_compositor_shutdown(ec);
+ wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
+ drm_head_destroy(to_drm_head(base));
+
if (b->gbm)
gbm_device_destroy(b->gbm);
+ udev_monitor_unref(b->udev_monitor);
udev_unref(b->udev);
weston_launcher_destroy(ec->launcher);
wl_array_release(&b->unused_crtcs);
- wl_array_release(&b->unused_connectors);
close(b->drm.fd);
+ free(b->drm.filename);
free(b);
}
struct udev_device *drm_device;
struct wl_event_loop *loop;
const char *seat_id = default_seat;
+ const char *session_seat;
int ret;
+ session_seat = getenv("XDG_SEAT");
+ if (session_seat)
+ seat_id = session_seat;
+
+ if (config->seat_id)
+ seat_id = config->seat_id;
+
weston_log("initializing drm backend\n");
b = zalloc(sizeof *b);
b->state_invalid = true;
b->drm.fd = -1;
wl_array_init(&b->unused_crtcs);
- wl_array_init(&b->unused_connectors);
/*
* KMS support for hardware planes cannot properly synchronize
b->compositor = compositor;
b->use_pixman = config->use_pixman;
b->pageflip_timeout = config->pageflip_timeout;
+ b->use_pixman_shadow = config->use_pixman_shadow;
compositor->backend = &b->base;
if (parse_gbm_format(config->gbm_format, GBM_FORMAT_XRGB8888, &b->gbm_format) < 0)
goto err_compositor;
- if (config->seat_id)
- seat_id = config->seat_id;
-
/* Check if we run drm-backend using weston-launch */
compositor->launcher = weston_launcher_connect(compositor, config->tty,
seat_id, true);
b->base.repaint_begin = drm_repaint_begin;
b->base.repaint_flush = drm_repaint_flush;
b->base.repaint_cancel = drm_repaint_cancel;
+ b->base.create_output = drm_output_create;
weston_setup_vt_switch_bindings(compositor);
goto err_sprite;
}
- if (create_outputs(b, drm_device) < 0) {
- weston_log("failed to create output for %s\n", b->drm.filename);
+ if (drm_backend_create_heads(b, drm_device) < 0) {
+ weston_log("Failed to create heads for %s\n", b->drm.filename);
goto err_udev_input;
}
static void
config_init_to_defaults(struct weston_drm_backend_config *config)
{
+ config->use_pixman_shadow = true;
}
WL_EXPORT int