xref: /openbsd-src/sys/dev/video.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: video.c,v 1.23 2008/11/11 12:37:07 mglocker Exp $	*/
2 /*
3  * Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
4  * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/proc.h>
22 #include <sys/errno.h>
23 #include <sys/ioctl.h>
24 #include <sys/fcntl.h>
25 #include <sys/device.h>
26 #include <sys/vnode.h>
27 #include <sys/kernel.h>
28 #include <sys/malloc.h>
29 #include <sys/conf.h>
30 #include <sys/videoio.h>
31 #include <uvm/uvm.h>
32 #include <uvm/uvm_pmap.h>
33 
34 #include <dev/video_if.h>
35 #include <dev/videovar.h>
36 
37 #ifdef VIDEO_DEBUG
38 #define	DPRINTF(x)	do { printf x; } while (0)
39 #else
40 #define DPRINTF(x)
41 #endif
42 
43 int	videoprobe(struct device *, void *, void *);
44 void	videoattach(struct device *, struct device *, void *);
45 int	videodetach(struct device *, int);
46 int	videoactivate(struct device *, enum devact);
47 int	videoprint(void *, const char *);
48 
49 void	video_intr(void *);
50 
51 struct cfattach video_ca = {
52 	sizeof(struct video_softc), videoprobe, videoattach,
53 	videodetach, videoactivate
54 };
55 
56 struct cfdriver video_cd = {
57 	NULL, "video", DV_DULL
58 };
59 
60 int
61 videoprobe(struct device *parent, void *match, void *aux)
62 {
63 	return (1);
64 }
65 
66 void
67 videoattach(struct device *parent, struct device *self, void *aux)
68 {
69 	struct video_softc *sc = (void *)self;
70 	struct video_attach_args *sa = aux;
71 	int video_buf_size = 0;
72 
73 	printf("\n");
74 	sc->hw_if = sa->hwif;
75 	sc->hw_hdl = sa->hdl;
76 	sc->sc_dev = parent;
77 
78 	if (sc->hw_if->get_bufsize)
79 		video_buf_size = (sc->hw_if->get_bufsize)(sc->hw_hdl);
80 	if (video_buf_size == EINVAL) {
81 		printf("video: could not request frame buffer size\n");
82 		return;
83 	}
84 
85 	sc->sc_fbuffer = malloc(video_buf_size, M_DEVBUF, M_NOWAIT);
86 	if (sc->sc_fbuffer == NULL) {
87 		printf("video: could not allocate frame buffer\n");
88 		return;
89 	}
90 }
91 
92 int
93 videoopen(dev_t dev, int flags, int fmt, struct proc *p)
94 {
95 	int	unit;
96 	struct video_softc *sc;
97 
98 	unit = VIDEOUNIT(dev);
99 	if (unit >= video_cd.cd_ndevs ||
100 	    (sc = video_cd.cd_devs[unit]) == NULL ||
101 	     sc->hw_if == NULL)
102 		return (ENXIO);
103 
104 	if (sc->sc_open & VIDEO_OPEN)
105 		return (EBUSY);
106 	sc->sc_open |= VIDEO_OPEN;
107 
108 	sc->sc_start_read = 0;
109 
110 	if (sc->hw_if->open != NULL)
111 		return (sc->hw_if->open(sc->hw_hdl, flags, &sc->sc_fsize,
112 		    sc->sc_fbuffer, video_intr, sc));
113 	else
114 		return (0);
115 }
116 
117 int
118 videoclose(dev_t dev, int flags, int fmt, struct proc *p)
119 {
120 	struct video_softc *sc;
121 	int r = 0;
122 
123 	sc = video_cd.cd_devs[VIDEOUNIT(dev)];
124 
125 	if (sc->hw_if->close != NULL)
126 		r = sc->hw_if->close(sc->hw_hdl);
127 
128 	sc->sc_open &= ~VIDEO_OPEN;
129 
130 	return (r);
131 }
132 
133 int
134 videoread(dev_t dev, struct uio *uio, int ioflag)
135 {
136 	struct video_softc *sc;
137 	int unit, error, size;
138 
139 	unit = VIDEOUNIT(dev);
140 	if (unit >= video_cd.cd_ndevs ||
141 	    (sc = video_cd.cd_devs[unit]) == NULL)
142 		return (ENXIO);
143 
144 	if (sc->sc_dying)
145 		return (EIO);
146 
147 	/* start the stream */
148 	if (sc->hw_if->start_read && !sc->sc_start_read) {
149 		error = sc->hw_if->start_read(sc->hw_hdl);
150 		if (error)
151 			return (error);
152 		sc->sc_start_read = 1;
153 	}
154 
155 	DPRINTF(("resid=%d\n", uio->uio_resid));
156 
157 	/* block userland read until a frame is ready */
158 	error = tsleep(sc, PWAIT | PCATCH, "vid_rd", 0);
159 	if (error)
160 		return (error);
161 
162 	/* move the frame to userland */
163 	if (sc->sc_fsize < uio->uio_resid)
164 		size = sc->sc_fsize;
165 	else
166 		size = uio->uio_resid;
167 	error = uiomove(sc->sc_fbuffer, size, uio);
168 	if (error)
169 		return (error);
170 
171 	DPRINTF(("uiomove successfully done (%d bytes)\n", size));
172 
173 	return (0);
174 }
175 
176 int
177 videoioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
178 {
179 	struct video_softc *sc;
180 	int unit, error;
181 
182 	unit = VIDEOUNIT(dev);
183 	if (unit >= video_cd.cd_ndevs ||
184 	    (sc = video_cd.cd_devs[unit]) == NULL || sc->hw_if == NULL)
185 		return (ENXIO);
186 
187 	DPRINTF(("video_ioctl(%d, '%c', %d)\n",
188 	    IOCPARM_LEN(cmd), IOCGROUP(cmd), cmd & 0xff));
189 
190 	error = EOPNOTSUPP;
191 	switch (cmd) {
192 	case VIDIOC_QUERYCAP:
193 		if (sc->hw_if->querycap)
194 			error = (sc->hw_if->querycap)(sc->hw_hdl,
195 			    (struct v4l2_capability *)data);
196 		break;
197 	case VIDIOC_ENUM_FMT:
198 		if (sc->hw_if->enum_fmt)
199 			error = (sc->hw_if->enum_fmt)(sc->hw_hdl,
200 			    (struct v4l2_fmtdesc *)data);
201 		break;
202 	case VIDIOC_ENUM_FRAMESIZES:
203 		if (sc->hw_if->enum_fsizes)
204 			error = (sc->hw_if->enum_fsizes)(sc->hw_hdl,
205 			    (struct v4l2_frmsizeenum *)data);
206 		break;
207 	case VIDIOC_ENUM_FRAMEINTERVALS:
208 		if (sc->hw_if->enum_fivals)
209 			error = (sc->hw_if->enum_fivals)(sc->hw_hdl,
210 			    (struct v4l2_frmivalenum *)data);
211 		break;
212 	case VIDIOC_S_FMT:
213 		if (!(flags & FWRITE))
214 			return (EACCES);
215 		if (sc->hw_if->s_fmt)
216 			error = (sc->hw_if->s_fmt)(sc->hw_hdl,
217 			    (struct v4l2_format *)data);
218 		break;
219 	case VIDIOC_G_FMT:
220 		if (sc->hw_if->g_fmt)
221 			error = (sc->hw_if->g_fmt)(sc->hw_hdl,
222 			    (struct v4l2_format *)data);
223 		break;
224 	case VIDIOC_ENUMINPUT:
225 		if (sc->hw_if->enum_input)
226 			error = (sc->hw_if->enum_input)(sc->hw_hdl,
227 			    (struct v4l2_input *)data);
228 		break;
229 	case VIDIOC_S_INPUT:
230 		if (sc->hw_if->s_input)
231 			error = (sc->hw_if->s_input)(sc->hw_hdl,
232 			    (int)*data);
233 		break;
234 	case VIDIOC_REQBUFS:
235 		if (sc->hw_if->reqbufs)
236 			error = (sc->hw_if->reqbufs)(sc->hw_hdl,
237 			    (struct v4l2_requestbuffers *)data);
238 		break;
239 	case VIDIOC_QUERYBUF:
240 		if (sc->hw_if->querybuf)
241 			error = (sc->hw_if->querybuf)(sc->hw_hdl,
242 			    (struct v4l2_buffer *)data);
243 		break;
244 	case VIDIOC_QBUF:
245 		if (sc->hw_if->qbuf)
246 			error = (sc->hw_if->qbuf)(sc->hw_hdl,
247 			    (struct v4l2_buffer *)data);
248 		break;
249 	case VIDIOC_DQBUF:
250 		if (sc->hw_if->dqbuf)
251 			error = (sc->hw_if->dqbuf)(sc->hw_hdl,
252 			    (struct v4l2_buffer *)data);
253 		break;
254 	case VIDIOC_STREAMON:
255 		if (sc->hw_if->streamon)
256 			error = (sc->hw_if->streamon)(sc->hw_hdl,
257 			    (int)*data);
258 		break;
259 	case VIDIOC_STREAMOFF:
260 		if (sc->hw_if->streamoff)
261 			error = (sc->hw_if->streamoff)(sc->hw_hdl,
262 			    (int)*data);
263 		break;
264 	case VIDIOC_TRY_FMT:
265 		if (sc->hw_if->try_fmt)
266 			error = (sc->hw_if->try_fmt)(sc->hw_hdl,
267 			    (struct v4l2_format *)data);
268 		break;
269 	case VIDIOC_QUERYCTRL:
270 		if (sc->hw_if->queryctrl)
271 			error = (sc->hw_if->queryctrl)(sc->hw_hdl,
272 			    (struct v4l2_queryctrl *)data);
273 		break;
274 	case VIDIOC_G_CTRL:
275 		if (sc->hw_if->g_ctrl)
276 			error = (sc->hw_if->g_ctrl)(sc->hw_hdl,
277 			    (struct v4l2_control *)data);
278 		break;
279 	case VIDIOC_S_CTRL:
280 		if (sc->hw_if->s_ctrl)
281 			error = (sc->hw_if->s_ctrl)(sc->hw_hdl,
282 			    (struct v4l2_control *)data);
283 		break;
284 	default:
285 		error = (ENOTTY);
286 	}
287 
288 	return (error);
289 }
290 
291 paddr_t
292 videommap(dev_t dev, off_t off, int prot)
293 {
294 	struct video_softc *sc;
295 	int unit;
296 	caddr_t p;
297 	paddr_t pa;
298 
299 	DPRINTF(("%s: off=%d, prot=%d\n", __func__, off, prot));
300 
301 	unit = VIDEOUNIT(dev);
302 	if (unit >= video_cd.cd_ndevs ||
303 	    (sc = video_cd.cd_devs[unit]) == NULL)
304 		return (-1);
305 
306 	if (sc->sc_dying)
307 		return (-1);
308 
309 	if (sc->hw_if->mappage == NULL)
310 		return (-1);
311 
312 	p = sc->hw_if->mappage(sc->hw_hdl, off, prot);
313 	if (p == NULL)
314 		return (-1);
315 	if (pmap_extract(pmap_kernel(), (vaddr_t)p, &pa) == FALSE)
316 		panic("videommap: invalid page");
317 
318 #if defined(__powerpc__) || defined(__sparc64__)
319 	return (pa);
320 #else
321 	return (atop(pa));
322 #endif
323 }
324 
325 /*
326  * Called from hardware driver. This is where the MI video driver gets
327  * probed/attached to the hardware driver
328  */
329 struct device *
330 video_attach_mi(struct video_hw_if *rhwp, void *hdlp, struct device *dev)
331 {
332 	struct video_attach_args arg;
333 
334 	arg.hwif = rhwp;
335 	arg.hdl = hdlp;
336 	return (config_found(dev, &arg, videoprint));
337 }
338 
339 void
340 video_intr(void *addr)
341 {
342 	struct video_softc *sc = (struct video_softc *)addr;
343 
344 	DPRINTF(("video_intr sc=%p\n", sc));
345 	wakeup(sc);
346 }
347 
348 int
349 videoprint(void *aux, const char *pnp)
350 {
351 	if (pnp != NULL)
352 		printf("video at %s", pnp);
353 	return (UNCONF);
354 }
355 
356 int
357 videodetach(struct device *self, int flags)
358 {
359 	struct video_softc *sc = (struct video_softc *)self;
360 	int maj, mn;
361 
362 	if (sc->sc_fbuffer != NULL)
363 		free(sc->sc_fbuffer, M_DEVBUF);
364 
365 	/* locate the major number */
366 	for (maj = 0; maj < nchrdev; maj++)
367 		if (cdevsw[maj].d_open == videoopen)
368 			break;
369 
370 	/* Nuke the vnodes for any open instances (calls close). */
371 	mn = self->dv_unit;
372 	vdevgone(maj, mn, mn, VCHR);
373 
374 	return (0);
375 }
376 
377 int
378 videoactivate(struct device *self, enum devact act)
379 {
380 	struct video_softc *sc = (struct video_softc *)self;
381 
382 	switch (act) {
383 	case DVACT_ACTIVATE:
384 		break;
385 
386 	case DVACT_DEACTIVATE:
387 		sc->sc_dying = 1;
388 		break;
389 	}
390 	return (0);
391 }
392