1108 lines
29 KiB
C
1108 lines
29 KiB
C
#include "ecore_drm2_private.h"
|
|
|
|
#define INSIDE(x, y, xx, yy, ww, hh) \
|
|
(((x) < ((xx) + (ww))) && ((y) < ((yy) + (hh))) && \
|
|
((x) >= (xx)) && ((y) >= (yy)))
|
|
|
|
#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 Eina_Thread_Queue *thq = NULL;
|
|
|
|
typedef struct
|
|
{
|
|
Eina_Thread_Queue_Msg head;
|
|
Ecore_Drm2_Thread_Op_Code code;
|
|
} Thread_Msg;
|
|
|
|
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 void
|
|
_ecore_drm2_display_state_thread_send(Ecore_Drm2_Thread_Op_Code code)
|
|
{
|
|
Thread_Msg *msg;
|
|
void *ref;
|
|
|
|
msg = eina_thread_queue_send(thq, sizeof(Thread_Msg), &ref);
|
|
msg->code = code;
|
|
eina_thread_queue_send_done(thq, ref);
|
|
}
|
|
|
|
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->drmConn->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.current;
|
|
|
|
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)
|
|
{
|
|
Eina_List *l = NULL;
|
|
Ecore_Drm2_Display_Mode *mode;
|
|
|
|
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->drmCrtc->x, disp->crtc->drmCrtc->y);
|
|
DBG("\tConnector: %d", disp->conn->id);
|
|
|
|
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);
|
|
}
|
|
|
|
EINA_LIST_FOREACH(disp->modes, l, mode)
|
|
{
|
|
DBG("\tAdded Mode: %dx%d@%.1f%s%s%s, %.1f MHz",
|
|
mode->width, mode->height, mode->refresh / 1000.0,
|
|
(mode->flags & DRM_MODE_TYPE_PREFERRED) ? ", preferred" : "",
|
|
(mode->flags & DRM_MODE_TYPE_DEFAULT) ? ", current" : "",
|
|
(disp->conn->drmConn->count_modes == 0) ? ", built-in" : "",
|
|
mode->info.clock / 1000.0);
|
|
}
|
|
|
|
/* DBG("\tCloned: %d", disp->cloned); */
|
|
DBG("\tPrimary: %d", disp->state.current->primary);
|
|
DBG("\tEnabled: %d", disp->state.current->enabled);
|
|
DBG("\tConnected: %d", disp->connected);
|
|
}
|
|
|
|
static double
|
|
_ecore_drm2_display_backlight_value_get(Ecore_Drm2_Display *disp, const char *attr)
|
|
{
|
|
const char *b = NULL;
|
|
double ret = 0.0;
|
|
|
|
if ((!disp) || (!disp->backlight.path)) return 0.0;
|
|
|
|
b = eeze_udev_syspath_get_sysattr(disp->backlight.path, attr);
|
|
if (!b) return 0.0;
|
|
|
|
ret = strtod(b, NULL);
|
|
if (ret < 0) ret = 0.0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
_ecore_drm2_display_backlight_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
Eina_List *devs, *l;
|
|
const char *dev, *t;
|
|
Ecore_Drm2_Backlight_Type type = 0;
|
|
Eina_Bool found = EINA_FALSE;
|
|
|
|
devs = eeze_udev_find_by_filter("backlight", NULL, NULL);
|
|
|
|
EINA_LIST_FOREACH(devs, l, dev)
|
|
{
|
|
t = eeze_udev_syspath_get_sysattr(dev, "type");
|
|
if (!t) continue;
|
|
|
|
if (!strcmp(t, "raw"))
|
|
type = ECORE_DRM2_BACKLIGHT_RAW;
|
|
else if (!strcmp(t, "platform"))
|
|
type = ECORE_DRM2_BACKLIGHT_PLATFORM;
|
|
else if (!strcmp(t, "firmware"))
|
|
type = ECORE_DRM2_BACKLIGHT_FIRMWARE;
|
|
|
|
if ((disp->conn->type == DRM_MODE_CONNECTOR_LVDS) ||
|
|
(disp->conn->type == DRM_MODE_CONNECTOR_eDP) ||
|
|
(type == ECORE_DRM2_BACKLIGHT_RAW))
|
|
found = EINA_TRUE;
|
|
|
|
eina_stringshare_del(t);
|
|
if (found) break;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
disp->backlight.type = type;
|
|
disp->backlight.path = eina_stringshare_add(dev);
|
|
disp->backlight.max =
|
|
_ecore_drm2_display_backlight_value_get(disp, "max_brightness");
|
|
disp->state.current->backlight =
|
|
_ecore_drm2_display_backlight_value_get(disp, "brightness");
|
|
}
|
|
|
|
EINA_LIST_FREE(devs, dev)
|
|
eina_stringshare_del(dev);
|
|
}
|
|
|
|
static Ecore_Drm2_Display_Mode *
|
|
_ecore_drm2_display_mode_create(const drmModeModeInfo *info)
|
|
{
|
|
Ecore_Drm2_Display_Mode *mode;
|
|
uint64_t refresh;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(info, NULL);
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL((info->htotal > 0), NULL);
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL((info->vtotal > 0), NULL);
|
|
|
|
mode = calloc(1, sizeof(Ecore_Drm2_Display_Mode));
|
|
if (!mode) return NULL;
|
|
|
|
mode->flags = 0;
|
|
mode->width = info->hdisplay;
|
|
mode->height = info->vdisplay;
|
|
|
|
refresh = (info->clock * 1000000LL / info->htotal +
|
|
info->vtotal / 2) / info->vtotal;
|
|
|
|
if (info->flags & DRM_MODE_FLAG_INTERLACE)
|
|
refresh *= 2;
|
|
if (info->flags & DRM_MODE_FLAG_DBLSCAN)
|
|
refresh /= 2;
|
|
if (info->vscan > 1)
|
|
refresh /= info->vscan;
|
|
|
|
mode->refresh = refresh;
|
|
mode->info = *info;
|
|
|
|
if (info->type & DRM_MODE_TYPE_PREFERRED)
|
|
mode->flags |= DRM_MODE_TYPE_PREFERRED;
|
|
|
|
return mode;
|
|
}
|
|
|
|
static void
|
|
_ecore_drm2_display_modes_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
int i = 0;
|
|
drmModeModeInfo crtc_mode;
|
|
Ecore_Drm2_Display_Mode *dmode;
|
|
Ecore_Drm2_Display_Mode *current = NULL, *pref = NULL, *best = NULL;
|
|
Eina_List *l = NULL;
|
|
|
|
memset(&crtc_mode, 0, sizeof(crtc_mode));
|
|
|
|
if (disp->crtc->drmCrtc->mode_valid)
|
|
crtc_mode = disp->crtc->drmCrtc->mode;
|
|
|
|
/* loop through connector modes and try to create mode */
|
|
for (; i < disp->conn->drmConn->count_modes; i++)
|
|
{
|
|
dmode =
|
|
_ecore_drm2_display_mode_create(&disp->conn->drmConn->modes[i]);
|
|
if (!dmode) continue;
|
|
|
|
/* append mode to display mode list */
|
|
disp->modes = eina_list_append(disp->modes, dmode);
|
|
}
|
|
|
|
/* try to select current mode */
|
|
EINA_LIST_REVERSE_FOREACH(disp->modes, l, dmode)
|
|
{
|
|
if (!memcmp(&crtc_mode, &dmode->info, sizeof(crtc_mode)))
|
|
current = dmode;
|
|
if (dmode->flags & DRM_MODE_TYPE_PREFERRED)
|
|
pref = dmode;
|
|
best = dmode;
|
|
}
|
|
|
|
if ((!current) && (crtc_mode.clock != 0))
|
|
{
|
|
current = _ecore_drm2_display_mode_create(&crtc_mode);
|
|
if (!current) goto err;
|
|
disp->modes = eina_list_append(disp->modes, current);
|
|
}
|
|
|
|
if (current) disp->state.current->mode = current;
|
|
else if (pref) disp->state.current->mode = pref;
|
|
else if (best) disp->state.current->mode = best;
|
|
|
|
if (!disp->state.current->mode) goto err;
|
|
|
|
disp->state.current->mode->flags |= DRM_MODE_TYPE_DEFAULT;
|
|
|
|
return;
|
|
|
|
err:
|
|
EINA_LIST_FREE(disp->modes, dmode)
|
|
free(dmode);
|
|
}
|
|
|
|
static void
|
|
_ecore_drm2_display_rotation_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
Ecore_Drm2_Plane *plane;
|
|
|
|
/* try to find primary plane for this display */
|
|
plane = _ecore_drm2_planes_primary_find(disp->dev, disp->crtc->id);
|
|
if (plane)
|
|
{
|
|
if (plane->state.current)
|
|
disp->state.current->rotation = plane->state.current->rotation.value;
|
|
else
|
|
{
|
|
drmModeObjectPropertiesPtr oprops;
|
|
|
|
/* NB: Sadly we cannot rely on plane->state.current being already
|
|
* filled by the time we reach this (due to threading),
|
|
* so we will query the plane properties we want directly */
|
|
|
|
/* query plane for rotations */
|
|
oprops =
|
|
sym_drmModeObjectGetProperties(plane->fd,
|
|
plane->drmPlane->plane_id,
|
|
DRM_MODE_OBJECT_PLANE);
|
|
if (oprops)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
for (; i < oprops->count_props; i++)
|
|
{
|
|
drmModePropertyPtr prop;
|
|
|
|
prop = sym_drmModeGetProperty(plane->fd, oprops->props[i]);
|
|
if (!prop) continue;
|
|
|
|
if (!strcmp(prop->name, "rotation"))
|
|
disp->state.current->rotation = oprops->prop_values[i];
|
|
|
|
sym_drmModeFreeProperty(prop);
|
|
}
|
|
sym_drmModeFreeObjectProperties(oprops);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ecore_drm2_display_state_fill(Ecore_Drm2_Display *disp)
|
|
{
|
|
char *name = NULL;
|
|
|
|
/* try to allocate space for current Display state */
|
|
disp->state.current = calloc(1, sizeof(Ecore_Drm2_Display_State));
|
|
if (!disp->state.current)
|
|
{
|
|
ERR("Could not allocate space for Display state");
|
|
return;
|
|
}
|
|
|
|
/* 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->drmConn->mmWidth;
|
|
disp->ph = disp->conn->drmConn->mmHeight;
|
|
|
|
/* get subpixel */
|
|
switch (disp->conn->drmConn->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 current rotation value */
|
|
_ecore_drm2_display_rotation_get(disp);
|
|
|
|
/* get backlight values */
|
|
_ecore_drm2_display_backlight_get(disp);
|
|
|
|
/* get available display modes */
|
|
_ecore_drm2_display_modes_get(disp);
|
|
|
|
/* get gamma from crtc */
|
|
disp->state.current->gamma = disp->crtc->drmCrtc->gamma_size;
|
|
|
|
/* get connected state */
|
|
disp->connected = (disp->conn->drmConn->connection == DRM_MODE_CONNECTED);
|
|
|
|
/* duplicate current state into pending so we can handle changes */
|
|
disp->state.pending = calloc(1, sizeof(Ecore_Drm2_Display_State));
|
|
if (disp->state.pending)
|
|
memcpy(disp->state.pending, disp->state.current, sizeof(Ecore_Drm2_Display_State));
|
|
|
|
/* send message to thread for debug printing display state */
|
|
_ecore_drm2_display_state_thread_send(ECORE_DRM2_THREAD_CODE_DEBUG);
|
|
}
|
|
|
|
static void
|
|
_ecore_drm2_display_state_thread(void *data, Ecore_Thread *thread EINA_UNUSED)
|
|
{
|
|
Ecore_Drm2_Display *disp;
|
|
Thread_Msg *msg;
|
|
void *ref;
|
|
|
|
disp = data;
|
|
|
|
eina_thread_name_set(eina_thread_self(), "Ecore-drm2-display");
|
|
|
|
while (!ecore_thread_check(thread))
|
|
{
|
|
msg = eina_thread_queue_wait(thq, &ref);
|
|
if (msg)
|
|
{
|
|
switch (msg->code)
|
|
{
|
|
case ECORE_DRM2_THREAD_CODE_FILL:
|
|
_ecore_drm2_display_state_fill(disp);
|
|
break;
|
|
case ECORE_DRM2_THREAD_CODE_DEBUG:
|
|
_ecore_drm2_display_state_debug(disp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
eina_thread_queue_wait_done(thq, ref);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ecore_drm2_display_state_thread_notify(void *data EINA_UNUSED, Ecore_Thread *thread EINA_UNUSED, void *msg)
|
|
{
|
|
free(msg);
|
|
}
|
|
|
|
static unsigned int
|
|
_ecore_drm2_display_vblank_pipe(Ecore_Drm2_Display *disp)
|
|
{
|
|
if (!disp->crtc) return 0;
|
|
|
|
if (disp->crtc->pipe > 1)
|
|
{
|
|
return (disp->crtc->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT) &
|
|
DRM_VBLANK_HIGH_CRTC_MASK;
|
|
}
|
|
else if (disp->crtc->pipe > 0)
|
|
return DRM_VBLANK_SECONDARY;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_ecore_drm2_display_clock_read(Ecore_Drm2_Display *disp, struct timespec *tsnow)
|
|
{
|
|
int ret;
|
|
|
|
ret = clock_gettime(disp->dev->clock_id, tsnow);
|
|
if (ret < 0)
|
|
{
|
|
tsnow->tv_sec = 0;
|
|
tsnow->tv_nsec = 0;
|
|
|
|
WRN("Failed to read display clock %#x: '%s' (%d)",
|
|
disp->dev->clock_id, strerror(errno), errno);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ecore_drm2_display_msc_update(Ecore_Drm2_Display *disp, unsigned int sequence)
|
|
{
|
|
uint32_t mh;
|
|
|
|
mh = disp->msc >> 32;
|
|
if (sequence < (disp->msc & 0xffffffff))
|
|
mh++;
|
|
|
|
disp->msc = ((uint64_t)mh << 32) + sequence;
|
|
}
|
|
|
|
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;
|
|
|
|
thq = eina_thread_queue_new();
|
|
|
|
/* 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->drmConn->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;
|
|
}
|
|
|
|
disp->dev = dev;
|
|
|
|
/* 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,
|
|
_ecore_drm2_display_state_thread_notify,
|
|
NULL, NULL, 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)
|
|
{
|
|
if (disp->thread) ecore_thread_cancel(disp->thread);
|
|
eina_stringshare_del(disp->backlight.path);
|
|
eina_stringshare_del(disp->relative.to);
|
|
eina_stringshare_del(disp->serial);
|
|
eina_stringshare_del(disp->model);
|
|
eina_stringshare_del(disp->make);
|
|
eina_stringshare_del(disp->name);
|
|
free(disp->state.pending);
|
|
free(disp->state.current);
|
|
free(disp);
|
|
}
|
|
|
|
if (thq)
|
|
{
|
|
eina_thread_queue_free(thq);
|
|
thq = NULL;
|
|
}
|
|
}
|
|
|
|
EAPI char *
|
|
ecore_drm2_display_name_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp->name, NULL);
|
|
return strdup(disp->name);
|
|
}
|
|
|
|
EAPI char *
|
|
ecore_drm2_display_model_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp->model, NULL);
|
|
return strdup(disp->model);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_mode_set(Ecore_Drm2_Display *disp, Ecore_Drm2_Display_Mode *mode, int x, int y)
|
|
{
|
|
Ecore_Drm2_Display_State *cstate, *pstate;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
EINA_SAFETY_ON_NULL_RETURN(mode);
|
|
EINA_SAFETY_ON_NULL_RETURN(disp->crtc);
|
|
|
|
cstate = disp->state.current;
|
|
pstate = disp->state.pending;
|
|
|
|
if ((cstate->x != x) || (cstate->y != y))
|
|
{
|
|
pstate->x = x;
|
|
pstate->y = y;
|
|
pstate->changes |= ECORE_DRM2_DISPLAY_STATE_POSITION;
|
|
}
|
|
|
|
if (cstate->mode != mode)
|
|
{
|
|
pstate->mode = mode;
|
|
pstate->changes |= ECORE_DRM2_DISPLAY_STATE_MODE;
|
|
}
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_drm2_display_backlight_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, EINA_FALSE);
|
|
return (disp->backlight.path != NULL);
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_drm2_display_connected_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, EINA_FALSE);
|
|
return disp->connected;
|
|
}
|
|
|
|
EAPI unsigned int
|
|
ecore_drm2_display_connector_type_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, 0);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp->conn, 0);
|
|
return disp->conn->type;
|
|
}
|
|
|
|
EAPI unsigned int
|
|
ecore_drm2_display_subpixel_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, 0);
|
|
return disp->subpixel;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_physical_size_get(Ecore_Drm2_Display *disp, int *w, int *h)
|
|
{
|
|
if (w) *w = 0;
|
|
if (h) *h = 0;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
|
|
if (w) *w = disp->pw;
|
|
if (h) *h = disp->ph;
|
|
}
|
|
|
|
EAPI int
|
|
ecore_drm2_display_dpms_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, -1);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp->conn, -1);
|
|
return disp->conn->state.current->dpms.value;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_dpms_set(Ecore_Drm2_Display *disp, uint64_t level)
|
|
{
|
|
Ecore_Drm2_Connector_State *cstate, *pstate;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
EINA_SAFETY_ON_NULL_RETURN(disp->conn);
|
|
|
|
cstate = disp->conn->state.current;
|
|
if (cstate->dpms.value == level) return;
|
|
|
|
pstate = disp->conn->state.pending;
|
|
pstate->dpms.value = level;
|
|
pstate->changes |= ECORE_DRM2_CONNECTOR_STATE_DPMS;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_drm2_display_enabled_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, EINA_FALSE);
|
|
return disp->state.current->enabled;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_enabled_set(Ecore_Drm2_Display *disp, Eina_Bool enabled)
|
|
{
|
|
Ecore_Drm2_Display_State *cstate, *pstate;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
|
|
cstate = disp->state.current;
|
|
pstate = disp->state.pending;
|
|
|
|
if (cstate->enabled == enabled) return;
|
|
|
|
/* TODO, FIXME */
|
|
|
|
pstate->enabled = enabled;
|
|
pstate->changes |= ECORE_DRM2_DISPLAY_STATE_ENABLED;
|
|
}
|
|
|
|
EAPI char *
|
|
ecore_drm2_display_edid_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
char *edid_str = NULL;
|
|
unsigned char *blob;
|
|
unsigned char fblob[128];
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp->conn, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp->conn->state.current, NULL);
|
|
|
|
blob = disp->conn->state.current->edid.data;
|
|
if (!blob)
|
|
{
|
|
memset(fblob, 0, sizeof(fblob));
|
|
blob = fblob;
|
|
}
|
|
|
|
edid_str = malloc((128 * 2) + 1);
|
|
if (edid_str)
|
|
{
|
|
unsigned int k, kk;
|
|
const char *hexch = "0123456789abcdef";
|
|
|
|
for (kk = 0, k = 0; k < 128; k++)
|
|
{
|
|
edid_str[kk] = hexch[(blob[k] >> 4) & 0xf];
|
|
edid_str[kk + 1] = hexch[blob[k] & 0xf];
|
|
kk += 2;
|
|
}
|
|
edid_str[kk] = 0;
|
|
}
|
|
|
|
return edid_str;
|
|
}
|
|
|
|
EAPI const Eina_List *
|
|
ecore_drm2_display_modes_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, NULL);
|
|
return disp->modes;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_mode_info_get(Ecore_Drm2_Display_Mode *mode, int *w, int *h, unsigned int *refresh, unsigned int *flags)
|
|
{
|
|
if (w) *w = 0;
|
|
if (h) *h = 0;
|
|
if (refresh) *refresh = 0;
|
|
if (flags) *flags = 0;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(mode);
|
|
|
|
if (w) *w = mode->width;
|
|
if (h) *h = mode->height;
|
|
if (refresh) *refresh = mode->refresh;
|
|
if (flags) *flags = mode->flags;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_drm2_display_primary_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, EINA_FALSE);
|
|
return disp->state.current->primary;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_primary_set(Ecore_Drm2_Display *disp, Eina_Bool primary)
|
|
{
|
|
Ecore_Drm2_Display_State *cstate, *pstate;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
|
|
cstate = disp->state.current;
|
|
pstate = disp->state.pending;
|
|
|
|
if (cstate->primary == primary) return;
|
|
|
|
/* TODO, FIXME */
|
|
|
|
pstate->primary = primary;
|
|
pstate->changes |= ECORE_DRM2_DISPLAY_STATE_PRIMARY;
|
|
}
|
|
|
|
EAPI const Eina_List *
|
|
ecore_drm2_displays_get(Ecore_Drm2_Device *dev)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(dev, EINA_FALSE);
|
|
return dev->displays;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_info_get(Ecore_Drm2_Display *disp, int *x, int *y, int *w, int *h, unsigned int *refresh)
|
|
{
|
|
Ecore_Drm2_Display_State *cstate;
|
|
|
|
if (x) *x = 0;
|
|
if (y) *y = 0;
|
|
if (w) *w = 0;
|
|
if (h) *h = 0;
|
|
if (refresh) *refresh = 0;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
EINA_SAFETY_ON_TRUE_RETURN(!disp->state.current->mode);
|
|
|
|
if (x) *x = disp->x;
|
|
if (y) *y = disp->y;
|
|
|
|
cstate = disp->state.current;
|
|
|
|
switch (cstate->rotation)
|
|
{
|
|
case ECORE_DRM2_ROTATION_90:
|
|
case ECORE_DRM2_ROTATION_270:
|
|
if (w) *w = cstate->mode->height;
|
|
if (h) *h = cstate->mode->width;
|
|
break;
|
|
case ECORE_DRM2_ROTATION_NORMAL:
|
|
case ECORE_DRM2_ROTATION_180:
|
|
default:
|
|
if (w) *w = cstate->mode->width;
|
|
if (h) *h = cstate->mode->height;
|
|
break;
|
|
}
|
|
|
|
if (refresh) *refresh = cstate->mode->refresh;
|
|
}
|
|
|
|
EAPI int
|
|
ecore_drm2_display_rotation_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, -1);
|
|
return disp->state.current->rotation;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_rotation_set(Ecore_Drm2_Display *disp, uint64_t rotation)
|
|
{
|
|
Ecore_Drm2_Display_State *cstate, *pstate;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
|
|
cstate = disp->state.current;
|
|
pstate = disp->state.pending;
|
|
|
|
if (cstate->rotation == rotation) return;
|
|
|
|
/* TODO, FIXME */
|
|
|
|
pstate->rotation = rotation;
|
|
pstate->changes |= ECORE_DRM2_DISPLAY_STATE_ROTATION;
|
|
}
|
|
|
|
EAPI Ecore_Drm2_Crtc *
|
|
ecore_drm2_display_crtc_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, NULL);
|
|
return disp->crtc;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_drm2_display_possible_crtc_get(Ecore_Drm2_Display *disp, Ecore_Drm2_Crtc *crtc)
|
|
{
|
|
drmModeConnector *conn;
|
|
drmModeEncoder *enc;
|
|
int i = 0;
|
|
Eina_Bool ret = EINA_FALSE;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp->conn, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp->conn->drmConn, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(crtc, EINA_FALSE);
|
|
|
|
conn = disp->conn->drmConn;
|
|
for (; i < conn->count_encoders; i++)
|
|
{
|
|
enc = sym_drmModeGetEncoder(disp->conn->fd, conn->encoders[i]);
|
|
if (!enc) continue;
|
|
|
|
if (enc->crtc_id != crtc->id) goto next;
|
|
|
|
if (enc->possible_crtcs & (1 << crtc->pipe))
|
|
ret = EINA_TRUE;
|
|
|
|
next:
|
|
sym_drmModeFreeEncoder(enc);
|
|
if (ret) break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
EAPI int
|
|
ecore_drm2_display_supported_rotations_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
Ecore_Drm2_Plane *plane;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, -1);
|
|
|
|
/* get the primary plane for this output */
|
|
plane = _ecore_drm2_planes_primary_find(disp->dev, disp->crtc->id);
|
|
if (!plane) return -1;
|
|
|
|
/* if plane state has not been filled yet, bail out */
|
|
/* NB: We could modify this to get the plane rotations directly from drm */
|
|
if (!plane->state.current) return -1;
|
|
|
|
/* return primary plane state supported_rotations */
|
|
return plane->state.current->supported_rotations;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_relative_mode_set(Ecore_Drm2_Display *disp, Ecore_Drm2_Relative_Mode mode)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
disp->relative.mode = mode;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_relative_to_set(Ecore_Drm2_Display *disp, const char *relative)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
eina_stringshare_replace(&disp->relative.to, relative);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_dpi_get(Ecore_Drm2_Display *disp, int *xdpi, int *ydpi)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
EINA_SAFETY_ON_TRUE_RETURN(!disp->state.current->enabled);
|
|
|
|
if (xdpi)
|
|
*xdpi = ((25.4 * (disp->state.current->mode->width)) / disp->pw);
|
|
|
|
if (ydpi)
|
|
*ydpi = ((25.4 * (disp->state.current->mode->height)) / disp->ph);
|
|
}
|
|
|
|
EAPI Ecore_Drm2_Display *
|
|
ecore_drm2_display_find(Ecore_Drm2_Device *dev, int x, int y)
|
|
{
|
|
Eina_List *l;
|
|
Ecore_Drm2_Display *disp;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(dev, NULL);
|
|
|
|
EINA_LIST_FOREACH(dev->displays, l, disp)
|
|
{
|
|
int ox, oy, ow, oh;
|
|
|
|
if (!disp->state.current->enabled) continue;
|
|
|
|
ecore_drm2_display_info_get(disp, &ox, &oy, &ow, &oh, NULL);
|
|
if (INSIDE(x, y, ox, oy, ow, oh))
|
|
return disp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm2_display_user_data_set(Ecore_Drm2_Display *disp, void *data)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN(disp);
|
|
disp->user_data = data;
|
|
}
|
|
|
|
EAPI void *
|
|
ecore_drm2_display_user_data_get(Ecore_Drm2_Display *disp)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, NULL);
|
|
return disp->user_data;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_drm2_display_blanktime_get(Ecore_Drm2_Display *disp, int seq, long *sec, long *usec)
|
|
{
|
|
drmVBlank vbl;
|
|
int ret;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(disp, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(sec, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(usec, EINA_FALSE);
|
|
|
|
memset(&vbl, 0, sizeof(vbl));
|
|
vbl.request.type = DRM_VBLANK_RELATIVE;
|
|
vbl.request.type |= _ecore_drm2_display_vblank_pipe(disp);
|
|
vbl.request.sequence = seq;
|
|
vbl.request.signal = 0;
|
|
|
|
/* try to get timestamp from drm vblank query */
|
|
ret = sym_drmWaitVBlank(disp->dev->fd, &vbl);
|
|
|
|
/* ret or zero timestamp is failure to get valid timestamp */
|
|
if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0))
|
|
{
|
|
struct timespec ts, tsnow, vblnow;
|
|
int64_t nsec, rnsec;
|
|
|
|
ts.tv_sec = vbl.reply.tval_sec;
|
|
ts.tv_nsec = vbl.reply.tval_usec * 1000;
|
|
|
|
/* read clock */
|
|
_ecore_drm2_display_clock_read(disp, &tsnow);
|
|
|
|
vblnow.tv_sec = tsnow.tv_sec - ts.tv_sec;
|
|
vblnow.tv_nsec = tsnow.tv_nsec - ts.tv_nsec;
|
|
if (vblnow.tv_nsec < 0)
|
|
{
|
|
vblnow.tv_sec--;
|
|
vblnow.tv_nsec += 1000000000;
|
|
}
|
|
|
|
rnsec = (1000000000000LL / disp->state.current->mode->refresh);
|
|
nsec = (int64_t)vblnow.tv_sec * 1000000000 + vblnow.tv_nsec;
|
|
if (nsec < rnsec)
|
|
{
|
|
/* update msc */
|
|
_ecore_drm2_display_msc_update(disp, vbl.reply.sequence);
|
|
return EINA_TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* TODO: Do we need to provide a timestamp using pageflip fallback ? */
|
|
}
|
|
}
|
|
|
|
*sec = vbl.reply.tval_sec;
|
|
*usec = vbl.reply.tval_usec;
|
|
return EINA_TRUE;
|
|
}
|