xref: /openbsd/sys/dev/video.c (revision 098ff4ac)
1 /*	$OpenBSD: video.c,v 1.59 2024/12/16 21:22:51 mvs Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
5  * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/errno.h>
23 #include <sys/event.h>
24 #include <sys/ioctl.h>
25 #include <sys/fcntl.h>
26 #include <sys/device.h>
27 #include <sys/vnode.h>
28 #include <sys/kernel.h>
29 #include <sys/malloc.h>
30 #include <sys/mutex.h>
31 #include <sys/conf.h>
32 #include <sys/proc.h>
33 #include <sys/videoio.h>
34 
35 #include <dev/video_if.h>
36 
37 #include <uvm/uvm_extern.h>
38 
39 /*
40  * Locks used to protect struct members and global data
41  *	a	atomic
42  *	m	sc_mtx
43  */
44 
45 #ifdef VIDEO_DEBUG
46 int video_debug = 1;
47 #define DPRINTF(l, x...) do { if ((l) <= video_debug) printf(x); } while (0)
48 #else
49 #define DPRINTF(l, x...)
50 #endif
51 
52 struct video_softc {
53 	struct device		 dev;
54 	void			*hw_hdl;	/* hardware driver handle */
55 	struct device		*sc_dev;	/* hardware device struct */
56 	const struct video_hw_if *hw_if;	/* hardware interface */
57 	char			 sc_dying;	/* device detached */
58 	struct process		*sc_owner;	/* owner process */
59 	uint8_t			 sc_open;	/* device opened */
60 
61 	struct mutex		 sc_mtx;
62 	int			 sc_fsize;
63 	uint8_t			*sc_fbuffer;
64 	caddr_t			 sc_fbuffer_mmap;
65 	size_t			 sc_fbufferlen;
66 	int			 sc_vidmode;	/* access mode */
67 #define		VIDMODE_NONE	0
68 #define		VIDMODE_MMAP	1
69 #define		VIDMODE_READ	2
70 	int			 sc_frames_ready;	/* [m] */
71 
72 	struct klist		 sc_rklist;		/* [m] read selector */
73 };
74 
75 int	videoprobe(struct device *, void *, void *);
76 void	videoattach(struct device *, struct device *, void *);
77 int	videodetach(struct device *, int);
78 int	videoactivate(struct device *, int);
79 int	videoprint(void *, const char *);
80 
81 void	video_intr(void *);
82 int	video_stop(struct video_softc *);
83 int	video_claim(struct video_softc *, struct process *);
84 
85 const struct cfattach video_ca = {
86 	sizeof(struct video_softc), videoprobe, videoattach,
87 	videodetach, videoactivate
88 };
89 
90 struct cfdriver video_cd = {
91 	NULL, "video", DV_DULL
92 };
93 
94 /*
95  * Global flag to control if video recording is enabled by kern.video.record.
96  */
97 int video_record_enable = 0;	/* [a] */
98 
99 int
videoprobe(struct device * parent,void * match,void * aux)100 videoprobe(struct device *parent, void *match, void *aux)
101 {
102 	return (1);
103 }
104 
105 void
videoattach(struct device * parent,struct device * self,void * aux)106 videoattach(struct device *parent, struct device *self, void *aux)
107 {
108 	struct video_softc *sc = (void *)self;
109 	struct video_attach_args *sa = aux;
110 
111 	printf("\n");
112 	sc->hw_if = sa->hwif;
113 	sc->hw_hdl = sa->hdl;
114 	sc->sc_dev = parent;
115 	sc->sc_fbufferlen = 0;
116 	sc->sc_owner = NULL;
117 	mtx_init(&sc->sc_mtx, IPL_MPFLOOR);
118 	klist_init_mutex(&sc->sc_rklist, &sc->sc_mtx);
119 
120 	if (sc->hw_if->get_bufsize)
121 		sc->sc_fbufferlen = (sc->hw_if->get_bufsize)(sc->hw_hdl);
122 	if (sc->sc_fbufferlen == 0) {
123 		printf("video: could not request frame buffer size\n");
124 		return;
125 	}
126 
127 	sc->sc_fbuffer = malloc(sc->sc_fbufferlen, M_DEVBUF, M_NOWAIT);
128 	if (sc->sc_fbuffer == NULL) {
129 		printf("video: could not allocate frame buffer\n");
130 		return;
131 	}
132 }
133 
134 int
videoopen(dev_t dev,int flags,int fmt,struct proc * p)135 videoopen(dev_t dev, int flags, int fmt, struct proc *p)
136 {
137 	int unit = VIDEOUNIT(dev);
138 	struct video_softc *sc;
139 	int error = 0;
140 
141 	KERNEL_ASSERT_LOCKED();
142 
143 	if (unit >= video_cd.cd_ndevs ||
144 	    (sc = video_cd.cd_devs[unit]) == NULL ||
145 	     sc->hw_if == NULL)
146 		return (ENXIO);
147 
148 	if (sc->sc_open) {
149 		DPRINTF(1, "%s: device already open\n", __func__);
150 		return (0);
151 	}
152 
153 	sc->sc_vidmode = VIDMODE_NONE;
154 	sc->sc_frames_ready = 0;
155 
156 	if (sc->hw_if->open != NULL) {
157 		error = sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize,
158 		    sc->sc_fbuffer, video_intr, sc);
159 	}
160 	if (error == 0) {
161 		sc->sc_open = 1;
162 		DPRINTF(1, "%s: set device to open\n", __func__);
163 	}
164 
165 	return (error);
166 }
167 
168 int
videoclose(dev_t dev,int flags,int fmt,struct proc * p)169 videoclose(dev_t dev, int flags, int fmt, struct proc *p)
170 {
171 	struct video_softc *sc;
172 	int error = 0;
173 
174 	KERNEL_ASSERT_LOCKED();
175 
176 	DPRINTF(1, "%s: last close\n", __func__);
177 
178 	sc = video_cd.cd_devs[VIDEOUNIT(dev)];
179 
180 	error = video_stop(sc);
181 	sc->sc_open = 0;
182 
183 	return (error);
184 }
185 
186 int
videoread(dev_t dev,struct uio * uio,int ioflag)187 videoread(dev_t dev, struct uio *uio, int ioflag)
188 {
189 	int unit = VIDEOUNIT(dev);
190 	struct video_softc *sc;
191 	int error;
192 	size_t size;
193 
194 	KERNEL_ASSERT_LOCKED();
195 
196 	if (unit >= video_cd.cd_ndevs ||
197 	    (sc = video_cd.cd_devs[unit]) == NULL)
198 		return (ENXIO);
199 
200 	if (sc->sc_dying)
201 		return (EIO);
202 
203 	if (sc->sc_vidmode == VIDMODE_MMAP)
204 		return (EBUSY);
205 
206 	if ((error = video_claim(sc, curproc->p_p)))
207 		return (error);
208 
209 	/* start the stream if not already started */
210 	if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
211  		error = sc->hw_if->start_read(sc->hw_hdl);
212  		if (error)
213  			return (error);
214 		sc->sc_vidmode = VIDMODE_READ;
215  	}
216 
217 	DPRINTF(1, "resid=%zu\n", uio->uio_resid);
218 
219 	mtx_enter(&sc->sc_mtx);
220 
221 	if (sc->sc_frames_ready < 1) {
222 		/* block userland read until a frame is ready */
223 		error = msleep_nsec(sc, &sc->sc_mtx, PWAIT | PCATCH,
224 		    "vid_rd", INFSLP);
225 		if (sc->sc_dying)
226 			error = EIO;
227 		if (error) {
228 			mtx_leave(&sc->sc_mtx);
229 			return (error);
230 		}
231 	}
232 	sc->sc_frames_ready--;
233 
234 	mtx_leave(&sc->sc_mtx);
235 
236 	/* move no more than 1 frame to userland, as per specification */
237 	size = ulmin(uio->uio_resid, sc->sc_fsize);
238 	if (!atomic_load_int(&video_record_enable))
239 		bzero(sc->sc_fbuffer, size);
240 	error = uiomove(sc->sc_fbuffer, size, uio);
241 	if (error)
242 		return (error);
243 
244 	DPRINTF(1, "uiomove successfully done (%zu bytes)\n", size);
245 
246 	return (0);
247 }
248 
249 int
videoioctl(dev_t dev,u_long cmd,caddr_t data,int flags,struct proc * p)250 videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
251 {
252 	int unit = VIDEOUNIT(dev);
253 	struct video_softc *sc;
254 	struct v4l2_buffer *vb = (struct v4l2_buffer *)data;
255 	int error;
256 
257 	KERNEL_ASSERT_LOCKED();
258 
259 	if (unit >= video_cd.cd_ndevs ||
260 	    (sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL)
261 		return (ENXIO);
262 
263 	DPRINTF(3, "video_ioctl(%zu, '%c', %zu)\n",
264 	    IOCPARM_LEN(cmd), (int) IOCGROUP(cmd), cmd & 0xff);
265 
266 	error = EOPNOTSUPP;
267 	switch (cmd) {
268 	case VIDIOC_G_CTRL:
269 		if (sc->hw_if->g_ctrl)
270 			error = (sc->hw_if->g_ctrl)(sc->hw_hdl,
271 			    (struct v4l2_control *)data);
272 		break;
273 	case VIDIOC_S_CTRL:
274 		if (sc->hw_if->s_ctrl)
275 			error = (sc->hw_if->s_ctrl)(sc->hw_hdl,
276 			    (struct v4l2_control *)data);
277 		break;
278 	default:
279 		error = (ENOTTY);
280 	}
281 	if (error != ENOTTY)
282 		return (error);
283 
284 	if ((error = video_claim(sc, p->p_p)))
285 		return (error);
286 
287 	/*
288 	 * The following IOCTLs can only be called by the device owner.
289 	 * For further shared IOCTLs please move it up.
290 	 */
291 	error = EOPNOTSUPP;
292 	switch (cmd) {
293 	case VIDIOC_QUERYCAP:
294 		if (sc->hw_if->querycap)
295 			error = (sc->hw_if->querycap)(sc->hw_hdl,
296 			    (struct v4l2_capability *)data);
297 		break;
298 	case VIDIOC_ENUM_FMT:
299 		if (sc->hw_if->enum_fmt)
300 			error = (sc->hw_if->enum_fmt)(sc->hw_hdl,
301 			    (struct v4l2_fmtdesc *)data);
302 		break;
303 	case VIDIOC_ENUM_FRAMESIZES:
304 		if (sc->hw_if->enum_fsizes)
305 			error = (sc->hw_if->enum_fsizes)(sc->hw_hdl,
306 			    (struct v4l2_frmsizeenum *)data);
307 		break;
308 	case VIDIOC_ENUM_FRAMEINTERVALS:
309 		if (sc->hw_if->enum_fivals)
310 			error = (sc->hw_if->enum_fivals)(sc->hw_hdl,
311 			    (struct v4l2_frmivalenum *)data);
312 		break;
313 	case VIDIOC_S_FMT:
314 		if (!(flags & FWRITE))
315 			return (EACCES);
316 		if (sc->hw_if->s_fmt)
317 			error = (sc->hw_if->s_fmt)(sc->hw_hdl,
318 			    (struct v4l2_format *)data);
319 		break;
320 	case VIDIOC_G_FMT:
321 		if (sc->hw_if->g_fmt)
322 			error = (sc->hw_if->g_fmt)(sc->hw_hdl,
323 			    (struct v4l2_format *)data);
324 		break;
325 	case VIDIOC_S_PARM:
326 		if (sc->hw_if->s_parm)
327 			error = (sc->hw_if->s_parm)(sc->hw_hdl,
328 			    (struct v4l2_streamparm *)data);
329 		break;
330 	case VIDIOC_G_PARM:
331 		if (sc->hw_if->g_parm)
332 			error = (sc->hw_if->g_parm)(sc->hw_hdl,
333 			    (struct v4l2_streamparm *)data);
334 		break;
335 	case VIDIOC_ENUMINPUT:
336 		if (sc->hw_if->enum_input)
337 			error = (sc->hw_if->enum_input)(sc->hw_hdl,
338 			    (struct v4l2_input *)data);
339 		break;
340 	case VIDIOC_S_INPUT:
341 		if (sc->hw_if->s_input)
342 			error = (sc->hw_if->s_input)(sc->hw_hdl,
343 			    (int)*data);
344 		break;
345 	case VIDIOC_G_INPUT:
346 		if (sc->hw_if->g_input)
347 			error = (sc->hw_if->g_input)(sc->hw_hdl,
348 			    (int *)data);
349 		break;
350 	case VIDIOC_REQBUFS:
351 		if (sc->hw_if->reqbufs)
352 			error = (sc->hw_if->reqbufs)(sc->hw_hdl,
353 			    (struct v4l2_requestbuffers *)data);
354 		break;
355 	case VIDIOC_QUERYBUF:
356 		if (sc->hw_if->querybuf)
357 			error = (sc->hw_if->querybuf)(sc->hw_hdl,
358 			    (struct v4l2_buffer *)data);
359 		break;
360 	case VIDIOC_QBUF:
361 		if (sc->hw_if->qbuf)
362 			error = (sc->hw_if->qbuf)(sc->hw_hdl,
363 			    (struct v4l2_buffer *)data);
364 		break;
365 	case VIDIOC_DQBUF:
366 		if (!sc->hw_if->dqbuf)
367 			break;
368 		/* should have called mmap() before now */
369 		if (sc->sc_vidmode != VIDMODE_MMAP) {
370 			error = EINVAL;
371 			break;
372 		}
373 		error = (sc->hw_if->dqbuf)(sc->hw_hdl,
374 		    (struct v4l2_buffer *)data);
375 		if (!atomic_load_int(&video_record_enable))
376 			bzero(sc->sc_fbuffer_mmap + vb->m.offset, vb->length);
377 		mtx_enter(&sc->sc_mtx);
378 		sc->sc_frames_ready--;
379 		mtx_leave(&sc->sc_mtx);
380 		break;
381 	case VIDIOC_STREAMON:
382 		if (sc->hw_if->streamon)
383 			error = (sc->hw_if->streamon)(sc->hw_hdl,
384 			    (int)*data);
385 		break;
386 	case VIDIOC_STREAMOFF:
387 		if (sc->hw_if->streamoff)
388 			error = (sc->hw_if->streamoff)(sc->hw_hdl,
389 			    (int)*data);
390 		if (!error) {
391 			/* Release device ownership and streaming buffers. */
392 			error = video_stop(sc);
393 		}
394 		break;
395 	case VIDIOC_TRY_FMT:
396 		if (sc->hw_if->try_fmt)
397 			error = (sc->hw_if->try_fmt)(sc->hw_hdl,
398 			    (struct v4l2_format *)data);
399 		break;
400 	case VIDIOC_QUERYCTRL:
401 		if (sc->hw_if->queryctrl)
402 			error = (sc->hw_if->queryctrl)(sc->hw_hdl,
403 			    (struct v4l2_queryctrl *)data);
404 		break;
405 	default:
406 		error = (ENOTTY);
407 	}
408 
409 	return (error);
410 }
411 
412 paddr_t
videommap(dev_t dev,off_t off,int prot)413 videommap(dev_t dev, off_t off, int prot)
414 {
415 	int unit = VIDEOUNIT(dev);
416 	struct video_softc *sc;
417 	caddr_t p;
418 	paddr_t pa;
419 
420 	KERNEL_ASSERT_LOCKED();
421 
422 	DPRINTF(2, "%s: off=%lld, prot=%d\n", __func__, off, prot);
423 
424 	if (unit >= video_cd.cd_ndevs ||
425 	    (sc = video_cd.cd_devs[unit]) == NULL)
426 		return (-1);
427 
428 	if (sc->sc_dying)
429 		return (-1);
430 
431 	if (sc->hw_if->mappage == NULL)
432 		return (-1);
433 
434 	p = sc->hw_if->mappage(sc->hw_hdl, off, prot);
435 	if (p == NULL)
436 		return (-1);
437 	if (pmap_extract(pmap_kernel(), (vaddr_t)p, &pa) == FALSE)
438 		panic("videommap: invalid page");
439 	sc->sc_vidmode = VIDMODE_MMAP;
440 
441 	/* store frame buffer base address for later blanking */
442 	if (off == 0)
443 		sc->sc_fbuffer_mmap = p;
444 
445 	return (pa);
446 }
447 
448 void
filt_videodetach(struct knote * kn)449 filt_videodetach(struct knote *kn)
450 {
451 	struct video_softc *sc = kn->kn_hook;
452 
453 	klist_remove(&sc->sc_rklist, kn);
454 }
455 
456 int
filt_videoread(struct knote * kn,long hint)457 filt_videoread(struct knote *kn, long hint)
458 {
459 	struct video_softc *sc = kn->kn_hook;
460 
461 	if (sc->sc_frames_ready > 0)
462 		return (1);
463 
464 	return (0);
465 }
466 
467 int
filt_videomodify(struct kevent * kev,struct knote * kn)468 filt_videomodify(struct kevent *kev, struct knote *kn)
469 {
470 	struct video_softc *sc = kn->kn_hook;
471 	int active;
472 
473 	mtx_enter(&sc->sc_mtx);
474 	active = knote_modify(kev, kn);
475 	mtx_leave(&sc->sc_mtx);
476 
477 	return (active);
478 }
479 
480 int
filt_videoprocess(struct knote * kn,struct kevent * kev)481 filt_videoprocess(struct knote *kn, struct kevent *kev)
482 {
483 	struct video_softc *sc = kn->kn_hook;
484 	int active;
485 
486 	mtx_enter(&sc->sc_mtx);
487 	active = knote_process(kn, kev);
488 	mtx_leave(&sc->sc_mtx);
489 
490 	return (active);
491 }
492 
493 const struct filterops video_filtops = {
494 	.f_flags	= FILTEROP_ISFD | FILTEROP_MPSAFE,
495 	.f_attach	= NULL,
496 	.f_detach	= filt_videodetach,
497 	.f_event	= filt_videoread,
498 	.f_modify	= filt_videomodify,
499 	.f_process	= filt_videoprocess,
500 };
501 
502 int
videokqfilter(dev_t dev,struct knote * kn)503 videokqfilter(dev_t dev, struct knote *kn)
504 {
505 	int unit = VIDEOUNIT(dev);
506 	struct video_softc *sc;
507 	int error;
508 
509 	KERNEL_ASSERT_LOCKED();
510 
511 	if (unit >= video_cd.cd_ndevs ||
512 	    (sc = video_cd.cd_devs[unit]) == NULL)
513 		return (ENXIO);
514 
515 	if (sc->sc_dying)
516 		return (ENXIO);
517 
518 	switch (kn->kn_filter) {
519 	case EVFILT_READ:
520 		kn->kn_fop = &video_filtops;
521 		kn->kn_hook = sc;
522 		break;
523 	default:
524 		return (EINVAL);
525 	}
526 
527 	if ((error = video_claim(sc, curproc->p_p)))
528 		return (error);
529 
530 	/*
531 	 * Start the stream in read() mode if not already started.  If
532 	 * the user wanted mmap() mode, he should have called mmap()
533 	 * before now.
534 	 */
535 	if (sc->sc_vidmode == VIDMODE_NONE && sc->hw_if->start_read) {
536 		if (sc->hw_if->start_read(sc->hw_hdl))
537 			return (ENXIO);
538 		sc->sc_vidmode = VIDMODE_READ;
539 	}
540 
541 	klist_insert(&sc->sc_rklist, kn);
542 
543 	return (0);
544 }
545 
546 int
video_submatch(struct device * parent,void * match,void * aux)547 video_submatch(struct device *parent, void *match, void *aux)
548 {
549         struct cfdata *cf = match;
550 
551 	return (cf->cf_driver == &video_cd);
552 }
553 
554 /*
555  * Called from hardware driver. This is where the MI video driver gets
556  * probed/attached to the hardware driver
557  */
558 struct device *
video_attach_mi(const struct video_hw_if * rhwp,void * hdlp,struct device * dev)559 video_attach_mi(const struct video_hw_if *rhwp, void *hdlp, struct device *dev)
560 {
561 	struct video_attach_args arg;
562 
563 	arg.hwif = rhwp;
564 	arg.hdl = hdlp;
565 	return (config_found_sm(dev, &arg, videoprint, video_submatch));
566 }
567 
568 void
video_intr(void * addr)569 video_intr(void *addr)
570 {
571 	struct video_softc *sc = (struct video_softc *)addr;
572 
573 	DPRINTF(3, "video_intr sc=%p\n", sc);
574 	mtx_enter(&sc->sc_mtx);
575 	if (sc->sc_vidmode != VIDMODE_NONE)
576 		sc->sc_frames_ready++;
577 	else
578 		printf("%s: interrupt but no streams!\n", __func__);
579 	if (sc->sc_vidmode == VIDMODE_READ)
580 		wakeup(sc);
581 	knote_locked(&sc->sc_rklist, 0);
582 	mtx_leave(&sc->sc_mtx);
583 }
584 
585 int
video_stop(struct video_softc * sc)586 video_stop(struct video_softc *sc)
587 {
588 	int error = 0;
589 
590 	DPRINTF(1, "%s: stream close\n", __func__);
591 
592 	if (sc->hw_if->close != NULL)
593 		error = sc->hw_if->close(sc->hw_hdl);
594 
595 	sc->sc_vidmode = VIDMODE_NONE;
596 	mtx_enter(&sc->sc_mtx);
597 	sc->sc_frames_ready = 0;
598 	mtx_leave(&sc->sc_mtx);
599 	sc->sc_owner = NULL;
600 
601 	return (error);
602 }
603 
604 int
video_claim(struct video_softc * sc,struct process * pr)605 video_claim(struct video_softc *sc, struct process *pr)
606 {
607 	if (sc->sc_owner != NULL && sc->sc_owner != pr) {
608 		DPRINTF(1, "%s: already owned=%p\n", __func__, sc->sc_owner);
609 		return (EBUSY);
610 	}
611 
612 	if (sc->sc_owner == NULL) {
613 		sc->sc_owner = pr;
614 		DPRINTF(1, "%s: new owner=%p\n", __func__, sc->sc_owner);
615 	}
616 
617 	return (0);
618 }
619 
620 int
videoprint(void * aux,const char * pnp)621 videoprint(void *aux, const char *pnp)
622 {
623 	if (pnp != NULL)
624 		printf("video at %s", pnp);
625 	return (UNCONF);
626 }
627 
628 int
videodetach(struct device * self,int flags)629 videodetach(struct device *self, int flags)
630 {
631 	struct video_softc *sc = (struct video_softc *)self;
632 	int maj, mn;
633 
634 	/* locate the major number */
635 	for (maj = 0; maj < nchrdev; maj++)
636 		if (cdevsw[maj].d_open == videoopen)
637 			break;
638 
639 	/* Nuke the vnodes for any open instances (calls close). */
640 	mn = self->dv_unit;
641 	vdevgone(maj, mn, mn, VCHR);
642 
643 	klist_invalidate(&sc->sc_rklist);
644 	klist_free(&sc->sc_rklist);
645 
646 	free(sc->sc_fbuffer, M_DEVBUF, sc->sc_fbufferlen);
647 
648 	return (0);
649 }
650 
651 int
videoactivate(struct device * self,int act)652 videoactivate(struct device *self, int act)
653 {
654 	struct video_softc *sc = (struct video_softc *)self;
655 
656 	switch (act) {
657 	case DVACT_DEACTIVATE:
658 		sc->sc_dying = 1;
659 		break;
660 	}
661 	return (0);
662 }
663