input: Rename wl_pointer to weston_pointer
[platform/upstream/weston.git] / src / compositor-drm.c
index c170f97..f39096e 100644 (file)
  * 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>
@@ -131,14 +136,21 @@ struct drm_fb {
        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;
@@ -255,8 +267,7 @@ drm_fb_create_dumb(struct drm_compositor *ec, unsigned width, unsigned height)
 
        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;
 
@@ -536,6 +547,27 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
 }
 
 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)
 {
@@ -623,6 +655,26 @@ drm_output_repaint(struct weston_output *output_base,
 }
 
 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)
 {
@@ -649,11 +701,16 @@ page_flip_handler(int fd, unsigned int frame,
        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;
@@ -672,9 +729,10 @@ drm_output_check_sprite_format(struct drm_sprite *s,
        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;
@@ -919,26 +977,10 @@ drm_assign_planes(struct weston_output *output)
 {
        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
@@ -1029,7 +1071,6 @@ drm_output_destroy(struct weston_output *output_base)
        weston_output_destroy(&output->base);
        wl_list_remove(&output->base.link);
 
-       free(output->name);
        free(output);
 }
 
@@ -1174,6 +1215,9 @@ init_egl(struct drm_compositor *ec)
 {
        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);
@@ -1473,6 +1517,143 @@ drm_output_fini_pixman(struct drm_output *output)
        }
 }
 
+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,
@@ -1504,6 +1685,7 @@ create_output_for_connector(struct drm_compositor *ec,
        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))
@@ -1511,7 +1693,7 @@ create_output_for_connector(struct drm_compositor *ec,
        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;
@@ -1546,7 +1728,7 @@ create_output_for_connector(struct drm_compositor *ec,
        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);
@@ -1600,7 +1782,7 @@ create_output_for_connector(struct drm_compositor *ec,
                output->base.current = &current->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;
        }
 
@@ -1629,18 +1811,28 @@ create_output_for_connector(struct drm_compositor *ec,
 
        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,
@@ -1665,7 +1857,6 @@ err_free:
        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;
@@ -1712,6 +1903,8 @@ create_sprites(struct drm_compositor *ec)
                       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);
        }
@@ -1973,6 +2166,16 @@ drm_compositor_set_modes(struct drm_compositor *compositor)
        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,
@@ -2016,12 +2219,12 @@ vt_func(struct weston_compositor *compositor, int event)
 
                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. */
@@ -2134,7 +2337,7 @@ planes_binding(struct wl_seat *seat, uint32_t time, uint32_t key, void *data)
 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;
@@ -2158,10 +2361,17 @@ drm_compositor_create(struct wl_display *display,
 
        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");
@@ -2430,7 +2640,7 @@ output_section_done(void *data)
 }
 
 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;