ecore-drm: Improve drm output creation, mode detection, and cloning support
authorChris Michael <cp.michael@samsung.com>
Tue, 7 Apr 2015 16:13:32 +0000 (12:13 -0400)
committerStefan Schmidt <s.schmidt@samsung.com>
Fri, 10 Apr 2015 09:09:49 +0000 (11:09 +0200)
Summary: This refactors the output creation code to support better
mode detection, cloning of hotplugged outputs, and initial support for
setting of output gamma (API to follow)

@fix

Signed-off-by: Chris Michael <cp.michael@samsung.com>
src/lib/ecore_drm/ecore_drm_output.c
src/lib/ecore_drm/ecore_drm_private.h

index 2315fde..950d604 100644 (file)
@@ -25,6 +25,39 @@ static const char *conn_types[] =
 
 EAPI int ECORE_DRM_EVENT_OUTPUT = 0;
 
+static void
+_ecore_drm_output_event_free(void *data EINA_UNUSED, void *event)
+{
+   Ecore_Drm_Event_Output *e = event;
+
+   eina_stringshare_del(e->make);
+   eina_stringshare_del(e->model);
+   free(event);
+}
+
+static void
+_ecore_drm_output_event_send(const Ecore_Drm_Output *output, Eina_Bool plug)
+{
+   Ecore_Drm_Event_Output *e;
+
+   if (!(e = calloc(1, sizeof(Ecore_Drm_Event_Output)))) return;
+   e->plug = plug;
+   e->id = output->crtc_id;
+   e->w = output->current_mode->width;
+   e->h = output->current_mode->height;
+   e->x = output->x;
+   e->y = output->y;
+   e->phys_width = output->phys_width;
+   e->phys_height = output->phys_height;
+   e->refresh = output->current_mode->refresh;
+   e->subpixel_order = output->subpixel;
+   e->make = eina_stringshare_ref(output->make);
+   e->model = eina_stringshare_ref(output->model);
+   e->transform = 0;
+   ecore_event_add(ECORE_DRM_EVENT_OUTPUT, e,
+                   _ecore_drm_output_event_free, NULL);
+}
+
 static drmModePropertyPtr
 _ecore_drm_output_property_get(int fd, drmModeConnectorPtr conn, const char *name)
 {
@@ -400,63 +433,65 @@ _ecore_drm_output_backlight_shutdown(Ecore_Drm_Backlight *backlight)
 }
 
 static Ecore_Drm_Output *
-_ecore_drm_output_create(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnector *conn, int x, int y)
+_ecore_drm_output_create(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnector *conn, int x, int y, Eina_Bool cloned)
 {
    Ecore_Drm_Output *output;
-   Ecore_Drm_Output_Mode *mode;
-   const char *conn_name;
-   char name[32];
-   int i = 0;
+   int i = -1;
+   char name[DRM_CONNECTOR_NAME_LEN];
+   const char *type;
+   drmModeCrtc *crtc;
    drmModeEncoder *enc;
    drmModeModeInfo crtc_mode;
-   drmModeCrtc *crtc;
+   Ecore_Drm_Output_Mode *mode, *current, *preferred, *best;
    Eina_List *l;
 
+   /* try to find a crtc for this connector */
    i = _ecore_drm_output_crtc_find(dev, res, conn);
-   if (i < 0)
-     {
-        ERR("Could not find crtc or encoder for connector");
-        return NULL;
-     }
+   if (i < 0) return NULL;
 
-   /* try to allocate space for output */
-   if (!(output = calloc(1, sizeof(Ecore_Drm_Output))))
-     {
-        ERR("Could not allocate space for output");
-        return NULL;
-     }
+   /* try to allocate space for new output */
+   if (!(output = calloc(1, sizeof(Ecore_Drm_Output)))) return NULL;
 
-   output->dev = dev;
    output->x = x;
    output->y = y;
+   output->dev = dev;
+   output->cloned = cloned;
    output->phys_width = conn->mmWidth;
    output->phys_height = conn->mmHeight;
-
    output->subpixel = conn->subpixel;
-   output->make = eina_stringshare_add("unknown");
-   output->model = eina_stringshare_add("unknown");
+
+   output->make = eina_stringshare_add("UNKNOWN");
+   output->model = eina_stringshare_add("UNKNOWN");
+   output->name = eina_stringshare_add("UNKNOWN");
 
    if (conn->connector_type < ALEN(conn_types))
-     conn_name = conn_types[conn->connector_type];
+     type = conn_types[conn->connector_type];
    else
-     conn_name = "UNKNOWN";
+     type = "UNKNOWN";
 
-   snprintf(name, sizeof(name), "%s-%d", conn_name, conn->connector_type_id);
-   output->name = eina_stringshare_add(name);
+   snprintf(name, sizeof(name), "%s-%d", type, conn->connector_type_id);
+   eina_stringshare_replace(&output->name, name);
 
    output->crtc_id = res->crtcs[i];
+   output->pipe = i;
    dev->crtc_allocator |= (1 << output->crtc_id);
    output->conn_id = conn->connector_id;
    dev->conn_allocator |= (1 << output->conn_id);
+
+   /* store original crtc so we can restore VT settings */
    output->crtc = drmModeGetCrtc(dev->drm.fd, output->crtc_id);
+
+   /* get if dpms is supported */
    output->dpms = _ecore_drm_output_property_get(dev->drm.fd, conn, "DPMS");
 
-   memset(&mode, 0, sizeof(mode));
+   memset(&crtc_mode, 0, sizeof(crtc_mode));
+
+   /* get the encoder currently driving this connector */
    if ((enc = drmModeGetEncoder(dev->drm.fd, conn->encoder_id)))
      {
         crtc = drmModeGetCrtc(dev->drm.fd, enc->crtc_id);
         drmModeFreeEncoder(enc);
-        if (!crtc) goto mode_err;
+        if (!crtc) goto err;
         if (crtc->mode_valid) crtc_mode = crtc->mode;
         drmModeFreeCrtc(crtc);
      }
@@ -464,66 +499,89 @@ _ecore_drm_output_create(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnecto
    for (i = 0; i < conn->count_modes; i++)
      {
         if (!(mode = _ecore_drm_output_mode_add(output, &conn->modes[i])))
-          {
-             ERR("Failed to add mode to output");
-             goto mode_err;
-          }
+          goto err;
      }
 
    EINA_LIST_REVERSE_FOREACH(output->modes, l, mode)
      {
         if (!memcmp(&crtc_mode, &mode->info, sizeof(crtc_mode)))
-          {
-             output->current_mode = mode;
-             break;
-          }
+          current = mode;
+        if (mode->flags & DRM_MODE_TYPE_PREFERRED)
+          preferred = mode;
+        best = mode;
      }
 
-   if ((!output->current_mode) && (crtc_mode.clock != 0))
+   if ((!current) && (crtc_mode.clock != 0))
      {
-        output->current_mode = _ecore_drm_output_mode_add(output, &crtc_mode);
-        if (!output->current_mode)
-          {
-             ERR("Failed to add mode to output");
-             goto mode_err;
-          }
+        if (!(current = _ecore_drm_output_mode_add(output, &crtc_mode)))
+          goto err;
      }
 
-   _ecore_drm_output_edid_find(output, conn);
+   if (current) output->current_mode = current;
+   else if (preferred) output->current_mode = preferred;
+   else if (best) output->current_mode = best;
 
-   dev->use_hw_accel = EINA_FALSE;
-   if (!_ecore_drm_output_software_setup(dev, output))
-     goto mode_err;
-   else
+   if (!output->current_mode) goto err;
+
+   output->current_mode->flags |= DRM_MODE_TYPE_DEFAULT;
+
+   if (drmModeSetCrtc(output->dev->drm.fd, output->crtc_id,
+                      output->crtc->buffer_id, 0, 0,
+                      &output->conn_id, 1, &output->current_mode->info) < 0)
      {
-        DBG("Output %s", output->name);
-        DBG("\tMake: %s", output->make);
-        DBG("\tModel: %s", output->model);
-        DBG("\tGeom: %d %d %d %d", output->x, output->y, 
-            output->current_mode->width, output->current_mode->height);
-        DBG("\tConnector: %d", output->conn_id);
-        DBG("\tCrtc: %d", output->crtc_id);
-        DBG("\tSetup for Software Rendering");
+        ERR("Failed to set Mode %dx%d for Output %s: %m",
+            output->current_mode->width, output->current_mode->height,
+            output->name);
+        goto err;
      }
 
+   /* try to init backlight */
    output->backlight = 
      _ecore_drm_output_backlight_init(output, conn->connector_type);
 
-   _ecore_drm_event_output_send(output, EINA_TRUE);
+   /* parse edid */
+   _ecore_drm_output_edid_find(output, conn);
+
+   /* TODO: implement support for LCMS ? */
+   output->gamma = output->crtc->gamma_size;
+
+   if (!_ecore_drm_output_software_setup(dev, output))
+     goto err;
+
+   dev->outputs = eina_list_append(dev->outputs, output);
+
+   DBG("Created New Output At %d,%d", output->x, output->y);
+   DBG("\tCrtc Pos: %d %d", output->crtc->x, output->crtc->y);
+   DBG("\tCrtc: %d", output->crtc_id);
+   DBG("\tConn: %d", output->conn_id);
+   DBG("\tMake: %s", output->make);
+   DBG("\tModel: %s", output->model);
+   DBG("\tName: %s", output->name);
+   DBG("\tCloned: %d", output->cloned);
+
+   EINA_LIST_FOREACH(output->modes, l, mode)
+     {
+        DBG("\tAdded Mode: %dx%d@%.1f%s%s%s",
+            mode->width, mode->height, (mode->refresh / 1000.0),
+            (mode->flags & DRM_MODE_TYPE_PREFERRED) ? ", preferred" : "",
+            (mode->flags & DRM_MODE_TYPE_DEFAULT) ? ", current" : "",
+            (conn->count_modes == 0) ? ", built-in" : "");
+     }
+
+   _ecore_drm_output_event_send(output, EINA_TRUE);
 
    return output;
 
-mode_err:
-   DBG("Removing Output %s", output->name);
-   eina_stringshare_del(output->make);
-   eina_stringshare_del(output->model);
-   eina_stringshare_del(output->name);
+err:
    EINA_LIST_FREE(output->modes, mode)
      free(mode);
    drmModeFreeProperty(output->dpms);
    drmModeFreeCrtc(output->crtc);
    dev->crtc_allocator &= ~(1 << output->crtc_id);
    dev->conn_allocator &= ~(1 << output->conn_id);
+   eina_stringshare_del(output->name);
+   eina_stringshare_del(output->model);
+   eina_stringshare_del(output->make);
    free(output);
    return NULL;
 }
@@ -541,11 +599,11 @@ _ecore_drm_output_free(Ecore_Drm_Output *output)
      _ecore_drm_output_backlight_shutdown(output->backlight);
 
    /* turn off hardware cursor */
-   drmModeSetCursor(output->drm_fd, output->crtc_id, 0, 0, 0);
+   drmModeSetCursor(output->dev->drm.fd, output->crtc_id, 0, 0, 0);
 
    /* restore crtc state */
    if (output->crtc)
-     drmModeSetCrtc(output->drm_fd, output->crtc->crtc_id, 
+     drmModeSetCrtc(output->dev->drm.fd, output->crtc->crtc_id,
                     output->crtc->buffer_id, output->crtc->x, output->crtc->y, 
                     &output->conn_id, 1, &output->crtc->mode);
 
@@ -616,124 +674,75 @@ finish:
 void
 _ecore_drm_outputs_update(Ecore_Drm_Device *dev)
 {
-   Ecore_Drm_Output *new_output;
-   drmModeConnector *connector;
    drmModeRes *res;
-   drmModeCrtc *crtc;
-   int x = 0, y = 0, i;
+   drmModeConnector *conn;
+   int i = 0, x = 0, y = 0;
+   Ecore_Drm_Output *output;
    uint32_t connected = 0, disconnects = 0;
-   Eina_List *l;
 
-   res = drmModeGetResources(dev->drm.fd);
-   if (!res)
-     {
-        ERR("Could not get resources for drm card: %m");
-        return;
-     }
+   /* try to get drm resources */
+   if (!(res = drmModeGetResources(dev->drm.fd))) return;
 
-   for (i = 0; i < res->count_connectors; i++)
+   /* find any new connects */
+   for (; i < res->count_connectors; i++)
      {
-        int connector_id = res->connectors[i];
+        int conn_id;
+
+        conn_id = res->connectors[i];
 
-        connector = drmModeGetConnector(dev->drm.fd, connector_id);
-        if (connector == NULL)
+        /* try to get the connector */
+        if (!(conn = drmModeGetConnector(dev->drm.fd, conn_id)))
           continue;
 
-        if (connector->connection != DRM_MODE_CONNECTED)
-          {
-             drmModeFreeConnector(connector);
-             continue;
-          }
+        /* test if connected */
+        if (conn->connection != DRM_MODE_CONNECTED) goto next;
 
-        connected |= (1 << connector_id);
+        connected |= (1 << conn_id);
 
-        if (!(dev->conn_allocator & (1 << connector_id)))
+        if (!(dev->conn_allocator & (1 << conn_id)))
           {
-             drmModeEncoder *enc;
-
-             if (!(new_output = _ecore_drm_output_create(dev, res, connector, x, y)))
+             if (eina_list_count(dev->outputs) > 0)
                {
-                  drmModeFreeConnector(connector);
-                  _ecore_drm_output_free(new_output);
-                  continue;
-               }
-
-             new_output->drm_fd = dev->drm.fd;
-
-             if (!(enc = drmModeGetEncoder(dev->drm.fd, connector->encoder_id)))
-               {
-                  drmModeFreeConnector(connector);
-                  _ecore_drm_output_free(new_output);
-                  continue;
-               }
+                  Ecore_Drm_Output *last;
 
-             if (!(crtc = drmModeGetCrtc(dev->drm.fd, enc->crtc_id)))
-               {
-                  drmModeFreeEncoder(enc);
-                  drmModeFreeConnector(connector);
-                  _ecore_drm_output_free(new_output);
-                  continue;
+                  if ((last = eina_list_last_data_get(dev->outputs)))
+                    x = last->x + last->current_mode->width;
+                  else
+                    x = 0;
                }
-
-             x += crtc->width;
-
-             drmModeFreeCrtc(crtc);
-             drmModeFreeEncoder(enc);
-
-             dev->outputs = eina_list_append(dev->outputs, new_output);
+             else
+               x = 0;
+
+             /* try to create a new output */
+             /* NB: hotplugged outputs will be set to cloned by default */
+             if (!(output =
+                   _ecore_drm_output_create(dev, res, conn, x, y, EINA_TRUE)))
+               goto next;
           }
-
-        drmModeFreeConnector(connector);
+next:
+        drmModeFreeConnector(conn);
      }
 
-   disconnects = dev->conn_allocator & ~connected;
+   drmModeFreeResources(res);
+
+   /* find any disconnects */
+   disconnects = (dev->conn_allocator & ~connected);
    if (disconnects)
      {
-        EINA_LIST_FOREACH(dev->outputs, l, new_output)
+        Eina_List *l;
+
+        EINA_LIST_FOREACH(dev->outputs, l, output)
           {
-             if (disconnects & (1 << new_output->conn_id))
+             if (disconnects & (1 << output->conn_id))
                {
-                  disconnects &= ~(1 << new_output->conn_id);
-                  _ecore_drm_event_output_send(new_output, EINA_FALSE);
-                  ecore_drm_output_free(new_output);
+                  disconnects &= ~(1 << output->conn_id);
+                  _ecore_drm_output_event_send(output, EINA_FALSE);
+                  ecore_drm_output_free(output);
                }
           }
      }
 }
 
-static void
-_ecore_drm_event_output_free(void *data EINA_UNUSED, void *event)
-{
-   Ecore_Drm_Event_Output *e = event;
-
-   eina_stringshare_del(e->make);
-   eina_stringshare_del(e->model);
-   free(event);
-}
-
-void
-_ecore_drm_event_output_send(const Ecore_Drm_Output *output, Eina_Bool plug)
-{
-   Ecore_Drm_Event_Output *e;
-
-   if (!(e = calloc(1, sizeof(Ecore_Drm_Event_Output)))) return;
-   e->plug = plug;
-   e->id = output->crtc_id;
-   e->w = output->current_mode->width;
-   e->h = output->current_mode->height;
-   e->x = output->x;
-   e->y = output->y;
-   e->phys_width = output->phys_width;
-   e->phys_height = output->phys_height;
-   e->refresh = output->current_mode->refresh;
-   e->subpixel_order = output->subpixel;
-   e->make = eina_stringshare_ref(output->make);
-   e->model = eina_stringshare_ref(output->model);
-   e->transform = 0;
-   ecore_event_add(ECORE_DRM_EVENT_OUTPUT, e,
-                   _ecore_drm_event_output_free, NULL);
-}
-
 /* public functions */
 
 /**
@@ -762,9 +771,10 @@ ecore_drm_outputs_create(Ecore_Drm_Device *dev)
    Ecore_Drm_Output *output = NULL;
    drmModeConnector *conn;
    drmModeRes *res;
-   drmModeCrtc *crtc;
    int i = 0, x = 0, y = 0;
 
+   EINA_SAFETY_ON_NULL_RETURN_VAL(dev, EINA_FALSE);
+
    /* DBG("Create outputs for %d", dev->drm.fd); */
 
    /* get the resources */
@@ -796,55 +806,25 @@ ecore_drm_outputs_create(Ecore_Drm_Device *dev)
         if (!(conn = drmModeGetConnector(dev->drm.fd, res->connectors[i])))
           continue;
 
-        if ((conn->connection == DRM_MODE_CONNECTED) && 
-            (conn->count_modes > 0))
-          {
-             drmModeEncoder *enc;
-
-             /* create output for this connector */
-             if (!(output = _ecore_drm_output_create(dev, res, conn, x, y)))
-               {
-                  /* free the connector */
-                  drmModeFreeConnector(conn);
-                  _ecore_drm_output_free(output);
-                  continue;
-               }
+        if (conn->connection != DRM_MODE_CONNECTED) goto next;
 
-             output->drm_fd = dev->drm.fd;
+        /* create output for this connector */
+        if (!(output =
+              _ecore_drm_output_create(dev, res, conn, x, y, EINA_FALSE)))
+          goto next;
 
-             if (!(enc = drmModeGetEncoder(dev->drm.fd, conn->encoder_id)))
-               {
-                  drmModeFreeConnector(conn);
-                  _ecore_drm_output_free(output);
-                  continue;
-               }
-
-             if (!(crtc = drmModeGetCrtc(dev->drm.fd, enc->crtc_id)))
-               {
-                  drmModeFreeEncoder(enc);
-                  drmModeFreeConnector(conn);
-                  _ecore_drm_output_free(output);
-                  continue;
-               }
-
-             x += crtc->width;
-
-             drmModeFreeCrtc(crtc);
-             drmModeFreeEncoder(enc);
-
-             dev->outputs = eina_list_append(dev->outputs, output);
-          }
+        x += output->current_mode->width;
 
+next:
         /* free the connector */
         drmModeFreeConnector(conn);
      }
 
+   /* TODO: Planes */
+
    ret = EINA_TRUE;
    if (eina_list_count(dev->outputs) < 1) 
-     {
-        ret = EINA_FALSE;
-        free(dev->crtcs);
-     }
+     ret = EINA_FALSE;
 
    /* free resources */
    drmModeFreeResources(res);
@@ -883,7 +863,7 @@ EAPI void
 ecore_drm_output_cursor_size_set(Ecore_Drm_Output *output, int handle, int w, int h)
 {
    if (!output) return;
-   drmModeSetCursor(output->drm_fd, output->crtc_id, handle, w, h);
+   drmModeSetCursor(output->dev->drm.fd, output->crtc_id, handle, w, h);
 }
 
 EAPI Eina_Bool 
@@ -896,7 +876,7 @@ ecore_drm_output_enable(Ecore_Drm_Output *output)
    ecore_drm_output_dpms_set(output, DRM_MODE_DPMS_ON);
 
    mode = output->current_mode;
-   if (drmModeSetCrtc(output->drm_fd, output->crtc_id, output->current->id, 
+   if (drmModeSetCrtc(output->dev->drm.fd, output->crtc_id, output->current->id, 
                       0, 0, &output->conn_id, 1, &mode->info) < 0)
      {
         ERR("Could not set output crtc: %m");
index a0bcf12..beea203 100644 (file)
@@ -116,7 +116,6 @@ struct _Ecore_Drm_Output
    drmModePropertyPtr dpms;
 
    int x, y, phys_width, phys_height;
-   int drm_fd;
 
    Eina_Bool need_repaint : 1;
    Eina_Bool repaint_scheduled : 1;
@@ -124,6 +123,7 @@ struct _Ecore_Drm_Output
    Eina_Bool pending_flip : 1;
    Eina_Bool pending_vblank : 1;
 
+   int pipe;
    const char *make, *model, *name;
    unsigned int subpixel;
 
@@ -141,6 +141,11 @@ struct _Ecore_Drm_Output
    Ecore_Drm_Fb *current, *next;
    Ecore_Drm_Fb *dumb[NUM_FRAME_BUFFERS];
    Ecore_Drm_Backlight *backlight;   
+
+   uint16_t gamma;
+
+   Eina_Bool enabled : 1;
+   Eina_Bool cloned : 1;
 };
 
 struct _Ecore_Drm_Seat
@@ -242,7 +247,6 @@ struct _Ecore_Drm_Sprite
 typedef void (*Ecore_Drm_Open_Cb)(void *data, int fd, Eina_Bool b);
 
 void _ecore_drm_event_activate_send(Eina_Bool active);
-void _ecore_drm_event_output_send(const Ecore_Drm_Output *output, Eina_Bool plug);
 
 Eina_Bool _ecore_drm_launcher_device_open(const char *device, Ecore_Drm_Open_Cb callback, void *data, int flags);
 int _ecore_drm_launcher_device_open_no_pending(const char *device, int flags);