ecore_drm2: Add code to fill atomic state in threads for Crtcs, Connectors,

Displays, and Planes
This commit is contained in:
Christopher Michael 2022-09-20 08:40:16 -04:00
parent 2958c10c93
commit 5dffb60708
6 changed files with 1169 additions and 45 deletions

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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'