diff --git a/src/lib/ecore_drm2/ecore_drm2_connectors.c b/src/lib/ecore_drm2/ecore_drm2_connectors.c new file mode 100644 index 0000000000..219c5ac46d --- /dev/null +++ b/src/lib/ecore_drm2/ecore_drm2_connectors.c @@ -0,0 +1,228 @@ +#include "ecore_drm2_private.h" + +#ifndef DRM_MODE_CONNECTOR_WRITEBACK +# define DRM_MODE_CONNECTOR_WRITEBACK 18 +#endif + +static void +_ecore_drm2_connector_state_debug(Ecore_Drm2_Connector *conn) +{ + DBG("Connector Atomic State Fill Complete"); + DBG("\tConnector: %d", conn->state->obj_id); + DBG("\t\tCrtc Id: %lu", (long)conn->state->crtc.value); + DBG("\t\tDPMS: %lu", (long)conn->state->dpms.value); + DBG("\t\tAspect Ratio: %lu", (long)conn->state->aspect.value); + DBG("\t\tScaling Mode: %lu", (long)conn->state->scaling.value); +} + +static void +_ecore_drm2_connector_state_fill(Ecore_Drm2_Connector *conn) +{ + Ecore_Drm2_Connector_State *cstate; + drmModeObjectPropertiesPtr oprops; + unsigned int i = 0; + + /* try to allocate space for connector Atomic state */ + conn->state = calloc(1, sizeof(Ecore_Drm2_Connector_State)); + if (!conn->state) + { + ERR("Could not allocate space for Connector state"); + return; + } + + cstate = conn->state; + cstate->obj_id = conn->id; + + /* get the properties of this connector from drm */ + oprops = + sym_drmModeObjectGetProperties(conn->fd, cstate->obj_id, + DRM_MODE_OBJECT_CONNECTOR); + if (!oprops) + { + free(conn->state); + return; + } + + for (; i < oprops->count_props; i++) + { + drmModePropertyPtr prop; + + /* try to get this individual property */ + prop = sym_drmModeGetProperty(conn->fd, oprops->props[i]); + if (!prop) continue; + + /* check for the properties we are interested in */ + if (!strcmp(prop->name, "CRTC_ID")) + { + cstate->crtc.id = prop->prop_id; + cstate->crtc.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "DPMS")) + { + cstate->dpms.id = prop->prop_id; + cstate->dpms.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "EDID")) + { + drmModePropertyBlobPtr bp; + + cstate->edid.id = oprops->prop_values[i]; + if (!cstate->edid.id) + { + cstate->edid.len = 0; + goto cont; + } + + bp = sym_drmModeGetPropertyBlob(conn->fd, cstate->edid.id); + if (!bp) goto cont; + + if ((!cstate->edid.data) || + memcmp(cstate->edid.data, bp->data, bp->length) != 0) + { + cstate->edid.data = eina_memdup(bp->data, bp->length, 1); + } + + cstate->edid.len = bp->length; + + if (cstate->edid.id != 0) + sym_drmModeCreatePropertyBlob(conn->fd, bp->data, bp->length, + &cstate->edid.id); + + sym_drmModeFreePropertyBlob(bp); + } + else if (!strcmp(prop->name, "aspect ratio")) + { + cstate->aspect.id = prop->prop_id; + cstate->aspect.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "scaling mode")) + { + cstate->scaling.id = prop->prop_id; + cstate->scaling.value = oprops->prop_values[i]; + } + +cont: + sym_drmModeFreeProperty(prop); + } + + sym_drmModeFreeObjectProperties(oprops); +} + +static void +_ecore_drm2_connector_state_thread(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Connector *conn; + + conn = data; + if (!conn->state) + _ecore_drm2_connector_state_fill(conn); + else + { + /* TODO: update atomic state for commit */ + } +} + +static void +_ecore_drm2_connector_state_thread_end(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Connector *conn; + + conn = data; + /* conn->thread = NULL; */ + _ecore_drm2_connector_state_debug(conn); +} + +static void +_ecore_drm2_connector_state_thread_cancel(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Connector *conn; + + conn = data; + conn->thread = NULL; +} + +static Ecore_Drm2_Connector * +_ecore_drm2_connector_create(Ecore_Drm2_Device *dev, drmModeConnector *conn, uint32_t id) +{ + Ecore_Drm2_Connector *c; + + /* try to allocate space for new connector */ + c = calloc(1, sizeof(Ecore_Drm2_Connector)); + if (!c) return NULL; + + c->id = id; + c->fd = dev->fd; + c->conn = conn; + c->type = conn->connector_type; + + /* check if this connector is a writeback */ + if (conn->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + c->writeback = EINA_TRUE; + + /* append this connector to list */ + dev->conns = eina_list_append(dev->conns, c); + + return c; +} + +Eina_Bool +_ecore_drm2_connectors_create(Ecore_Drm2_Device *dev) +{ + Ecore_Drm2_Connector *c; + drmModeConnector *conn; + drmModeRes *res; + int i = 0; + + /* try to get drm resources */ + res = sym_drmModeGetResources(dev->fd); + if (!res) return EINA_FALSE; + + /* TOOD: set dev->min/max width & height ? */ + + for (; i < res->count_connectors; i++) + { + uint32_t conn_id; + + conn_id = res->connectors[i]; + + /* try to get this connector from drm */ + conn = sym_drmModeGetConnector(dev->fd, conn_id); + if (!conn) continue; + + /* try to create a new connector */ + c = _ecore_drm2_connector_create(dev, conn, conn_id); + if (!c) goto err; + + /* NB: Use an explicit thread to fill crtc atomic state */ + c->thread = + ecore_thread_feedback_run(_ecore_drm2_connector_state_thread, + NULL, //_ecore_drm2_connector_state_thread_notify, + _ecore_drm2_connector_state_thread_end, + _ecore_drm2_connector_state_thread_cancel, + c, EINA_TRUE); + + } + + sym_drmModeFreeResources(res); + return EINA_TRUE; + +err: + _ecore_drm2_connectors_destroy(dev); + sym_drmModeFreeConnector(conn); + sym_drmModeFreeResources(res); + return EINA_FALSE; +} + +void +_ecore_drm2_connectors_destroy(Ecore_Drm2_Device *dev) +{ + Ecore_Drm2_Connector *conn; + + EINA_LIST_FREE(dev->conns, conn) + { + if (conn->thread) ecore_thread_cancel(conn->thread); + if (conn->conn) sym_drmModeFreeConnector(conn->conn); + free(conn->state); + free(conn); + } +} diff --git a/src/lib/ecore_drm2/ecore_drm2_crtcs.c b/src/lib/ecore_drm2/ecore_drm2_crtcs.c new file mode 100644 index 0000000000..6829437a09 --- /dev/null +++ b/src/lib/ecore_drm2/ecore_drm2_crtcs.c @@ -0,0 +1,211 @@ +#include "ecore_drm2_private.h" + +static void +_ecore_drm2_crtc_state_debug(Ecore_Drm2_Crtc *crtc) +{ + DBG("CRTC Atomic State Fill Complete"); + DBG("\tCrtc: %d", crtc->state->obj_id); + DBG("\t\tMode: %d", crtc->state->mode.value); + DBG("\t\tActive: %lu", (long)crtc->state->active.value); +} + +static void +_ecore_drm2_crtc_state_fill(Ecore_Drm2_Crtc *crtc) +{ + Ecore_Drm2_Crtc_State *cstate; + drmModeObjectPropertiesPtr oprops; + unsigned int i = 0; + + /* try to allocate space for CRTC Atomic state */ + crtc->state = calloc(1, sizeof(Ecore_Drm2_Crtc_State)); + if (!crtc->state) + { + ERR("Could not allocate space for CRTC state"); + return; + } + + cstate = crtc->state; + cstate->obj_id = crtc->dcrtc->crtc_id; + + /* get the properties of this crtc from drm */ + oprops = + sym_drmModeObjectGetProperties(crtc->fd, cstate->obj_id, + DRM_MODE_OBJECT_CRTC); + if (!oprops) + { + free(crtc->state); + return; + } + + for (; i < oprops->count_props; i++) + { + drmModePropertyPtr prop; + + /* try to get this individual property */ + prop = sym_drmModeGetProperty(crtc->fd, oprops->props[i]); + if (!prop) continue; + + /* check for the properties we are interested in */ + if (!strcmp(prop->name, "MODE_ID")) + { + drmModePropertyBlobPtr bp; + + cstate->mode.id = prop->prop_id; + cstate->mode.value = oprops->prop_values[i]; + + if (!cstate->mode.value) + { + cstate->mode.len = 0; + goto cont; + } + + bp = sym_drmModeGetPropertyBlob(crtc->fd, cstate->mode.value); + if (!bp) goto cont; + + if ((!cstate->mode.data) || + memcmp(cstate->mode.data, bp->data, bp->length) != 0) + { + cstate->mode.data = eina_memdup(bp->data, bp->length, 1); + } + + cstate->mode.len = bp->length; + + if (cstate->mode.value != 0) + sym_drmModeCreatePropertyBlob(crtc->fd, bp->data, bp->length, + &cstate->mode.value); + + sym_drmModeFreePropertyBlob(bp); + } + else if (!strcmp(prop->name, "ACTIVE")) + { + cstate->active.id = prop->prop_id; + cstate->active.value = oprops->prop_values[i]; + } + /* TODO: We don't actually use this value yet */ + /* else if (!strcmp(prop->name, "BACKGROUND_COLOR")) */ + /* { */ + /* cstate->background.id = prop->prop_id; */ + /* cstate->background.value = oprops->prop_values[i]; */ + /* } */ + +cont: + sym_drmModeFreeProperty(prop); + } + + sym_drmModeFreeObjectProperties(oprops); +} + +static void +_ecore_drm2_crtc_state_thread(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Crtc *crtc; + + crtc = data; + if (!crtc->state) + _ecore_drm2_crtc_state_fill(crtc); + else + { + /* TODO: update atomic state for commit */ + } +} + +static void +_ecore_drm2_crtc_state_thread_end(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Crtc *crtc; + + crtc = data; + /* crtc->thread = NULL; */ + _ecore_drm2_crtc_state_debug(crtc); +} + +static void +_ecore_drm2_crtc_state_thread_cancel(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Crtc *crtc; + + crtc = data; + crtc->thread = NULL; +} + +static Ecore_Drm2_Crtc * +_ecore_drm2_crtc_create(Ecore_Drm2_Device *dev, drmModeCrtcPtr dcrtc, uint32_t pipe) +{ + Ecore_Drm2_Crtc *crtc; + + /* try to allocate space for a crtc */ + crtc = calloc(1, sizeof(Ecore_Drm2_Crtc)); + if (!crtc) + { + ERR("Could not allocate space for CRTC"); + goto err; + } + + crtc->id = dcrtc->crtc_id; + crtc->fd = dev->fd; + crtc->pipe = pipe; + crtc->dcrtc = dcrtc; + + /* add this crtc to the list */ + dev->crtcs = eina_list_append(dev->crtcs, crtc); + + return crtc; + +err: + free(crtc); + return NULL; +} + +Eina_Bool +_ecore_drm2_crtcs_create(Ecore_Drm2_Device *dev) +{ + Ecore_Drm2_Crtc *crtc; + drmModeCrtcPtr c; + drmModeRes *res; + int i = 0; + + /* try to get drm resources */ + res = sym_drmModeGetResources(dev->fd); + if (!res) return EINA_FALSE; + + for (; i < res->count_crtcs; i++) + { + /* try to get this crtc from drm */ + c = sym_drmModeGetCrtc(dev->fd, res->crtcs[i]); + + /* try to create a crtc */ + crtc = _ecore_drm2_crtc_create(dev, c, i); + if (!crtc) goto err; + + /* NB: Use an explicit thread to fill crtc atomic state */ + crtc->thread = + ecore_thread_feedback_run(_ecore_drm2_crtc_state_thread, + NULL, //_ecore_drm2_crtc_state_thread_notify, + _ecore_drm2_crtc_state_thread_end, + _ecore_drm2_crtc_state_thread_cancel, + crtc, EINA_TRUE); + } + + sym_drmModeFreeResources(res); + return EINA_TRUE; + +err: + _ecore_drm2_crtcs_destroy(dev); + sym_drmModeFreeCrtc(c); + sym_drmModeFreeResources(res); + return EINA_FALSE; +} + +void +_ecore_drm2_crtcs_destroy(Ecore_Drm2_Device *dev) +{ + Ecore_Drm2_Crtc *crtc; + + EINA_LIST_FREE(dev->crtcs, crtc) + { + if (crtc->thread) ecore_thread_cancel(crtc->thread); + if (crtc->dcrtc) sym_drmModeFreeCrtc(crtc->dcrtc); + free(crtc->state); + free(crtc); + } +} diff --git a/src/lib/ecore_drm2/ecore_drm2_device.c b/src/lib/ecore_drm2/ecore_drm2_device.c index ec280cbf7e..fc538f4351 100644 --- a/src/lib/ecore_drm2/ecore_drm2_device.c +++ b/src/lib/ecore_drm2/ecore_drm2_device.c @@ -1,23 +1,36 @@ #include "ecore_drm2_private.h" -/* external variable for using atomic */ -Eina_Bool _ecore_drm2_atomic_use = EINA_FALSE; +#ifndef DRM_CLIENT_CAP_ASPECT_RATIO +# define DRM_CLIENT_CAP_ASPECT_RATIO 4 +#endif +#ifndef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS +# define DRM_CLIENT_CAP_WRITEBACK_CONNECTORS 5 +#endif /* local functions */ static Eina_Bool -_ecore_drm2_device_atomic_capable_get(int fd) +_ecore_drm2_device_cb_session_active(void *data, int type EINA_UNUSED, void *event) { - Eina_Bool ret = EINA_TRUE; + Ecore_Drm2_Device *dev; + Elput_Event_Session_Active *ev; - if (sym_drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) - ret = EINA_FALSE; + dev = data; + ev = event; + + if (ev->active) + { + /* TODO: wake compositor, compositor damage all, set state_invalid = true */ + /* NB: Input enable is already done inside elput */ + } else { - if (sym_drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1) < 0) - ret = EINA_FALSE; + /* TODO: compositor offscreen, output->repaint_needed = false */ + /* NB: Input disable is already done inside elput */ } - return ret; + /* TODO: raise ecore_drm2_event_active ?? */ + + return ECORE_CALLBACK_RENEW; } static Eina_Bool @@ -108,6 +121,60 @@ cont: return ret; } +static Eina_Bool +_ecore_drm2_device_kms_caps_get(Ecore_Drm2_Device *dev) +{ + uint64_t cap; + int ret = 0; + + /* get drm presentation clock */ + ret = sym_drmGetCap(dev->fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap); + if ((ret == 0) && (cap == 1)) + dev->clock_id = CLOCK_MONOTONIC; + else + dev->clock_id = CLOCK_REALTIME; + + /* get drm cursor width & height */ + dev->cursor.width = 64; + dev->cursor.height = 64; + ret = sym_drmGetCap(dev->fd, DRM_CAP_CURSOR_WIDTH, &cap); + if (ret == 0) dev->cursor.width = cap; + ret = sym_drmGetCap(dev->fd, DRM_CAP_CURSOR_HEIGHT, &cap); + if (ret == 0) dev->cursor.height = cap; + + /* try to enable universal planes ... without This, not even Atomic works */ + ret = sym_drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + if (ret) + { + ERR("Drm card does not support universal planes"); + return EINA_FALSE; + } + + /* test if crtc_in_vblank_event is supported */ + /* NB: our callbacks do not check for this yet, but it's new. + * Very useful tho. tells us when crtc is vblank */ + /* NB: This is NOT necessarily needed for ATOMIC support */ + ret = sym_drmGetCap(dev->fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap); + if (ret != 0) cap = 0; + + /* try to enable atomic modesetting support */ + ret = sym_drmSetClientCap(dev->fd, DRM_CLIENT_CAP_ATOMIC, 1); + dev->atomic = ((ret == 0) && (cap == 1)); + + /* test if gbm can do modifiers */ + /* ret = sym_drmGetCap(dev->fd, DRM_CAP_ADDFB2_MODIFIERS, &cap); */ + /* if (ret == 0) dev->gbm_mods = cap; */ + + /* set writeback connector support */ + sym_drmSetClientCap(dev->fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); + + /* test to see if aspect ratio is supported */ + ret = sym_drmSetClientCap(dev->fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1); + dev->aspect_ratio = (ret == 0); + + return EINA_TRUE; +} + /* API functions */ EAPI Ecore_Drm2_Device * ecore_drm2_device_open(const char *seat, unsigned int tty) @@ -152,39 +219,81 @@ ecore_drm2_device_open(const char *seat, unsigned int tty) goto input_err; } - /* test if this device can do Atomic Modesetting */ - _ecore_drm2_atomic_use = _ecore_drm2_device_atomic_capable_get(dev->fd); - if (!_ecore_drm2_atomic_use) + /* try to get the kms capabilities of this device */ + if (!_ecore_drm2_device_kms_caps_get(dev)) { - WRN("Could not enable Atomic Modesetting support"); - goto atomic_err; + ERR("Could not get kms capabilities for device"); + goto caps_err; } - /* try to allocate space for Atomic State */ - dev->atomic_state = calloc(1, sizeof(Ecore_Drm2_Atomic_State)); - if (!dev->atomic_state) + /* try to create crtcs */ + if (!_ecore_drm2_crtcs_create(dev)) { - ERR("Failed to allocate device atomic state"); - goto atomic_err; + ERR("Could not create crtcs"); + goto caps_err; } - /* try to fill atomic state */ - if (!_ecore_drm2_atomic_state_fill(dev->atomic_state, dev->fd)) + /* try to create planes */ + if (!_ecore_drm2_planes_create(dev)) { - ERR("Failed to fill Atomic State"); - goto atomic_fill_err; + ERR("Could not create planes"); + goto plane_err; } - /* TODO: event handlers for session_active & device_change */ + /* try to create connectors */ + if (!_ecore_drm2_connectors_create(dev)) + { + ERR("Could not create connectors"); + goto conn_err; + } + + /* try to create displays */ + if (!_ecore_drm2_displays_create(dev)) + { + ERR("Could not create displays"); + goto disp_err; + } + + /* TODO: check dmabuf import capable ? + * + * NB: This will require EGL extension: EGL_EXT_image_dma_buf_import + * so will likely need to be done in the compositor + */ + + /* TODO: check explicit sync support + * + * NB: requires native_fence_sync & wait_sync + * NB: native_fence_sync requires EGL_KHR_fence_sync + * NB: wait_sync requires EGL_KHR_wait_sync + * + * NB: These need to be done in the compositor + */ + + /* TODO: enable content protection if atomic ? + * + * NB: This should be done in the compositor + */ + + dev->session_hdlr = + ecore_event_handler_add(ELPUT_EVENT_SESSION_ACTIVE, + _ecore_drm2_device_cb_session_active, dev); + + /* TODO: event handler for device_change */ /* cleanup path variable */ eina_stringshare_del(path); return dev; -atomic_fill_err: - free(dev->atomic_state); -atomic_err: +/* atomic_fill_err: */ +/* free(dev->atomic_state); */ +disp_err: + _ecore_drm2_connectors_destroy(dev); +plane_err: + _ecore_drm2_crtcs_destroy(dev); +conn_err: + _ecore_drm2_planes_destroy(dev); +caps_err: elput_input_shutdown(dev->em); input_err: elput_manager_close(dev->em, dev->fd); @@ -202,7 +311,14 @@ ecore_drm2_device_close(Ecore_Drm2_Device *dev) { EINA_SAFETY_ON_NULL_RETURN(dev); - _ecore_drm2_atomic_state_free(dev->atomic_state); + /* _ecore_drm2_atomic_state_free(dev->atomic_state); */ + + _ecore_drm2_displays_destroy(dev); + _ecore_drm2_connectors_destroy(dev); + _ecore_drm2_planes_destroy(dev); + _ecore_drm2_crtcs_destroy(dev); + + ecore_event_handler_del(dev->session_hdlr); elput_input_shutdown(dev->em); elput_manager_close(dev->em, dev->fd); @@ -214,24 +330,10 @@ ecore_drm2_device_close(Ecore_Drm2_Device *dev) EAPI void ecore_drm2_device_cursor_size_get(Ecore_Drm2_Device *dev, int *width, int *height) { - uint64_t caps; - int ret = -1; - EINA_SAFETY_ON_NULL_RETURN(dev); - if (width) - { - *width = 64; - ret = sym_drmGetCap(dev->fd, DRM_CAP_CURSOR_WIDTH, &caps); - if (ret == 0) *width = caps; - } - - if (height) - { - *height = 64; - ret = sym_drmGetCap(dev->fd, DRM_CAP_CURSOR_HEIGHT, &caps); - if (ret == 0) *height = caps; - } + if (width) *width = dev->cursor.width; + if (height) *height = dev->cursor.height; } EAPI void diff --git a/src/lib/ecore_drm2/ecore_drm2_displays.c b/src/lib/ecore_drm2/ecore_drm2_displays.c new file mode 100644 index 0000000000..2086c805bc --- /dev/null +++ b/src/lib/ecore_drm2/ecore_drm2_displays.c @@ -0,0 +1,317 @@ +#include "ecore_drm2_private.h" + +#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-I", "DVI-D", "DVI-A", + "Composite", "S-Video", "LVDS", "Component", "DIN", + "DisplayPort", "HDMI-A", "HDMI-B", "TV", "eDP", "Virtual", "DSI", +}; + +static char * +_ecore_drm2_display_name_get(Ecore_Drm2_Connector *conn) +{ + char name[DRM_CONNECTOR_NAME_LEN]; + const char *type = NULL; + + if (conn->type < EINA_C_ARRAY_LENGTH(conn_types)) + type = conn_types[conn->type]; + else + type = "UNKNOWN"; + + snprintf(name, sizeof(name), "%s-%d", type, conn->conn->connector_type_id); + return strdup(name); +} + +static void +_ecore_drm2_display_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 > 4) text[0] = '\0'; +} + +static int +_ecore_drm2_display_edid_parse(Ecore_Drm2_Display *disp, 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; + + disp->edid.pnp[0] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1; + disp->edid.pnp[1] = + 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x3) * 8) + + ((data[EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1; + disp->edid.pnp[2] = 'A' + (data[EDID_OFFSET_PNPID + 1] & 0x1f) - 1; + disp->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(disp->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_drm2_display_edid_parse_string(&data[i + 5], disp->edid.monitor); + else if (data[i + 3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) + _ecore_drm2_display_edid_parse_string(&data[i + 5], disp->edid.serial); + else if (data[i + 3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) + _ecore_drm2_display_edid_parse_string(&data[i + 5], disp->edid.eisa); + } + + return 0; +} + +static void +_ecore_drm2_display_edid_get(Ecore_Drm2_Display *disp) +{ + Ecore_Drm2_Connector_State *cstate; + int ret = 0; + + cstate = disp->conn->state; + + ret = _ecore_drm2_display_edid_parse(disp, cstate->edid.data, cstate->edid.len); + if (!ret) + { + if (disp->edid.pnp[0] != '\0') + eina_stringshare_replace(&disp->make, disp->edid.pnp); + if (disp->edid.monitor[0] != '\0') + eina_stringshare_replace(&disp->model, disp->edid.monitor); + if (disp->edid.serial[0] != '\0') + eina_stringshare_replace(&disp->serial, disp->edid.serial); + } +} + +static void +_ecore_drm2_display_state_debug(Ecore_Drm2_Display *disp) +{ + DBG("Display Atomic State Fill Complete"); + + DBG("\tName: %s", disp->name); + DBG("\tMake: %s", disp->make); + DBG("\tModel: %s", disp->model); + DBG("\tSerial: %s", disp->serial); + + DBG("\tCrtc: %d", disp->crtc->id); + DBG("\tCrtc Pos: %d %d", disp->crtc->dcrtc->x, disp->crtc->dcrtc->y); + DBG("\tConnector: %d", disp->conn->id); + + /* DBG("\tCloned: %d", disp->cloned); */ + DBG("\tPrimary: %d", disp->primary); + DBG("\tEnabled: %d", disp->enabled); + DBG("\tConnected: %d", disp->connected); + + if (disp->backlight.path) + { + DBG("\tBacklight"); + switch (disp->backlight.type) + { + case ECORE_DRM2_BACKLIGHT_RAW: + DBG("\t\tType: Raw"); + break; + case ECORE_DRM2_BACKLIGHT_PLATFORM: + DBG("\t\tType: Platform"); + break; + case ECORE_DRM2_BACKLIGHT_FIRMWARE: + DBG("\t\tType: Firmware"); + break; + } + DBG("\t\tPath: %s", disp->backlight.path); + } +} + +static void +_ecore_drm2_display_state_fill(Ecore_Drm2_Display *disp) +{ + char *name = NULL; + + /* get display name */ + name = _ecore_drm2_display_name_get(disp->conn); + disp->name = eina_stringshare_add(name); + free(name); + + disp->make = eina_stringshare_add("unknown"); + disp->model = eina_stringshare_add("unknown"); + disp->serial = eina_stringshare_add("unknown"); + + /* get edid and parse */ + _ecore_drm2_display_edid_get(disp); + + /* get physical dimensions */ + disp->pw = disp->conn->conn->mmWidth; + disp->ph = disp->conn->conn->mmHeight; + + /* get subpixel */ + switch (disp->conn->conn->subpixel) + { + case DRM_MODE_SUBPIXEL_NONE: + disp->subpixel = 1; + break; + case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: + disp->subpixel = 2; + break; + case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: + disp->subpixel = 3; + break; + case DRM_MODE_SUBPIXEL_VERTICAL_RGB: + disp->subpixel = 4; + break; + case DRM_MODE_SUBPIXEL_VERTICAL_BGR: + disp->subpixel = 5; + break; + case DRM_MODE_SUBPIXEL_UNKNOWN: + default: + disp->subpixel = 0; + break; + } + + /* get connected state */ + disp->connected = (disp->conn->conn->connection == DRM_MODE_CONNECTED); +} + +static void +_ecore_drm2_display_state_thread(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Display *disp; + + disp = data; + /* TODO: FIXME: Should this check for disp->state ? */ + if (!disp->name) + _ecore_drm2_display_state_fill(disp); + else + { + /* TODO: update atomic state for commit */ + } +} + +static void +_ecore_drm2_display_state_thread_end(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Display *disp; + + disp = data; + /* disp->thread = NULL; */ + _ecore_drm2_display_state_debug(disp); +} + +static void +_ecore_drm2_display_state_thread_cancel(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Display *disp; + + disp = data; + disp->thread = NULL; +} + +Eina_Bool +_ecore_drm2_displays_create(Ecore_Drm2_Device *dev) +{ + Ecore_Drm2_Display *disp; + Ecore_Drm2_Connector *c; + Ecore_Drm2_Crtc *crtc; + Eina_List *l = NULL, *ll = NULL; + + /* go through list of connectors and create displays */ + EINA_LIST_FOREACH(dev->conns, l, c) + { + drmModeEncoder *encoder; + drmModeCrtc *dcrtc; + + /* try to get the encoder from drm */ + encoder = sym_drmModeGetEncoder(dev->fd, c->conn->encoder_id); + if (!encoder) continue; + + /* try to get the crtc from drm */ + dcrtc = sym_drmModeGetCrtc(dev->fd, encoder->crtc_id); + if (!dcrtc) goto cont; + + /* try to allocate space for new display */ + disp = calloc(1, sizeof(Ecore_Drm2_Display)); + if (!disp) + { + WRN("Could not allocate space for Display"); + sym_drmModeFreeCrtc(dcrtc); + goto cont; + } + + /* try to find crtc matching dcrtc->crtc_id and assign to display */ + EINA_LIST_FOREACH(dev->crtcs, ll, crtc) + { + if (crtc->id == dcrtc->crtc_id) + { + disp->crtc = crtc; + break; + } + } + + sym_drmModeFreeCrtc(dcrtc); + + disp->fd = dev->fd; + disp->conn = c; + + /* append this display to the list */ + dev->displays = eina_list_append(dev->displays, disp); + + disp->thread = + ecore_thread_feedback_run(_ecore_drm2_display_state_thread, + NULL, // _ecore_drm2_display_state_thread_notify + _ecore_drm2_display_state_thread_end, + _ecore_drm2_display_state_thread_cancel, + disp, EINA_TRUE); + +cont: + sym_drmModeFreeEncoder(encoder); + } + + return EINA_TRUE; +} + +void +_ecore_drm2_displays_destroy(Ecore_Drm2_Device *dev) +{ + Ecore_Drm2_Display *disp; + + EINA_LIST_FREE(dev->displays, disp) + { + eina_stringshare_del(disp->serial); + eina_stringshare_del(disp->model); + eina_stringshare_del(disp->make); + eina_stringshare_del(disp->name); + free(disp); + } +} diff --git a/src/lib/ecore_drm2/ecore_drm2_planes.c b/src/lib/ecore_drm2/ecore_drm2_planes.c new file mode 100644 index 0000000000..d412ba0d7f --- /dev/null +++ b/src/lib/ecore_drm2/ecore_drm2_planes.c @@ -0,0 +1,263 @@ +#include "ecore_drm2_private.h" + +static void +_ecore_drm2_plane_state_debug(Ecore_Drm2_Plane *plane) +{ + DBG("Plane Atomic State Fill Complete"); + DBG("\tPlane: %d", plane->state->obj_id); + DBG("\t\tCrtc: %lu", (long)plane->state->cid.value); + DBG("\t\tFB: %lu", (long)plane->state->fid.value); + switch (plane->state->type.value) + { + case DRM_PLANE_TYPE_OVERLAY: + DBG("\t\tType: Overlay Plane"); + break; + case DRM_PLANE_TYPE_PRIMARY: + DBG("\t\tType: Primary Plane"); + break; + case DRM_PLANE_TYPE_CURSOR: + DBG("\t\tType: Cursor Plane"); + break; + default: + break; + } +} + +static void +_ecore_drm2_plane_state_fill(Ecore_Drm2_Plane *plane) +{ + Ecore_Drm2_Plane_State *pstate; + drmModeObjectPropertiesPtr oprops; + drmModePlanePtr p; + unsigned int i = 0; + + plane->state = calloc(1, sizeof(Ecore_Drm2_Plane_State)); + if (!plane->state) + { + ERR("Could not allocate space for plane state"); + return; + } + + p = plane->dplane; + pstate = plane->state; + + pstate->obj_id = plane->id; + pstate->mask = p->possible_crtcs; + pstate->num_formats = p->count_formats; + + pstate->formats = calloc(p->count_formats, sizeof(uint32_t)); + for (; i < p->count_formats; i++) + pstate->formats[i] = p->formats[i]; + + /* try to fill get drm properties of this plane */ + oprops = + sym_drmModeObjectGetProperties(plane->fd, pstate->obj_id, + DRM_MODE_OBJECT_PLANE); + if (!oprops) return; + + /* fill atomic state */ + for (i = 0; i < oprops->count_props; i++) + { + drmModePropertyPtr prop; + + prop = sym_drmModeGetProperty(plane->fd, oprops->props[i]); + if (!prop) continue; + + if (!strcmp(prop->name, "CRTC_ID")) + { + pstate->cid.id = prop->prop_id; + pstate->cid.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "FB_ID")) + { + pstate->fid.id = prop->prop_id; + pstate->fid.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "CRTC_X")) + { + pstate->cx.id = prop->prop_id; + pstate->cx.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "CRTC_Y")) + { + pstate->cy.id = prop->prop_id; + pstate->cy.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "CRTC_W")) + { + pstate->cw.id = prop->prop_id; + pstate->cw.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "CRTC_H")) + { + pstate->ch.id = prop->prop_id; + pstate->ch.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "SRC_X")) + { + pstate->sx.id = prop->prop_id; + pstate->sx.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "SRC_Y")) + { + pstate->sy.id = prop->prop_id; + pstate->sy.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "SRC_W")) + { + pstate->sw.id = prop->prop_id; + pstate->sw.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "SRC_H")) + { + pstate->sh.id = prop->prop_id; + pstate->sh.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "type")) + { + pstate->type.id = prop->prop_id; + pstate->type.value = oprops->prop_values[i]; + } + else if (!strcmp(prop->name, "rotation")) + { + int k = 0; + + pstate->rotation.id = prop->prop_id; + pstate->rotation.value = oprops->prop_values[i]; + + for (k = 0; k < prop->count_enums; k++) + { + int r = -1; + + /* DBG("\t\t\tRotation: %s", prop->enums[k].name); */ + if (!strcmp(prop->enums[k].name, "rotate-0")) + r = ECORE_DRM2_ROTATION_NORMAL; + else if (!strcmp(prop->enums[k].name, "rotate-90")) + r = ECORE_DRM2_ROTATION_90; + else if (!strcmp(prop->enums[k].name, "rotate-180")) + r = ECORE_DRM2_ROTATION_180; + else if (!strcmp(prop->enums[k].name, "rotate-270")) + r = ECORE_DRM2_ROTATION_270; + else if (!strcmp(prop->enums[k].name, "reflect-x")) + r = ECORE_DRM2_ROTATION_REFLECT_X; + else if (!strcmp(prop->enums[k].name, "reflect-y")) + r = ECORE_DRM2_ROTATION_REFLECT_Y; + + if (r != -1) + { + pstate->supported_rotations |= r; + pstate->rotation_map[ffs(r)] = + 1ULL << prop->enums[k].value; + } + } + } + + sym_drmModeFreeProperty(prop); + } + + sym_drmModeFreeObjectProperties(oprops); +} + +static void +_ecore_drm2_plane_state_thread(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Plane *plane; + + plane = data; + if (!plane->state) + _ecore_drm2_plane_state_fill(plane); + else + { + /* TODO: update atomic state for commit */ + } +} + +static void +_ecore_drm2_plane_state_thread_end(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Plane *plane; + + plane = data; + _ecore_drm2_plane_state_debug(plane); +} + +static void +_ecore_drm2_plane_state_thread_cancel(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecore_Drm2_Plane *plane; + + plane = data; + plane->thread = NULL; +} + +static Ecore_Drm2_Plane * +_ecore_drm2_plane_create(Ecore_Drm2_Device *dev, drmModePlanePtr p, uint32_t index) +{ + Ecore_Drm2_Plane *plane; + + /* try to allocate space for a new plane */ + plane = calloc(1, sizeof(Ecore_Drm2_Plane)); + if (!plane) + { + ERR("Could not allocate space for plane"); + return EINA_FALSE; + } + + plane->fd = dev->fd; + plane->id = index; + plane->dplane = p; + + /* append this plane to the list */ + dev->planes = eina_list_append(dev->planes, plane); + + return plane; +} + +Eina_Bool +_ecore_drm2_planes_create(Ecore_Drm2_Device *dev) +{ + Ecore_Drm2_Plane *plane; + drmModePlaneResPtr pres; + uint32_t i = 0; + + /* try to get plane resources from drm */ + pres = sym_drmModeGetPlaneResources(dev->fd); + if (!pres) return EINA_FALSE; + + for (; i < pres->count_planes; i++) + { + drmModePlanePtr p; + + /* try to get this plane from drm */ + p = sym_drmModeGetPlane(dev->fd, pres->planes[i]); + if (!p) continue; + + /* try to create a plane */ + plane = _ecore_drm2_plane_create(dev, p, pres->planes[i]); + if (!plane) continue; + + /* NB: Use an explicit thread to fill plane atomic state */ + plane->thread = + ecore_thread_feedback_run(_ecore_drm2_plane_state_thread, + NULL, // _ecore_drm2_plane_state_thread_notify + _ecore_drm2_plane_state_thread_end, + _ecore_drm2_plane_state_thread_cancel, + plane, EINA_TRUE); + } + + sym_drmModeFreePlaneResources(pres); + return EINA_TRUE; +} + +void +_ecore_drm2_planes_destroy(Ecore_Drm2_Device *dev) +{ + Ecore_Drm2_Plane *plane; + + EINA_LIST_FREE(dev->planes, plane) + { + if (plane->dplane) sym_drmModeFreePlane(plane->dplane); + free(plane->state); + free(plane); + } +} diff --git a/src/lib/ecore_drm2/meson.build b/src/lib/ecore_drm2/meson.build index dc672dc4ba..eb775fac60 100644 --- a/src/lib/ecore_drm2/meson.build +++ b/src/lib/ecore_drm2/meson.build @@ -7,7 +7,10 @@ ecore_drm2_header_src = [ ] ecore_drm2_src = files([ - 'ecore_drm2_atomic.c', + 'ecore_drm2_planes.c', + 'ecore_drm2_displays.c', + 'ecore_drm2_connectors.c', + 'ecore_drm2_crtcs.c', 'ecore_drm2_device.c', 'ecore_drm2.c', 'ecore_drm2_private.h'