diff options
Diffstat (limited to 'src/lib/ecore_drm2/ecore_drm2_outputs.c')
-rw-r--r-- | src/lib/ecore_drm2/ecore_drm2_outputs.c | 886 |
1 files changed, 886 insertions, 0 deletions
diff --git a/src/lib/ecore_drm2/ecore_drm2_outputs.c b/src/lib/ecore_drm2/ecore_drm2_outputs.c new file mode 100644 index 0000000000..d7f1c39f03 --- /dev/null +++ b/src/lib/ecore_drm2/ecore_drm2_outputs.c @@ -0,0 +1,886 @@ +#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 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 +_output_debug(Ecore_Drm2_Output *output, const drmModeConnector *conn) +{ + Eina_List *l; + Ecore_Drm2_Output_Mode *omode; + + DBG("Created New Output At %d,%d", output->x, output->y); + DBG("\tCrtc Pos: %d %d", output->ocrtc->x, output->ocrtc->y); + DBG("\tCrtc: %d", output->crtc_id); + DBG("\tConn: %d", output->conn_id); + DBG("\tName: %s", output->name); + DBG("\tMake: %s", output->make); + DBG("\tModel: %s", output->model); + DBG("\tSerial: %s", output->serial); + DBG("\tCloned: %d", output->cloned); + DBG("\tPrimary: %d", output->primary); + DBG("\tConnected: %d", output->connected); + DBG("\tEnabled: %d", output->enabled); + + if (output->backlight.path) + { + DBG("\tBacklight"); + switch (output->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", output->backlight.path); + } + + EINA_LIST_FOREACH(output->modes, l, omode) + { + DBG("\tAdded Mode: %dx%d@%.1f%s%s%s", + omode->width, omode->height, (omode->refresh / 1000.0), + (omode->flags & DRM_MODE_TYPE_PREFERRED) ? ", preferred" : "", + (omode->flags & DRM_MODE_TYPE_DEFAULT) ? ", current" : "", + (conn->count_modes == 0) ? ", built-in" : ""); + } +} + +static void +_cb_output_event_free(void *data EINA_UNUSED, void *event) +{ + Ecore_Drm2_Event_Output_Changed *ev; + + ev = event; + eina_stringshare_del(ev->make); + eina_stringshare_del(ev->model); + eina_stringshare_del(ev->name); + free(ev); +} + +static void +_output_event_send(Ecore_Drm2_Output *output) +{ + Ecore_Drm2_Event_Output_Changed *ev; + + ev = calloc(1, sizeof(Ecore_Drm2_Event_Output_Changed)); + if (!ev) return; + + ev->id = output->crtc_id; + + ev->x = output->x; + ev->y = output->y; + ev->w = output->current_mode->width; + ev->h = output->current_mode->height; + ev->phys_width = output->pw; + ev->phys_height = output->ph; + ev->refresh = output->current_mode->refresh; + + ev->scale = output->scale; + ev->subpixel = output->subpixel; + ev->transform = output->transform; + ev->connected = output->connected; + ev->enabled = output->enabled; + + ev->name = eina_stringshare_ref(output->name); + ev->make = eina_stringshare_ref(output->make); + ev->model = eina_stringshare_ref(output->model); + + ecore_event_add(ECORE_DRM2_EVENT_OUTPUT_CHANGED, ev, + _cb_output_event_free, NULL); +} + +static void +_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 > 4) text[0] = '\0'; +} + +static int +_output_edid_parse(Ecore_Drm2_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) + _output_edid_parse_string(&data[i + 5], output->edid.monitor); + else if (data[i + 3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) + _output_edid_parse_string(&data[i + 5], output->edid.serial); + else if (data[i + 3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) + _output_edid_parse_string(&data[i + 5], output->edid.eisa); + } + + return 0; +} + +static void +_output_edid_find(Ecore_Drm2_Output *output, const drmModeConnector *conn) +{ + drmModePropertyBlobPtr blob = NULL; + drmModePropertyPtr prop; + int i = 0, ret = 0; + + for (; i < conn->count_props && !blob; i++) + { + if (!(prop = drmModeGetProperty(output->fd, conn->props[i]))) + continue; + if ((prop->flags & DRM_MODE_PROP_BLOB) && + (!strcmp(prop->name, "EDID"))) + { + blob = drmModeGetPropertyBlob(output->fd, conn->prop_values[i]); + } + drmModeFreeProperty(prop); + if (blob) break; + } + + if (!blob) return; + + output->edid.blob = eina_memdup(blob->data, blob->length, 1); + + ret = _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 int +_output_crtc_find(const drmModeRes *res, const drmModeConnector *conn, Ecore_Drm2_Device *dev) +{ + drmModeEncoder *enc; + uint32_t pcrtcs; + int i = 0, j = 0; + + for (j = 0; j < conn->count_encoders; j++) + { + enc = drmModeGetEncoder(dev->fd, conn->encoders[j]); + if (!enc) continue; + + pcrtcs = enc->possible_crtcs; + drmModeFreeEncoder(enc); + + for (i = 0; i < res->count_crtcs; i++) + { + if ((pcrtcs & (1 << i)) && + (!(dev->alloc.crtc & (1 << res->crtcs[i])))) + return i; + } + } + + return -1; +} + +static char * +_output_name_get(const drmModeConnector *conn) +{ + char name[DRM_CONNECTOR_NAME_LEN]; + const char *type = NULL; + + if (conn->connector_type < EINA_C_ARRAY_LENGTH(conn_types)) + type = conn_types[conn->connector_type]; + else + type = "UNKNOWN"; + + snprintf(name, sizeof(name), "%s-%d", type, conn->connector_type_id); + return strdup(name); +} + +static Ecore_Drm2_Output_Mode * +_output_mode_add(Ecore_Drm2_Output *output, const drmModeModeInfo *info) +{ + Ecore_Drm2_Output_Mode *mode; + uint64_t refresh; + + mode = calloc(1, sizeof(Ecore_Drm2_Output_Mode)); + if (!mode) return NULL; + + mode->flags = 0; + mode->width = info->hdisplay; + mode->height = info->vdisplay; + + refresh = (info->clock * 1000LL / 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; + + output->modes = eina_list_append(output->modes, mode); + + return mode; +} + +static void +_output_modes_create(Ecore_Drm2_Device *dev, Ecore_Drm2_Output *output, const drmModeConnector *conn) +{ + int i = 0; + drmModeCrtc *crtc; + drmModeEncoder *enc; + drmModeModeInfo crtc_mode; + Ecore_Drm2_Output_Mode *omode; + Ecore_Drm2_Output_Mode *current = NULL, *preferred = NULL, *best = NULL; + Eina_List *l = NULL; + + memset(&crtc_mode, 0, sizeof(crtc_mode)); + + enc = drmModeGetEncoder(dev->fd, conn->encoder_id); + if (enc) + { + crtc = drmModeGetCrtc(dev->fd, enc->crtc_id); + drmModeFreeEncoder(enc); + if (!crtc) return; + if (crtc->mode_valid) crtc_mode = crtc->mode; + drmModeFreeCrtc(crtc); + } + + for (i = 0; i < conn->count_modes; i++) + { + omode = _output_mode_add(output, &conn->modes[i]); + if (!omode) continue; + } + + EINA_LIST_REVERSE_FOREACH(output->modes, l, omode) + { + if (!memcmp(&crtc_mode, &omode->info, sizeof(crtc_mode))) + current = omode; + if (omode->flags & DRM_MODE_TYPE_PREFERRED) + preferred = omode; + best = omode; + } + + if ((!current) && (crtc_mode.clock != 0)) + { + current = _output_mode_add(output, &crtc_mode); + if (!current) goto err; + } + + if (current) output->current_mode = current; + else if (preferred) output->current_mode = preferred; + else if (best) output->current_mode = best; + + if (!output->current_mode) goto err; + + output->current_mode->flags |= DRM_MODE_TYPE_DEFAULT; + + return; + +err: + EINA_LIST_FREE(output->modes, omode) + free(omode); +} + +static drmModePropertyPtr +_output_dpms_property_get(int fd, const drmModeConnector *conn) +{ + drmModePropertyPtr prop; + int i = 0; + + for (; i < conn->count_props; i++) + { + prop = drmModeGetProperty(fd, conn->props[i]); + if (!prop) continue; + + if (!strcmp(prop->name, "DPMS")) return prop; + + drmModeFreeProperty(prop); + } + + return NULL; +} + +static void +_output_backlight_init(Ecore_Drm2_Output *output, unsigned int conn_type) +{ + Eina_List *devs, *l; + const char *dev, *t; + Eina_Bool found = EINA_FALSE; + Ecore_Drm2_Backlight_Type type = 0; + + 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 ((conn_type == DRM_MODE_CONNECTOR_LVDS) || + (conn_type == DRM_MODE_CONNECTOR_eDP) || + (type == ECORE_DRM2_BACKLIGHT_RAW)) + found = EINA_TRUE; + + eina_stringshare_del(t); + if (found) break; + } + + if (found) + { + output->backlight.type = type; + output->backlight.path = eina_stringshare_add(dev); + } + + EINA_LIST_FREE(devs, dev) + eina_stringshare_del(dev); +} + +static void +_output_scale_init(Ecore_Drm2_Output *output, Ecore_Drm2_Transform transform, unsigned int scale) +{ + output->transform = transform; + + if ((output->enabled) && (output->current_mode)) + { + switch (transform) + { + case ECORE_DRM2_TRANSFORM_90: + case ECORE_DRM2_TRANSFORM_270: + case ECORE_DRM2_TRANSFORM_FLIPPED_90: + case ECORE_DRM2_TRANSFORM_FLIPPED_270: + output->w = output->current_mode->height; + output->h = output->current_mode->width; + break; + case ECORE_DRM2_TRANSFORM_NORMAL: + case ECORE_DRM2_TRANSFORM_180: + case ECORE_DRM2_TRANSFORM_FLIPPED: + case ECORE_DRM2_TRANSFORM_FLIPPED_180: + output->w = output->current_mode->width; + output->h = output->current_mode->height; + break; + default: + break; + } + } + + output->scale = scale; + output->w /= scale; + output->h /= scale; +} + +static void +_output_matrix_rotate_xy(Eina_Matrix3 *matrix, double x, double y) +{ + Eina_Matrix4 tmp, m; + + eina_matrix4_identity(&tmp); + eina_matrix4_values_set(&tmp, x, y, 0, 0, -y, x, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 1); + + eina_matrix3_matrix4_to(&m, matrix); + eina_matrix4_multiply(&m, &m, &tmp); + eina_matrix4_matrix3_to(matrix, &m); +} + +static void +_output_matrix_update(Ecore_Drm2_Output *output) +{ + Eina_Matrix3 m3; + + eina_matrix4_identity(&output->matrix); + eina_matrix4_matrix3_to(&m3, &output->matrix); + eina_matrix3_translate(&m3, -output->x, -output->y); + + switch (output->transform) + { + case ECORE_DRM2_TRANSFORM_FLIPPED: + case ECORE_DRM2_TRANSFORM_FLIPPED_90: + case ECORE_DRM2_TRANSFORM_FLIPPED_180: + case ECORE_DRM2_TRANSFORM_FLIPPED_270: + eina_matrix3_translate(&m3, -output->w, 0); + break; + default: + break; + } + + switch (output->transform) + { + case ECORE_DRM2_TRANSFORM_NORMAL: + case ECORE_DRM2_TRANSFORM_FLIPPED: + default: + break; + case ECORE_DRM2_TRANSFORM_90: + case ECORE_DRM2_TRANSFORM_FLIPPED_90: + eina_matrix3_translate(&m3, 0, -output->h); + _output_matrix_rotate_xy(&m3, 0, 1); + break; + case ECORE_DRM2_TRANSFORM_180: + case ECORE_DRM2_TRANSFORM_FLIPPED_180: + eina_matrix3_translate(&m3, -output->w, -output->h); + _output_matrix_rotate_xy(&m3, -1, 0); + break; + case ECORE_DRM2_TRANSFORM_270: + case ECORE_DRM2_TRANSFORM_FLIPPED_270: + eina_matrix3_translate(&m3, -output->w, 0); + _output_matrix_rotate_xy(&m3, 0, -1); + break; + } + + if (output->scale != 1) + eina_matrix3_scale(&m3, output->scale, output->scale); + + eina_matrix3_matrix4_to(&output->matrix, &m3); + eina_matrix4_inverse(&output->inverse, &output->matrix); +} + +static Eina_Bool +_output_create(Ecore_Drm2_Device *dev, const drmModeRes *res, const drmModeConnector *conn, int x, int y, int *w, Eina_Bool cloned) +{ + Ecore_Drm2_Output *output; + int i = 0; + + if (w) *w = 0; + + i = _output_crtc_find(res, conn, dev); + if (i < 0) return EINA_FALSE; + + output = calloc(1, sizeof(Ecore_Drm2_Output)); + if (!output) return EINA_FALSE; + + output->fd = dev->fd; + output->x = x; + output->y = y; + output->cloned = cloned; + output->pw = conn->mmWidth; + output->ph = conn->mmHeight; + + switch (conn->subpixel) + { + case DRM_MODE_SUBPIXEL_UNKNOWN: + output->subpixel = 0; // WL_OUTPUT_SUBPIXEL_UNKNOWN + break; + case DRM_MODE_SUBPIXEL_NONE: + output->subpixel = 1; // WL_OUTPUT_SUBPIXEL_NONE + break; + case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: + output->subpixel = 2; // WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB + break; + case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: + output->subpixel = 3; // WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR + break; + case DRM_MODE_SUBPIXEL_VERTICAL_RGB: + output->subpixel = 4; // WL_OUTPUT_SUBPIXEL_VERTICAL_RGB + break; + case DRM_MODE_SUBPIXEL_VERTICAL_BGR: + output->subpixel = 5; // WL_OUTPUT_SUBPIXEL_VERTICAL_BGR + break; + default: + output->subpixel = 0; + break; + } + + output->name = eina_stringshare_add(_output_name_get(conn)); + output->make = eina_stringshare_add("unknown"); + output->model = eina_stringshare_add("unknown"); + output->serial = eina_stringshare_add("unknown"); + + output->pipe = i; + output->crtc_id = res->crtcs[i]; + output->conn_id = conn->connector_id; + output->conn_type = conn->connector_type; + + output->connected = (conn->connection == DRM_MODE_CONNECTED); + + output->ocrtc = drmModeGetCrtc(dev->fd, output->crtc_id); + + output->dpms = _output_dpms_property_get(dev->fd, conn); + + _output_backlight_init(output, conn->connector_type); + + /* TODO: gamma */ + + _output_modes_create(dev, output, conn); + + _output_edid_find(output, conn); + + if (output->connected) output->enabled = EINA_TRUE; + + _output_scale_init(output, ECORE_DRM2_TRANSFORM_NORMAL, 1); + _output_matrix_update(output); + + if (!eina_list_count(dev->outputs)) + output->primary = EINA_TRUE; + + dev->alloc.crtc |= (1 << output->crtc_id); + dev->alloc.conn |= (1 << output->conn_id); + dev->outputs = eina_list_append(dev->outputs, output); + + _output_debug(output, conn); + + if ((output->enabled) && (output->current_mode)) + { + if (w) *w = output->current_mode->width; + } + + return EINA_TRUE; +} + +static void +_outputs_update(Ecore_Drm2_Device *dev) +{ + drmModeRes *res; + drmModeConnector *conn; + uint32_t connected = 0, disconnected = 0; + int i = 0, x = 0, y = 0; + + res = drmModeGetResources(dev->fd); + if (!res) return; + + for (i = 0; i < res->count_connectors; i++) + { + conn = drmModeGetConnector(dev->fd, res->connectors[i]); + if (!conn) continue; + + if (conn->connection != DRM_MODE_CONNECTED) goto next; + + connected |= (1 << res->connectors[i]); + + if (!(dev->alloc.conn & (1 << res->connectors[i]))) + { + if (dev->outputs) + { + Ecore_Drm2_Output *last; + + last = eina_list_last_data_get(dev->outputs); + if (last) x = last->x + last->current_mode->width; + else x = 0; + } + else + x = 0; + + if (!_output_create(dev, res, conn, x, y, NULL, EINA_TRUE)) + goto next; + } + +next: + drmModeFreeConnector(conn); + } + + drmModeFreeResources(res); + + disconnected = (dev->alloc.conn & ~connected); + if (disconnected) + { + Ecore_Drm2_Output *output; + Eina_List *l; + + EINA_LIST_FOREACH(dev->outputs, l, output) + { + if (disconnected & (1 << output->conn_id)) + { + disconnected &= ~(1 << output->conn_id); + output->connected = EINA_FALSE; + output->enabled = EINA_FALSE; + _output_event_send(output); + } + } + } +} + +static void +_cb_output_event(const char *device EINA_UNUSED, Eeze_Udev_Event event EINA_UNUSED, void *data, Eeze_Udev_Watch *watch EINA_UNUSED) +{ + Ecore_Drm2_Device *dev; + + dev = data; + _outputs_update(dev); +} + +static void +_output_destroy(Ecore_Drm2_Device *dev, Ecore_Drm2_Output *output) +{ + dev->alloc.crtc &= ~(1 << output->crtc_id); + dev->alloc.conn &= ~(1 << output->conn_id); + + eina_stringshare_del(output->backlight.path); + eina_stringshare_del(output->name); + eina_stringshare_del(output->make); + eina_stringshare_del(output->model); + eina_stringshare_del(output->serial); + + drmModeFreeProperty(output->dpms); + free(output->edid.blob); + + free(output); +} + +EAPI Eina_Bool +ecore_drm2_outputs_create(Ecore_Drm2_Device *device) +{ + drmModeConnector *conn; + drmModeRes *res; + int i = 0, x = 0, y = 0, w = 0; + int events = 0; + + EINA_SAFETY_ON_NULL_RETURN_VAL(device, EINA_FALSE); + EINA_SAFETY_ON_TRUE_RETURN_VAL((device->fd < 0), EINA_FALSE); + + res = drmModeGetResources(device->fd); + if (!res) return EINA_FALSE; + + device->crtcs = calloc(res->count_crtcs, sizeof(uint32_t)); + if (!device->crtcs) goto err; + + device->min.width = res->min_width; + device->min.height = res->min_height; + device->max.width = res->max_width; + device->max.height = res->max_height; + + device->num_crtcs = res->count_crtcs; + memcpy(device->crtcs, res->crtcs, sizeof(uint32_t) * res->count_crtcs); + + for (i = 0; i < res->count_connectors; i++) + { + conn = drmModeGetConnector(device->fd, res->connectors[i]); + if (!conn) continue; + + if (!_output_create(device, res, conn, x, y, &w, EINA_FALSE)) + goto next; + + x += w; + +next: + drmModeFreeConnector(conn); + } + + if (eina_list_count(device->outputs) < 1) goto err; + + drmModeFreeResources(res); + + events = (EEZE_UDEV_EVENT_ADD | EEZE_UDEV_EVENT_REMOVE | + EEZE_UDEV_EVENT_CHANGE); + + device->watch = + eeze_udev_watch_add(EEZE_UDEV_TYPE_DRM, events, + _cb_output_event, device); + + return EINA_TRUE; + +err: + drmModeFreeResources(res); + return EINA_FALSE; +} + +EAPI void +ecore_drm2_outputs_destroy(Ecore_Drm2_Device *device) +{ + Ecore_Drm2_Output *output; + + EINA_SAFETY_ON_NULL_RETURN(device); + + EINA_LIST_FREE(device->outputs, output) + _output_destroy(device, output); +} + +EAPI const Eina_List * +ecore_drm2_outputs_get(Ecore_Drm2_Device *device) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(device, NULL); + return device->outputs; +} + +EAPI int +ecore_drm2_output_dpms_get(Ecore_Drm2_Output *output) +{ + drmModeObjectProperties *props; + drmModePropertyRes *prop; + int val = -1; + unsigned int i; + + EINA_SAFETY_ON_NULL_RETURN_VAL(output, -1); + + props = + drmModeObjectGetProperties(output->fd, output->conn_id, + DRM_MODE_OBJECT_CONNECTOR); + if (!props) return -1; + + for (i = 0; i < props->count_props; i++) + { + prop = drmModeGetProperty(output->fd, props->props[i]); + if (!prop) continue; + + if (!strcmp(prop->name, "DPMS")) + val = props->prop_values[i]; + + drmModeFreeProperty(prop); + } + + drmModeFreeObjectProperties(props); + + return val; +} + +EAPI void +ecore_drm2_output_dpms_set(Ecore_Drm2_Output *output, int level) +{ + EINA_SAFETY_ON_NULL_RETURN(output); + EINA_SAFETY_ON_TRUE_RETURN(!output->enabled); + + drmModeConnectorSetProperty(output->fd, output->conn_id, + output->dpms->prop_id, level); +} + +EAPI char * +ecore_drm2_output_edid_get(Ecore_Drm2_Output *output) +{ + char *edid_str = NULL; + unsigned char *blob; + + EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(output->edid.blob, NULL); + + blob = output->edid.blob; + + 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 Eina_Bool +ecore_drm2_output_backlight_get(Ecore_Drm2_Output *output) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(output, EINA_FALSE); + return (output->backlight.path != NULL); +} + +EAPI Ecore_Drm2_Output * +ecore_drm2_output_find(Ecore_Drm2_Device *device, int x, int y) +{ + Eina_List *l; + Ecore_Drm2_Output *output; + + EINA_SAFETY_ON_NULL_RETURN_VAL(device, NULL); + + EINA_LIST_FOREACH(device->outputs, l, output) + { + int ox, oy, ow, oh; + + if (!output->enabled) continue; + + ox = output->x; + oy = output->y; + ow = output->current_mode->width; + oh = output->current_mode->height; + + if (INSIDE(x, y, ox, oy, ow, oh)) + return output; + } + + return NULL; +} + +EAPI void +ecore_drm2_output_geometry_get(Ecore_Drm2_Output *output, int *x, int *y, int *w, int *h) +{ + EINA_SAFETY_ON_NULL_RETURN(output); + EINA_SAFETY_ON_TRUE_RETURN(!output->enabled); + + if (x) *x = output->x; + if (y) *y = output->y; + if (w) *w = output->current_mode->width; + if (h) *h = output->current_mode->height; +} + +EAPI unsigned int +ecore_drm2_output_crtc_get(Ecore_Drm2_Output *output) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(output, 0); + return output->crtc_id; +} |