static bool test(data_t *data, enum pipe pipe, igt_output_t *output) { igt_plane_t *primary; drmModeModeInfo *mode; struct igt_fb fb[2]; int fd, ret; /* select the pipe we want to use */ igt_output_set_pipe(output, pipe); igt_display_commit(&data->display); if (!output->valid) { igt_output_set_pipe(output, PIPE_ANY); igt_display_commit(&data->display); return false; } primary = igt_output_get_plane(output, IGT_PLANE_PRIMARY); mode = igt_output_get_mode(output); igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay, DRM_FORMAT_XRGB8888, LOCAL_I915_FORMAT_MOD_X_TILED, 0.0, 0.0, 0.0, &fb[0]); igt_plane_set_fb(primary, &fb[0]); igt_display_commit2(&data->display, COMMIT_LEGACY); fd = drm_open_driver(DRIVER_INTEL); ret = drmDropMaster(data->drm_fd); igt_assert_eq(ret, 0); ret = drmSetMaster(fd); igt_assert_eq(ret, 0); igt_create_color_fb(fd, mode->hdisplay, mode->vdisplay, DRM_FORMAT_XRGB8888, LOCAL_I915_FORMAT_MOD_X_TILED, 0.0, 0.0, 0.0, &fb[1]); ret = drmModePageFlip(fd, output->config.crtc->crtc_id, fb[1].fb_id, DRM_MODE_PAGE_FLIP_EVENT, data); igt_assert_eq(ret, 0); ret = close(fd); igt_assert_eq(ret, 0); ret = drmSetMaster(data->drm_fd); igt_assert_eq(ret, 0); igt_plane_set_fb(primary, NULL); igt_output_set_pipe(output, PIPE_ANY); igt_display_commit(&data->display); igt_remove_fb(data->drm_fd, &fb[0]); return true; }
static void activate (ply_renderer_backend_t *backend) { ply_list_node_t *node; ply_trace ("taking master and scanning out"); backend->is_active = true; drmSetMaster (backend->device_fd); node = ply_list_get_first_node (backend->heads); while (node != NULL) { ply_list_node_t *next_node; ply_renderer_head_t *head; head = (ply_renderer_head_t *) ply_list_node_get_data (node); next_node = ply_list_get_next_node (backend->heads, node); if (head->scan_out_buffer_id != 0) { /* Flush out any pending drawing to the buffer */ flush_head (backend, head); /* Then send the buffer to the monitor */ ply_renderer_head_set_scan_out_buffer (backend, head, head->scan_out_buffer_id); } node = next_node; } }
static Bool TegraScreenInit(SCREEN_INIT_ARGS_DECL) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); TegraPtr tegra = TegraPTR(pScrn); VisualPtr visual; int ret; pScrn->pScreen = pScreen; ret = drmSetMaster(tegra->fd); if (ret) { ErrorF("Unable to set master\n"); return FALSE; } /* HW dependent - FIXME */ pScrn->displayWidth = pScrn->virtualX; if (!drmmode_create_initial_bos(pScrn, &tegra->drmmode)) return FALSE; if (tegra->drmmode.shadow_enable) { tegra->drmmode.shadow_fb = calloc(1, pScrn->displayWidth * pScrn->virtualY * ((pScrn->bitsPerPixel + 7) >> 3)); if (!tegra->drmmode.shadow_fb) tegra->drmmode.shadow_enable = FALSE; }
static void init(void) { int fd = drmOpen("kgsl", NULL); drmSetMaster(fd); dev = fd_device_new(fd); pipe = fd_pipe_new(dev, FD_PIPE_2D); context_bos[0] = fd_bo_new(dev, 0x1000, DRM_FREEDRENO_GEM_TYPE_KMEM); context_bos[1] = fd_bo_new(dev, 0x9000, DRM_FREEDRENO_GEM_TYPE_KMEM); context_bos[2] = fd_bo_new(dev, 0x81000, DRM_FREEDRENO_GEM_TYPE_KMEM); next_ring(); ring_pre(ring); BEGIN_RING(8); OUT_RING (ring, REGM(VGV1_DIRTYBASE, 3)); OUT_RELOC (ring, context_bos[0]); /* VGV1_DIRTYBASE */ OUT_RELOC (ring, context_bos[1]); /* VGV1_CBASE1 */ OUT_RELOC (ring, context_bos[2]); /* VGV1_UBASE2 */ OUT_RING (ring, 0x11000000); OUT_RING (ring, 0x10fff000); OUT_RING (ring, 0x10ffffff); OUT_RING (ring, 0x0d000404); END_RING (); }
static void activate (ply_renderer_backend_t *backend) { ply_list_node_t *node; ply_trace ("taking master and scanning out"); backend->is_active = true; drmSetMaster (backend->device_fd); node = ply_list_get_first_node (backend->heads); while (node != NULL) { ply_list_node_t *next_node; ply_renderer_head_t *head; head = (ply_renderer_head_t *) ply_list_node_get_data (node); next_node = ply_list_get_next_node (backend->heads, node); if (head->scan_out_buffer_id != 0) ply_renderer_head_set_scan_out_buffer (backend, head, head->scan_out_buffer_id); node = next_node; } }
bool CDRMUtils::InitDrm() { if(m_fd >= 0) { /* caps need to be set before allocating connectors, encoders, crtcs, and planes */ auto ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); if (ret) { CLog::Log(LOGERROR, "CDRMUtils::%s - failed to set Universal planes capability: %s", __FUNCTION__, strerror(errno)); return false; } if(!GetResources()) { return false; } if(!GetConnector()) { return false; } if(!GetEncoder()) { return false; } if(!GetCrtc()) { return false; } if(!GetPlanes()) { return false; } } drmModeFreeResources(m_drm_resources); m_drm_resources = nullptr; if(m_fd < 0) { return false; } if(!GetPreferredMode()) { return false; } drmSetMaster(m_fd); m_orig_crtc = drmModeGetCrtc(m_fd, m_crtc->crtc->crtc_id); return true; }
static void start_devices(void) { int i; for (i = 0; i < num_drm_fds; ++i) { if (drmSetMaster(drm_fds[i]) < 0) die("failed to set DRM master"); } }
int weston_launcher_drm_set_master(struct weston_compositor *compositor, int drm_fd, char master) { struct msghdr msg; struct cmsghdr *cmsg; struct iovec iov; char control[CMSG_SPACE(sizeof(drm_fd))]; int ret; ssize_t len; struct weston_launcher_set_master message; union cmsg_data *data; if (compositor->launcher_sock == -1) { if (master) return drmSetMaster(drm_fd); else return drmDropMaster(drm_fd); } memset(&msg, 0, sizeof msg); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof control; cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(drm_fd)); data = (union cmsg_data *) CMSG_DATA(cmsg); data->fd = drm_fd; msg.msg_controllen = cmsg->cmsg_len; iov.iov_base = &message; iov.iov_len = sizeof message; message.header.opcode = WESTON_LAUNCHER_DRM_SET_MASTER; message.set_master = master; do { len = sendmsg(compositor->launcher_sock, &msg, 0); } while (len < 0 && errno == EINTR); if (len < 0) return -1; do { len = recv(compositor->launcher_sock, &ret, sizeof ret, 0); } while (len < 0 && errno == EINTR); if (len < 0) return -1; return ret; }
static int handle_signal(struct weston_launch *wl) { struct signalfd_siginfo sig; int pid, status, ret; if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) { error(0, errno, "reading signalfd failed"); return -1; } switch (sig.ssi_signo) { case SIGCHLD: pid = waitpid(-1, &status, 0); if (pid == wl->child) { wl->child = 0; if (WIFEXITED(status)) ret = WEXITSTATUS(status); else if (WIFSIGNALED(status)) /* * If weston dies because of signal N, we * return 10+N. This is distinct from * weston-launch dying because of a signal * (128+N). */ ret = 10 + WTERMSIG(status); else ret = 0; quit(wl, ret); } break; case SIGTERM: case SIGINT: if (wl->child) kill(wl->child, sig.ssi_signo); break; case SIGUSR1: send_reply(wl, WESTON_LAUNCHER_DEACTIVATE); close_input_fds(wl); drmDropMaster(wl->drm_fd); ioctl(wl->tty, VT_RELDISP, 1); break; case SIGUSR2: ioctl(wl->tty, VT_RELDISP, VT_ACKACQ); drmSetMaster(wl->drm_fd); send_reply(wl, WESTON_LAUNCHER_ACTIVATE); break; default: return -1; } return 0; }
static void acquire_vt(void *data) { struct MPGLContext *ctx = data; MP_VERBOSE(ctx->vo, "Acquiring VT"); if (USE_MASTER) { struct priv *p = ctx->priv; if (drmSetMaster(p->kms->fd)) { MP_WARN(ctx->vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno)); } } crtc_setup(ctx); }
mg::PlatformPriority probe_graphics_platform(mo::ProgramOption const& options) { mir::assert_entry_point_signature<mg::PlatformProbe>(&probe_graphics_platform); auto const unparsed_arguments = options.unparsed_command_line(); auto platform_option_used = false; for (auto const& token : unparsed_arguments) { if (token == (std::string("--") + vt_option_name)) platform_option_used = true; } if (options.is_set(vt_option_name)) platform_option_used = true; auto udev = std::make_shared<mir::udev::Context>(); mir::udev::Enumerator drm_devices{udev}; drm_devices.match_subsystem("drm"); drm_devices.match_sysname("card[0-9]*"); drm_devices.scan_devices(); if (drm_devices.begin() == drm_devices.end()) return mg::PlatformPriority::unsupported; // Check for master int tmp_fd = -1; for (auto& device : drm_devices) { tmp_fd = open(device.devnode(), O_RDWR | O_CLOEXEC); if (tmp_fd >= 0) break; } if (tmp_fd >= 0) { if (drmSetMaster(tmp_fd) >= 0) { drmDropMaster(tmp_fd); drmClose(tmp_fd); return mg::PlatformPriority::best; } else drmClose(tmp_fd); } if (platform_option_used) return mg::PlatformPriority::best; return mg::PlatformPriority::unsupported; }
/* * Set as the master of a DRM device. */ int gralloc_drm_set_master(struct gralloc_drm_t *drm) { int ret; ret = drmSetMaster(drm->fd); if (ret) { LOGE("Error: drmSetMaster failed: %s\n", strerror(errno)); return -errno; } else { drm->first_post = 1; drm->master = 1; return 0; } }
/* * This gets called when gaining control of the VT, and from ScreenInit(). */ static Bool TegraEnterVT(VT_FUNC_ARGS_DECL) { SCRN_INFO_PTR(arg); TegraPtr tegra = TegraPTR(pScrn); pScrn->vtSema = TRUE; if (drmSetMaster(tegra->fd)) xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "drmSetMaster failed: %s\n", strerror(errno)); if (!drmmode_set_desired_modes(pScrn, &tegra->drmmode)) return FALSE; return TRUE; }
static int handle_setmaster(struct weston_launch *wl, struct msghdr *msg, ssize_t len) { int ret = -1; struct cmsghdr *cmsg; struct weston_launcher_set_master *message; union cmsg_data *data; if (len != sizeof(*message)) { error(0, 0, "missing value in setmaster request"); goto out; } message = msg->msg_iov->iov_base; cmsg = CMSG_FIRSTHDR(msg); if (!cmsg || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { error(0, 0, "invalid control message"); goto out; } data = (union cmsg_data *) CMSG_DATA(cmsg); if (data->fd == -1) { error(0, 0, "missing drm fd in socket request"); goto out; } if (message->set_master) ret = drmSetMaster(data->fd); else ret = drmDropMaster(data->fd); close(data->fd); out: do { len = send(wl->sock[0], &ret, sizeof ret, 0); } while (len < 0 && errno == EINTR); if (len < 0) return -1; return 0; }
static Bool SetMaster(ScrnInfoPtr pScrn) { modesettingPtr ms = modesettingPTR(pScrn); int ret; #ifdef XF86_PDEV_SERVER_FD if (ms->pEnt->location.type == BUS_PLATFORM && (ms->pEnt->location.id.plat->flags & XF86_PDEV_SERVER_FD)) return TRUE; #endif ret = drmSetMaster(ms->fd); if (ret) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "drmSetMaster failed: %s\n", strerror(errno)); return ret == 0; }
static int vt_handler(int signal_number, void *data) { struct weston_launcher *launcher = data; struct weston_compositor *compositor = launcher->compositor; if (compositor->session_active) { compositor->session_active = 0; wl_signal_emit(&compositor->session_signal, compositor); drmDropMaster(launcher->drm_fd); ioctl(launcher->tty, VT_RELDISP, 1); } else { ioctl(launcher->tty, VT_RELDISP, VT_ACKACQ); drmSetMaster(launcher->drm_fd); compositor->session_active = 1; wl_signal_emit(&compositor->session_signal, compositor); } return 1; }
/** As long as we are using our fake DRI driver inside of Mesa, we only want * to implement the minimum here to make Mesa load it. */ Bool VBOXDRIScreenInit(ScrnInfoPtr pScrn, ScreenPtr pScreen, VBOXPtr pVBox) { DRI2InfoRec DRI2Info; unsigned i; memset(&DRI2Info, 0, sizeof(DRI2Info)); pVBox->drmFD = -1; for (i = 0; i < RT_ELEMENTS(devicePaths); ++i) { int fd = open(devicePaths[i], O_RDWR); if (fd >= 0) { drmVersionPtr pVersion = drmGetVersion(fd); if ( pVersion && pVersion->name_len && !strcmp(pVersion->name, VBOX_DRM_DRIVER_NAME) && drmSetMaster(fd) == 0) { TRACE_LOG("Opened drm device %s\n", devicePaths[i]); DRI2Info.deviceName = devicePaths[i]; /* Keep the driver open and hope that the path won't change. */ pVBox->drmFD = fd; drmFreeVersion(pVersion); break; } close(fd); drmFreeVersion(pVersion); } } if (!DRI2Info.deviceName) return FALSE; DRI2Info.version = 3; DRI2Info.fd = pVBox->drmFD; DRI2Info.driverName = VBOX_DRI_DRIVER_NAME; DRI2Info.CopyRegion = VBOXDRICopyRegion; DRI2Info.Wait = NULL; DRI2Info.CreateBuffer = VBOXDRICreateBuffer; DRI2Info.DestroyBuffer = VBOXDRIDestroyBuffer; return DRI2ScreenInit(pScreen, &DRI2Info); }
static int video_wake_up(struct uterm_video *video) { int ret; if (video_is_awake(video)) return 0; ret = drmSetMaster(video->dumb.fd); if (ret) { log_err("cannot set DRM-master"); return -EACCES; } video->flags |= VIDEO_AWAKE; ret = hotplug(video); if (ret) { video->flags &= ~VIDEO_AWAKE; drmDropMaster(video->dumb.fd); return ret; } show_displays(video); return 0; }
int main(int argc, char *argv[]) { uint64_t has_dumb; int ret, fd, opt, i; void *map; struct drm_mode_destroy_dumb dreq; struct drm_mode_create_dumb creq; struct drm_mode_map_dumb mreq; drmModePlaneRes *resources; drmModePlane *plane = NULL; uint32_t handle, stride, size; uint32_t plane_id, crtc_id; uint32_t width, height; uint32_t posx, posy; uint32_t fb; /* parse command line */ while ((opt = getopt(argc, argv, "x:y:w:v:c:p:h")) != -1) { switch (opt) { case 'x': posx = atoi(optarg); break; case 'y': posy = atoi(optarg); break; case 'w': width = atoi(optarg); break; case 'v': height = atoi(optarg); break; case 'p': plane_id = atoi(optarg); break; case 'c': crtc_id = atoi(optarg); break; case 'h': default: printf("usage: -h] -c <connector> -e <encoder> -m <mode>\n"); printf("\t-h: this help message\n"); printf("\t-c <crtc> crtc id, default is 0\n"); printf("\t-p <plane> plane id, default is 0\n"); printf("\t-x <posx> plane top left corner xpos, default is 0'\n"); printf("\t-y <posy> plane top left corner ypos, default is 0'\n"); printf("\t-w <width> plane width, default is 0'\n"); printf("\t-v <height> plane height, default is 0'\n"); exit(0); } } /* open drm device */ fd = open(device_name, O_RDWR | O_CLOEXEC); if (fd < 0) { perror("cannot open drm device"); exit(-1); } drmSetMaster(fd); /* check dumb buffer support */ if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0) { perror("DRM_CAP_DUMB_BUFFER ioctl"); ret = -EFAULT; goto err_close; } if (!has_dumb) { fprintf(stderr, "driver does not support dumb buffers\n"); ret = -EFAULT; goto err_close; } /* get plane */ resources = drmModeGetPlaneResources(fd); if (!resources || resources->count_planes == 0) { fprintf(stderr, "drmModeGetPlaneResources failed\n"); ret = -ENODEV; goto err_close; } for (i = 0; i < resources->count_planes; i++) { drmModePlane *p = drmModeGetPlane(fd, resources->planes[i]); if (!p) continue; if (p->plane_id == plane_id) { plane = p; break; } drmModeFreePlane(plane); } if (!plane) { fprintf(stderr, "couldn't find specified plane\n"); ret = -ENODEV; goto err_close; } /* create dumb buffer object */ memset(&creq, 0, sizeof(creq)); creq.height = height; creq.width = width; creq.bpp = 32; ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); if (ret) { fprintf(stderr, "failed drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB)\n"); goto err_close; } handle = creq.handle; stride = creq.pitch; size = creq.size; /* create framebuffer for dumb buffer object */ ret = drmModeAddFB(fd, width, height, 24, 32, stride, handle, &fb); if (ret) { fprintf(stderr, "cannot add drm framebuffer for dumb buffer object\n"); goto err_destroy_dumb; } /* map dumb buffer object */ memset(&mreq, 0, sizeof(mreq)); mreq.handle = handle; ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); if (ret) { fprintf(stderr, "failed drmIoctl(DRM_IOCTL_MODE_MAP_DUMB)\n"); goto err_destroy_fb; } map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset); if (map == MAP_FAILED) { fprintf(stderr, "cannot mmap dumb buffer\n"); goto err_destroy_fb; } /* setup new plane */ ret = drmModeSetPlane(fd, plane_id, crtc_id, fb, 0, posx, posy, width, height, 0, 0, width << 16, height << 16); if (ret) { fprintf(stderr, "cannot set plane\n"); goto err_unmap; } /* draw on the screen */ draw_test_image((uint32_t *) map, width, height); getchar(); draw_fancy_image((uint32_t *) map, width, height); getchar(); drmModeSetPlane(fd, plane_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); err_unmap: if (map) munmap(map, size); err_destroy_fb: drmModeRmFB(fd, fb); err_destroy_dumb: memset(&dreq, 0, sizeof(dreq)); dreq.handle = handle; ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); if (ret) { fprintf(stderr, "cannot destroy dumb buffer\n"); } err_close: close(fd); return ret; }
int main(int argc, char **argv) { int fd; uint32_t handle[MAX_VMS], stride[MAX_VMS]; drmModeRes *resources; drmModeConnector *connector; drmModeEncoder *encoder; drmModeModeInfo *mode; int i; uint32_t fb_id[MAX_VMS], total_vms = 0, current_vm; struct drm_i915_gem_vgtfb create; fd = open("/dev/dri/card0", O_RDWR); drmSetMaster(fd); /* Find the first available connector with modes */ resources = drmModeGetResources(fd); if (!resources) { fprintf(stderr, "drmModeGetResources failed\n"); return -1; } for (i = 0; i < resources->count_connectors; i++) { connector = drmModeGetConnector(fd, resources->connectors[i]); if (connector == NULL) continue; if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes > 0) break; drmModeFreeConnector(connector); } if (i == resources->count_connectors) { fprintf(stderr, "No currently active connector found.\n"); return -1; } printf("Using connector: %s\n", connector_type_str(connector->connector_type)); for (i = 0; i < resources->count_encoders; i++) { encoder = drmModeGetEncoder(fd, resources->encoders[i]); if (encoder == NULL) continue; if (encoder->encoder_id == connector->encoder_id) break; drmModeFreeEncoder(encoder); } for (i = 0; i < connector->count_modes; i++) { mode = &connector->modes[i]; if (!strcmp(mode->name, "1680x1050")) break; } printf("Using mode: %s\n", mode->name); for (i = 0; i < (argc - 1) && i < MAX_VMS; i++) { int ret; create.vmid = atoi(argv[i+1]); printf("Requesting object for VM %d\n", create.vmid); if (ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_VGTFB, &create)) { printf("drmIoctl failed for domain %d. ret = %d\n", create.vmid, ret); continue; } handle[i] = create.handle; stride[i] = create.stride; /* Create a KMS framebuffer handle to set a mode with */ if (ret = drmModeAddFB(fd, mode->hdisplay, mode->vdisplay, 24, 32, stride[i], handle[i], &fb_id[i])) { printf("drmModeAddFB failed for handle %d. ret = %d\n", handle[i], ret); continue; } total_vms++; } drmDropMaster(fd); if (total_vms == 0) return -1; current_vm = 0; do { drmSetMaster(fd); drmModeSetCrtc(fd, encoder->crtc_id, fb_id[current_vm], 0, 0, &connector->connector_id, 1, mode); current_vm++; current_vm %= total_vms; drmDropMaster(fd); } while (getchar() != EOF); return 0; }
static void modeset_destroy_fb(int fd, struct modeset_buf *buf) { if (buf->map) { munmap(buf->map, buf->size); } if (buf->fb) { drmModeRmFB(fd, buf->fb); } if (buf->handle) { struct drm_mode_destroy_dumb dreq = { .handle = buf->handle, }; drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); } } static int modeset_create_fb(struct vo *vo, int fd, struct modeset_buf *buf) { int ret = 0; buf->handle = 0; // create dumb buffer struct drm_mode_create_dumb creq = { .width = buf->width, .height = buf->height, .bpp = 32, }; ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); if (ret < 0) { MP_ERR(vo, "Cannot create dumb buffer: %s\n", mp_strerror(errno)); ret = -errno; goto end; } buf->stride = creq.pitch; buf->size = creq.size; buf->handle = creq.handle; // create framebuffer object for the dumb-buffer ret = drmModeAddFB(fd, buf->width, buf->height, 24, 32, buf->stride, buf->handle, &buf->fb); if (ret) { MP_ERR(vo, "Cannot create framebuffer: %s\n", mp_strerror(errno)); ret = -errno; goto end; } // prepare buffer for memory mapping struct drm_mode_map_dumb mreq = { .handle = buf->handle, }; ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); if (ret) { MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno)); ret = -errno; goto end; } // perform actual memory mapping buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset); if (buf->map == MAP_FAILED) { MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno)); ret = -errno; goto end; } memset(buf->map, 0, buf->size); end: if (ret == 0) { return 0; } modeset_destroy_fb(fd, buf); return ret; } static int modeset_find_crtc(struct vo *vo, int fd, drmModeRes *res, drmModeConnector *conn, struct modeset_dev *dev) { for (unsigned int i = 0; i < conn->count_encoders; ++i) { drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]); if (!enc) { MP_WARN(vo, "Cannot retrieve encoder %u:%u: %s\n", i, conn->encoders[i], mp_strerror(errno)); continue; } // iterate all global CRTCs for (unsigned int j = 0; j < res->count_crtcs; ++j) { // check whether this CRTC works with the encoder if (!(enc->possible_crtcs & (1 << j))) continue; dev->enc = enc; dev->crtc = enc->crtc_id; return 0; } drmModeFreeEncoder(enc); } MP_ERR(vo, "Connector %u has no suitable CRTC\n", conn->connector_id); return -ENOENT; } static bool is_connector_valid(struct vo *vo, int conn_id, drmModeConnector *conn, bool silent) { if (!conn) { if (!silent) { MP_ERR(vo, "Cannot get connector %d: %s\n", conn_id, mp_strerror(errno)); } return false; } if (conn->connection != DRM_MODE_CONNECTED) { if (!silent) { MP_ERR(vo, "Connector %d is disconnected\n", conn_id); } return false; } if (conn->count_modes == 0) { if (!silent) { MP_ERR(vo, "Connector %d has no valid modes\n", conn_id); } return false; } return true; } static int modeset_prepare_dev(struct vo *vo, int fd, int conn_id, struct modeset_dev **out) { struct modeset_dev *dev = NULL; drmModeConnector *conn = NULL; int ret = 0; *out = NULL; drmModeRes *res = drmModeGetResources(fd); if (!res) { MP_ERR(vo, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno)); ret = -errno; goto end; } if (conn_id == -1) { // get the first connected connector for (int i = 0; i < res->count_connectors; i++) { conn = drmModeGetConnector(fd, res->connectors[i]); if (is_connector_valid(vo, i, conn, true)) { conn_id = i; break; } if (conn) { drmModeFreeConnector(conn); conn = NULL; } } if (conn_id == -1) { MP_ERR(vo, "No connected connectors found\n"); ret = -ENODEV; goto end; } } if (conn_id < 0 || conn_id >= res->count_connectors) { MP_ERR(vo, "Bad connector ID. Max valid connector ID = %u\n", res->count_connectors); ret = -ENODEV; goto end; } conn = drmModeGetConnector(fd, res->connectors[conn_id]); if (!is_connector_valid(vo, conn_id, conn, false)) { ret = -ENODEV; goto end; } dev = talloc_zero(vo->priv, struct modeset_dev); dev->conn = conn->connector_id; dev->front_buf = 0; dev->mode = conn->modes[0]; dev->bufs[0].width = conn->modes[0].hdisplay; dev->bufs[0].height = conn->modes[0].vdisplay; dev->bufs[1].width = conn->modes[0].hdisplay; dev->bufs[1].height = conn->modes[0].vdisplay; MP_INFO(vo, "Connector using mode %ux%u\n", dev->bufs[0].width, dev->bufs[0].height); ret = modeset_find_crtc(vo, fd, res, conn, dev); if (ret) { MP_ERR(vo, "Connector %d has no valid CRTC\n", conn_id); goto end; } for (unsigned int i = 0; i < BUF_COUNT; i++) { ret = modeset_create_fb(vo, fd, &dev->bufs[i]); if (ret) { MP_ERR(vo, "Cannot create framebuffer for connector %d\n", conn_id); for (unsigned int j = 0; j < i; j++) { modeset_destroy_fb(fd, &dev->bufs[j]); } goto end; } } end: if (conn) { drmModeFreeConnector(conn); conn = NULL; } if (res) { drmModeFreeResources(res); res = NULL; } if (ret == 0) { *out = dev; } else { talloc_free(dev); } return ret; } static void modeset_page_flipped(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { struct priv *p = data; p->pflip_happening = false; } static int setup_vo_crtc(struct vo *vo) { struct priv *p = vo->priv; if (p->active) return 0; p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc); int ret = drmModeSetCrtc(p->fd, p->dev->crtc, p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb, 0, 0, &p->dev->conn, 1, &p->dev->mode); p->active = true; return ret; } static void release_vo_crtc(struct vo *vo) { struct priv *p = vo->priv; if (!p->active) return; p->active = false; // wait for current page flip while (p->pflip_happening) { int ret = drmHandleEvent(p->fd, &p->ev); if (ret) { MP_ERR(vo, "drmHandleEvent failed: %i\n", ret); break; } } if (p->old_crtc) { drmModeSetCrtc(p->fd, p->old_crtc->crtc_id, p->old_crtc->buffer_id, p->old_crtc->x, p->old_crtc->y, &p->dev->conn, 1, &p->dev->mode); drmModeFreeCrtc(p->old_crtc); p->old_crtc = NULL; } } static void release_vt(void *data) { struct vo *vo = data; release_vo_crtc(vo); if (USE_MASTER) { //this function enables support for switching to x, weston etc. //however, for whatever reason, it can be called only by root users. //until things change, this is commented. struct priv *p = vo->priv; if (drmDropMaster(p->fd)) { MP_WARN(vo, "Failed to drop DRM master: %s\n", mp_strerror(errno)); } } } static void acquire_vt(void *data) { struct vo *vo = data; if (USE_MASTER) { struct priv *p = vo->priv; if (drmSetMaster(p->fd)) { MP_WARN(vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno)); } } setup_vo_crtc(vo); } static int wait_events(struct vo *vo, int64_t until_time_us) { struct priv *p = vo->priv; int64_t wait_us = until_time_us - mp_time_us(); int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000); vt_switcher_poll(&p->vt_switcher, timeout_ms); return 0; } static void wakeup(struct vo *vo) { struct priv *p = vo->priv; vt_switcher_interrupt_poll(&p->vt_switcher); } static int reconfig(struct vo *vo, struct mp_image_params *params, int flags) { struct priv *p = vo->priv; vo->dwidth = p->device_w; vo->dheight = p->device_h; vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd); int32_t w = p->dst.x1 - p->dst.x0; int32_t h = p->dst.y1 - p->dst.y0; // p->osd contains the parameters assuming OSD rendering in window // coordinates, but OSD can only be rendered in the intersection // between window and video rectangle (i.e. not into panscan borders). p->osd.w = w; p->osd.h = h; p->osd.mt = MPMIN(0, p->osd.mt); p->osd.mb = MPMIN(0, p->osd.mb); p->osd.mr = MPMIN(0, p->osd.mr); p->osd.ml = MPMIN(0, p->osd.ml); p->x = (p->device_w - w) >> 1; p->y = (p->device_h - h) >> 1; mp_sws_set_from_cmdline(p->sws, vo->opts->sws_opts); p->sws->src = *params; p->sws->dst = (struct mp_image_params) { .imgfmt = IMGFMT_BGR0, .w = w, .h = h, .d_w = w, .d_h = h, }; talloc_free(p->cur_frame); p->cur_frame = mp_image_alloc(IMGFMT_BGR0, p->device_w, p->device_h); mp_image_params_guess_csp(&p->sws->dst); mp_image_set_params(p->cur_frame, &p->sws->dst); struct modeset_buf *buf = p->dev->bufs; memset(buf[0].map, 0, buf[0].size); memset(buf[1].map, 0, buf[1].size); if (mp_sws_reinit(p->sws) < 0) return -1; vo->want_redraw = true; return 0; } static void draw_image(struct vo *vo, mp_image_t *mpi) { struct priv *p = vo->priv; if (p->active) { struct mp_image src = *mpi; struct mp_rect src_rc = p->src; src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x); src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y); mp_image_crop_rc(&src, src_rc); mp_sws_scale(p->sws, p->cur_frame, &src); osd_draw_on_image(vo->osd, p->osd, src.pts, 0, p->cur_frame); struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf]; int32_t shift = (p->device_w * p->y + p->x) * 4; memcpy_pic(front_buf->map + shift, p->cur_frame->planes[0], (p->dst.x1 - p->dst.x0) * 4, p->dst.y1 - p->dst.y0, p->device_w * 4, p->cur_frame->stride[0]); } if (mpi != p->last_input) { talloc_free(p->last_input); p->last_input = mpi; } } static void flip_page(struct vo *vo) { struct priv *p = vo->priv; if (!p->active || p->pflip_happening) return; int ret = drmModePageFlip(p->fd, p->dev->crtc, p->dev->bufs[p->dev->front_buf].fb, DRM_MODE_PAGE_FLIP_EVENT, p); if (ret) { MP_WARN(vo, "Cannot flip page for connector\n"); } else { p->dev->front_buf++; p->dev->front_buf %= BUF_COUNT; p->pflip_happening = true; } // poll page flip finish event const int timeout_ms = 3000; struct pollfd fds[1] = { { .events = POLLIN, .fd = p->fd }, }; poll(fds, 1, timeout_ms); if (fds[0].revents & POLLIN) { ret = drmHandleEvent(p->fd, &p->ev); if (ret != 0) { MP_ERR(vo, "drmHandleEvent failed: %i\n", ret); return; } } } static void uninit(struct vo *vo) { struct priv *p = vo->priv; if (p->dev) { release_vo_crtc(vo); modeset_destroy_fb(p->fd, &p->dev->bufs[1]); modeset_destroy_fb(p->fd, &p->dev->bufs[0]); drmModeFreeEncoder(p->dev->enc); } vt_switcher_destroy(&p->vt_switcher); talloc_free(p->last_input); talloc_free(p->cur_frame); talloc_free(p->dev); close(p->fd); } static int preinit(struct vo *vo) { struct priv *p = vo->priv; p->sws = mp_sws_alloc(vo); p->fd = -1; p->ev.version = DRM_EVENT_CONTEXT_VERSION; p->ev.page_flip_handler = modeset_page_flipped; if (vt_switcher_init(&p->vt_switcher, vo->log)) goto err; vt_switcher_acquire(&p->vt_switcher, acquire_vt, vo); vt_switcher_release(&p->vt_switcher, release_vt, vo); if (modeset_open(vo, &p->fd, p->device_path)) goto err; if (modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev)) goto err; assert(p->dev); p->device_w = p->dev->bufs[0].width; p->device_h = p->dev->bufs[0].height; if (setup_vo_crtc(vo)) { MP_ERR(vo, "Cannot set CRTC for connector %u: %s\n", p->connector_id, mp_strerror(errno)); goto err; } return 0; err: uninit(vo); return -1; } static int query_format(struct vo *vo, int format) { return sws_isSupportedInput(imgfmt2pixfmt(format)); } static int control(struct vo *vo, uint32_t request, void *data) { struct priv *p = vo->priv; switch (request) { case VOCTRL_SCREENSHOT_WIN: *(struct mp_image**)data = mp_image_new_copy(p->cur_frame); return VO_TRUE; case VOCTRL_REDRAW_FRAME: draw_image(vo, p->last_input); return VO_TRUE; case VOCTRL_GET_PANSCAN: return VO_TRUE; case VOCTRL_SET_PANSCAN: if (vo->config_ok) reconfig(vo, vo->params, 0); return VO_TRUE; } return VO_NOTIMPL; } #define OPT_BASE_STRUCT struct priv const struct vo_driver video_out_drm = { .name = "drm", .description = "Direct Rendering Manager", .preinit = preinit, .query_format = query_format, .reconfig = reconfig, .control = control, .draw_image = draw_image, .flip_page = flip_page, .uninit = uninit, .wait_events = wait_events, .wakeup = wakeup, .priv_size = sizeof(struct priv), .options = (const struct m_option[]) { OPT_STRING("devpath", device_path, 0), OPT_INT("connector", connector_id, 0), {0}, }, .priv_defaults = &(const struct priv) {