1 1.1 martin /* $NetBSD: viogpu.c,v 1.1 2025/07/26 14:18:13 martin Exp $ */ 2 1.1 martin /* $OpenBSD: viogpu.c,v 1.3 2023/05/29 08:13:35 sf Exp $ */ 3 1.1 martin 4 1.1 martin /* 5 1.1 martin * Copyright (c) 2024-2025 The NetBSD Foundation, Inc. 6 1.1 martin * All rights reserved. 7 1.1 martin * 8 1.1 martin * Redistribution and use in source and binary forms, with or without 9 1.1 martin * modification, are permitted provided that the following conditions 10 1.1 martin * are met: 11 1.1 martin * 1. Redistributions of source code must retain the above copyright 12 1.1 martin * notice, this list of conditions and the following disclaimer. 13 1.1 martin * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 martin * notice, this list of conditions and the following disclaimer in the 15 1.1 martin * documentation and/or other materials provided with the distribution. 16 1.1 martin * 17 1.1 martin * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 1.1 martin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 1.1 martin * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 1.1 martin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 1.1 martin * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 1.1 martin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 1.1 martin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 1.1 martin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 1.1 martin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 1.1 martin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 1.1 martin * POSSIBILITY OF SUCH DAMAGE. 28 1.1 martin */ 29 1.1 martin 30 1.1 martin /* 31 1.1 martin * Copyright (c) 2021-2023 joshua stein <jcs (at) openbsd.org> 32 1.1 martin * 33 1.1 martin * Permission to use, copy, modify, and distribute this software for any 34 1.1 martin * purpose with or without fee is hereby granted, provided that the above 35 1.1 martin * copyright notice and this permission notice appear in all copies. 36 1.1 martin * 37 1.1 martin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 38 1.1 martin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 39 1.1 martin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 40 1.1 martin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 41 1.1 martin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 42 1.1 martin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 43 1.1 martin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 44 1.1 martin */ 45 1.1 martin 46 1.1 martin #include <sys/cdefs.h> 47 1.1 martin 48 1.1 martin #include <sys/param.h> 49 1.1 martin #include <sys/systm.h> 50 1.1 martin #include <sys/bus.h> 51 1.1 martin #include <sys/condvar.h> 52 1.1 martin #include <sys/device.h> 53 1.1 martin #include <sys/intr.h> 54 1.1 martin #include <sys/kernel.h> 55 1.1 martin #include <sys/mutex.h> 56 1.1 martin 57 1.1 martin #include <dev/pci/virtioreg.h> 58 1.1 martin #include <dev/pci/virtiovar.h> 59 1.1 martin #include <dev/pci/viogpu.h> 60 1.1 martin 61 1.1 martin #include <dev/rasops/rasops.h> 62 1.1 martin 63 1.1 martin #include <dev/wscons/wsconsio.h> 64 1.1 martin #include <dev/wscons/wsdisplayvar.h> 65 1.1 martin #include <dev/wscons/wsdisplay_vconsvar.h> 66 1.1 martin 67 1.1 martin #include <prop/proplib.h> 68 1.1 martin 69 1.1 martin struct viogpu_softc; 70 1.1 martin 71 1.1 martin static int viogpu_match(device_t, cfdata_t, void *); 72 1.1 martin static void viogpu_attach(device_t, device_t, void *); 73 1.1 martin static void viogpu_attach_postintr(device_t); 74 1.1 martin static int viogpu_cmd_sync(struct viogpu_softc *, void *, size_t, void *, 75 1.1 martin size_t); 76 1.1 martin static int viogpu_cmd_req(struct viogpu_softc *, void *, size_t, size_t); 77 1.1 martin static void viogpu_screen_update(void *); 78 1.1 martin static int viogpu_vq_done(struct virtqueue *vq); 79 1.1 martin 80 1.1 martin static int viogpu_get_display_info(struct viogpu_softc *); 81 1.1 martin static int viogpu_create_2d(struct viogpu_softc *, uint32_t, uint32_t, 82 1.1 martin uint32_t); 83 1.1 martin static int viogpu_set_scanout(struct viogpu_softc *, uint32_t, uint32_t, 84 1.1 martin uint32_t, uint32_t); 85 1.1 martin static int viogpu_attach_backing(struct viogpu_softc *, uint32_t, 86 1.1 martin bus_dmamap_t); 87 1.1 martin static int viogpu_transfer_to_host_2d(struct viogpu_softc *sc, uint32_t, 88 1.1 martin uint32_t, uint32_t, uint32_t, 89 1.1 martin uint32_t); 90 1.1 martin static int viogpu_flush_resource(struct viogpu_softc *, uint32_t, 91 1.1 martin uint32_t, uint32_t, uint32_t, uint32_t); 92 1.1 martin 93 1.1 martin static int viogpu_wsioctl(void *, void *, u_long, void *, int, 94 1.1 martin struct lwp *); 95 1.1 martin 96 1.1 martin static void viogpu_init_screen(void *, struct vcons_screen *, int, long *); 97 1.1 martin 98 1.1 martin static void viogpu_cursor(void *, int, int, int); 99 1.1 martin static void viogpu_putchar(void *, int, int, u_int, long); 100 1.1 martin static void viogpu_copycols(void *, int, int, int, int); 101 1.1 martin static void viogpu_erasecols(void *, int, int, int, long); 102 1.1 martin static void viogpu_copyrows(void *, int, int, int); 103 1.1 martin static void viogpu_eraserows(void *, int, int, long); 104 1.1 martin static void viogpu_replaceattr(void *, long, long); 105 1.1 martin 106 1.1 martin struct virtio_gpu_resource_attach_backing_entries { 107 1.1 martin struct virtio_gpu_ctrl_hdr hdr; 108 1.1 martin __le32 resource_id; 109 1.1 martin __le32 nr_entries; 110 1.1 martin struct virtio_gpu_mem_entry entries[1]; 111 1.1 martin } __packed; 112 1.1 martin 113 1.1 martin #define VIOGPU_CMD_DMA_SIZE \ 114 1.1 martin MAX(sizeof(struct virtio_gpu_resp_display_info), \ 115 1.1 martin MAX(sizeof(struct virtio_gpu_resource_create_2d), \ 116 1.1 martin MAX(sizeof(struct virtio_gpu_set_scanout), \ 117 1.1 martin MAX(sizeof(struct virtio_gpu_resource_attach_backing_entries), \ 118 1.1 martin MAX(sizeof(struct virtio_gpu_transfer_to_host_2d), \ 119 1.1 martin sizeof(struct virtio_gpu_resource_flush)))))) + \ 120 1.1 martin sizeof(struct virtio_gpu_ctrl_hdr) 121 1.1 martin 122 1.1 martin struct viogpu_softc { 123 1.1 martin device_t sc_dev; 124 1.1 martin struct virtio_softc *sc_virtio; 125 1.1 martin #define VQCTRL 0 126 1.1 martin #define VQCURS 1 127 1.1 martin struct virtqueue sc_vqs[2]; 128 1.1 martin 129 1.1 martin bus_dma_segment_t sc_dma_seg; 130 1.1 martin bus_dmamap_t sc_dma_map; 131 1.1 martin void *sc_cmd; 132 1.1 martin int sc_fence_id; 133 1.1 martin 134 1.1 martin int sc_fb_height; 135 1.1 martin int sc_fb_width; 136 1.1 martin bus_dma_segment_t sc_fb_dma_seg; 137 1.1 martin bus_dmamap_t sc_fb_dma_map; 138 1.1 martin size_t sc_fb_dma_size; 139 1.1 martin void *sc_fb_dma_kva; 140 1.1 martin 141 1.1 martin struct wsscreen_descr sc_wsd; 142 1.1 martin const struct wsscreen_descr *sc_scrlist[1]; 143 1.1 martin struct wsscreen_list sc_wsl; 144 1.1 martin struct vcons_data sc_vd; 145 1.1 martin struct vcons_screen sc_vcs; 146 1.1 martin bool is_console; 147 1.1 martin 148 1.1 martin void (*ri_cursor)(void *, int, int, int); 149 1.1 martin void (*ri_putchar)(void *, int, int, u_int, long); 150 1.1 martin void (*ri_copycols)(void *, int, int, int, int); 151 1.1 martin void (*ri_erasecols)(void *, int, int, int, long); 152 1.1 martin void (*ri_copyrows)(void *, int, int, int); 153 1.1 martin void (*ri_eraserows)(void *, int, int, long); 154 1.1 martin void (*ri_replaceattr)(void *, long, long); 155 1.1 martin 156 1.1 martin /* 157 1.1 martin * sc_mutex protects is_requesting, needs_update, and req_wait. It is 158 1.1 martin * also held while submitting and reading the return values of 159 1.1 martin * asynchronous commands and for the full duration of synchronous 160 1.1 martin * commands. 161 1.1 martin */ 162 1.1 martin kmutex_t sc_mutex; 163 1.1 martin bool is_requesting; 164 1.1 martin bool needs_update; 165 1.1 martin kcondvar_t req_wait; 166 1.1 martin void *update_soft_ih; 167 1.1 martin size_t cur_cmd_size; 168 1.1 martin size_t cur_ret_size; 169 1.1 martin }; 170 1.1 martin 171 1.1 martin CFATTACH_DECL_NEW(viogpu, sizeof(struct viogpu_softc), 172 1.1 martin viogpu_match, viogpu_attach, NULL, NULL); 173 1.1 martin 174 1.1 martin #if VIOGPU_DEBUG 175 1.1 martin #define VIOGPU_FEATURES (VIRTIO_GPU_F_VIRGL | VIRTIO_GPU_F_EDID) 176 1.1 martin #else 177 1.1 martin #define VIOGPU_FEATURES 0 178 1.1 martin #endif 179 1.1 martin 180 1.1 martin static struct wsdisplay_accessops viogpu_accessops = { 181 1.1 martin .ioctl = viogpu_wsioctl, 182 1.1 martin .mmap = NULL, /* This would require signalling on write to 183 1.1 martin * update the screen. */ 184 1.1 martin .alloc_screen = NULL, 185 1.1 martin .free_screen = NULL, 186 1.1 martin .show_screen = NULL, 187 1.1 martin .load_font = NULL, 188 1.1 martin .pollc = NULL, 189 1.1 martin .scroll = NULL, 190 1.1 martin }; 191 1.1 martin 192 1.1 martin static int 193 1.1 martin viogpu_match(device_t parent, cfdata_t match, void *aux) 194 1.1 martin { 195 1.1 martin struct virtio_attach_args *va = aux; 196 1.1 martin 197 1.1 martin if (va->sc_childdevid == VIRTIO_DEVICE_ID_GPU) 198 1.1 martin return 1; 199 1.1 martin 200 1.1 martin return 0; 201 1.1 martin } 202 1.1 martin 203 1.1 martin static void 204 1.1 martin viogpu_attach(device_t parent, device_t self, void *aux) 205 1.1 martin { 206 1.1 martin struct viogpu_softc *sc = device_private(self); 207 1.1 martin struct virtio_softc *vsc = device_private(parent); 208 1.1 martin int error; 209 1.1 martin 210 1.1 martin if (virtio_child(vsc) != NULL) { 211 1.1 martin aprint_error("child already attached for %s\n", 212 1.1 martin device_xname(parent)); 213 1.1 martin return; 214 1.1 martin } 215 1.1 martin 216 1.1 martin sc->sc_dev = self; 217 1.1 martin sc->sc_virtio = vsc; 218 1.1 martin 219 1.1 martin mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); 220 1.1 martin cv_init(&sc->req_wait, "vgpu_req"); 221 1.1 martin sc->update_soft_ih = softint_establish(SOFTINT_NET, 222 1.1 martin viogpu_screen_update, sc); 223 1.1 martin sc->needs_update = false; 224 1.1 martin sc->is_requesting = false; 225 1.1 martin sc->sc_fence_id = 0; 226 1.1 martin 227 1.1 martin virtio_child_attach_start(vsc, self, IPL_VM, 228 1.1 martin VIOGPU_FEATURES, VIRTIO_COMMON_FLAG_BITS); 229 1.1 martin 230 1.1 martin if (!virtio_version_1(vsc)) { 231 1.1 martin aprint_error_dev(sc->sc_dev, "requires virtio version 1\n"); 232 1.1 martin goto err; 233 1.1 martin } 234 1.1 martin 235 1.1 martin /* Allocate command and cursor virtqueues. */ 236 1.1 martin virtio_init_vq_vqdone(vsc, &sc->sc_vqs[VQCTRL], 0, viogpu_vq_done); 237 1.1 martin error = virtio_alloc_vq(vsc, &sc->sc_vqs[VQCTRL], NBPG, 1, "control"); 238 1.1 martin if (error != 0) { 239 1.1 martin aprint_error_dev(sc->sc_dev, "alloc_vq failed: %d\n", error); 240 1.1 martin goto err; 241 1.1 martin } 242 1.1 martin 243 1.1 martin virtio_init_vq_vqdone(vsc, &sc->sc_vqs[VQCURS], 1, viogpu_vq_done); 244 1.1 martin error = virtio_alloc_vq(vsc, &sc->sc_vqs[VQCURS], NBPG, 1, "cursor"); 245 1.1 martin if (error != 0) { 246 1.1 martin aprint_error_dev(sc->sc_dev, "alloc_vq failed: %d\n", error); 247 1.1 martin goto free_vq0; 248 1.1 martin } 249 1.1 martin 250 1.1 martin if (virtio_child_attach_finish(vsc, sc->sc_vqs, 251 1.1 martin __arraycount(sc->sc_vqs), NULL, 252 1.1 martin VIRTIO_F_INTR_MPSAFE | VIRTIO_F_INTR_SOFTINT) != 0) 253 1.1 martin goto free_vqs; 254 1.1 martin 255 1.1 martin /* Interrupts are required for synchronous commands in attachment. */ 256 1.1 martin config_interrupts(self, viogpu_attach_postintr); 257 1.1 martin 258 1.1 martin return; 259 1.1 martin 260 1.1 martin free_vqs: 261 1.1 martin virtio_free_vq(vsc, &sc->sc_vqs[VQCURS]); 262 1.1 martin free_vq0: 263 1.1 martin virtio_free_vq(vsc, &sc->sc_vqs[VQCTRL]); 264 1.1 martin err: 265 1.1 martin virtio_child_attach_failed(vsc); 266 1.1 martin cv_destroy(&sc->req_wait); 267 1.1 martin mutex_destroy(&sc->sc_mutex); 268 1.1 martin return; 269 1.1 martin } 270 1.1 martin 271 1.1 martin static void 272 1.1 martin viogpu_attach_postintr(device_t self) 273 1.1 martin { 274 1.1 martin struct viogpu_softc *sc = device_private(self); 275 1.1 martin struct virtio_softc *vsc = sc->sc_virtio; 276 1.1 martin struct wsemuldisplaydev_attach_args waa; 277 1.1 martin struct rasops_info *ri; 278 1.1 martin prop_dictionary_t dict; 279 1.1 martin long defattr; 280 1.1 martin int nsegs; 281 1.1 martin int error; 282 1.1 martin 283 1.1 martin /* Set up DMA space for sending commands. */ 284 1.1 martin error = bus_dmamap_create(virtio_dmat(vsc), VIOGPU_CMD_DMA_SIZE, 1, 285 1.1 martin VIOGPU_CMD_DMA_SIZE, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, 286 1.1 martin &sc->sc_dma_map); 287 1.1 martin if (error != 0) { 288 1.1 martin aprint_error_dev(sc->sc_dev, "bus_dmamap_create failed: %d\n", 289 1.1 martin error); 290 1.1 martin goto err; 291 1.1 martin } 292 1.1 martin error = bus_dmamem_alloc(virtio_dmat(vsc), VIOGPU_CMD_DMA_SIZE, 16, 0, 293 1.1 martin &sc->sc_dma_seg, 1, &nsegs, BUS_DMA_NOWAIT); 294 1.1 martin if (error != 0) { 295 1.1 martin aprint_error_dev(sc->sc_dev, "bus_dmamem_alloc failed: %d\n", 296 1.1 martin error); 297 1.1 martin goto destroy; 298 1.1 martin } 299 1.1 martin error = bus_dmamem_map(virtio_dmat(vsc), &sc->sc_dma_seg, nsegs, 300 1.1 martin VIOGPU_CMD_DMA_SIZE, &sc->sc_cmd, BUS_DMA_NOWAIT); 301 1.1 martin if (error != 0) { 302 1.1 martin aprint_error_dev(sc->sc_dev, "bus_dmamem_map failed: %d\n", 303 1.1 martin error); 304 1.1 martin goto free; 305 1.1 martin } 306 1.1 martin memset(sc->sc_cmd, 0, VIOGPU_CMD_DMA_SIZE); 307 1.1 martin error = bus_dmamap_load(virtio_dmat(vsc), sc->sc_dma_map, sc->sc_cmd, 308 1.1 martin VIOGPU_CMD_DMA_SIZE, NULL, BUS_DMA_NOWAIT); 309 1.1 martin if (error != 0) { 310 1.1 martin aprint_error_dev(sc->sc_dev, "bus_dmamap_load failed: %d\n", 311 1.1 martin error); 312 1.1 martin goto unmap; 313 1.1 martin } 314 1.1 martin 315 1.1 martin if (viogpu_get_display_info(sc) != 0) 316 1.1 martin goto unmap; 317 1.1 martin 318 1.1 martin /* Set up DMA space for actual framebuffer. */ 319 1.1 martin sc->sc_fb_dma_size = sc->sc_fb_width * sc->sc_fb_height * 4; 320 1.1 martin error = bus_dmamap_create(virtio_dmat(vsc), sc->sc_fb_dma_size, 1, 321 1.1 martin sc->sc_fb_dma_size, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, 322 1.1 martin &sc->sc_fb_dma_map); 323 1.1 martin if (error != 0) { 324 1.1 martin aprint_error_dev(sc->sc_dev, "bus_dmamap_create failed: %d\n", 325 1.1 martin error); 326 1.1 martin goto unmap; 327 1.1 martin } 328 1.1 martin error = bus_dmamem_alloc(virtio_dmat(vsc), sc->sc_fb_dma_size, 1024, 0, 329 1.1 martin &sc->sc_fb_dma_seg, 1, &nsegs, BUS_DMA_NOWAIT); 330 1.1 martin if (error != 0) { 331 1.1 martin aprint_error_dev(sc->sc_dev, "bus_dmamem_alloc failed: %d\n", 332 1.1 martin error); 333 1.1 martin goto fb_destroy; 334 1.1 martin } 335 1.1 martin error = bus_dmamem_map(virtio_dmat(vsc), &sc->sc_fb_dma_seg, nsegs, 336 1.1 martin sc->sc_fb_dma_size, &sc->sc_fb_dma_kva, BUS_DMA_NOWAIT); 337 1.1 martin if (error != 0) { 338 1.1 martin aprint_error_dev(sc->sc_dev, "bus_dmamem_map failed: %d\n", 339 1.1 martin error); 340 1.1 martin goto fb_free; 341 1.1 martin } 342 1.1 martin memset(sc->sc_fb_dma_kva, 0, sc->sc_fb_dma_size); 343 1.1 martin error = bus_dmamap_load(virtio_dmat(vsc), sc->sc_fb_dma_map, 344 1.1 martin sc->sc_fb_dma_kva, sc->sc_fb_dma_size, NULL, BUS_DMA_NOWAIT); 345 1.1 martin if (error != 0) { 346 1.1 martin aprint_error_dev(sc->sc_dev, "bus_dmamap_load failed: %d\n", 347 1.1 martin error); 348 1.1 martin goto fb_unmap; 349 1.1 martin } 350 1.1 martin 351 1.1 martin if (viogpu_create_2d(sc, 1, sc->sc_fb_width, sc->sc_fb_height) != 0) 352 1.1 martin goto fb_unmap; 353 1.1 martin 354 1.1 martin if (viogpu_attach_backing(sc, 1, sc->sc_fb_dma_map) != 0) 355 1.1 martin goto fb_unmap; 356 1.1 martin 357 1.1 martin if (viogpu_set_scanout(sc, 0, 1, sc->sc_fb_width, 358 1.1 martin sc->sc_fb_height) != 0) 359 1.1 martin goto fb_unmap; 360 1.1 martin 361 1.1 martin #ifdef WSDISPLAY_MULTICONS 362 1.1 martin sc->is_console = true; 363 1.1 martin #else 364 1.1 martin sc->is_console = false; 365 1.1 martin #endif 366 1.1 martin dict = device_properties(self); 367 1.1 martin prop_dictionary_get_bool(dict, "is_console", &sc->is_console); 368 1.1 martin 369 1.1 martin sc->sc_wsd = (struct wsscreen_descr){ 370 1.1 martin "std", 371 1.1 martin 0, 0, 372 1.1 martin NULL, 373 1.1 martin 8, 16, 374 1.1 martin WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 375 1.1 martin NULL 376 1.1 martin }; 377 1.1 martin 378 1.1 martin sc->sc_scrlist[0] = &sc->sc_wsd; 379 1.1 martin sc->sc_wsl.nscreens = __arraycount(sc->sc_scrlist); 380 1.1 martin sc->sc_wsl.screens = sc->sc_scrlist; 381 1.1 martin 382 1.1 martin vcons_init(&sc->sc_vd, sc, &sc->sc_wsd, &viogpu_accessops); 383 1.1 martin sc->sc_vd.init_screen = viogpu_init_screen; 384 1.1 martin 385 1.1 martin vcons_init_screen(&sc->sc_vd, &sc->sc_vcs, 1, &defattr); 386 1.1 martin sc->sc_vcs.scr_flags |= VCONS_SCREEN_IS_STATIC; 387 1.1 martin ri = &sc->sc_vcs.scr_ri; 388 1.1 martin 389 1.1 martin sc->sc_wsd.textops = &ri->ri_ops; 390 1.1 martin sc->sc_wsd.capabilities = ri->ri_caps; 391 1.1 martin sc->sc_wsd.nrows = ri->ri_rows; 392 1.1 martin sc->sc_wsd.ncols = ri->ri_cols; 393 1.1 martin 394 1.1 martin if (sc->is_console) { 395 1.1 martin wsdisplay_cnattach(&sc->sc_wsd, ri, 0, 0, defattr); 396 1.1 martin vcons_replay_msgbuf(&sc->sc_vcs); 397 1.1 martin } 398 1.1 martin 399 1.1 martin device_printf(sc->sc_dev, "%dx%d, %dbpp\n", ri->ri_width, 400 1.1 martin ri->ri_height, ri->ri_depth); 401 1.1 martin 402 1.1 martin waa.scrdata = &sc->sc_wsl; 403 1.1 martin waa.accessops = &viogpu_accessops; 404 1.1 martin waa.accesscookie = &sc->sc_vd; 405 1.1 martin waa.console = sc->is_console; 406 1.1 martin 407 1.1 martin config_found(self, &waa, wsemuldisplaydevprint, CFARGS_NONE); 408 1.1 martin 409 1.1 martin return; 410 1.1 martin 411 1.1 martin fb_unmap: 412 1.1 martin bus_dmamem_unmap(virtio_dmat(vsc), &sc->sc_fb_dma_kva, 413 1.1 martin sc->sc_fb_dma_size); 414 1.1 martin fb_free: 415 1.1 martin bus_dmamem_free(virtio_dmat(vsc), &sc->sc_fb_dma_seg, 1); 416 1.1 martin fb_destroy: 417 1.1 martin bus_dmamap_destroy(virtio_dmat(vsc), sc->sc_fb_dma_map); 418 1.1 martin unmap: 419 1.1 martin bus_dmamem_unmap(virtio_dmat(vsc), &sc->sc_cmd, VIOGPU_CMD_DMA_SIZE); 420 1.1 martin free: 421 1.1 martin bus_dmamem_free(virtio_dmat(vsc), &sc->sc_dma_seg, 1); 422 1.1 martin destroy: 423 1.1 martin bus_dmamap_destroy(virtio_dmat(vsc), sc->sc_dma_map); 424 1.1 martin err: 425 1.1 martin aprint_error_dev(sc->sc_dev, "DMA setup failed\n"); 426 1.1 martin virtio_free_vq(vsc, &sc->sc_vqs[VQCURS]); 427 1.1 martin virtio_free_vq(vsc, &sc->sc_vqs[VQCTRL]); 428 1.1 martin virtio_child_attach_failed(vsc); 429 1.1 martin cv_destroy(&sc->req_wait); 430 1.1 martin mutex_destroy(&sc->sc_mutex); 431 1.1 martin return; 432 1.1 martin } 433 1.1 martin 434 1.1 martin /* 435 1.1 martin * This carries out a command synchronously, unlike the commands used to 436 1.1 martin * update the screen. 437 1.1 martin */ 438 1.1 martin static int 439 1.1 martin viogpu_cmd_sync(struct viogpu_softc *sc, void *cmd, size_t cmd_size, 440 1.1 martin void *ret, size_t ret_size) 441 1.1 martin { 442 1.1 martin int error; 443 1.1 martin 444 1.1 martin mutex_enter(&sc->sc_mutex); 445 1.1 martin 446 1.1 martin while (sc->is_requesting == true) 447 1.1 martin cv_wait(&sc->req_wait, &sc->sc_mutex); 448 1.1 martin 449 1.1 martin error = viogpu_cmd_req(sc, cmd, cmd_size, ret_size); 450 1.1 martin if (error != 0) 451 1.1 martin goto out; 452 1.1 martin 453 1.1 martin while (sc->is_requesting == true) 454 1.1 martin cv_wait(&sc->req_wait, &sc->sc_mutex); 455 1.1 martin 456 1.1 martin if (ret != NULL) 457 1.1 martin memcpy(ret, (char *)sc->sc_cmd + cmd_size, ret_size); 458 1.1 martin 459 1.1 martin out: 460 1.1 martin mutex_exit(&sc->sc_mutex); 461 1.1 martin 462 1.1 martin return error; 463 1.1 martin } 464 1.1 martin 465 1.1 martin static void 466 1.1 martin viogpu_screen_update(void *arg) 467 1.1 martin { 468 1.1 martin struct viogpu_softc *sc = arg; 469 1.1 martin 470 1.1 martin mutex_enter(&sc->sc_mutex); 471 1.1 martin 472 1.1 martin if (sc->is_requesting == false) 473 1.1 martin viogpu_transfer_to_host_2d(sc, 1, 0, 0, sc->sc_fb_width, 474 1.1 martin sc->sc_fb_height); 475 1.1 martin else 476 1.1 martin sc->needs_update = true; 477 1.1 martin 478 1.1 martin mutex_exit(&sc->sc_mutex); 479 1.1 martin } 480 1.1 martin 481 1.1 martin static int 482 1.1 martin viogpu_cmd_req(struct viogpu_softc *sc, void *cmd, size_t cmd_size, 483 1.1 martin size_t ret_size) 484 1.1 martin { 485 1.1 martin struct virtio_softc *vsc = sc->sc_virtio; 486 1.1 martin struct virtqueue *vq = &sc->sc_vqs[VQCTRL]; 487 1.1 martin struct virtio_gpu_ctrl_hdr *hdr = 488 1.1 martin (struct virtio_gpu_ctrl_hdr *)sc->sc_cmd; 489 1.1 martin int slot, error; 490 1.1 martin 491 1.1 martin memcpy(sc->sc_cmd, cmd, cmd_size); 492 1.1 martin memset((char *)sc->sc_cmd + cmd_size, 0, ret_size); 493 1.1 martin 494 1.1 martin #if VIOGPU_DEBUG 495 1.1 martin printf("%s: [%zu -> %zu]: ", __func__, cmd_size, ret_size); 496 1.1 martin for (int i = 0; i < cmd_size; i++) { 497 1.1 martin printf(" %02x", ((unsigned char *)sc->sc_cmd)[i]); 498 1.1 martin } 499 1.1 martin printf("\n"); 500 1.1 martin #endif 501 1.1 martin 502 1.1 martin hdr->flags |= virtio_rw32(vsc, VIRTIO_GPU_FLAG_FENCE); 503 1.1 martin hdr->fence_id = virtio_rw64(vsc, ++sc->sc_fence_id); 504 1.1 martin 505 1.1 martin error = virtio_enqueue_prep(vsc, vq, &slot); 506 1.1 martin if (error != 0) 507 1.1 martin panic("%s: control vq busy", device_xname(sc->sc_dev)); 508 1.1 martin 509 1.1 martin error = virtio_enqueue_reserve(vsc, vq, slot, 510 1.1 martin sc->sc_dma_map->dm_nsegs + 1); 511 1.1 martin if (error != 0) 512 1.1 martin panic("%s: control vq busy", device_xname(sc->sc_dev)); 513 1.1 martin 514 1.1 martin bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dma_map, 0, cmd_size, 515 1.1 martin BUS_DMASYNC_PREWRITE); 516 1.1 martin virtio_enqueue_p(vsc, vq, slot, sc->sc_dma_map, 0, cmd_size, true); 517 1.1 martin 518 1.1 martin bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dma_map, cmd_size, ret_size, 519 1.1 martin BUS_DMASYNC_PREREAD); 520 1.1 martin virtio_enqueue_p(vsc, vq, slot, sc->sc_dma_map, cmd_size, ret_size, 521 1.1 martin false); 522 1.1 martin 523 1.1 martin virtio_enqueue_commit(vsc, vq, slot, true); 524 1.1 martin 525 1.1 martin sc->cur_cmd_size = cmd_size; 526 1.1 martin sc->cur_ret_size = ret_size; 527 1.1 martin sc->is_requesting = true; 528 1.1 martin 529 1.1 martin return 0; 530 1.1 martin } 531 1.1 martin 532 1.1 martin static int 533 1.1 martin viogpu_vq_done(struct virtqueue *vq) 534 1.1 martin { 535 1.1 martin struct virtio_softc *vsc = vq->vq_owner; 536 1.1 martin struct viogpu_softc *sc = device_private(virtio_child(vsc)); 537 1.1 martin struct virtio_gpu_ctrl_hdr *resp; 538 1.1 martin int slot, len; 539 1.1 martin uint32_t cmd_type, resp_type; 540 1.1 martin uint64_t resp_fence, expect_fence; 541 1.1 martin bool next_req_sent = false; 542 1.1 martin 543 1.1 martin mutex_enter(&sc->sc_mutex); 544 1.1 martin 545 1.1 martin while (virtio_dequeue(vsc, vq, &slot, &len) != 0) 546 1.1 martin ; 547 1.1 martin 548 1.1 martin virtio_dequeue_commit(vsc, vq, slot); 549 1.1 martin 550 1.1 martin bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dma_map, 0, sc->cur_cmd_size, 551 1.1 martin BUS_DMASYNC_POSTWRITE); 552 1.1 martin bus_dmamap_sync(virtio_dmat(vsc), sc->sc_dma_map, sc->cur_cmd_size, 553 1.1 martin sc->cur_ret_size, BUS_DMASYNC_POSTREAD); 554 1.1 martin 555 1.1 martin resp = (struct virtio_gpu_ctrl_hdr *)((char *)sc->sc_cmd + 556 1.1 martin sc->cur_cmd_size); 557 1.1 martin 558 1.1 martin cmd_type = virtio_rw32(vsc, 559 1.1 martin ((struct virtio_gpu_ctrl_hdr *)sc->sc_cmd)->type); 560 1.1 martin resp_type = virtio_rw32(vsc, resp->type); 561 1.1 martin resp_fence = virtio_rw64(vsc, resp->fence_id); 562 1.1 martin expect_fence = sc->sc_fence_id; 563 1.1 martin 564 1.1 martin switch (cmd_type) { 565 1.1 martin case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: 566 1.1 martin /* The second command for screen updating must be issued. */ 567 1.1 martin if (resp_type == VIRTIO_GPU_RESP_OK_NODATA) { 568 1.1 martin viogpu_flush_resource(sc, 1, 0, 0, sc->sc_fb_width, 569 1.1 martin sc->sc_fb_height); 570 1.1 martin next_req_sent = true; 571 1.1 martin } 572 1.1 martin break; 573 1.1 martin case VIRTIO_GPU_CMD_RESOURCE_FLUSH: 574 1.1 martin if (sc->needs_update == true) { 575 1.1 martin viogpu_transfer_to_host_2d(sc, 1, 0, 0, 576 1.1 martin sc->sc_fb_width, sc->sc_fb_height); 577 1.1 martin sc->needs_update = false; 578 1.1 martin next_req_sent = true; 579 1.1 martin } 580 1.1 martin break; 581 1.1 martin default: 582 1.1 martin /* Other command types are called synchronously. */ 583 1.1 martin break; 584 1.1 martin } 585 1.1 martin 586 1.1 martin if (next_req_sent == false) { 587 1.1 martin sc->is_requesting = false; 588 1.1 martin cv_broadcast(&sc->req_wait); 589 1.1 martin } 590 1.1 martin 591 1.1 martin mutex_exit(&sc->sc_mutex); 592 1.1 martin 593 1.1 martin if (resp_type != VIRTIO_GPU_RESP_OK_NODATA) { 594 1.1 martin switch (cmd_type) { 595 1.1 martin case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: 596 1.1 martin device_printf(sc->sc_dev, 597 1.1 martin "failed TRANSFER_TO_HOST: %d\n", resp_type); 598 1.1 martin break; 599 1.1 martin case VIRTIO_GPU_CMD_RESOURCE_FLUSH: 600 1.1 martin device_printf(sc->sc_dev, 601 1.1 martin "failed RESOURCE_FLUSH: %d\n", resp_type); 602 1.1 martin break; 603 1.1 martin default: 604 1.1 martin break; 605 1.1 martin } 606 1.1 martin } 607 1.1 martin 608 1.1 martin if (resp_fence != expect_fence) 609 1.1 martin printf("%s: return fence id not right (0x%" PRIx64 " != 0x%" 610 1.1 martin PRIx64 ")\n", __func__, resp_fence, expect_fence); 611 1.1 martin 612 1.1 martin return 0; 613 1.1 martin } 614 1.1 martin 615 1.1 martin static int 616 1.1 martin viogpu_get_display_info(struct viogpu_softc *sc) 617 1.1 martin { 618 1.1 martin struct virtio_softc *vsc = sc->sc_virtio; 619 1.1 martin struct virtio_gpu_ctrl_hdr hdr = { 0 }; 620 1.1 martin struct virtio_gpu_resp_display_info info = { 0 }; 621 1.1 martin 622 1.1 martin hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_GET_DISPLAY_INFO); 623 1.1 martin 624 1.1 martin viogpu_cmd_sync(sc, &hdr, sizeof(hdr), &info, sizeof(info)); 625 1.1 martin 626 1.1 martin if (virtio_rw32(vsc, info.hdr.type) != 627 1.1 martin VIRTIO_GPU_RESP_OK_DISPLAY_INFO) { 628 1.1 martin device_printf(sc->sc_dev, "failed getting display info\n"); 629 1.1 martin return 1; 630 1.1 martin } 631 1.1 martin 632 1.1 martin if (!info.pmodes[0].enabled) { 633 1.1 martin device_printf(sc->sc_dev, "pmodes[0] is not enabled\n"); 634 1.1 martin return 1; 635 1.1 martin } 636 1.1 martin 637 1.1 martin sc->sc_fb_width = virtio_rw32(vsc, info.pmodes[0].r.width); 638 1.1 martin sc->sc_fb_height = virtio_rw32(vsc, info.pmodes[0].r.height); 639 1.1 martin 640 1.1 martin return 0; 641 1.1 martin } 642 1.1 martin 643 1.1 martin static int 644 1.1 martin viogpu_create_2d(struct viogpu_softc *sc, uint32_t resource_id, uint32_t width, 645 1.1 martin uint32_t height) 646 1.1 martin { 647 1.1 martin struct virtio_softc *vsc = sc->sc_virtio; 648 1.1 martin struct virtio_gpu_resource_create_2d res = { 0 }; 649 1.1 martin struct virtio_gpu_ctrl_hdr resp = { 0 }; 650 1.1 martin 651 1.1 martin res.hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D); 652 1.1 martin res.resource_id = virtio_rw32(vsc, resource_id); 653 1.1 martin res.format = virtio_rw32(vsc, VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM); 654 1.1 martin res.width = virtio_rw32(vsc, width); 655 1.1 martin res.height = virtio_rw32(vsc, height); 656 1.1 martin 657 1.1 martin viogpu_cmd_sync(sc, &res, sizeof(res), &resp, sizeof(resp)); 658 1.1 martin 659 1.1 martin if (virtio_rw32(vsc, resp.type) != VIRTIO_GPU_RESP_OK_NODATA) { 660 1.1 martin device_printf(sc->sc_dev, "failed CREATE_2D: %d\n", 661 1.1 martin virtio_rw32(vsc, resp.type)); 662 1.1 martin return 1; 663 1.1 martin } 664 1.1 martin 665 1.1 martin return 0; 666 1.1 martin } 667 1.1 martin 668 1.1 martin static int 669 1.1 martin viogpu_set_scanout(struct viogpu_softc *sc, uint32_t scanout_id, 670 1.1 martin uint32_t resource_id, uint32_t width, uint32_t height) 671 1.1 martin { 672 1.1 martin struct virtio_softc *vsc = sc->sc_virtio; 673 1.1 martin struct virtio_gpu_set_scanout ss = { 0 }; 674 1.1 martin struct virtio_gpu_ctrl_hdr resp = { 0 }; 675 1.1 martin 676 1.1 martin ss.hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_SET_SCANOUT); 677 1.1 martin ss.scanout_id = virtio_rw32(vsc, scanout_id); 678 1.1 martin ss.resource_id = virtio_rw32(vsc, resource_id); 679 1.1 martin ss.r.width = virtio_rw32(vsc, width); 680 1.1 martin ss.r.height = virtio_rw32(vsc, height); 681 1.1 martin 682 1.1 martin viogpu_cmd_sync(sc, &ss, sizeof(ss), &resp, sizeof(resp)); 683 1.1 martin 684 1.1 martin if (virtio_rw32(vsc, resp.type) != VIRTIO_GPU_RESP_OK_NODATA) { 685 1.1 martin device_printf(sc->sc_dev, "failed SET_SCANOUT: %d\n", 686 1.1 martin virtio_rw32(vsc, resp.type)); 687 1.1 martin return 1; 688 1.1 martin } 689 1.1 martin 690 1.1 martin return 0; 691 1.1 martin } 692 1.1 martin 693 1.1 martin static int 694 1.1 martin viogpu_attach_backing(struct viogpu_softc *sc, uint32_t resource_id, 695 1.1 martin bus_dmamap_t dmamap) 696 1.1 martin { 697 1.1 martin struct virtio_softc *vsc = sc->sc_virtio; 698 1.1 martin struct virtio_gpu_resource_attach_backing_entries backing = { 0 }; 699 1.1 martin struct virtio_gpu_ctrl_hdr resp = { 0 }; 700 1.1 martin 701 1.1 martin backing.hdr.type = virtio_rw32(vsc, 702 1.1 martin VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING); 703 1.1 martin backing.resource_id = virtio_rw32(vsc, resource_id); 704 1.1 martin backing.nr_entries = virtio_rw32(vsc, __arraycount(backing.entries)); 705 1.1 martin backing.entries[0].addr = virtio_rw64(vsc, dmamap->dm_segs[0].ds_addr); 706 1.1 martin backing.entries[0].length = virtio_rw32(vsc, 707 1.1 martin dmamap->dm_segs[0].ds_len); 708 1.1 martin 709 1.1 martin if (dmamap->dm_nsegs > 1) 710 1.1 martin printf("%s: TODO: send all %d segs\n", __func__, 711 1.1 martin dmamap->dm_nsegs); 712 1.1 martin 713 1.1 martin #if VIOGPU_DEBUG 714 1.1 martin printf("%s: backing addr 0x%" PRIx64 " length %d\n", __func__, 715 1.1 martin backing.entries[0].addr, backing.entries[0].length); 716 1.1 martin #endif 717 1.1 martin 718 1.1 martin viogpu_cmd_sync(sc, &backing, sizeof(backing), &resp, sizeof(resp)); 719 1.1 martin 720 1.1 martin if (virtio_rw32(vsc, resp.type) != VIRTIO_GPU_RESP_OK_NODATA) { 721 1.1 martin device_printf(sc->sc_dev, "failed ATTACH_BACKING: %d\n", 722 1.1 martin virtio_rw32(vsc, resp.type)); 723 1.1 martin return 1; 724 1.1 martin } 725 1.1 martin 726 1.1 martin return 0; 727 1.1 martin } 728 1.1 martin 729 1.1 martin static int 730 1.1 martin viogpu_transfer_to_host_2d(struct viogpu_softc *sc, uint32_t resource_id, 731 1.1 martin uint32_t x, uint32_t y, uint32_t width, 732 1.1 martin uint32_t height) 733 1.1 martin { 734 1.1 martin struct virtio_softc *vsc = sc->sc_virtio; 735 1.1 martin struct virtio_gpu_transfer_to_host_2d tth = { 0 }; 736 1.1 martin 737 1.1 martin tth.hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D); 738 1.1 martin tth.resource_id = virtio_rw32(vsc, resource_id); 739 1.1 martin tth.r.x = virtio_rw32(vsc, x); 740 1.1 martin tth.r.y = virtio_rw32(vsc, y); 741 1.1 martin tth.r.width = virtio_rw32(vsc, width); 742 1.1 martin tth.r.height = virtio_rw32(vsc, height); 743 1.1 martin tth.offset = virtio_rw64(vsc, (y * sc->sc_fb_width + x) * 744 1.1 martin 4 /* bpp / 8 */); 745 1.1 martin 746 1.1 martin viogpu_cmd_req(sc, &tth, sizeof(tth), 747 1.1 martin sizeof(struct virtio_gpu_ctrl_hdr)); 748 1.1 martin 749 1.1 martin return 0; 750 1.1 martin } 751 1.1 martin 752 1.1 martin static int 753 1.1 martin viogpu_flush_resource(struct viogpu_softc *sc, uint32_t resource_id, 754 1.1 martin uint32_t x, uint32_t y, uint32_t width, uint32_t height) 755 1.1 martin { 756 1.1 martin struct virtio_softc *vsc = sc->sc_virtio; 757 1.1 martin struct virtio_gpu_resource_flush flush = { 0 }; 758 1.1 martin 759 1.1 martin flush.hdr.type = virtio_rw32(vsc, VIRTIO_GPU_CMD_RESOURCE_FLUSH); 760 1.1 martin flush.resource_id = virtio_rw32(vsc, resource_id); 761 1.1 martin flush.r.x = virtio_rw32(vsc, x); 762 1.1 martin flush.r.y = virtio_rw32(vsc, y); 763 1.1 martin flush.r.width = virtio_rw32(vsc, width); 764 1.1 martin flush.r.height = virtio_rw32(vsc, height); 765 1.1 martin 766 1.1 martin viogpu_cmd_req(sc, &flush, sizeof(flush), 767 1.1 martin sizeof(struct virtio_gpu_ctrl_hdr)); 768 1.1 martin 769 1.1 martin return 0; 770 1.1 martin } 771 1.1 martin 772 1.1 martin static int 773 1.1 martin viogpu_wsioctl(void *v, void *vs, u_long cmd, void *data, int flag, 774 1.1 martin struct lwp *l) 775 1.1 martin { 776 1.1 martin struct rasops_info *ri = v; 777 1.1 martin struct wsdisplayio_fbinfo *fbi; 778 1.1 martin struct wsdisplay_fbinfo *wdf; 779 1.1 martin 780 1.1 martin switch (cmd) { 781 1.1 martin case WSDISPLAYIO_GTYPE: 782 1.1 martin *(u_int *)data = WSDISPLAY_TYPE_VIOGPU; 783 1.1 martin return 0; 784 1.1 martin case WSDISPLAYIO_GET_FBINFO: 785 1.1 martin fbi = (struct wsdisplayio_fbinfo *)data; 786 1.1 martin return wsdisplayio_get_fbinfo(ri, fbi); 787 1.1 martin case WSDISPLAYIO_GINFO: 788 1.1 martin wdf = (struct wsdisplay_fbinfo *)data; 789 1.1 martin wdf->height = ri->ri_height; 790 1.1 martin wdf->width = ri->ri_width; 791 1.1 martin wdf->depth = ri->ri_depth; 792 1.1 martin wdf->cmsize = 0; 793 1.1 martin return 0; 794 1.1 martin case WSDISPLAYIO_LINEBYTES: 795 1.1 martin *(u_int *)data = ri->ri_stride; 796 1.1 martin return 0; 797 1.1 martin case WSDISPLAYIO_SMODE: 798 1.1 martin return 0; 799 1.1 martin case WSDISPLAYIO_GVIDEO: 800 1.1 martin case WSDISPLAYIO_SVIDEO: 801 1.1 martin return 0; 802 1.1 martin } 803 1.1 martin 804 1.1 martin return EPASSTHROUGH; 805 1.1 martin } 806 1.1 martin 807 1.1 martin static void 808 1.1 martin viogpu_init_screen(void *cookie, struct vcons_screen *scr, int existing, 809 1.1 martin long *defattr) 810 1.1 martin { 811 1.1 martin struct viogpu_softc *sc = cookie; 812 1.1 martin struct rasops_info *ri = &scr->scr_ri; 813 1.1 martin 814 1.1 martin ri->ri_bits = sc->sc_fb_dma_kva; 815 1.1 martin ri->ri_flg = RI_CENTER | RI_CLEAR; 816 1.1 martin #if BYTE_ORDER == BIG_ENDIAN 817 1.1 martin ri->ri_flg |= RI_BSWAP; 818 1.1 martin #endif 819 1.1 martin ri->ri_depth = 32; 820 1.1 martin ri->ri_width = sc->sc_fb_width; 821 1.1 martin ri->ri_height = sc->sc_fb_height; 822 1.1 martin ri->ri_stride = ri->ri_width * ri->ri_depth / 8; 823 1.1 martin ri->ri_bpos = 0; /* B8G8R8X8 */ 824 1.1 martin ri->ri_bnum = 8; 825 1.1 martin ri->ri_gpos = 8; 826 1.1 martin ri->ri_gnum = 8; 827 1.1 martin ri->ri_rpos = 16; 828 1.1 martin ri->ri_rnum = 8; 829 1.1 martin rasops_init(ri, 0, 0); 830 1.1 martin ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_HILIT; 831 1.1 martin rasops_reconfig(ri, sc->sc_fb_height / ri->ri_font->fontheight, 832 1.1 martin sc->sc_fb_width / ri->ri_font->fontwidth); 833 1.1 martin 834 1.1 martin /* 835 1.1 martin * Replace select text operations with wrappers that update the screen 836 1.1 martin * after the operation. 837 1.1 martin */ 838 1.1 martin sc->ri_cursor = ri->ri_ops.cursor; 839 1.1 martin sc->ri_putchar = ri->ri_ops.putchar; 840 1.1 martin sc->ri_copycols = ri->ri_ops.copycols; 841 1.1 martin sc->ri_erasecols = ri->ri_ops.erasecols; 842 1.1 martin sc->ri_copyrows = ri->ri_ops.copyrows; 843 1.1 martin sc->ri_eraserows = ri->ri_ops.eraserows; 844 1.1 martin sc->ri_replaceattr = ri->ri_ops.replaceattr; 845 1.1 martin ri->ri_ops.cursor = ri->ri_ops.cursor == NULL ? NULL : viogpu_cursor; 846 1.1 martin ri->ri_ops.putchar = ri->ri_ops.putchar == NULL ? NULL : 847 1.1 martin viogpu_putchar; 848 1.1 martin ri->ri_ops.copycols = ri->ri_ops.copycols == NULL ? NULL : 849 1.1 martin viogpu_copycols; 850 1.1 martin ri->ri_ops.erasecols = ri->ri_ops.erasecols == NULL ? NULL : 851 1.1 martin viogpu_erasecols; 852 1.1 martin ri->ri_ops.copyrows = ri->ri_ops.copyrows == NULL ? NULL : 853 1.1 martin viogpu_copyrows; 854 1.1 martin ri->ri_ops.eraserows = ri->ri_ops.eraserows == NULL ? NULL : 855 1.1 martin viogpu_eraserows; 856 1.1 martin ri->ri_ops.replaceattr = ri->ri_ops.replaceattr == NULL ? NULL : 857 1.1 martin viogpu_replaceattr; 858 1.1 martin } 859 1.1 martin 860 1.1 martin static void 861 1.1 martin viogpu_cursor(void *c, int on, int row, int col) 862 1.1 martin { 863 1.1 martin struct rasops_info *ri = c; 864 1.1 martin struct vcons_screen *vscr = ri->ri_hw; 865 1.1 martin struct viogpu_softc *sc = vscr->scr_vd->cookie; 866 1.1 martin 867 1.1 martin sc->ri_cursor(c, on, row, col); 868 1.1 martin 869 1.1 martin softint_schedule(sc->update_soft_ih); 870 1.1 martin } 871 1.1 martin 872 1.1 martin static void 873 1.1 martin viogpu_putchar(void *c, int row, int col, u_int uc, long attr) 874 1.1 martin { 875 1.1 martin struct rasops_info *ri = c; 876 1.1 martin struct vcons_screen *vscr = ri->ri_hw; 877 1.1 martin struct viogpu_softc *sc = vscr->scr_vd->cookie; 878 1.1 martin 879 1.1 martin sc->ri_putchar(c, row, col, uc, attr); 880 1.1 martin 881 1.1 martin softint_schedule(sc->update_soft_ih); 882 1.1 martin } 883 1.1 martin 884 1.1 martin static void 885 1.1 martin viogpu_copycols(void *c, int row, int srccol, int dstcol, int ncols) 886 1.1 martin { 887 1.1 martin struct rasops_info *ri = c; 888 1.1 martin struct vcons_screen *vscr = ri->ri_hw; 889 1.1 martin struct viogpu_softc *sc = vscr->scr_vd->cookie; 890 1.1 martin 891 1.1 martin sc->ri_copycols(c, row, srccol, dstcol, ncols); 892 1.1 martin 893 1.1 martin softint_schedule(sc->update_soft_ih); 894 1.1 martin } 895 1.1 martin 896 1.1 martin static void 897 1.1 martin viogpu_erasecols(void *c, int row, int startcol, int ncols, long attr) 898 1.1 martin { 899 1.1 martin struct rasops_info *ri = c; 900 1.1 martin struct vcons_screen *vscr = ri->ri_hw; 901 1.1 martin struct viogpu_softc *sc = vscr->scr_vd->cookie; 902 1.1 martin 903 1.1 martin sc->ri_erasecols(c, row, startcol, ncols, attr); 904 1.1 martin 905 1.1 martin softint_schedule(sc->update_soft_ih); 906 1.1 martin } 907 1.1 martin 908 1.1 martin static void 909 1.1 martin viogpu_copyrows(void *c, int srcrow, int dstrow, int nrows) 910 1.1 martin { 911 1.1 martin struct rasops_info *ri = c; 912 1.1 martin struct vcons_screen *vscr = ri->ri_hw; 913 1.1 martin struct viogpu_softc *sc = vscr->scr_vd->cookie; 914 1.1 martin 915 1.1 martin sc->ri_copyrows(c, srcrow, dstrow, nrows); 916 1.1 martin 917 1.1 martin softint_schedule(sc->update_soft_ih); 918 1.1 martin } 919 1.1 martin 920 1.1 martin static void 921 1.1 martin viogpu_eraserows(void *c, int row, int nrows, long attr) 922 1.1 martin { 923 1.1 martin struct rasops_info *ri = c; 924 1.1 martin struct vcons_screen *vscr = ri->ri_hw; 925 1.1 martin struct viogpu_softc *sc = vscr->scr_vd->cookie; 926 1.1 martin 927 1.1 martin sc->ri_eraserows(c, row, nrows, attr); 928 1.1 martin 929 1.1 martin softint_schedule(sc->update_soft_ih); 930 1.1 martin } 931 1.1 martin 932 1.1 martin static void 933 1.1 martin viogpu_replaceattr(void *c, long oldattr, long newattr) 934 1.1 martin { 935 1.1 martin struct rasops_info *ri = c; 936 1.1 martin struct vcons_screen *vscr = ri->ri_hw; 937 1.1 martin struct viogpu_softc *sc = vscr->scr_vd->cookie; 938 1.1 martin 939 1.1 martin sc->ri_replaceattr(c, oldattr, newattr); 940 1.1 martin 941 1.1 martin softint_schedule(sc->update_soft_ih); 942 1.1 martin } 943