static void deactivate (ply_renderer_backend_t *backend) { ply_trace ("dropping master"); drmDropMaster (backend->device_fd); backend->is_active = false; }
static void quit(struct weston_launch *wl, int status) { struct vt_mode mode = { 0 }; int err; close(wl->signalfd); close(wl->sock[0]); if (wl->new_user) { err = pam_close_session(wl->ph, 0); if (err) fprintf(stderr, "pam_close_session failed: %d: %s\n", err, pam_strerror(wl->ph, err)); pam_end(wl->ph, err); } if (ioctl(wl->tty, KDSKBMUTE, 0) && ioctl(wl->tty, KDSKBMODE, wl->kb_mode)) fprintf(stderr, "failed to restore keyboard mode: %m\n"); if (ioctl(wl->tty, KDSETMODE, KD_TEXT)) fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n"); /* We have to drop master before we switch the VT back in * VT_AUTO, so we don't risk switching to a VT with another * display server, that will then fail to set drm master. */ drmDropMaster(wl->drm_fd); mode.mode = VT_AUTO; if (ioctl(wl->tty, VT_SETMODE, &mode) < 0) fprintf(stderr, "could not reset vt handling\n"); exit(status); }
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 video_sleep(struct uterm_video *video) { if (!video_is_awake(video)) return; drmDropMaster(video->dumb.fd); video->flags &= ~VIDEO_AWAKE; }
static void TegraLeaveVT(VT_FUNC_ARGS_DECL) { SCRN_INFO_PTR(arg); TegraPtr tegra = TegraPTR(pScrn); xf86_hide_cursors(pScrn); pScrn->vtSema = FALSE; drmDropMaster(tegra->fd); }
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; }
/* * Drop from the master of a DRM device. */ void gralloc_drm_drop_master(struct gralloc_drm_t *drm) { int ret; if (!drm->master) return; ret = drmDropMaster(drm->fd); if (ret) LOGE("Error: drmDropMaster failed: %s\n", strerror(errno)); drm->master = 0; }
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; }
static void release_vt(void *data) { struct MPGLContext *ctx = data; MP_VERBOSE(ctx->vo, "Releasing VT"); crtc_release(ctx); 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 = ctx->priv; if (drmDropMaster(p->kms->fd)) { MP_WARN(ctx->vo, "Failed to drop DRM master: %s\n", mp_strerror(errno)); } } }
static void video_destroy(struct uterm_video *video) { struct dumb_video *dumb = &video->dumb; struct uterm_display *disp; while ((disp = video->displays)) { video->displays = disp->next; disp->next = NULL; unbind_display(disp); } log_info("free drm device"); ev_eloop_rm_fd(dumb->efd); drmDropMaster(dumb->fd); close(dumb->fd); }
static void stop_devices(bool fatal) { int i; for (i = 0; i < num_drm_fds; ++i) { if (drmDropMaster(drm_fds[i]) < 0 && fatal) die("drmDropMaster:"); } for (i = 0; i < num_input_fds; ++i) { if (ioctl(input_fds[i], EVIOCREVOKE, 0) < 0 && errno != ENODEV && fatal) die("ioctl EVIOCREVOKE:"); close(input_fds[i]); } num_input_fds = 0; }
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 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; }
void CDRMUtils::DestroyDrm() { RestoreOriginalMode(); drmDropMaster(m_fd); close(m_fd); m_fd = -1; drmModeFreeResources(m_drm_resources); m_drm_resources = nullptr; drmModeFreeConnector(m_connector->connector); drmModeFreeObjectProperties(m_connector->props); delete [] m_connector->props_info; delete m_connector; m_connector = nullptr; drmModeFreeEncoder(m_encoder->encoder); delete m_encoder; m_encoder = nullptr; drmModeFreeCrtc(m_crtc->crtc); drmModeFreeObjectProperties(m_crtc->props); delete [] m_crtc->props_info; delete m_crtc; m_crtc = nullptr; drmModeFreePlane(m_primary_plane->plane); drmModeFreeObjectProperties(m_primary_plane->props); delete [] m_primary_plane->props_info; delete m_primary_plane; m_primary_plane = nullptr; drmModeFreePlane(m_overlay_plane->plane); drmModeFreeObjectProperties(m_overlay_plane->props); delete [] m_overlay_plane->props_info; delete m_overlay_plane; m_overlay_plane = nullptr; }
static void launcher_direct_restore(struct weston_launcher *launcher_base) { struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base); struct vt_mode mode = { 0 }; if (ioctl(launcher->tty, KDSKBMUTE, 0) && ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode)) weston_log("failed to restore kb mode: %m\n"); if (ioctl(launcher->tty, KDSETMODE, KD_TEXT)) weston_log("failed to set KD_TEXT mode on tty: %m\n"); /* We have to drop master before we switch the VT back in * VT_AUTO, so we don't risk switching to a VT with another * display server, that will then fail to set drm master. */ drmDropMaster(launcher->drm_fd); mode.mode = VT_AUTO; if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) weston_log("could not reset vt handling\n"); }
static int video_init(struct uterm_video *video, const char *node) { int ret; struct dumb_video *dumb = &video->dumb; uint64_t has_dumb; log_info("probing %s", node); dumb->fd = open(node, O_RDWR | O_CLOEXEC); if (dumb->fd < 0) { log_err("cannot open drm device %s (%d): %m", node, errno); return -EFAULT; } drmDropMaster(dumb->fd); if (drmGetCap(dumb->fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 || !has_dumb) { log_err("driver does not support dumb buffers"); ret = -EFAULT; goto err_close; } ret = ev_eloop_new_fd(video->eloop, &dumb->efd, dumb->fd, EV_READABLE, event, video); if (ret) goto err_close; video->flags |= VIDEO_HOTPLUG; log_info("new drm device via %s", node); return 0; err_close: close(dumb->fd); return ret; }
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; }
unsigned int init_pci(unsigned char bus, const unsigned char forcemem) { int ret = pci_system_init(); if (ret) die(_("Failed to init pciaccess")); struct pci_id_match match; match.vendor_id = 0x1002; match.device_id = PCI_MATCH_ANY; match.subvendor_id = PCI_MATCH_ANY; match.subdevice_id = PCI_MATCH_ANY; match.device_class = 0; match.device_class_mask = 0; match.match_data = 0; struct pci_device_iterator *iter = pci_id_match_iterator_create(&match); struct pci_device *dev = NULL; char busid[32]; while ((dev = pci_device_next(iter))) { pci_device_probe(dev); if ((dev->device_class & 0x00ffff00) != 0x00030000 && (dev->device_class & 0x00ffff00) != 0x00038000) continue; snprintf(busid, sizeof(busid), "pci:%04x:%02x:%02x.%u", dev->domain, dev->bus, dev->dev, dev->func); if (!bus || bus == dev->bus) break; } pci_iterator_destroy(iter); if (!dev) die(_("Can't find Radeon cards")); const unsigned int device_id = dev->device_id; int reg = 2; if (getfamily(device_id) >= BONAIRE) reg = 5; if (!dev->regions[reg].size) die(_("Can't get the register area size")); // printf("Found area %p, size %lu\n", area, dev->regions[reg].size); // DRM support for VRAM drm_fd = drmOpen(NULL, busid); if (drm_fd >= 0) { drmVersionPtr ver = drmGetVersion(drm_fd); if (strcmp(ver->name, "radeon") != 0 && strcmp(ver->name, "amdgpu") != 0) { close(drm_fd); drm_fd = -1; } strcpy(drm_name, ver->name); drmFreeVersion(ver); } if (drm_fd < 0 && access("/dev/ati/card0", F_OK) == 0) // fglrx path drm_fd = open("/dev/ati/card0", O_RDWR); use_ioctl = 0; if (drm_fd >= 0) { authenticate_drm(drm_fd); uint32_t rreg = 0x8010; use_ioctl = get_drm_value(drm_fd, RADEON_INFO_READ_REG, &rreg); } if (forcemem) { printf(_("Forcing the /dev/mem path.\n")); use_ioctl = 0; } if (!use_ioctl) { int mem = open("/dev/mem", O_RDONLY); if (mem < 0) die(_("Cannot access GPU registers, are you root?")); area = mmap(NULL, MMAP_SIZE, PROT_READ, MAP_PRIVATE, mem, dev->regions[reg].base_addr + 0x8000); if (area == MAP_FAILED) die(_("mmap failed")); } bits.vram = 0; if (drm_fd < 0) { printf(_("Failed to open DRM node, no VRAM support.\n")); } else { drmDropMaster(drm_fd); drmVersionPtr ver = drmGetVersion(drm_fd); /* printf("Version %u.%u.%u, name %s\n", ver->version_major, ver->version_minor, ver->version_patchlevel, ver->name);*/ if (ver->version_major < 2 || (ver->version_major == 2 && ver->version_minor < 36)) { printf(_("Kernel too old for VRAM reporting.\n")); drmFreeVersion(ver); goto out; } drmFreeVersion(ver); // No version indicator, so we need to test once // We use different codepaths for radeon and amdgpu // We store vram_size and check below if the ret value is sane if (strcmp(drm_name, "radeon") == 0) { struct drm_radeon_gem_info gem; ret = drmCommandWriteRead(drm_fd, DRM_RADEON_GEM_INFO, &gem, sizeof(gem)); vramsize = gem.vram_size; } else if (strcmp(drm_name, "amdgpu") == 0) { #ifdef ENABLE_AMDGPU struct drm_amdgpu_info_vram_gtt vram_gtt = {}; struct drm_amdgpu_info request; memset(&request, 0, sizeof(request)); request.return_pointer = (unsigned long) &vram_gtt; request.return_size = sizeof(vram_gtt); request.query = AMDGPU_INFO_VRAM_GTT; ret = drmCommandWrite(drm_fd, DRM_AMDGPU_INFO, &request, sizeof(request)); vramsize = vram_gtt.vram_size; #else printf(_("amdgpu DRM driver is used, but amdgpu VRAM size reporting is not enabled\n")); #endif } if (ret) { printf(_("Failed to get VRAM size, error %d\n"), ret); goto out; } ret = getvram(); if (ret == 0) { if (strcmp(drm_name, "amdgpu") == 0) { #ifndef ENABLE_AMDGPU printf(_("amdgpu DRM driver is used, but amdgpu VRAM usage reporting is not enabled\n")); #endif } printf(_("Failed to get VRAM usage, kernel likely too old\n")); goto out; } bits.vram = 1; } out: pci_system_cleanup(); return device_id; }
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) {