diff --git a/janus/src/logging.h b/janus/src/logging.h index 73f582144..dec4f089d 100644 --- a/janus/src/logging.h +++ b/janus/src/logging.h @@ -36,11 +36,3 @@ JANUS_LOG(LOG_ERR, "[%s/%-9s] " x_msg ": %s\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__, m_perror_str); \ free(m_perror_str); \ } - -#define US_ONCE(...) { \ - const int m_reported = __LINE__; \ - if (m_reported != once) { \ - __VA_ARGS__; \ - once = m_reported; \ - } \ - } diff --git a/src/libs/tools.h b/src/libs/tools.h index 51b08c0f2..3e931eedb 100644 --- a/src/libs/tools.h +++ b/src/libs/tools.h @@ -72,6 +72,14 @@ (m_a > m_b ? m_a : m_b); \ }) +#define US_ONCE(...) { \ + const int m_reported = __LINE__; \ + if (m_reported != once) { \ + __VA_ARGS__; \ + once = m_reported; \ + } \ + } + INLINE char *us_strdup(const char *str) { char *const new = strdup(str); diff --git a/src/v4p/drm.c b/src/v4p/drm.c index f0db7d2ac..681a0a442 100644 --- a/src/v4p/drm.c +++ b/src/v4p/drm.c @@ -44,14 +44,10 @@ #include "../libs/frametext.h" -static void _drm_vsync_callback(int fd, uint n_frame, uint sec, uint usec, void *v_run); -static int _drm_expose_raw(us_drm_s *drm, const us_frame_s *frame); -static void _drm_cleanup(us_drm_s *drm); -static int _drm_ensure(us_drm_s *drm, const us_frame_s *frame, float hz); +static void _drm_vsync_callback(int fd, uint n_frame, uint sec, uint usec, void *v_buf); static int _drm_check_status(us_drm_s *drm); static int _drm_find_sink(us_drm_s *drm, uint width, uint height, float hz); -static int _drm_init_buffers(us_drm_s *drm); -static int _drm_start_video(us_drm_s *drm); +static int _drm_init_buffers(us_drm_s *drm, const us_device_s *dev); static u32 _find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, u32 *taken_crtcs); static const char *_connector_type_to_string(u32 type); @@ -70,162 +66,119 @@ us_drm_s *us_drm_init(void) { US_CALLOC(run, 1); run->fd = -1; run->status_fd = -1; + run->has_vsync = true; run->ft = us_frametext_init(); - run->state = US_DRM_STATE_CLOSED; us_drm_s *drm; US_CALLOC(drm, 1); // drm->path = "/dev/dri/card0"; drm->path = "/dev/dri/by-path/platform-gpu-card"; drm->port = "HDMI-A-1"; - drm->n_bufs = 4; drm->timeout = 5; drm->run = run; return drm; } void us_drm_destroy(us_drm_s *drm) { - _drm_cleanup(drm); us_frametext_destroy(drm->run->ft); US_DELETE(drm->run, free); US_DELETE(drm, free); // cppcheck-suppress uselessAssignmentPtrArg } -int us_drm_wait_for_vsync(us_drm_s *drm) { +int us_drm_open(us_drm_s *drm, const us_device_s *dev) { us_drm_runtime_s *const run = drm->run; - if (_drm_ensure(drm, NULL, 0) < 0) { - return -1; - } - if (run->has_vsync) { - return 0; - } - - struct timeval timeout = {.tv_sec = drm->timeout}; - fd_set fds; - FD_ZERO(&fds); - FD_SET(run->fd, &fds); + assert(run->fd < 0); - _D_LOG_DEBUG("Calling select() for VSync ..."); - const int result = select(run->fd + 1, &fds, NULL, NULL, &timeout); - if (result < 0) { - _D_LOG_PERROR("Can't select(%d) device for VSync", run->fd); - goto error; - } else if (result == 0) { - _D_LOG_ERROR("Device timeout while waiting VSync"); - goto error; + switch (_drm_check_status(drm)) { + case 0: break; + case -2: goto unplugged; + default: goto error; } - drmEventContext ctx = { - .version = DRM_EVENT_CONTEXT_VERSION, - .page_flip_handler = _drm_vsync_callback, - }; - _D_LOG_DEBUG("Handling DRM event (maybe VSync) ..."); - if (drmHandleEvent(run->fd, &ctx) < 0) { - _D_LOG_PERROR("Can't handle DRM event"); + _D_LOG_INFO("Configuring DRM device for %s ...", (dev == NULL ? "STUB" : "DMA")); + + if ((run->fd = open(drm->path, O_RDWR | O_CLOEXEC | O_NONBLOCK)) < 0) { + _D_LOG_PERROR("Can't open DRM device"); goto error; } - return 0; - -error: - _drm_cleanup(drm); - _D_LOG_ERROR("Device destroyed due an error (vsync)"); - return -1; -} - -int us_drm_expose(us_drm_s *drm, us_drm_expose_e ex, const us_frame_s *frame, float hz) { - us_drm_runtime_s *const run = drm->run; + _D_LOG_DEBUG("DRM device fd=%d opened", run->fd); - if (_drm_ensure(drm, frame, hz) < 0) { - return -1; + int stub = 0; // Open the real device with DMA + if (dev == NULL) { + stub = US_DRM_STUB_USER; + } else if (dev->run->format != V4L2_PIX_FMT_RGB24) { + stub = US_DRM_STUB_BAD_FORMAT; + char fourcc_str[8]; + us_fourcc_to_string(dev->run->format, fourcc_str, 8); + _D_LOG_ERROR("Input format %s is not supported, forcing to STUB ...", fourcc_str); } - const drmModeModeInfo *const mode = &run->mode; - bool msg_drawn = false; - -# define DRAW_MSG(x_msg) { \ - us_frametext_draw(run->ft, (x_msg), mode->hdisplay, mode->vdisplay); \ - frame = run->ft->frame; \ - msg_drawn = true; \ - } - - if (frame == NULL) { - switch (ex) { - case US_DRM_EXPOSE_NO_SIGNAL: - DRAW_MSG("=== PiKVM ===\n \n< NO SIGNAL >"); - break; - case US_DRM_EXPOSE_BUSY: - DRAW_MSG("=== PiKVM ===\n \n< ONLINE IS ACTIVE >"); - break; - default: - DRAW_MSG("=== PiKVM ===\n \n< ??? >"); +# define CHECK_CAP(x_cap) { \ + _D_LOG_DEBUG("Checking %s ...", #x_cap); \ + u64 m_check; \ + if (drmGetCap(run->fd, x_cap, &m_check) < 0) { \ + _D_LOG_PERROR("Can't check " #x_cap); \ + goto error; \ + } \ + if (!m_check) { \ + _D_LOG_ERROR(#x_cap " is not supported"); \ + goto error; \ + } \ } - } else if (mode->hdisplay != frame->width/* || mode->vdisplay != frame->height*/) { - // XXX: At least we'll try to show something instead of nothing ^^^ - char msg[1024]; - US_SNPRINTF(msg, 1023, - "=== PiKVM ===" - "\n \n< UNSUPPORTED RESOLUTION >" - "\n \n< %ux%up%.02f >" - "\n \nby this display", - frame->width, frame->height, hz); - DRAW_MSG(msg); - } else if (frame->format != V4L2_PIX_FMT_RGB24) { - DRAW_MSG( - "=== PiKVM ===" - "\n \n< UNSUPPORTED CAPTURE FORMAT >" - "\n \nIt shouldn't happen ever." - "\n \nPlease check the logs and report a bug:" - "\n \n- https://github.com/pikvm/pikvm -"); + CHECK_CAP(DRM_CAP_DUMB_BUFFER); + if (stub == 0) { + CHECK_CAP(DRM_CAP_PRIME); } +# undef CHECK_CAP -# undef DRAW_MSG + const uint width = (stub > 0 ? 0 : dev->run->width); + const uint height = (stub > 0 ? 0 : dev->run->height); + const uint hz = (stub > 0 ? 0 : dev->run->hz); + switch (_drm_find_sink(drm, width, height, hz)) { + case 0: break; + case -2: goto unplugged; + default: goto error; + } + if ((stub == 0) && (width != run->mode.hdisplay || height < run->mode.vdisplay)) { + // We'll try to show something instead of nothing if height != vdisplay + stub = US_DRM_STUB_BAD_RESOLUTION; + _D_LOG_ERROR("There is no appropriate modes for the capture, forcing to STUB ..."); + } - if (_drm_expose_raw(drm, frame) < 0) { - _drm_cleanup(drm); - _D_LOG_ERROR("Device destroyed due an error (expose)"); + if (_drm_init_buffers(drm, (stub > 0 ? NULL : dev)) < 0) { + goto error; } - return (msg_drawn ? -1 : 0); -} -static void _drm_vsync_callback(int fd, uint n_frame, uint sec, uint usec, void *v_run) { - (void)fd; - (void)n_frame; - (void)sec; - (void)usec; - us_drm_runtime_s *const run = v_run; - run->has_vsync = true; - _D_LOG_DEBUG("Got VSync signal"); -} + run->saved_crtc = drmModeGetCrtc(run->fd, run->crtc_id); + _D_LOG_DEBUG("Setting up CRTC ..."); + if (drmModeSetCrtc(run->fd, run->crtc_id, run->bufs[0].id, 0, 0, &run->conn_id, 1, &run->mode) < 0) { + _D_LOG_PERROR("Can't set CRTC"); + goto error; + } -static int _drm_expose_raw(us_drm_s *drm, const us_frame_s *frame) { - us_drm_runtime_s *const run = drm->run; - us_drm_buffer_s *const buf = &run->bufs[run->next_n_buf]; + run->unplugged_reported = false; + _D_LOG_INFO("Opened for %s ...", (stub == 0 ? "DMA" : "STUB")); + return stub; - _D_LOG_DEBUG("Exposing%s framebuffer n_buf=%u, vsync=%d ...", - (frame == NULL ? " EMPTY" : ""), run->next_n_buf, run->has_vsync); +error: + us_drm_close(drm); + return -1; - if (frame == NULL) { - memset(buf->data, 0, buf->allocated); - } else { - memcpy(buf->data, frame->data, US_MIN(frame->used, buf->allocated)); +unplugged: + if (!run->unplugged_reported) { + _D_LOG_INFO("Display is not plugged"); + run->unplugged_reported = true; } - - run->has_vsync = false; - const int retval = drmModePageFlip( - run->fd, run->crtc_id, buf->id, - DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_ASYNC, - run); - run->next_n_buf = (run->next_n_buf + 1) % run->n_bufs; - return retval; + us_drm_close(drm); + return -2; } -static void _drm_cleanup(us_drm_s *drm) { +void us_drm_close(us_drm_s *drm) { us_drm_runtime_s *const run = drm->run; - _D_LOG_DEBUG("Cleaning up ..."); - if (run->saved_crtc != NULL) { + _D_LOG_DEBUG("Restoring CRTC ..."); if (drmModeSetCrtc(run->fd, run->saved_crtc->crtc_id, run->saved_crtc->buffer_id, run->saved_crtc->x, run->saved_crtc->y, @@ -238,11 +191,9 @@ static void _drm_cleanup(us_drm_s *drm) { } if (run->bufs != NULL) { + _D_LOG_DEBUG("Releasing buffers ..."); for (uint n_buf = 0; n_buf < run->n_bufs; ++n_buf) { us_drm_buffer_s *const buf = &run->bufs[n_buf]; - if (buf->data != NULL && munmap(buf->data, buf->allocated)) { - _D_LOG_PERROR("Can't unmap buffer=%u", n_buf); - } if (buf->fb_added && drmModeRmFB(run->fd, buf->id) < 0) { _D_LOG_PERROR("Can't remove buffer=%u", n_buf); } @@ -252,145 +203,194 @@ static void _drm_cleanup(us_drm_s *drm) { _D_LOG_PERROR("Can't destroy dumb buffer=%u", n_buf); } } + if (buf->data != NULL && munmap(buf->data, buf->allocated)) { + _D_LOG_PERROR("Can't unmap buffer=%u", n_buf); + } } US_DELETE(run->bufs, free); run->n_bufs = 0; } + const bool say = (run->fd >= 0); US_CLOSE_FD(run->status_fd); US_CLOSE_FD(run->fd); run->crtc_id = 0; - run->next_n_buf = 0; - run->has_vsync = false; - if (run->state == US_DRM_STATE_OK) { - _D_LOG_INFO("Stopped"); + run->has_vsync = true; + run->stub_n_buf = 0; + + if (say) { + _D_LOG_INFO("Closed"); } - run->state = US_DRM_STATE_CLOSED; } -static int _drm_ensure(us_drm_s *drm, const us_frame_s *frame, float hz) { +int us_drm_wait_for_vsync(us_drm_s *drm) { us_drm_runtime_s *const run = drm->run; switch (_drm_check_status(drm)) { case 0: break; - case -2: goto unplugged; - default: goto error; + case -2: return -2; + default: return -1; } - if (frame == NULL && run->state == US_DRM_STATE_OK) { + if (run->has_vsync) { return 0; - } else if ( - frame != NULL - && run->p_width == frame->width - && run->p_height == frame->height - && run->p_hz == hz - && run->state <= US_DRM_STATE_CLOSED - ) { - return (run->state == US_DRM_STATE_OK ? 0 : -1); } - const us_drm_state_e saved_state = run->state; - _drm_cleanup(drm); - if (saved_state > US_DRM_STATE_CLOSED) { - run->state = saved_state; + struct timeval timeout = {.tv_sec = drm->timeout}; + fd_set fds; + FD_ZERO(&fds); + FD_SET(run->fd, &fds); + + _D_LOG_DEBUG("Calling select() for VSync ..."); + const int result = select(run->fd + 1, &fds, NULL, NULL, &timeout); + if (result < 0) { + _D_LOG_PERROR("Can't select(%d) device for VSync", run->fd); + return -1; + } else if (result == 0) { + _D_LOG_ERROR("Device timeout while waiting VSync"); + return -1; } - run->p_width = (frame != NULL ? frame->width : 0); // 0 for find the native resolution - run->p_height = (frame != NULL ? frame->height : 0); - run->p_hz = hz; + drmEventContext ctx = { + .version = DRM_EVENT_CONTEXT_VERSION, + .page_flip_handler = _drm_vsync_callback, + }; + _D_LOG_DEBUG("Handling DRM event (maybe VSync) ..."); + if (drmHandleEvent(run->fd, &ctx) < 0) { + _D_LOG_PERROR("Can't handle DRM event"); + return -1; + } + return 0; +} - _D_LOG_INFO("Configuring DRM device ..."); +static void _drm_vsync_callback(int fd, uint n_frame, uint sec, uint usec, void *v_buf) { + (void)fd; + (void)n_frame; + (void)sec; + (void)usec; + us_drm_buffer_s *const buf = v_buf; + *buf->ctx.has_vsync = true; + _D_LOG_DEBUG("Got VSync signal"); +} - if ((run->fd = open(drm->path, O_RDWR | O_CLOEXEC | O_NONBLOCK)) < 0) { - _D_LOG_PERROR("Can't open DRM device"); - goto error; - } +int us_drm_expose_stub(us_drm_s *drm, us_drm_stub_e stub, const us_device_s *dev) { + us_drm_runtime_s *const run = drm->run; -# define CHECK_CAP(x_cap) { \ - u64 m_check; \ - if (drmGetCap(run->fd, x_cap, &m_check) < 0) { \ - _D_LOG_PERROR("Can't check " #x_cap); \ - goto error; \ - } \ - if (!m_check) { \ - _D_LOG_ERROR(#x_cap " is not supported"); \ - goto error; \ - } \ - } - CHECK_CAP(DRM_CAP_DUMB_BUFFER); - // CHECK_CAP(DRM_CAP_PRIME); -# undef CHECK_CAP + assert(run->stub_n_buf >= 0); - switch (_drm_find_sink(drm, run->p_width, run->p_height, run->p_hz)) { + switch (_drm_check_status(drm)) { case 0: break; - case -2: goto unplugged; - default: goto error; + case -2: return -2; + default: return -1; + } + +# define DRAW_MSG(x_msg) us_frametext_draw(run->ft, (x_msg), run->mode.hdisplay, run->mode.vdisplay) + switch (stub) { + case US_DRM_STUB_BAD_RESOLUTION: { + assert(dev != NULL); + char msg[1024]; + US_SNPRINTF(msg, 1023, + "=== PiKVM ===" + "\n \n< UNSUPPORTED RESOLUTION >" + "\n \n< %ux%up%.02f >" + "\n \nby this display", + dev->run->width, dev->run->height, dev->run->hz); + DRAW_MSG(msg); + break; + }; + case US_DRM_STUB_BAD_FORMAT: + DRAW_MSG( + "=== PiKVM ===" + "\n \n< UNSUPPORTED CAPTURE FORMAT >" + "\n \nIt shouldn't happen ever." + "\n \nPlease check the logs and report a bug:" + "\n \n- https://github.com/pikvm/pikvm -"); + break; + case US_DRM_STUB_NO_SIGNAL: + DRAW_MSG("=== PiKVM ===\n \n< NO SIGNAL >"); + break; + case US_DRM_STUB_BUSY: + DRAW_MSG("=== PiKVM ===\n \n< ONLINE IS ACTIVE >"); + break; + default: + DRAW_MSG("=== PiKVM ===\n \n< ??? >"); + break; } +# undef DRAW_MSG - const float mode_hz = _get_refresh_rate(&run->mode); - if (frame == NULL) { - run->p_width = run->mode.hdisplay; - run->p_height = run->mode.vdisplay; - run->p_hz = mode_hz; - } - _D_LOG_INFO("Using %s mode: %ux%up%.02f", - drm->port, run->mode.hdisplay, run->mode.vdisplay, mode_hz); + us_drm_buffer_s *const buf = &run->bufs[run->stub_n_buf]; - if (_drm_init_buffers(drm) < 0) { - goto error; - } + run->has_vsync = false; - if (_drm_start_video(drm) < 0) { - goto error; + _D_LOG_DEBUG("Copying STUB frame ...") + memcpy(buf->data, run->ft->frame->data, US_MIN(run->ft->frame->used, buf->allocated)); + + _D_LOG_DEBUG("Exposing STUB framebuffer n_buf=%u ...", run->stub_n_buf); + const int retval = drmModePageFlip( + run->fd, run->crtc_id, buf->id, + DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_ASYNC, + buf); + if (retval < 0) { + _D_LOG_PERROR("Can't expose STUB framebuffer n_buf=%u ...", run->stub_n_buf); } + _D_LOG_DEBUG("Exposed STUB framebuffer n_buf=%u", run->stub_n_buf); - _D_LOG_INFO("Showing ..."); - run->state = US_DRM_STATE_OK; - return 0; + run->stub_n_buf = (run->stub_n_buf + 1) % run->n_bufs; + return retval; +} -error: - _drm_cleanup(drm); - _D_LOG_ERROR("Device destroyed due an error (ensure)"); - return -1; +int us_drm_expose_dma(us_drm_s *drm, const us_hw_buffer_s *hw) { + us_drm_runtime_s *const run = drm->run; + us_drm_buffer_s *const buf = &run->bufs[hw->buf.index]; -unplugged: - if (run->state != US_DRM_STATE_NO_DISPLAY) { - _D_LOG_INFO("Display %s is not plugged", drm->port); + run->has_vsync = false; + + _D_LOG_DEBUG("Exposing DMA framebuffer n_buf=%u ...", hw->buf.index); + const int retval = drmModePageFlip( + run->fd, run->crtc_id, buf->id, + DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_ASYNC, + buf); + if (retval < 0) { + _D_LOG_PERROR("Can't expose DMA framebuffer n_buf=%u ...", run->stub_n_buf); } - _drm_cleanup(drm); - run->state = US_DRM_STATE_NO_DISPLAY; - return -2; + _D_LOG_DEBUG("Exposed DMA framebuffer n_buf=%u", run->stub_n_buf); + return retval; } static int _drm_check_status(us_drm_s *drm) { us_drm_runtime_s *run = drm->run; if (run->status_fd < 0) { + _D_LOG_DEBUG("Trying to find status file ..."); struct stat st; if (stat(drm->path, &st) < 0) { _D_LOG_PERROR("Can't stat() DRM device"); goto error; } const uint mi = minor(st.st_rdev); + _D_LOG_DEBUG("DRM device minor(st_rdev)=%u", mi); char path[128]; US_SNPRINTF(path, 127, "/sys/class/drm/card%u-%s/status", mi, drm->port); + _D_LOG_DEBUG("Opening status file %s ...", path); if ((run->status_fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) { - _D_LOG_PERROR("Can't open DRM device status file: %s", path); + _D_LOG_PERROR("Can't open status file: %s", path); goto error; } + _D_LOG_DEBUG("Status file fd=%d opened", run->status_fd); } char status_ch; if (read(run->status_fd, &status_ch, 1) != 1) { - _D_LOG_PERROR("Can't read connector status"); + _D_LOG_PERROR("Can't read status file"); goto error; } if (lseek(run->status_fd, 0, SEEK_SET) != 0) { - _D_LOG_PERROR("Can't rewind connector status"); + _D_LOG_PERROR("Can't rewind status file"); goto error; } + _D_LOG_DEBUG("Current display status: %c", status_ch); return (status_ch == 'd' ? -2 : 0); error: @@ -434,7 +434,7 @@ static int _drm_find_sink(us_drm_s *drm, uint width, uint height, float hz) { drm->port, conn->connector_type, conn->connector_type_id); if (conn->connection != DRM_MODE_CONNECTED) { - _D_LOG_DEBUG("Display is not connected"); + _D_LOG_ERROR("Connector for port %s has !DRM_MODE_CONNECTED", drm->port); drmModeFreeConnector(conn); goto done; } @@ -467,13 +467,16 @@ static int _drm_find_sink(us_drm_s *drm, uint width, uint height, float hz) { if (best == NULL) { best = pref; } if (best == NULL) { best = (conn->count_modes > 0 ? &conn->modes[0] : NULL); } if (best == NULL) { - _D_LOG_ERROR("Can't find any appropriate resolutions"); + _D_LOG_ERROR("Can't find any appropriate display modes"); drmModeFreeConnector(conn); goto unplugged; } assert(best->hdisplay > 0); assert(best->vdisplay > 0); + _D_LOG_INFO("The best display mode is: %ux%up%.02f", + best->hdisplay, best->vdisplay, _get_refresh_rate(best)); + u32 taken_crtcs = 0; // Unused here if ((run->crtc_id = _find_crtc(run->fd, res, conn, &taken_crtcs)) == 0) { _D_LOG_ERROR("Can't find CRTC"); @@ -496,31 +499,66 @@ static int _drm_find_sink(us_drm_s *drm, uint width, uint height, float hz) { return -2; } -static int _drm_init_buffers(us_drm_s *drm) { +static int _drm_init_buffers(us_drm_s *drm, const us_device_s *dev) { us_drm_runtime_s *const run = drm->run; - _D_LOG_DEBUG("Initializing %u buffers ...", drm->n_bufs); + const uint n_bufs = (dev == NULL ? 4 : dev->run->n_bufs); + const char *name = (dev == NULL ? "STUB" : "DMA"); - US_CALLOC(run->bufs, drm->n_bufs); - for (run->n_bufs = 0; run->n_bufs < drm->n_bufs; ++run->n_bufs) { + _D_LOG_DEBUG("Initializing %u %s buffers ...", n_bufs, name); + + US_CALLOC(run->bufs, n_bufs); + for (run->n_bufs = 0; run->n_bufs < n_bufs; ++run->n_bufs) { const uint n_buf = run->n_bufs; us_drm_buffer_s *const buf = &run->bufs[n_buf]; - struct drm_mode_create_dumb create = { - .width = run->mode.hdisplay, - .height = run->mode.vdisplay, - .bpp = 24, - }; - if (drmIoctl(run->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create) < 0) { - _D_LOG_PERROR("Can't create dumb buffer=%u", n_buf); - return -1; - } - buf->handle = create.handle; - buf->dumb_created = true; + buf->ctx.has_vsync = &run->has_vsync; - u32 handles[4] = {create.handle}; - u32 strides[4] = {create.pitch}; + u32 handles[4] = {0}; + u32 strides[4] = {0}; u32 offsets[4] = {0}; + + if (dev == NULL) { + struct drm_mode_create_dumb create = { + .width = run->mode.hdisplay, + .height = run->mode.vdisplay, + .bpp = 24, + }; + if (drmIoctl(run->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create) < 0) { + _D_LOG_PERROR("Can't create %s buffer=%u", name, n_buf); + return -1; + } + buf->handle = create.handle; + buf->dumb_created = true; + + struct drm_mode_map_dumb map = {.handle = create.handle}; + if (drmIoctl(run->fd, DRM_IOCTL_MODE_MAP_DUMB, &map) < 0) { + _D_LOG_PERROR("Can't prepare dumb buffer=%u to mapping", n_buf); + return -1; + } + if ((buf->data = mmap( + NULL, create.size, + PROT_READ | PROT_WRITE, MAP_SHARED, + run->fd, map.offset + )) == MAP_FAILED) { + _D_LOG_PERROR("Can't map buffer=%u", n_buf); + return -1; + } + memset(buf->data, 0, create.size); + buf->allocated = create.size; + + handles[0] = create.handle; + strides[0] = create.pitch; + + } else { + if (drmPrimeFDToHandle(run->fd, dev->run->hw_bufs[n_buf].dma_fd, &buf->handle) < 0) { + _D_LOG_PERROR("Can't import DMA buffer=%u from capture device", n_buf); + return -1; + } + handles[0] = buf->handle; + strides[0] = dev->run->stride; + } + if (drmModeAddFB2( run->fd, run->mode.hdisplay, run->mode.vdisplay, DRM_FORMAT_RGB888, @@ -530,38 +568,6 @@ static int _drm_init_buffers(us_drm_s *drm) { return -1; } buf->fb_added = true; - - struct drm_mode_map_dumb map = {.handle = create.handle}; - if (drmIoctl(run->fd, DRM_IOCTL_MODE_MAP_DUMB, &map) < 0) { - _D_LOG_PERROR("Can't prepare dumb buffer=%u to mapping", n_buf); - return -1; - } - - if ((buf->data = mmap( - NULL, create.size, - PROT_READ | PROT_WRITE, MAP_SHARED, - run->fd, map.offset - )) == MAP_FAILED) { - _D_LOG_PERROR("Can't map buffer=%u", n_buf); - return -1; - } - memset(buf->data, 0, create.size); - buf->allocated = create.size; - } - return 0; -} - -static int _drm_start_video(us_drm_s *drm) { - us_drm_runtime_s *const run = drm->run; - run->saved_crtc = drmModeGetCrtc(run->fd, run->crtc_id); - _D_LOG_DEBUG("Setting up CRTC ..."); - if (drmModeSetCrtc(run->fd, run->crtc_id, run->bufs[0].id, 0, 0, &run->conn_id, 1, &run->mode) < 0) { - _D_LOG_PERROR("Can't set CRTC"); - return -1; - } - if (_drm_expose_raw(drm, NULL) < 0) { - _D_LOG_PERROR("Can't flip the first page"); - return -1; } return 0; } diff --git a/src/v4p/drm.h b/src/v4p/drm.h index f288aad7b..6a41b1a64 100644 --- a/src/v4p/drm.h +++ b/src/v4p/drm.h @@ -28,19 +28,16 @@ #include "../libs/types.h" #include "../libs/frame.h" #include "../libs/frametext.h" +#include "../libs/device.h" typedef enum { - US_DRM_EXPOSE_FRAME = 0, - US_DRM_EXPOSE_NO_SIGNAL, - US_DRM_EXPOSE_BUSY, -} us_drm_expose_e; - -typedef enum { - US_DRM_STATE_OK = 0, - US_DRM_STATE_CLOSED, - US_DRM_STATE_NO_DISPLAY, -} us_drm_state_e; + US_DRM_STUB_USER = 1, + US_DRM_STUB_BAD_RESOLUTION, + US_DRM_STUB_BAD_FORMAT, + US_DRM_STUB_NO_SIGNAL, + US_DRM_STUB_BUSY, +} us_drm_stub_e; typedef struct { u32 id; @@ -49,11 +46,13 @@ typedef struct { uz allocated; bool dumb_created; bool fb_added; + struct { + bool *has_vsync; + } ctx; } us_drm_buffer_s; typedef struct { int status_fd; - int fd; u32 crtc_id; u32 conn_id; @@ -61,22 +60,15 @@ typedef struct { us_drm_buffer_s *bufs; uint n_bufs; drmModeCrtc *saved_crtc; - uint next_n_buf; bool has_vsync; - + int stub_n_buf; us_frametext_s *ft; - - uint p_width; - uint p_height; - float p_hz; - - us_drm_state_e state; + bool unplugged_reported; } us_drm_runtime_s; typedef struct { char *path; char *port; - uint n_bufs; uint timeout; us_drm_runtime_s *run; @@ -86,5 +78,9 @@ typedef struct { us_drm_s *us_drm_init(void); void us_drm_destroy(us_drm_s *drm); +int us_drm_open(us_drm_s *drm, const us_device_s *dev); +void us_drm_close(us_drm_s *drm); + +int us_drm_expose_stub(us_drm_s *drm, us_drm_stub_e stub, const us_device_s *dev); +int us_drm_expose_dma(us_drm_s *drm, const us_hw_buffer_s *hw); int us_drm_wait_for_vsync(us_drm_s *drm); -int us_drm_expose(us_drm_s *drm, us_drm_expose_e ex, const us_frame_s *frame, float hz); diff --git a/src/v4p/main.c b/src/v4p/main.c index 144b2bd53..dacd6add1 100644 --- a/src/v4p/main.c +++ b/src/v4p/main.c @@ -164,61 +164,102 @@ static void _main_loop(void) { us_device_s *dev = us_device_init(); dev->path = "/dev/kvmd-video"; - dev->n_bufs = drm->n_bufs; + dev->n_bufs = 6; dev->format = V4L2_PIX_FMT_RGB24; dev->dv_timings = true; dev->persistent = true; + dev->dma_export = true; + dev->dma_required = true; + int once = 0; + int drm_opened = -1; while (!atomic_load(&_g_stop)) { + if (drm_opened <= 0) { + if ((drm_opened = us_drm_open(drm, NULL)) < 0) { + goto close; + } + } + assert(drm_opened > 0); + if (atomic_load(&_g_ustreamer_online)) { - if (us_drm_wait_for_vsync(drm) == 0) { - us_drm_expose(drm, US_DRM_EXPOSE_BUSY, NULL, 0); + US_ONCE({ US_LOG_INFO("DRM: Online stream is active"); }); + if (us_drm_wait_for_vsync(drm) < 0) { + goto close; } - if (dev->run->fd >= 0) { + if (us_drm_expose_stub(drm, US_DRM_STUB_BUSY, NULL) < 0) { goto close; - } else { - _slowdown(); - continue; } + _slowdown(); + continue; } if (us_device_open(dev) < 0) { - if (us_drm_wait_for_vsync(drm) == 0) { - us_drm_expose(drm, US_DRM_EXPOSE_NO_SIGNAL, NULL, 0); + if (us_drm_wait_for_vsync(drm) < 0) { + goto close; } + if (us_drm_expose_stub(drm, US_DRM_STUB_NO_SIGNAL, NULL) < 0) { + goto close; + } + _slowdown(); + continue; + } + + us_drm_close(drm); + if ((drm_opened = us_drm_open(drm, dev)) < 0) { goto close; } + once = 0; + + us_hw_buffer_s *prev_hw = NULL; while (!atomic_load(&_g_stop)) { if (atomic_load(&_g_ustreamer_online)) { goto close; } if (us_drm_wait_for_vsync(drm) < 0) { - _slowdown(); - continue; + goto close; + } + + if (prev_hw != NULL) { + if (us_device_release_buffer(dev, prev_hw) < 0) { + goto close; + } + prev_hw = NULL; } us_hw_buffer_s *hw; - const int buf_index = us_device_grab_buffer(dev, &hw); - switch (buf_index) { + const int n_buf = us_device_grab_buffer(dev, &hw); + switch (n_buf) { case -2: continue; // Broken frame case -1: goto close; // Any error } - assert(buf_index >= 0); + assert(n_buf >= 0); - const int exposed = us_drm_expose(drm, US_DRM_EXPOSE_FRAME, &hw->raw, dev->run->hz); - if (us_device_release_buffer(dev, hw) < 0) { - goto close; + int exposed; + if (drm_opened == 0) { + exposed = us_drm_expose_dma(drm, hw); + prev_hw = hw; + } else { + exposed = us_drm_expose_stub(drm, drm_opened, dev); + if (us_device_release_buffer(dev, hw) < 0) { + goto close; + } } + if (exposed < 0) { + goto close; + } else if (drm_opened > 0) { _slowdown(); - continue; } } close: + us_drm_close(drm); + drm_opened = -1; + us_device_close(dev); + _slowdown(); }