ecore-drm: Implement edid parsing for Ecore_Drm_Output
authorChris Michael <cp.michael@samsung.com>
Wed, 4 Mar 2015 19:53:06 +0000 (14:53 -0500)
committerChris Michael <cp.michael@samsung.com>
Wed, 4 Mar 2015 19:53:06 +0000 (14:53 -0500)
Summary: This implements edid parsing to obtain output make and model
so we can get better output names. This also fixes a false FIXME
statement in ecore_drm_output_physical_size_get function. As it turns
out, we don't need to get these values from edid parsing as they are
already available in the drm connector.

@feature

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

index a2d7f10..a86c4e6 100644 (file)
@@ -3,9 +3,18 @@
 #endif
 
 #include "ecore_drm_private.h"
+#include <ctype.h>
 
 #define ALEN(array) (sizeof(array) / sizeof(array)[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 const char *conn_types[] = 
 {
    "None", "VGA", "DVI", "DVI", "DVI",
@@ -17,6 +26,109 @@ EAPI int ECORE_DRM_EVENT_OUTPUT = 0;
 
 /* local functions */
 
+static void 
+_ecore_drm_output_edid_parse_string(const uint8_t *data, char text[])
+{
+   int i = 0, rep = 0;
+
+   strncpy(text, (const char *)data, 12);
+
+   for (; text[i] != '\0'; i++)
+     {
+        if ((text[i] == '\n') || (text[i] == '\r'))
+          {
+             text[i] = '\0';
+             break;
+          }
+     }
+
+   for (i = 0; text[i] != '\0'; i++)
+     {
+        if (!isprint(text[i]))
+          {
+             text[i] = '-';
+             rep++;
+          }
+     }
+
+   if (rep > 0) text[i] = '\0';
+}
+
+static int 
+_ecore_drm_output_edid_parse(Ecore_Drm_Output *output, const uint8_t *data, size_t len)
+{
+   int i = 0;
+   uint32_t serial;
+
+   if (len < 128) return -1;
+   if ((data[0] != 0x00) || (data[1] != 0xff)) return -1;
+
+   output->edid.pnp[0] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1;
+   output->edid.pnp[1] = 
+     'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x3) * 8) + 
+     ((data[EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1;
+   output->edid.pnp[2] = 'A' + (data[EDID_OFFSET_PNPID + 1] & 0x1f) - 1;
+   output->edid.pnp[3] = '\0';
+
+   serial = (uint32_t) data[EDID_OFFSET_SERIAL + 0];
+   serial += (uint32_t) data[EDID_OFFSET_SERIAL + 1] * 0x100;
+   serial += (uint32_t) data[EDID_OFFSET_SERIAL + 2] * 0x10000;
+   serial += (uint32_t) data[EDID_OFFSET_SERIAL + 3] * 0x1000000;
+   if (serial > 0)
+     sprintf(output->edid.serial, "%lu", (unsigned long)serial);
+
+   for (i = EDID_OFFSET_DATA_BLOCKS; i <= EDID_OFFSET_LAST_BLOCK; i += 18)
+     {
+        if (data[i] != 0) continue;
+        if (data[i + 2] != 0) continue;
+
+        if (data[i + 3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME)
+          _ecore_drm_output_edid_parse_string(&data[i+5], output->edid.monitor);
+        else if (data[i + 3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER)
+          _ecore_drm_output_edid_parse_string(&data[i+5], output->edid.serial);
+        else if (data[i + 3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING)
+          _ecore_drm_output_edid_parse_string(&data[i+5], output->edid.eisa);
+     }
+
+   return 0;
+}
+
+static void 
+_ecore_drm_output_edid_find(Ecore_Drm_Output *output, drmModeConnector *conn)
+{
+   drmModePropertyBlobPtr blob = NULL;
+   drmModePropertyPtr prop;
+   int i = 0, ret = 0;
+
+   for (; i < conn->count_props && !blob; i++)
+     {
+        if (!(prop = drmModeGetProperty(output->dev->drm.fd, conn->props[i])))
+          continue;
+        if ((prop->flags & DRM_MODE_PROP_BLOB) && 
+            (!strcmp(prop->name, "EDID")))
+          {
+             blob = drmModeGetPropertyBlob(output->dev->drm.fd, 
+                                           conn->prop_values[i]);
+          }
+        drmModeFreeProperty(prop);
+     }
+
+   if (!blob) return;
+
+   ret = _ecore_drm_output_edid_parse(output, blob->data, blob->length);
+   if (!ret)
+     {
+        if (output->edid.pnp[0] != '\0')
+          eina_stringshare_replace(&output->make, output->edid.pnp);
+        if (output->edid.monitor[0] != '\0')
+          eina_stringshare_replace(&output->model, output->edid.monitor);
+        /* if (output->edid.serial[0] != '\0') */
+        /*   eina_stringshare_replace(&output->serial, output->edid.serial); */
+     }
+
+   drmModeFreePropertyBlob(blob);
+}
+
 static Eina_Bool 
 _ecore_drm_output_software_setup(Ecore_Drm_Device *dev, Ecore_Drm_Output *output)
 {
@@ -299,6 +411,8 @@ _ecore_drm_output_create(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnecto
    output->dev = dev;
    output->x = x;
    output->y = y;
+   output->phys_width = conn->mmWidth;
+   output->phys_height = conn->mmHeight;
 
    output->subpixel = conn->subpixel;
    output->make = eina_stringshare_add("unknown");
@@ -352,6 +466,8 @@ _ecore_drm_output_create(Ecore_Drm_Device *dev, drmModeRes *res, drmModeConnecto
         if (!output->current_mode) goto mode_err;
      }
 
+   _ecore_drm_output_edid_find(output, conn);
+
    dev->use_hw_accel = EINA_FALSE;
    if (!_ecore_drm_output_software_setup(dev, output))
      goto mode_err;
@@ -967,9 +1083,8 @@ ecore_drm_output_physical_size_get(Ecore_Drm_Output *output, int *w, int *h)
 {
    EINA_SAFETY_ON_NULL_RETURN(output);
 
-   //FIXME: This needs to be set when EDID parsing works
-   if (w) *w = 0;
-   if (h) *h = 0;
+   if (w) *w = output->phys_width;
+   if (h) *h = output->phys_height;
 }
 
 EAPI unsigned int