* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#define _GNU_SOURCE
#include <errno.h>
#include <stdlib.h>
+#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
void *map;
};
+struct drm_edid {
+ char eisa_id[13];
+ char monitor_name[13];
+ char pnp_id[5];
+ char serial_number[13];
+};
+
struct drm_output {
struct weston_output base;
- char *name;
uint32_t crtc_id;
int pipe;
uint32_t connector_id;
drmModeCrtcPtr original_crtc;
+ struct drm_edid edid;
int vblank_pending;
int page_flip_pending;
memset(&map_arg, 0, sizeof(map_arg));
map_arg.handle = fb->handle;
- drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg);
-
+ ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg);
if (ret)
goto err_add_fb;
}
static void
+drm_output_set_gamma(struct weston_output *output_base,
+ uint16_t size, uint16_t *r, uint16_t *g, uint16_t *b)
+{
+ int rc;
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *compositor = (struct drm_compositor *) output->base.compositor;
+
+ /* check */
+ if (output_base->gamma_size != size)
+ return;
+ if (!output->original_crtc)
+ return;
+
+ rc = drmModeCrtcSetGamma(compositor->drm.fd,
+ output->crtc_id,
+ size, r, g, b);
+ if (rc)
+ weston_log("set gamma failed: %m\n");
+}
+
+static void
drm_output_repaint(struct weston_output *output_base,
pixman_region32_t *damage)
{
}
static void
+drm_output_start_repaint_loop(struct weston_output *output_base)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *compositor = (struct drm_compositor *)
+ output_base->compositor;
+ uint32_t fb_id;
+
+ if (output->current)
+ fb_id = output->current->fb_id;
+ else
+ fb_id = output->original_crtc->buffer_id;
+
+ if (drmModePageFlip(compositor->drm.fd, output->crtc_id, fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
+ weston_log("queueing pageflip failed: %m\n");
+ return;
+ }
+}
+
+static void
vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
void *data)
{
struct drm_output *output = (struct drm_output *) data;
uint32_t msecs;
- output->page_flip_pending = 0;
+ /* 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;
+ }
- drm_output_release_fb(output, output->current);
- output->current = output->next;
- output->next = NULL;
+ output->page_flip_pending = 0;
if (!output->vblank_pending) {
msecs = sec * 1000 + usec / 1000;
if (format == GBM_FORMAT_ARGB8888) {
pixman_region32_t r;
- pixman_region32_init(&r);
- pixman_region32_subtract(&r, &es->transform.boundingbox,
- &es->transform.opaque);
+ pixman_region32_init_rect(&r, 0, 0,
+ es->geometry.width,
+ es->geometry.height);
+ pixman_region32_subtract(&r, &r, &es->opaque);
if (!pixman_region32_not_empty(&r))
format = GBM_FORMAT_XRGB8888;
{
struct drm_compositor *c =
(struct drm_compositor *) output->compositor;
- struct drm_output *drm_output = (struct drm_output *) output;
- struct drm_sprite *s;
struct weston_surface *es, *next;
pixman_region32_t overlap, surface_overlap;
struct weston_plane *primary, *next_plane;
- /* Reset the opaque region of the planes */
- pixman_region32_fini(&drm_output->cursor_plane.opaque);
- pixman_region32_init(&drm_output->cursor_plane.opaque);
- pixman_region32_fini(&drm_output->fb_plane.opaque);
- pixman_region32_init(&drm_output->fb_plane.opaque);
-
- wl_list_for_each (s, &c->sprite_list, link) {
- if (!drm_sprite_crtc_supported(output, s->possible_crtcs))
- continue;
-
- pixman_region32_fini(&s->plane.opaque);
- pixman_region32_init(&s->plane.opaque);
- }
-
/*
* Find a surface for each sprite in the output using some heuristics:
* 1) size
weston_output_destroy(&output->base);
wl_list_remove(&output->base.link);
- free(output->name);
free(output);
}
{
ec->gbm = gbm_create_device(ec->drm.fd);
+ if (!ec->gbm)
+ return -1;
+
if (gl_renderer_create(&ec->base, ec->gbm, gl_renderer_opaque_attribs,
NULL) < 0) {
gbm_device_destroy(ec->gbm);
}
}
+static void
+edid_parse_string(const uint8_t *data, char text[])
+{
+ int i;
+ int replaced = 0;
+
+ /* this is always 12 bytes, but we can't guarantee it's null
+ * terminated or not junk. */
+ strncpy(text, (const char *) data, 12);
+
+ /* remove insane chars */
+ for (i = 0; text[i] != '\0'; i++) {
+ if (text[i] == '\n' ||
+ text[i] == '\r') {
+ text[i] = '\0';
+ break;
+ }
+ }
+
+ /* ensure string is printable */
+ for (i = 0; text[i] != '\0'; i++) {
+ if (!isprint(text[i])) {
+ text[i] = '-';
+ replaced++;
+ }
+ }
+
+ /* if the string is random junk, ignore the string */
+ if (replaced > 4)
+ text[0] = '\0';
+}
+
+#define EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe
+#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc
+#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff
+#define EDID_OFFSET_DATA_BLOCKS 0x36
+#define EDID_OFFSET_LAST_BLOCK 0x6c
+#define EDID_OFFSET_PNPID 0x08
+#define EDID_OFFSET_SERIAL 0x0c
+
+static int
+edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length)
+{
+ int i;
+ uint32_t serial_number;
+
+ /* check header */
+ if (length < 128)
+ return -1;
+ if (data[0] != 0x00 || data[1] != 0xff)
+ return -1;
+
+ /* decode the PNP ID from three 5 bit words packed into 2 bytes
+ * /--08--\/--09--\
+ * 7654321076543210
+ * |\---/\---/\---/
+ * R C1 C2 C3 */
+ edid->pnp_id[0] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1;
+ edid->pnp_id[1] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x3) * 8) + ((data[EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1;
+ edid->pnp_id[2] = 'A' + (data[EDID_OFFSET_PNPID + 1] & 0x1f) - 1;
+ edid->pnp_id[3] = '\0';
+
+ /* maybe there isn't a ASCII serial number descriptor, so use this instead */
+ serial_number = (uint32_t) data[EDID_OFFSET_SERIAL + 0];
+ serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 1] * 0x100;
+ serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 2] * 0x10000;
+ serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 3] * 0x1000000;
+ if (serial_number > 0)
+ sprintf(edid->serial_number, "%lu", (unsigned long) serial_number);
+
+ /* parse EDID data */
+ for (i = EDID_OFFSET_DATA_BLOCKS;
+ i <= EDID_OFFSET_LAST_BLOCK;
+ i += 18) {
+ /* ignore pixel clock data */
+ if (data[i] != 0)
+ continue;
+ if (data[i+2] != 0)
+ continue;
+
+ /* any useful blocks? */
+ if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME) {
+ edid_parse_string(&data[i+5],
+ edid->monitor_name);
+ } else if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) {
+ edid_parse_string(&data[i+5],
+ edid->serial_number);
+ } else if (data[i+3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) {
+ edid_parse_string(&data[i+5],
+ edid->eisa_id);
+ }
+ }
+ return 0;
+}
+
+static void
+find_and_parse_output_edid(struct drm_compositor *ec,
+ struct drm_output *output,
+ drmModeConnector *connector)
+{
+ drmModePropertyBlobPtr edid_blob = NULL;
+ drmModePropertyPtr property;
+ int i;
+ int rc;
+
+ for (i = 0; i < connector->count_props && !edid_blob; i++) {
+ property = drmModeGetProperty(ec->drm.fd, connector->props[i]);
+ if (!property)
+ continue;
+ if ((property->flags & DRM_MODE_PROP_BLOB) &&
+ !strcmp(property->name, "EDID")) {
+ edid_blob = drmModeGetPropertyBlob(ec->drm.fd,
+ connector->prop_values[i]);
+ }
+ drmModeFreeProperty(property);
+ }
+ if (!edid_blob)
+ return;
+
+ rc = edid_parse(&output->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')
+ output->base.make = output->edid.pnp_id;
+ if (output->edid.monitor_name[0] != '\0')
+ output->base.model = output->edid.monitor_name;
+ if (output->edid.serial_number[0] != '\0')
+ output->base.serial_number = output->edid.serial_number;
+ }
+ drmModeFreePropertyBlob(edid_blob);
+}
+
static int
create_output_for_connector(struct drm_compositor *ec,
drmModeRes *resources,
output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel);
output->base.make = "unknown";
output->base.model = "unknown";
+ output->base.serial_number = "unknown";
wl_list_init(&output->base.mode_list);
if (connector->connector_type < ARRAY_LENGTH(connector_type_names))
else
type_name = "UNKNOWN";
snprintf(name, 32, "%s%d", type_name, connector->connector_type_id);
- output->name = strdup(name);
+ output->base.name = strdup(name);
output->crtc_id = resources->crtcs[i];
output->pipe = i;
configured = NULL;
wl_list_for_each(temp, &configured_output_list, link) {
- if (strcmp(temp->name, output->name) == 0) {
+ if (strcmp(temp->name, output->base.name) == 0) {
if (temp->mode)
weston_log("%s mode \"%s\" in config\n",
temp->name, temp->mode);
output->base.current = ¤t->base;
if (output->base.current == NULL) {
- weston_log("no available modes for %s\n", output->name);
+ weston_log("no available modes for %s\n", output->base.name);
goto err_free;
}
wl_list_insert(ec->base.output_list.prev, &output->base.link);
+ find_and_parse_output_edid(ec, output, connector);
+
output->base.origin = output->base.current;
+ 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;
+
weston_plane_init(&output->cursor_plane, 0, 0);
weston_plane_init(&output->fb_plane, 0, 0);
+ weston_compositor_stack_plane(&ec->base, &output->cursor_plane, NULL);
+ weston_compositor_stack_plane(&ec->base, &output->fb_plane,
+ &ec->base.primary_plane);
+
weston_log("Output %s, (connector %d, crtc %d)\n",
- output->name, output->connector_id, output->crtc_id);
+ output->base.name, output->connector_id, output->crtc_id);
wl_list_for_each(m, &output->base.mode_list, link)
weston_log_continue(" mode %dx%d@%.1f%s%s%s\n",
m->width, m->height, m->refresh / 1000.0,
drmModeFreeCrtc(output->original_crtc);
ec->crtc_allocator &= ~(1 << output->crtc_id);
ec->connector_allocator &= ~(1 << output->connector_id);
- free(output->name);
free(output);
return -1;
plane->count_formats * sizeof(plane->formats[0]));
drmModeFreePlane(plane);
weston_plane_init(&sprite->plane, 0, 0);
+ weston_compositor_stack_plane(&ec->base, &sprite->plane,
+ &ec->base.primary_plane);
wl_list_insert(&ec->sprite_list, &sprite->link);
}
int ret;
wl_list_for_each(output, &compositor->base.output_list, base.link) {
+ if (!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);
+ continue;
+ }
+
drm_mode = (struct drm_mode *) output->base.current;
ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id,
output->current->fb_id, 0, 0,
compositor->focus = 0;
ec->prev_state = compositor->state;
- compositor->state = WESTON_COMPOSITOR_SLEEPING;
+ weston_compositor_offscreen(compositor);
/* If we have a repaint scheduled (either from a
* pending pageflip or the idle handler), make sure we
* cancel that so we don't try to pageflip when we're
- * vt switched away. The SLEEPING state will prevent
+ * vt switched away. The OFFSCREEN state will prevent
* further attemps at repainting. When we switch
* back, we schedule a repaint, which will process
* pending frame callbacks. */
static struct weston_compositor *
drm_compositor_create(struct wl_display *display,
int connector, const char *seat, int tty, int pixman,
- int argc, char *argv[], const char *config_file)
+ int *argc, char *argv[], const char *config_file)
{
struct drm_compositor *ec;
struct udev_device *drm_device;
if (weston_compositor_init(&ec->base, display, argc, argv,
config_file) < 0) {
- weston_log("weston_compositor_init failed\n");
+ weston_log("%s failed\n", __func__);
goto err_base;
}
+ /* Check if we run drm-backend using weston-launch */
+ if (ec->base.launcher_sock == -1 && geteuid() != 0) {
+ weston_log("fatal: drm backend should be run "
+ "using weston-launch binary or as root\n");
+ goto err_compositor;
+ }
+
ec->udev = udev_new();
if (ec->udev == NULL) {
weston_log("failed to initialize udev context\n");
}
WL_EXPORT struct weston_compositor *
-backend_init(struct wl_display *display, int argc, char *argv[],
+backend_init(struct wl_display *display, int *argc, char *argv[],
const char *config_file)
{
int connector = 0, tty = 0, use_pixman = 0;