#include "e.h" #include "e_mod_main.h" /* local function prototypes */ static void _cb_tty(E_Compositor *comp, int event); static int _cb_drm_input(int fd, unsigned int mask __UNUSED__, void *data __UNUSED__); static int _cb_drm_udev_event(int fd __UNUSED__, unsigned int mask __UNUSED__, void *data); static void _cb_drm_page_flip(int fd __UNUSED__, unsigned int frame __UNUSED__, unsigned int sec, unsigned int usec, void *data); static void _cb_drm_vblank(int fd __UNUSED__, unsigned int frame __UNUSED__, unsigned int sec __UNUSED__, unsigned int usec __UNUSED__, void *data); static Eina_Bool _egl_init(E_Drm_Compositor *dcomp, struct udev_device *dev); static void _sprites_init(E_Drm_Compositor *dcomp); static void _sprites_shutdown(E_Drm_Compositor *dcomp); static Eina_Bool _outputs_init(E_Drm_Compositor *dcomp, unsigned int conn, struct udev_device *drm_device); static Eina_Bool _output_create(E_Drm_Compositor *dcomp, drmModeRes *res, drmModeConnector *conn, int x, int y, struct udev_device *drm_device); static void _outputs_update(E_Drm_Compositor *dcomp, struct udev_device *drm_device); static Eina_Bool _udev_event_is_hotplug(E_Drm_Compositor *dcomp, struct udev_device *dev); /* local variables */ static drmModeModeInfo builtin_mode = { 63500, /* clock */ 1024, 1072, 1176, 1328, 0, 768, 771, 775, 798, 0, 59920, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, 0, "1024x768" }; /* external variables */ E_Drm_Compositor *_drm_comp = NULL; EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Drm" }; EAPI void * e_modapi_init(E_Module *m) { struct wl_display *disp; struct udev_enumerate *ue; struct udev_list_entry *entry; struct udev_device *drm_dev = NULL; struct wl_event_loop *loop; const char *seat = NULL; DLOGFN(__FILE__, __LINE__, __FUNCTION__); /* try to get the wayland display */ if (!(disp = (struct wl_display *)m->data)) return NULL; /* allocate space for the drm compositor */ if (!(_drm_comp = malloc(sizeof(E_Drm_Compositor)))) return NULL; memset(_drm_comp, 0, sizeof(E_Drm_Compositor)); if (!(_drm_comp->udev = udev_new())) { free(_drm_comp); return NULL; } _drm_comp->base.display = disp; if (!(_drm_comp->tty = e_tty_create(&_drm_comp->base, _cb_tty, 0))) { free(_drm_comp); return NULL; } ue = udev_enumerate_new(_drm_comp->udev); udev_enumerate_add_match_subsystem(ue, "drm"); udev_enumerate_add_match_sysname(ue, "card[0-9]*"); udev_enumerate_scan_devices(ue); udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(ue)) { struct udev_device *dev; const char *path = NULL; path = udev_list_entry_get_name(entry); dev = udev_device_new_from_syspath(_drm_comp->udev, path); if (!(seat = udev_device_get_property_value(dev, "ID_SEAT"))) seat = "seat0"; if (!strcmp(seat, "seat0")) { drm_dev = dev; break; } udev_device_unref(dev); } if (!drm_dev) { free(_drm_comp); return NULL; } /* init egl */ if (!_egl_init(_drm_comp, drm_dev)) { free(_drm_comp); return NULL; } /* _drm_comp->base.destroy = _cb_destroy; */ _drm_comp->base.focus = EINA_TRUE; _drm_comp->prev_state = E_COMPOSITOR_STATE_ACTIVE; glGenFramebuffers(1, &_drm_comp->base.fbo); glBindFramebuffer(GL_FRAMEBUFFER, _drm_comp->base.fbo); if (!e_compositor_init(&_drm_comp->base, disp)) { free(_drm_comp); return NULL; } wl_list_init(&_drm_comp->sprites); _sprites_init(_drm_comp); if (!_outputs_init(_drm_comp, 0, drm_dev)) { free(_drm_comp); return NULL; } udev_device_unref(drm_dev); udev_enumerate_unref(ue); e_evdev_input_create(&_drm_comp->base, _drm_comp->udev, seat); loop = wl_display_get_event_loop(_drm_comp->base.display); _drm_comp->drm_source = wl_event_loop_add_fd(loop, _drm_comp->drm.fd, WL_EVENT_READABLE, _cb_drm_input, _drm_comp); _drm_comp->udev_monitor = udev_monitor_new_from_netlink(_drm_comp->udev, "udev"); if (!_drm_comp->udev_monitor) { free(_drm_comp); return NULL; } udev_monitor_filter_add_match_subsystem_devtype(_drm_comp->udev_monitor, "drm", NULL); _drm_comp->udev_drm_source = wl_event_loop_add_fd(loop, udev_monitor_get_fd(_drm_comp->udev_monitor), WL_EVENT_READABLE, _cb_drm_udev_event, _drm_comp); if (udev_monitor_enable_receiving(_drm_comp->udev_monitor) < 0) { free(_drm_comp); return NULL; } return &_drm_comp->base; } EAPI int e_modapi_shutdown(E_Module *m __UNUSED__) { E_Input_Device *input, *next; DLOGFN(__FILE__, __LINE__, __FUNCTION__); e_compositor_shutdown(&_drm_comp->base); gbm_device_destroy(_drm_comp->gbm); _sprites_shutdown(_drm_comp); drmDropMaster(_drm_comp->drm.fd); e_tty_destroy(_drm_comp->tty); wl_list_for_each_safe(input, next, &_drm_comp->base.inputs, link) e_evdev_input_destroy(input); free(_drm_comp); _drm_comp = NULL; return 1; } EAPI int e_modapi_save(E_Module *m __UNUSED__) { return 1; } /* local function prototypes */ static void _cb_tty(E_Compositor *comp, int event) { E_Output *output; E_Drm_Output *doutput; E_Sprite *sprite; E_Input_Device *input; DLOGFN(__FILE__, __LINE__, __FUNCTION__); switch (event) { case 0: // TTY_ENTER_VT comp->focus = EINA_TRUE; if (drmSetMaster(_drm_comp->drm.fd)) { printf("Failed to set master: %m\n"); wl_display_terminate(comp->display); } comp->state = _drm_comp->prev_state; e_drm_output_set_modes(_drm_comp); e_compositor_damage_all(comp); wl_list_for_each(input, &comp->inputs, link) e_evdev_add_devices(_drm_comp->udev, input); break; case 1: // TTY_LEAVE_VT comp->focus = EINA_FALSE; _drm_comp->prev_state = comp->state; comp->state = E_COMPOSITOR_STATE_SLEEPING; wl_list_for_each(output, &comp->outputs, link) { output->repaint_needed = EINA_FALSE; e_drm_output_set_cursor(output, NULL); } doutput = container_of(comp->outputs.next, E_Drm_Output, base.link); wl_list_for_each(sprite, &_drm_comp->sprites, link) drmModeSetPlane(_drm_comp->drm.fd, sprite->plane_id, doutput->crtc_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); wl_list_for_each(input, &comp->inputs, link) e_evdev_remove_devices(input); if (drmDropMaster(_drm_comp->drm.fd < 0)) printf("Failed to drop master: %m\n"); break; } } static int _cb_drm_input(int fd, unsigned int mask __UNUSED__, void *data __UNUSED__) { drmEventContext ectxt; DLOGFN(__FILE__, __LINE__, __FUNCTION__); memset(&ectxt, 0, sizeof(ectxt)); ectxt.version = DRM_EVENT_CONTEXT_VERSION; ectxt.page_flip_handler = _cb_drm_page_flip; ectxt.vblank_handler = _cb_drm_vblank; drmHandleEvent(fd, &ectxt); return 1; } static int _cb_drm_udev_event(int fd __UNUSED__, unsigned int mask __UNUSED__, void *data) { E_Drm_Compositor *dcomp; struct udev_device *event; DLOGFN(__FILE__, __LINE__, __FUNCTION__); if (!(dcomp = data)) return 1; event = udev_monitor_receive_device(dcomp->udev_monitor); if (_udev_event_is_hotplug(dcomp, event)) _outputs_update(dcomp, event); udev_device_unref(event); return 1; } static void _cb_drm_page_flip(int fd __UNUSED__, unsigned int frame __UNUSED__, unsigned int sec, unsigned int usec, void *data) { E_Drm_Output *doutput; E_Drm_Compositor *dcomp; unsigned int msecs; DLOGFN(__FILE__, __LINE__, __FUNCTION__); if (!(doutput = data)) return; dcomp = (E_Drm_Compositor *)doutput->base.compositor; if (doutput->scanout_buffer) { e_buffer_post_release(doutput->scanout_buffer); wl_list_remove(&doutput->scanout_buffer_destroy_listener.link); doutput->scanout_buffer = NULL; drmModeRmFB(dcomp->drm.fd, doutput->fs_surf_fb_id); doutput->fs_surf_fb_id = 0; } if (doutput->pending_scanout_buffer) { doutput->scanout_buffer = doutput->pending_scanout_buffer; wl_list_remove(&doutput->pending_scanout_buffer_destroy_listener.link); wl_signal_add(&doutput->scanout_buffer->resource.destroy_signal, &doutput->scanout_buffer_destroy_listener); doutput->pending_scanout_buffer = NULL; doutput->fs_surf_fb_id = doutput->pending_fs_surf_fb_id; doutput->pending_fs_surf_fb_id = 0; } msecs = sec * 1000 + usec / 1000; e_output_finish_frame(&doutput->base, msecs); } static void _cb_drm_vblank(int fd __UNUSED__, unsigned int frame __UNUSED__, unsigned int sec __UNUSED__, unsigned int usec __UNUSED__, void *data) { E_Sprite *es; E_Drm_Compositor *dcomp; DLOGFN(__FILE__, __LINE__, __FUNCTION__); if (!(es = data)) return; dcomp = es->compositor; if (es->surface) { e_buffer_post_release(es->surface->buffer); wl_list_remove(&es->destroy_listener.link); es->surface = NULL; drmModeRmFB(dcomp->drm.fd, es->fb_id); es->fb_id = 0; } if (es->pending_surface) { wl_list_remove(&es->pending_destroy_listener.link); wl_signal_add(&es->pending_surface->buffer->resource.destroy_signal, &es->destroy_listener); es->surface = es->pending_surface; es->pending_surface = NULL; es->fb_id = es->pending_fb_id; es->pending_fb_id = 0; } } static Eina_Bool _egl_init(E_Drm_Compositor *dcomp, struct udev_device *dev) { EGLint maj, min; const char *ext, *fname, *snum; int fd = 0; static const EGLint ctxt_att[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; DLOGFN(__FILE__, __LINE__, __FUNCTION__); if ((snum = udev_device_get_sysnum(dev))) dcomp->drm.id = atoi(snum); if ((!snum) || (dcomp->drm.id < 0)) return EINA_FALSE; fname = udev_device_get_devnode(dev); if (!(fd = open(fname, (O_RDWR | O_CLOEXEC)))) return EINA_FALSE; dcomp->drm.fd = fd; dcomp->gbm = gbm_create_device(dcomp->drm.fd); // dcomp->base.egl_display = eglGetDisplay(dcomp->base.display); dcomp->base.egl_display = eglGetDisplay(dcomp->gbm); if (!dcomp->base.egl_display) return EINA_FALSE; if (!eglInitialize(dcomp->base.egl_display, &maj, &min)) return EINA_FALSE; ext = eglQueryString(dcomp->base.egl_display, EGL_EXTENSIONS); if (!strstr(ext, "EGL_KHR_surfaceless_gles2")) return EINA_FALSE; if (!eglBindAPI(EGL_OPENGL_ES_API)) return EINA_FALSE; dcomp->base.egl_context = eglCreateContext(dcomp->base.egl_display, NULL, EGL_NO_CONTEXT, ctxt_att); if (!dcomp->base.egl_context) return EINA_FALSE; if (!eglMakeCurrent(dcomp->base.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, dcomp->base.egl_context)) return EINA_FALSE; return EINA_TRUE; } static void _sprites_init(E_Drm_Compositor *dcomp) { E_Sprite *es; drmModePlaneRes *res; drmModePlane *plane; unsigned int i = 0; DLOGFN(__FILE__, __LINE__, __FUNCTION__); if (!(res = drmModeGetPlaneResources(dcomp->drm.fd))) return; for (i = 0; i < res->count_planes; i++) { if (!(plane = drmModeGetPlane(dcomp->drm.fd, res->planes[i]))) continue; if (!(es = e_sprite_create(dcomp, plane))) { free(plane); continue; } drmModeFreePlane(plane); wl_list_insert(&dcomp->sprites, &es->link); } free(res->planes); free(res); } static void _sprites_shutdown(E_Drm_Compositor *dcomp) { E_Drm_Output *doutput; E_Sprite *es, *next; DLOGFN(__FILE__, __LINE__, __FUNCTION__); doutput = container_of(dcomp->base.outputs.next, E_Drm_Output, base.link); wl_list_for_each_safe(es, next, &dcomp->sprites, link) { drmModeSetPlane(dcomp->drm.fd, es->plane_id, doutput->crtc_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); drmModeRmFB(dcomp->drm.fd, es->fb_id); free(es); } } static Eina_Bool _outputs_init(E_Drm_Compositor *dcomp, unsigned int conn, struct udev_device *drm_device) { drmModeConnector *connector; drmModeRes *res; int i = 0, x = 0, y = 0; DLOGFN(__FILE__, __LINE__, __FUNCTION__); if (!(res = drmModeGetResources(dcomp->drm.fd))) return EINA_FALSE; if (!(dcomp->crtcs = calloc(res->count_crtcs, sizeof(unsigned int)))) { drmModeFreeResources(res); return EINA_FALSE; } dcomp->num_crtcs = res->count_crtcs; memcpy(dcomp->crtcs, res->crtcs, sizeof(unsigned int) * dcomp->num_crtcs); for (i = 0; i < res->count_connectors; i++) { if (!(connector = drmModeGetConnector(dcomp->drm.fd, res->connectors[i]))) continue; if ((connector->connection == DRM_MODE_CONNECTED) && ((conn == 0) || (connector->connector_id == conn))) { if (!_output_create(dcomp, res, connector, x, y, drm_device)) { drmModeFreeConnector(connector); continue; } x += container_of(dcomp->base.outputs.prev, E_Output, link)->current->w; } drmModeFreeConnector(connector); } if (wl_list_empty(&dcomp->base.outputs)) { drmModeFreeResources(res); return EINA_FALSE; } drmModeFreeResources(res); return EINA_TRUE; } static Eina_Bool _output_create(E_Drm_Compositor *dcomp, drmModeRes *res, drmModeConnector *conn, int x, int y, struct udev_device *drm_device) { E_Drm_Output *output; E_Drm_Output_Mode *mode, *next; drmModeEncoder *encoder; int i = 0, ret = 0; unsigned int hdl, stride; if (!(encoder = drmModeGetEncoder(dcomp->drm.fd, conn->encoders[0]))) return EINA_FALSE; for (i = 0; i < res->count_crtcs; i++) { if ((encoder->possible_crtcs & (1 << i)) && !(dcomp->crtc_alloc & (1 << res->crtcs[i]))) break; } if (i == res->count_crtcs) { drmModeFreeEncoder(encoder); return EINA_FALSE; } if (!(output = malloc(sizeof(E_Drm_Output)))) { drmModeFreeEncoder(encoder); return EINA_FALSE; } memset(output, 0, sizeof(E_Drm_Output)); output->fb_id[0] = -1; output->fb_id[1] = -1; output->base.subpixel = e_drm_output_subpixel_convert(conn->subpixel); output->base.make = "unknown"; output->base.model = "unknown"; wl_list_init(&output->base.modes); output->crtc_id = res->crtcs[i]; dcomp->crtc_alloc |= (1 << output->crtc_id); output->conn_id = conn->connector_id; dcomp->conn_alloc |= (1 << output->conn_id); output->orig_crtc = drmModeGetCrtc(dcomp->drm.fd, output->crtc_id); drmModeFreeEncoder(encoder); for (i = 0; i < conn->count_modes; i++) { if (!e_drm_output_add_mode(output, &conn->modes[i])) goto efree; } if (conn->count_modes == 0) { if (!e_drm_output_add_mode(output, &builtin_mode)) goto efree; } mode = container_of(output->base.modes.next, E_Drm_Output_Mode, base.link); output->base.current = &mode->base; mode->base.flags = (WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED); glGenRenderbuffers(2, output->rbo); for (i = 0; i < 2; i++) { glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]); output->bo[i] = gbm_bo_create(dcomp->gbm, output->base.current->w, output->base.current->h, GBM_BO_FORMAT_XRGB8888, (GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING)); if (!output->bo[i]) goto ebuffs; output->image[i] = dcomp->base.create_image(dcomp->base.egl_display, NULL, EGL_NATIVE_PIXMAP_KHR, output->bo[i], NULL); if (!output->image[i]) goto ebuffs; dcomp->base.image_target_renderbuffer_storage(GL_RENDERBUFFER, output->image[i]); stride = gbm_bo_get_pitch(output->bo[i]); hdl = gbm_bo_get_handle(output->bo[i]).u32; ret = drmModeAddFB(dcomp->drm.fd, output->base.current->w, output->base.current->h, 24, 32, stride, hdl, &output->fb_id[i]); if (ret) goto ebuffs; } output->current = 0; glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, output->rbo[output->current]); ret = drmModeSetCrtc(dcomp->drm.fd, output->crtc_id, output->fb_id[output->current ^ 1], 0, 0, &output->conn_id, 1, &mode->info); if (ret) goto efb; /* TODO: backlight init */ e_output_init(&output->base, &dcomp->base, x, y, conn->mmWidth, conn->mmHeight, 0); wl_list_insert(dcomp->base.outputs.prev, &output->base.link); output->scanout_buffer_destroy_listener.notify = e_drm_output_scanout_buffer_destroy; output->pending_scanout_buffer_destroy_listener.notify = e_drm_output_pending_scanout_buffer_destroy; output->pending_fs_surf_fb_id = 0; output->base.repaint = e_drm_output_repaint; output->base.destroy = e_drm_output_destroy; output->base.assign_planes = e_drm_output_assign_planes; output->base.set_dpms = e_drm_output_set_dpms; return EINA_TRUE; efb: glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); ebuffs: for (i = 0; i < 2; i++) { if ((int)output->fb_id[i] != -1) drmModeRmFB(dcomp->drm.fd, output->fb_id[i]); if (output->image[i]) dcomp->base.destroy_image(dcomp->base.egl_display, output->image[i]); if (output->bo[i]) gbm_bo_destroy(output->bo[i]); } glBindRenderbuffer(GL_RENDERBUFFER, 0); glDeleteRenderbuffers(2, output->rbo); efree: wl_list_for_each_safe(mode, next, &output->base.modes, base.link) { wl_list_remove(&mode->base.link); free(mode); } drmModeFreeCrtc(output->orig_crtc); dcomp->crtc_alloc &= ~(1 << output->crtc_id); dcomp->conn_alloc &= ~(1 << output->conn_id); free(output); return EINA_TRUE; } static void _outputs_update(E_Drm_Compositor *dcomp, struct udev_device *drm_device) { drmModeConnector *conn; drmModeRes *res; E_Drm_Output *doutput, *next; int x = 0, y = 0; int xo = 0, yo = 0; unsigned int connected = 0, disconnects = 0; int i = 0; if (!(res = drmModeGetResources(dcomp->drm.fd))) return; for (i = 0; i < res->count_connectors; i++) { int conn_id; conn_id = res->connectors[i]; if (!(conn = drmModeGetConnector(dcomp->drm.fd, conn_id))) continue; if (conn->connection != DRM_MODE_CONNECTED) { drmModeFreeConnector(conn); continue; } connected |= (1 << conn_id); if (!(dcomp->conn_alloc & (1 << conn_id))) { E_Output *last; last = container_of(dcomp->base.outputs.prev, E_Output, link); if (!wl_list_empty(&dcomp->base.outputs)) x = last->x + last->current->w; else x = 0; y = 0; _output_create(dcomp, res, conn, x, y, drm_device); } drmModeFreeConnector(conn); } drmModeFreeResources(res); if ((disconnects = dcomp->conn_alloc & ~connected)) { wl_list_for_each_safe(doutput, next, &dcomp->base.outputs, base.link) { if ((xo != 0) || (yo != 0)) e_output_move(&doutput->base, doutput->base.x - xo, doutput->base.y - yo); if (disconnects & (1 << doutput->conn_id)) { disconnects &= ~(1 << doutput->conn_id); xo += doutput->base.current->w; e_drm_output_destroy(&doutput->base); } } } if (dcomp->conn_alloc == 0) wl_display_terminate(dcomp->base.display); } static Eina_Bool _udev_event_is_hotplug(E_Drm_Compositor *dcomp, struct udev_device *dev) { const char *snum, *val; DLOGFN(__FILE__, __LINE__, __FUNCTION__); snum = udev_device_get_sysnum(dev); if ((!snum) || (atoi(snum) != dcomp->drm.id)) return EINA_FALSE; if (!(val = udev_device_get_property_value(dev, "HOTPLUG"))) return EINA_FALSE; if (!strcmp(val, "1")) return EINA_TRUE; return EINA_FALSE; }