viogpu.c revision 1.1 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