From 4f30a24e0767fc947357543cbaea4e0ffdc8a5d0 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 2 Sep 2026 13:12:56 +0100 Subject: [PATCH 1/3] wsdisplay: add framebuffer damage reporting API --- share/man/man4/wsdisplay.4 | 33 +++++++++++++++++++++++++++++++++ sys/dev/wscons/wsconsio.h | 17 +++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/share/man/man4/wsdisplay.4 b/share/man/man4/wsdisplay.4 index 031c1d56a7a89..52270977389b2 100644 --- a/share/man/man4/wsdisplay.4 +++ b/share/man/man4/wsdisplay.4 @@ -238,6 +238,39 @@ field contains and the .Va fbi_cmapinfo field contains the number of color map entries. +.Pp +The +.Va fbi_flags +field may contain +.Dv WSFB_VRAM_IS_RAM , +.Dv WSFB_VRAM_IS_SPLIT , +or +.Dv WSFB_VRAM_NEEDS_DAMAGE . +The last flag indicates that changes to the mapped framebuffer must be +reported with +.Dv WSDISPLAYIO_DAMAGE . +.It Dv WSDISPLAYIO_DAMAGE Pq Li "struct wsdisplay_damage" +Report a changed rectangle of a mapped framebuffer. +The returned framebuffer information must include +.Dv WSFB_VRAM_NEEDS_DAMAGE +for this call to be necessary. +The argument structure is as follows: +.Bd -literal -offset indent +struct wsdisplay_damage { + uint32_t flags; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; +}; +.Ed +.Pp +The rectangle must be within the framebuffer bounds. +If +.Dv WSDISPLAY_DAMAGE_WAIT +is set in +.Va flags , +the call waits until all queued framebuffer updates have completed. .It Dv WSDISPLAYIO_GINFO Pq Li "struct wsdisplay_fbinfo" Retrieve basic information about a framebuffer display. The returned structure is as follows: diff --git a/sys/dev/wscons/wsconsio.h b/sys/dev/wscons/wsconsio.h index 8d81defeb3ab5..22ebb0402ab4e 100644 --- a/sys/dev/wscons/wsconsio.h +++ b/sys/dev/wscons/wsconsio.h @@ -688,6 +688,7 @@ struct wsdisplayio_fbinfo { /* fbi_flags */ #define WSFB_VRAM_IS_RAM 0x0001 /* hint for wsfb - don't shadow */ #define WSFB_VRAM_IS_SPLIT 0x0002 /* workaround for wildcat... */ +#define WSFB_VRAM_NEEDS_DAMAGE 0x0004 /* framebuffer needs damage reports */ #define WSDISPLAYIO_GET_FBINFO _IOWR('W', 104, struct wsdisplayio_fbinfo) @@ -753,4 +754,20 @@ struct wsdisplay_getfont { */ #define WSDISPLAYIO_SET_DEPTH _IOW('W', 109, int) +/* + * Report a changed framebuffer rectangle. + * + * This is only necessary if WSFB_VRAM_NEEDS_DAMAGE is set in fbi_flags. + */ +struct wsdisplay_damage { + uint32_t flags; +#define WSDISPLAY_DAMAGE_WAIT 0x0001 /* wait for the update to finish */ + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; +}; + +#define WSDISPLAYIO_DAMAGE _IOWR('W', 110, struct wsdisplay_damage) + #endif /* _DEV_WSCONS_WSCONSIO_H_ */ From 33f98006bed81774c92d3e90e7fee3685c1b98dc Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 2 Sep 2026 13:13:06 +0100 Subject: [PATCH 2/3] viogpu: support mmap and damage updates --- share/man/man4/viogpu.4 | 14 ++-- sys/dev/pci/viogpu.c | 153 ++++++++++++++++++++++++++++++++++------ 2 files changed, 138 insertions(+), 29 deletions(-) diff --git a/share/man/man4/viogpu.4 b/share/man/man4/viogpu.4 index 149e375e78050..9b8dd71915313 100644 --- a/share/man/man4/viogpu.4 +++ b/share/man/man4/viogpu.4 @@ -31,21 +31,21 @@ driver provides support for the .Xr virtio 4 GPU interface provided by QEMU and other virtual machines to create a .Xr wscons 4 -console. It does not provide access to the 3D functionality of the virtio GPU -device. It does not support mapping the framebuffer for generic graphics use -by drivers such as +console. +The framebuffer can be mapped for generic graphics use by drivers such as .Xr wsfb 4 . -.El +The driver does not provide access to the 3D functionality of the virtio GPU +device. .Sh SEE ALSO .Xr intro 4 , -.Xr virtio 4 +.Xr virtio 4 , .Xr wscons 4 , -.Xr wsdisplay 4 , +.Xr wsdisplay 4 .Sh HISTORY The .Nm driver first appeared in -.Ox 7.4 +.Ox 7.4 and was ported for .Nx 11 . .Sh AUTHORS diff --git a/sys/dev/pci/viogpu.c b/sys/dev/pci/viogpu.c index f41836a8cfc98..ae35db660e992 100644 --- a/sys/dev/pci/viogpu.c +++ b/sys/dev/pci/viogpu.c @@ -75,6 +75,9 @@ static int viogpu_cmd_sync(struct viogpu_softc *, void *, size_t, void *, size_t); static int viogpu_cmd_req(struct viogpu_softc *, void *, size_t, size_t); static void viogpu_screen_update(void *); +static void viogpu_queue_update(struct viogpu_softc *, uint32_t, + uint32_t, uint32_t, uint32_t); +static void viogpu_start_update(struct viogpu_softc *); static int viogpu_vq_done(struct virtqueue *vq); static int viogpu_get_display_info(struct viogpu_softc *); @@ -92,6 +95,7 @@ static int viogpu_flush_resource(struct viogpu_softc *, uint32_t, static int viogpu_wsioctl(void *, void *, u_long, void *, int, struct lwp *); +static paddr_t viogpu_wsmmap(void *, void *, off_t, int); static void viogpu_init_screen(void *, struct vcons_screen *, int, long *); @@ -154,14 +158,16 @@ struct viogpu_softc { void (*ri_replaceattr)(void *, long, long); /* - * sc_mutex protects is_requesting, needs_update, and req_wait. It is - * also held while submitting and reading the return values of + * sc_mutex protects is_requesting, the update rectangles, and req_wait. + * It is also held while submitting and reading the return values of * asynchronous commands and for the full duration of synchronous * commands. */ kmutex_t sc_mutex; bool is_requesting; - bool needs_update; + bool update_pending; + struct wsdisplay_damage update_rect; + struct wsdisplay_damage active_rect; kcondvar_t req_wait; void *update_soft_ih; size_t cur_cmd_size; @@ -179,8 +185,7 @@ CFATTACH_DECL_NEW(viogpu, sizeof(struct viogpu_softc), static struct wsdisplay_accessops viogpu_accessops = { .ioctl = viogpu_wsioctl, - .mmap = NULL, /* This would require signalling on write to - * update the screen. */ + .mmap = viogpu_wsmmap, .alloc_screen = NULL, .free_screen = NULL, .show_screen = NULL, @@ -220,7 +225,7 @@ viogpu_attach(device_t parent, device_t self, void *aux) cv_init(&sc->req_wait, "vgpu_req"); sc->update_soft_ih = softint_establish(SOFTINT_NET, viogpu_screen_update, sc); - sc->needs_update = false; + sc->update_pending = false; sc->is_requesting = false; sc->sc_fence_id = 0; @@ -465,14 +470,61 @@ viogpu_screen_update(void *arg) struct viogpu_softc *sc = arg; mutex_enter(&sc->sc_mutex); + viogpu_queue_update(sc, 0, 0, sc->sc_fb_width, sc->sc_fb_height); + mutex_exit(&sc->sc_mutex); +} - if (sc->is_requesting == false) - viogpu_transfer_to_host_2d(sc, 1, 0, 0, sc->sc_fb_width, - sc->sc_fb_height); - else - sc->needs_update = true; +/* + * Merge an update into the pending rectangle and start it if the control + * queue is idle. sc_mutex must be held. + */ +static void +viogpu_queue_update(struct viogpu_softc *sc, uint32_t x, uint32_t y, + uint32_t width, uint32_t height) +{ + struct wsdisplay_damage *d = &sc->update_rect; + uint32_t x2 = x + width; + uint32_t y2 = y + height; + + KASSERT(mutex_owned(&sc->sc_mutex)); + KASSERT(width != 0 && height != 0); + + if (!sc->update_pending) { + d->x = x; + d->y = y; + d->width = width; + d->height = height; + sc->update_pending = true; + } else { + uint32_t old_x2 = d->x + d->width; + uint32_t old_y2 = d->y + d->height; + + d->x = uimin(d->x, x); + d->y = uimin(d->y, y); + d->width = uimax(old_x2, x2) - d->x; + d->height = uimax(old_y2, y2) - d->y; + } - mutex_exit(&sc->sc_mutex); + if (!sc->is_requesting) + viogpu_start_update(sc); +} + +/* Start the pending update. sc_mutex must be held and the queue idle. */ +static void +viogpu_start_update(struct viogpu_softc *sc) +{ + struct wsdisplay_damage *d = &sc->update_rect; + + KASSERT(mutex_owned(&sc->sc_mutex)); + KASSERT(sc->update_pending); + + sc->active_rect = *d; + sc->update_pending = false; + + /* The host is about to read the framebuffer backing store. */ + bus_dmamap_sync(virtio_dmat(sc->sc_virtio), sc->sc_fb_dma_map, 0, + sc->sc_fb_dma_size, BUS_DMASYNC_PREWRITE); + viogpu_transfer_to_host_2d(sc, 1, d->x, d->y, d->width, d->height); } static int @@ -562,16 +614,20 @@ viogpu_vq_done(struct virtqueue *vq) case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: /* The second command for screen updating must be issued. */ if (resp_type == VIRTIO_GPU_RESP_OK_NODATA) { - viogpu_flush_resource(sc, 1, 0, 0, sc->sc_fb_width, - sc->sc_fb_height); + struct wsdisplay_damage *d = &sc->active_rect; + + viogpu_flush_resource(sc, 1, d->x, d->y, d->width, + d->height); next_req_sent = true; - } + } else + bus_dmamap_sync(virtio_dmat(vsc), sc->sc_fb_dma_map, + 0, sc->sc_fb_dma_size, BUS_DMASYNC_POSTWRITE); break; case VIRTIO_GPU_CMD_RESOURCE_FLUSH: - if (sc->needs_update == true) { - viogpu_transfer_to_host_2d(sc, 1, 0, 0, - sc->sc_fb_width, sc->sc_fb_height); - sc->needs_update = false; + bus_dmamap_sync(virtio_dmat(vsc), sc->sc_fb_dma_map, 0, + sc->sc_fb_dma_size, BUS_DMASYNC_POSTWRITE); + if (sc->update_pending) { + viogpu_start_update(sc); next_req_sent = true; } break; @@ -580,7 +636,16 @@ viogpu_vq_done(struct virtqueue *vq) break; } - if (next_req_sent == false) { + /* On an update error, discard that rectangle but keep later damage. */ + if (!next_req_sent && + (cmd_type == VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D || + cmd_type == VIRTIO_GPU_CMD_RESOURCE_FLUSH) && + sc->update_pending) { + viogpu_start_update(sc); + next_req_sent = true; + } + + if (!next_req_sent) { sc->is_requesting = false; cv_broadcast(&sc->req_wait); } @@ -770,9 +835,13 @@ static int viogpu_wsioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) { - struct rasops_info *ri = v; + struct vcons_data *vd = v; + struct viogpu_softc *sc = vd->cookie; + struct rasops_info *ri = &((struct vcons_screen *)vs)->scr_ri; + struct wsdisplay_damage *d; struct wsdisplayio_fbinfo *fbi; struct wsdisplay_fbinfo *wdf; + int error; switch (cmd) { case WSDISPLAYIO_GTYPE: @@ -780,7 +849,34 @@ viogpu_wsioctl(void *v, void *vs, u_long cmd, void *data, int flag, return 0; case WSDISPLAYIO_GET_FBINFO: fbi = (struct wsdisplayio_fbinfo *)data; - return wsdisplayio_get_fbinfo(ri, fbi); + error = wsdisplayio_get_fbinfo(ri, fbi); + if (error == 0) + fbi->fbi_flags |= WSFB_VRAM_IS_RAM | + WSFB_VRAM_NEEDS_DAMAGE; + return error; + case WSDISPLAYIO_DAMAGE: + d = (struct wsdisplay_damage *)data; + if (d->flags & ~WSDISPLAY_DAMAGE_WAIT) + return EINVAL; + if (d->width != 0 && + (d->x >= sc->sc_fb_width || + d->width > sc->sc_fb_width - d->x)) + return EINVAL; + if (d->height != 0 && + (d->y >= sc->sc_fb_height || + d->height > sc->sc_fb_height - d->y)) + return EINVAL; + + mutex_enter(&sc->sc_mutex); + if (d->width != 0 && d->height != 0) + viogpu_queue_update(sc, d->x, d->y, d->width, + d->height); + if (d->flags & WSDISPLAY_DAMAGE_WAIT) { + while (sc->is_requesting || sc->update_pending) + cv_wait(&sc->req_wait, &sc->sc_mutex); + } + mutex_exit(&sc->sc_mutex); + return 0; case WSDISPLAYIO_GINFO: wdf = (struct wsdisplay_fbinfo *)data; wdf->height = ri->ri_height; @@ -801,6 +897,19 @@ viogpu_wsioctl(void *v, void *vs, u_long cmd, void *data, int flag, return EPASSTHROUGH; } +static paddr_t +viogpu_wsmmap(void *v, void *vs, off_t off, int prot) +{ + struct vcons_data *vd = v; + struct viogpu_softc *sc = vd->cookie; + + if (off < 0 || off >= sc->sc_fb_dma_size) + return -1; + + return bus_dmamem_mmap(virtio_dmat(sc->sc_virtio), + &sc->sc_fb_dma_seg, 1, off, prot, BUS_DMA_WAITOK); +} + static void viogpu_init_screen(void *cookie, struct vcons_screen *scr, int existing, long *defattr) From 2a32fa7a1668c5fdd40e5e4494a599df6c317371 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 2 Sep 2026 13:13:17 +0100 Subject: [PATCH 3/3] udl: support wsdisplay damage updates --- share/man/man4/udl.4 | 2 + sys/dev/usb/udl.c | 102 ++++++++++++++++++++++--------------------- 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/share/man/man4/udl.4 b/share/man/man4/udl.4 index b05aa98b8afe5..4ea560e2b9357 100644 --- a/share/man/man4/udl.4 +++ b/share/man/man4/udl.4 @@ -38,6 +38,8 @@ The .Nm driver provides support for DisplayLink DL-1x0/1x5 based USB LCD screens, docks, and display adapters. +The framebuffer can be mapped for generic graphics use by drivers such as +.Xr wsfb 4 . .Pp The following devices are supported: .Pp diff --git a/sys/dev/usb/udl.c b/sys/dev/usb/udl.c index 783b9d77f6a4a..4af7d3dc5f6f1 100644 --- a/sys/dev/usb/udl.c +++ b/sys/dev/usb/udl.c @@ -90,9 +90,6 @@ __KERNEL_RCSID(0, "$NetBSD: udl.c,v 1.35 2024/10/02 17:22:45 tsutsui Exp $"); #include #include -#ifdef notyet -#include -#endif /* * Defines. @@ -147,21 +144,19 @@ static void udl_copy_rect(struct udl_softc *, int, int, int, int, int, int); static void udl_fill_rect(struct udl_softc *, uint16_t, int, int, int, int); -#ifdef notyet static void udl_draw_rect(struct udl_softc *, - struct udl_ioctl_damage *); + const struct wsdisplay_damage *); static void udl_draw_rect_comp(struct udl_softc *, - struct udl_ioctl_damage *); -#endif + const struct wsdisplay_damage *); static inline void udl_copy_line(struct udl_softc *, int, int, int); static inline void udl_fill_line(struct udl_softc *, uint16_t, int, int); static inline void udl_draw_line(struct udl_softc *, uint16_t *, int, int); -#ifdef notyet -static inline void udl_draw_line_comp(struct udl_softc *, uint16_t *, int, +static inline int udl_cmd_add_buf_comp(struct udl_softc *, uint16_t *, int); -#endif +static inline void udl_draw_line_comp(struct udl_softc *, uint16_t *, + int, int); static int udl_cmd_send(struct udl_softc *); static void udl_cmd_send_async(struct udl_softc *); @@ -566,10 +561,10 @@ static int udl_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) { struct udl_softc *sc = v; -#ifdef notyet - struct udl_ioctl_damage *d; -#endif + struct wsdisplay_damage *d; + struct wsdisplayio_fbinfo *fbi; struct wsdisplay_fbinfo *wdf; + int error; u_int mode; switch (cmd) { @@ -585,6 +580,14 @@ udl_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) wdf->cmsize = 0; return 0; + case WSDISPLAYIO_GET_FBINFO: + fbi = (struct wsdisplayio_fbinfo *)data; + error = wsdisplayio_get_fbinfo(&sc->sc_ri, fbi); + if (error == 0) + fbi->fbi_flags |= WSFB_VRAM_IS_RAM | + WSFB_VRAM_NEEDS_DAMAGE; + return error; + case WSDISPLAYIO_GVIDEO: *(u_int *)data = sc->sc_blank; return 0; @@ -630,7 +633,8 @@ udl_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) udl_cmd_send_async(sc); udl_cmdq_flush(sc); udl_comp_load(sc); - udl_startstop(sc, false); + /* Updates are submitted with WSDISPLAYIO_DAMAGE. */ + udl_startstop(sc, true); break; default: return EINVAL; @@ -642,25 +646,30 @@ udl_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) *(u_int *)data = sc->sc_width * (sc->sc_depth / 8); return 0; -#ifdef notyet - /* - * XXX - * OpenBSD allows device specific ioctl()s and use this - * UDLIO_DAMAGE for the damage extension ops of X servers. - * Before blindly pulling such interfaces, probably we should - * discuss how such devices should be handled which have - * in-direct framebuffer memories that should be transferred - * per updated rectangle regions via MI wscons APIs. - */ - case UDLIO_DAMAGE: - d = (struct udl_ioctl_damage *)data; - d->status = UDLIO_STATUS_OK; - if (sc->sc_flags & UDL_COMPRDY) - udl_draw_rect_comp(sc, d); - else - udl_draw_rect(sc, d); + case WSDISPLAYIO_DAMAGE: + d = (struct wsdisplay_damage *)data; + if (d->flags & ~WSDISPLAY_DAMAGE_WAIT) + return EINVAL; + if (d->width != 0 && + (d->x >= sc->sc_width || + d->width > sc->sc_width - d->x)) + return EINVAL; + if (d->height != 0 && + (d->y >= sc->sc_height || + d->height > sc->sc_height - d->y)) + return EINVAL; + if (sc->sc_mode != WSDISPLAYIO_MODE_DUMBFB || + sc->sc_fbmem == NULL) + return EBUSY; + if (d->width != 0 && d->height != 0) { + if (sc->sc_flags & UDL_COMPRDY) + udl_draw_rect_comp(sc, d); + else + udl_draw_rect(sc, d); + } + if (d->flags & WSDISPLAY_DAMAGE_WAIT) + udl_cmdq_flush(sc); return 0; -#endif } return EPASSTHROUGH; @@ -674,15 +683,13 @@ udl_mmap(void *v, void *vs, off_t off, int prot) paddr_t paddr; bool rv __diagused; - if (off < 0 || off > roundup2(UDL_FBMEM_SIZE(sc), PAGE_SIZE)) + if (off < 0 || off >= roundup2(UDL_FBMEM_SIZE(sc), PAGE_SIZE)) return -1; /* allocate framebuffer memory */ if (udl_fbmem_alloc(sc) != 0) return -1; - udl_startstop(sc, false); - vaddr = (vaddr_t)sc->sc_fbmem + off; rv = pmap_extract(pmap_kernel(), vaddr, &paddr); KASSERT(rv); @@ -1259,16 +1266,15 @@ udl_fill_rect(struct udl_softc *sc, uint16_t rgb16, int x, int y, int width, } } -#ifdef notyet static void -udl_draw_rect(struct udl_softc *sc, struct udl_ioctl_damage *d) +udl_draw_rect(struct udl_softc *sc, const struct wsdisplay_damage *d) { int sbase, soff, ebase, eoff, x, y, width, width_cur, height; - x = d->x1; - y = d->y1; - width = d->x2 - d->x1; - height = d->y2 - d->y1; + x = d->x; + y = d->y; + width = d->width; + height = d->height; sbase = y * sc->sc_width; ebase = (y + height) * sc->sc_width; @@ -1295,14 +1301,15 @@ udl_draw_rect(struct udl_softc *sc, struct udl_ioctl_damage *d) } static void -udl_draw_rect_comp(struct udl_softc *sc, struct udl_ioctl_damage *d) +udl_draw_rect_comp(struct udl_softc *sc, + const struct wsdisplay_damage *d) { int soff, eoff, x, y, width, height; - x = d->x1; - y = d->y1; - width = d->x2 - d->x1; - height = d->y2 - d->y1; + x = d->x; + y = d->y; + width = d->width; + height = d->height; soff = y * sc->sc_width + x; eoff = (y + height) * sc->sc_width + x; @@ -1317,7 +1324,6 @@ udl_draw_rect_comp(struct udl_softc *sc, struct udl_ioctl_damage *d) udl_cmd_send_async(sc); } -#endif static inline void udl_copy_line(struct udl_softc *sc, int soff, int doff, int width) @@ -1363,7 +1369,6 @@ udl_draw_line(struct udl_softc *sc, uint16_t *buf, int off, int width) udl_cmd_add_buf(sc, buf, width); } -#ifdef notyet static inline int udl_cmd_add_buf_comp(struct udl_softc *sc, uint16_t *buf, int width) { @@ -1473,7 +1478,6 @@ udl_draw_line_comp(struct udl_softc *sc, uint16_t *buf, int off, int width) width -= width_cur; } } -#endif static int udl_cmd_send(struct udl_softc *sc)