#include "ecore_drm2_private.h" #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_cb_session_active(void *data, int type EINA_UNUSED, void *event) { Ecore_Drm2_Device *dev; Elput_Event_Session_Active *ev; Ecore_Drm2_Event_Activate *eevent; dev = data; ev = event; if (ev->active) { Eina_List *l; Ecore_Drm2_Display *disp; EINA_LIST_FOREACH(dev->displays, l, disp) ecore_drm2_display_dpms_set(disp, DRM_MODE_DPMS_ON); } eevent = calloc(1, sizeof(Ecore_Drm2_Event_Activate)); if (!eevent) return ECORE_CALLBACK_RENEW; eevent->active = ev->active; ecore_event_add(ECORE_DRM2_EVENT_ACTIVATE, eevent, NULL, NULL); return ECORE_CALLBACK_RENEW; } static Eina_Bool _ecore_drm2_device_cb_device_change(void *data, int type EINA_UNUSED, void *event) { Elput_Event_Device_Change *ev; Ecore_Drm2_Device *dev; ev = event; dev = data; if (ev->type == ELPUT_DEVICE_ADDED) { Eina_Stringshare *name; Ecore_Drm2_Display *disp; name = elput_device_output_name_get(ev->device); if (!name) { disp = eina_list_data_get(dev->displays); if (disp) ecore_drm2_device_calibrate(dev, disp->w, disp->h); } else { Eina_List *l; EINA_LIST_FOREACH(dev->displays, l, disp) { if (eina_streq(disp->name, name)) { ecore_drm2_device_calibrate(dev, disp->w, disp->h); break; } } } } return ECORE_CALLBACK_RENEW; } static Eina_Bool _ecore_drm2_device_modeset_capable_get(int fd) { Eina_Bool ret = EINA_TRUE; drmModeRes *res; res = sym_drmModeGetResources(fd); if (!res) return EINA_FALSE; if ((res->count_crtcs <= 0) || (res->count_connectors <= 0) || (res->count_encoders <= 0)) ret = EINA_FALSE; sym_drmModeFreeResources(res); return ret; } static const char * _ecore_drm2_device_path_get(Elput_Manager *em, const char *seat) { Eina_List *devs, *l; const char *denv = NULL, *dev = NULL, *chosen = NULL, *ret = NULL; Eina_Bool found = EINA_FALSE, ms = EINA_FALSE; EINA_SAFETY_ON_NULL_RETURN_VAL(em, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(seat, NULL); denv = getenv("ECORE_DRM2_CARD"); if (denv) devs = eeze_udev_find_by_subsystem_sysname("drm", denv); else devs = eeze_udev_find_by_subsystem_sysname("drm", "card[0-9]*"); if (!devs) return NULL; EINA_LIST_FOREACH(devs, l, dev) { int fd = -1; const char *dpath, *dseat, *dparent; dpath = eeze_udev_syspath_get_devpath(dev); if (!dpath) continue; dseat = eeze_udev_syspath_get_property(dev, "ID_SEAT"); if (!dseat) dseat = eina_stringshare_add("seat0"); if (strcmp(seat, dseat)) goto cont; fd = elput_manager_open(em, dpath, -1); if (fd < 0) goto cont; ms = _ecore_drm2_device_modeset_capable_get(fd); elput_manager_close(em, fd); if (!ms) goto cont; chosen = dev; dparent = eeze_udev_syspath_get_parent_filtered(dev, "pci", NULL); if (dparent) { const char *id; id = eeze_udev_syspath_get_sysattr(dparent, "boot_vga"); if (id) { if (!strcmp(id, "1")) found = EINA_TRUE; eina_stringshare_del(id); } eina_stringshare_del(dparent); } cont: eina_stringshare_del(dpath); eina_stringshare_del(dseat); if (found) break; } if (chosen) ret = eeze_udev_syspath_get_devpath(chosen); EINA_LIST_FREE(devs, dev) eina_stringshare_del(dev); 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) { const char *path; Ecore_Drm2_Device *dev; /* try to allocate space for device structure */ dev = calloc(1, sizeof(Ecore_Drm2_Device)); if (!dev) return NULL; /* try to connect to Elput manager */ dev->em = elput_manager_connect(seat, tty); if (!dev->em) { ERR("Could not connect to input manager"); goto man_err; } /* try to get drm device path */ path = _ecore_drm2_device_path_get(dev->em, seat); if (!path) { ERR("Could not find drm device on seat %s", seat); goto path_err; } /* try to open this device */ dev->fd = elput_manager_open(dev->em, path, -1); if (dev->fd < 0) { ERR("Could not open drm device %s", path); goto open_err; } DBG("Opened DRM Device: %s", path); /* try to enable elput input */ if (!elput_input_init(dev->em)) { ERR("Could not initialize Elput Input"); goto input_err; } /* try to get the kms capabilities of this device */ if (!_ecore_drm2_device_kms_caps_get(dev)) { ERR("Could not get kms capabilities for device"); goto caps_err; } /* try to create crtcs */ if (!_ecore_drm2_crtcs_create(dev)) { ERR("Could not create crtcs"); goto caps_err; } /* try to create planes */ if (!_ecore_drm2_planes_create(dev)) { ERR("Could not create planes"); goto plane_err; } /* 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); dev->device_hdlr = ecore_event_handler_add(ELPUT_EVENT_DEVICE_CHANGE, _ecore_drm2_device_cb_device_change, dev); /* cleanup path variable */ eina_stringshare_del(path); return dev; disp_err: _ecore_drm2_connectors_destroy(dev); conn_err: _ecore_drm2_planes_destroy(dev); plane_err: _ecore_drm2_crtcs_destroy(dev); caps_err: elput_input_shutdown(dev->em); input_err: elput_manager_close(dev->em, dev->fd); open_err: eina_stringshare_del(path); path_err: elput_manager_disconnect(dev->em); man_err: free(dev); return NULL; } EAPI void ecore_drm2_device_close(Ecore_Drm2_Device *dev) { EINA_SAFETY_ON_NULL_RETURN(dev); _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->device_hdlr); ecore_event_handler_del(dev->session_hdlr); elput_input_shutdown(dev->em); elput_manager_close(dev->em, dev->fd); elput_manager_disconnect(dev->em); free(dev); } EAPI void ecore_drm2_device_cursor_size_get(Ecore_Drm2_Device *dev, int *width, int *height) { EINA_SAFETY_ON_NULL_RETURN(dev); if (width) *width = dev->cursor.width; if (height) *height = dev->cursor.height; } EAPI void ecore_drm2_device_preferred_depth_get(Ecore_Drm2_Device *dev, int *depth, int *bpp) { uint64_t caps; int ret = -1; EINA_SAFETY_ON_NULL_RETURN(dev); ret = sym_drmGetCap(dev->fd, DRM_CAP_DUMB_PREFERRED_DEPTH, &caps); if (ret == 0) { if (depth) *depth = caps; if (bpp) *bpp = caps; } } EAPI void ecore_drm2_device_screen_size_range_get(Ecore_Drm2_Device *dev, int *minw, int *minh, int *maxw, int *maxh) { drmModeRes *res; if (minw) *minw = 0; if (minh) *minh = 0; if (maxw) *maxw = 0; if (maxh) *maxh = 0; EINA_SAFETY_ON_NULL_RETURN(dev); res = sym_drmModeGetResources(dev->fd); if (!res) return; if (minw) *minw = res->min_width; if (minh) *minh = res->min_height; if (maxw) *maxw = res->max_width; if (maxh) *maxh = res->max_height; sym_drmModeFreeResources(res); } EAPI const Eina_List * ecore_drm2_device_crtcs_get(Ecore_Drm2_Device *dev) { EINA_SAFETY_ON_NULL_RETURN_VAL(dev, NULL); return dev->crtcs; } EAPI void ecore_drm2_device_pointer_max_set(Ecore_Drm2_Device *dev, int w, int h) { EINA_SAFETY_ON_NULL_RETURN(dev); EINA_SAFETY_ON_NULL_RETURN(dev->em); elput_input_pointer_max_set(dev->em, w, h); } EAPI Eina_Bool ecore_drm2_device_pointer_rotation_set(Ecore_Drm2_Device *dev, int rotation) { EINA_SAFETY_ON_NULL_RETURN_VAL(dev, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(dev->em, EINA_FALSE); return elput_input_pointer_rotation_set(dev->em, rotation); } EAPI void ecore_drm2_device_calibrate(Ecore_Drm2_Device *dev, int w, int h) { EINA_SAFETY_ON_NULL_RETURN(dev); EINA_SAFETY_ON_NULL_RETURN(dev->em); elput_input_devices_calibrate(dev->em, w, h); } EAPI Eina_Bool ecore_drm2_device_vt_set(Ecore_Drm2_Device *dev, int vt) { EINA_SAFETY_ON_NULL_RETURN_VAL(dev, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(dev->em, EINA_FALSE); return elput_manager_vt_set(dev->em, vt); } EAPI int ecore_drm2_device_clock_id_get(Ecore_Drm2_Device *dev) { EINA_SAFETY_ON_NULL_RETURN_VAL(dev, -1); return dev->clock_id; } EAPI void ecore_drm2_device_pointer_xy_get(Ecore_Drm2_Device *dev, int *x, int *y) { if (x) *x = 0; if (y) *y = 0; EINA_SAFETY_ON_NULL_RETURN(dev); EINA_SAFETY_ON_NULL_RETURN(dev->em); elput_input_pointer_xy_get(dev->em, NULL, x, y); }