xref: /netbsd/sys/dev/isa/isv.c (revision 1a918832)
1*1a918832Sdholland /*	$NetBSD: isv.c,v 1.7 2014/07/25 08:10:37 dholland Exp $ */
2d3627477Sdyoung 
3d3627477Sdyoung /*-
4d3627477Sdyoung  * Copyright (c) 2008 The NetBSD Foundation, Inc.
5d3627477Sdyoung  * All rights reserved.
6d3627477Sdyoung  *
7d3627477Sdyoung  * This code is derived from software contributed to The NetBSD Foundation
8d3627477Sdyoung  * by David Young.
9d3627477Sdyoung  *
10d3627477Sdyoung  * Redistribution and use in source and binary forms, with or without
11d3627477Sdyoung  * modification, are permitted provided that the following conditions
12d3627477Sdyoung  * are met:
13d3627477Sdyoung  * 1. Redistributions of source code must retain the above copyright
14d3627477Sdyoung  *    notice, this list of conditions and the following disclaimer.
15d3627477Sdyoung  * 2. Redistributions in binary form must reproduce the above copyright
16d3627477Sdyoung  *    notice, this list of conditions and the following disclaimer in the
17d3627477Sdyoung  *    documentation and/or other materials provided with the distribution.
18d3627477Sdyoung  *
19d3627477Sdyoung  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20d3627477Sdyoung  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21d3627477Sdyoung  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22d3627477Sdyoung  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23d3627477Sdyoung  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24d3627477Sdyoung  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25d3627477Sdyoung  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26d3627477Sdyoung  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27d3627477Sdyoung  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28d3627477Sdyoung  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29d3627477Sdyoung  * POSSIBILITY OF SUCH DAMAGE.
30d3627477Sdyoung  */
31d3627477Sdyoung 
32d3627477Sdyoung #include <sys/cdefs.h>
33*1a918832Sdholland __KERNEL_RCSID(0, "$NetBSD: isv.c,v 1.7 2014/07/25 08:10:37 dholland Exp $");
34d3627477Sdyoung 
35d3627477Sdyoung #include <sys/param.h>
36d3627477Sdyoung #include <sys/systm.h>
37d3627477Sdyoung #include <sys/kernel.h>
38d3627477Sdyoung #include <sys/device.h>
39d3627477Sdyoung #include <sys/conf.h>
40e9fb4f07Suebayasi 
41e9fb4f07Suebayasi #include <uvm/uvm_extern.h>
42d3627477Sdyoung 
43d3627477Sdyoung #include <sys/bus.h>
44d3627477Sdyoung 
45d3627477Sdyoung #include <dev/isa/isareg.h>
46d3627477Sdyoung #include <dev/isa/isavar.h>
47d3627477Sdyoung 
48d3627477Sdyoung #include <dev/isa/isvio.h>
49d3627477Sdyoung 
50d3627477Sdyoung #define	ISV_CONTROL	0x0		/* control: write-only */
51d3627477Sdyoung #define	ISV_CONTROL_MODE_MASK		__BIT(0)
52d3627477Sdyoung #define	ISV_CONTROL_MODE_CAPTURE	__SHIFTIN(0, ISV_CONTROL_MODE_MASK)
53d3627477Sdyoung #define	ISV_CONTROL_MODE_READ		__SHIFTIN(1, ISV_CONTROL_MODE_MASK)
54d3627477Sdyoung #define	ISV_CONTROL_COUNTER_MASK	__BIT(1)
55d3627477Sdyoung #define	ISV_CONTROL_COUNTER_RESET	__SHIFTIN(1, ISV_CONTROL_COUNTER_MASK)
56d3627477Sdyoung #define	ISV_CONTROL_COUNTER_AUTOINC	__SHIFTIN(0, ISV_CONTROL_COUNTER_MASK)
57d3627477Sdyoung 
58d3627477Sdyoung #define	ISV_DATA	ISV_CONTROL	/* data: read-only */
59d3627477Sdyoung 
60d3627477Sdyoung #define ISV_STATUS	0x2		/* status: read-only */
61d3627477Sdyoung #define ISV_STATUS_VIDEO_MASK		__BIT(15)
62d3627477Sdyoung #define ISV_STATUS_VIDEO_RETRACE	__SHIFTIN(0, ISV_STATUS_VIDEO_MASK)
63d3627477Sdyoung #define ISV_STATUS_VIDEO_WRITE		__SHIFTIN(1, ISV_STATUS_VIDEO_MASK)
64d3627477Sdyoung 
65d3627477Sdyoung struct isv_regs {
66d3627477Sdyoung 	bus_space_tag_t		ir_bt;
67d3627477Sdyoung 	bus_space_handle_t	ir_bh;
68d3627477Sdyoung };
69d3627477Sdyoung 
70d3627477Sdyoung enum isv_state {
71d3627477Sdyoung 	  ISV_S_CAPTURE0 = 0
72d3627477Sdyoung 	, ISV_S_CAPTURE1 = 1
73d3627477Sdyoung 	, ISV_S_CAPTURE2 = 2
74d3627477Sdyoung 	, ISV_S_RETRACE = 3
75d3627477Sdyoung };
76d3627477Sdyoung 
77d3627477Sdyoung struct isv_softc {
78d3627477Sdyoung 	struct isv_regs	sc_ir;
79d3627477Sdyoung 	device_t	sc_dev;
80d3627477Sdyoung 	uint16_t	*sc_frame;
81d3627477Sdyoung 	int		sc_speed;
82d3627477Sdyoung };
83d3627477Sdyoung 
84d3627477Sdyoung extern struct cfdriver isv_cd;
85d3627477Sdyoung 
86d3627477Sdyoung static dev_type_ioctl(isv_ioctl);
87d3627477Sdyoung static dev_type_open(isv_open);
88d3627477Sdyoung static dev_type_mmap(isv_mmap);
89d3627477Sdyoung 
90d3627477Sdyoung static int	isv_capture(struct isv_softc *);
91d3627477Sdyoung static int 	isv_match(device_t, cfdata_t, void *);
92d3627477Sdyoung static void 	isv_attach(device_t, device_t, void *);
93d3627477Sdyoung static int 	isv_detach(device_t, int);
94d3627477Sdyoung static uint16_t isv_read(struct isv_regs *, bus_size_t);
95d3627477Sdyoung static void	isv_write(struct isv_regs *, bus_size_t, uint16_t);
96d3627477Sdyoung static bool	isv_retrace(struct isv_regs *);
97d3627477Sdyoung static int	isv_retrace_wait(struct isv_regs *, int *,
98d3627477Sdyoung     const struct timeval *);
99d3627477Sdyoung static int	isv_capture_wait(struct isv_regs *, int *,
100d3627477Sdyoung     const struct timeval *);
101d3627477Sdyoung static bool	isv_delta(int *, bool);
102d3627477Sdyoung static int	isv_probe(struct isv_regs *);
103d3627477Sdyoung 
104d3627477Sdyoung CFATTACH_DECL_NEW(isv_isa, sizeof(struct isv_softc),
105d3627477Sdyoung     isv_match, isv_attach, isv_detach, NULL);
106d3627477Sdyoung 
107d3627477Sdyoung const struct cdevsw isv_cdevsw = {
10876258fa0Sdholland 	.d_open = isv_open,
10976258fa0Sdholland 	.d_close = nullclose,
11076258fa0Sdholland 	.d_read = noread,
11176258fa0Sdholland 	.d_write = nowrite,
11276258fa0Sdholland 	.d_ioctl = isv_ioctl,
11376258fa0Sdholland 	.d_stop = nostop,
11476258fa0Sdholland 	.d_tty = notty,
11576258fa0Sdholland 	.d_poll = nopoll,
11676258fa0Sdholland 	.d_mmap = isv_mmap,
11776258fa0Sdholland 	.d_kqfilter = nokqfilter,
118*1a918832Sdholland 	.d_discard = nodiscard,
11976258fa0Sdholland 	.d_flag = D_OTHER
120d3627477Sdyoung };
121d3627477Sdyoung 
122d3627477Sdyoung static uint16_t
isv_read(struct isv_regs * ir,bus_size_t reg)123d3627477Sdyoung isv_read(struct isv_regs *ir, bus_size_t reg)
124d3627477Sdyoung {
125d3627477Sdyoung 	return bus_space_read_2(ir->ir_bt, ir->ir_bh, reg);
126d3627477Sdyoung }
127d3627477Sdyoung 
128d3627477Sdyoung static void
isv_write(struct isv_regs * ir,bus_size_t reg,uint16_t val)129d3627477Sdyoung isv_write(struct isv_regs *ir, bus_size_t reg, uint16_t val)
130d3627477Sdyoung {
131d3627477Sdyoung 	bus_space_write_2(ir->ir_bt, ir->ir_bh, reg, val);
132d3627477Sdyoung }
133d3627477Sdyoung 
134d3627477Sdyoung static bool
isv_retrace(struct isv_regs * ir)135d3627477Sdyoung isv_retrace(struct isv_regs *ir)
136d3627477Sdyoung {
137d3627477Sdyoung 	uint16_t video;
138d3627477Sdyoung 
139d3627477Sdyoung 	video = isv_read(ir, ISV_STATUS) & ISV_STATUS_VIDEO_MASK;
140d3627477Sdyoung 	return video == ISV_STATUS_VIDEO_RETRACE;
141d3627477Sdyoung }
142d3627477Sdyoung 
143d3627477Sdyoung #define state_and_input(__state, __retrace)	\
144d3627477Sdyoung 	(((__state) << 1) | ((__retrace) ? 1 : 0))
145d3627477Sdyoung 
146d3627477Sdyoung static bool
isv_delta(int * state,bool retrace)147d3627477Sdyoung isv_delta(int *state, bool retrace)
148d3627477Sdyoung {
149d3627477Sdyoung 	bool transition = false;
150d3627477Sdyoung 
151d3627477Sdyoung 	switch (state_and_input(*state, retrace)) {
152d3627477Sdyoung 	case state_and_input(ISV_S_CAPTURE0, false):
153d3627477Sdyoung 	case state_and_input(ISV_S_RETRACE, true):
154d3627477Sdyoung 		break;
155d3627477Sdyoung 	case state_and_input(ISV_S_CAPTURE2, true):
156d3627477Sdyoung 		transition = true;
157d3627477Sdyoung 		/*FALLTHROUGH*/
158d3627477Sdyoung 	case state_and_input(ISV_S_CAPTURE1, true):
159d3627477Sdyoung 	case state_and_input(ISV_S_CAPTURE0, true):
160d3627477Sdyoung 		(*state)++;
161d3627477Sdyoung 		break;
162d3627477Sdyoung 	case state_and_input(ISV_S_RETRACE, false):
163d3627477Sdyoung 		transition = true;
164d3627477Sdyoung 		/*FALLTHROUGH*/
165d3627477Sdyoung 	case state_and_input(ISV_S_CAPTURE2, false):
166d3627477Sdyoung 	case state_and_input(ISV_S_CAPTURE1, false):
167d3627477Sdyoung 		*state = ISV_S_CAPTURE0;
168d3627477Sdyoung 		break;
169d3627477Sdyoung 	}
170d3627477Sdyoung 	return transition;
171d3627477Sdyoung }
172d3627477Sdyoung 
173d3627477Sdyoung static int
isv_probe(struct isv_regs * ir)174d3627477Sdyoung isv_probe(struct isv_regs *ir)
175d3627477Sdyoung {
176d3627477Sdyoung 	int state, transitions;
177d3627477Sdyoung 	struct timeval end, now,
178d3627477Sdyoung 	    wait = {.tv_sec = 0, .tv_usec = 1000000 * 4 / 30};
179d3627477Sdyoung 
180d3627477Sdyoung 	aprint_debug("%s: resetting\n", __func__);
181d3627477Sdyoung 	isv_write(ir, ISV_CONTROL,
182d3627477Sdyoung 	    ISV_CONTROL_MODE_CAPTURE|ISV_CONTROL_COUNTER_AUTOINC);
183d3627477Sdyoung 
184d3627477Sdyoung 	aprint_debug("%s: waiting\n", __func__);
185d3627477Sdyoung 
186d3627477Sdyoung 	microtime(&now);
187d3627477Sdyoung 	timeradd(&now, &wait, &end);
188d3627477Sdyoung 
189d3627477Sdyoung 	state = transitions = 0;
190d3627477Sdyoung 
191d3627477Sdyoung 	do {
192d3627477Sdyoung 		if (isv_delta(&state, isv_retrace(ir)))
193d3627477Sdyoung 			transitions++;
194d3627477Sdyoung 
195d3627477Sdyoung 		if (state == ISV_S_CAPTURE0 || state == ISV_S_RETRACE)
196d3627477Sdyoung 			microtime(&now);
197d3627477Sdyoung 	} while (timercmp(&now, &end, <));
198d3627477Sdyoung 
199d3627477Sdyoung 	aprint_debug("%s: %d transitions\n", __func__, transitions);
200d3627477Sdyoung 
201d3627477Sdyoung 	return transitions >= 4 && transitions <= 10;
202d3627477Sdyoung }
203d3627477Sdyoung 
204d3627477Sdyoung static int
isv_match(device_t parent,cfdata_t match,void * aux)205d3627477Sdyoung isv_match(device_t parent, cfdata_t match, void *aux)
206d3627477Sdyoung {
207d3627477Sdyoung 	struct isv_regs ir;
208d3627477Sdyoung 	struct isa_attach_args *ia = aux;
209d3627477Sdyoung 	int rv;
210d3627477Sdyoung 
211d3627477Sdyoung 	/* Must supply an address */
212d3627477Sdyoung 	if (ia->ia_nio < 1 || ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT)
213d3627477Sdyoung 		return 0;
214d3627477Sdyoung 
215d3627477Sdyoung 	ir.ir_bt = ia->ia_iot;
216d3627477Sdyoung 
217d3627477Sdyoung 	if (bus_space_map(ir.ir_bt, ia->ia_io[0].ir_addr, 8, 0, &ir.ir_bh))
218d3627477Sdyoung 		return 0;
219d3627477Sdyoung 
220d3627477Sdyoung 	rv = isv_probe(&ir);
221d3627477Sdyoung 
222d3627477Sdyoung 	bus_space_unmap(ir.ir_bt, ir.ir_bh, 8);
223d3627477Sdyoung 
224d3627477Sdyoung 	if (rv) {
225d3627477Sdyoung 		ia->ia_nio = 1;
226d3627477Sdyoung 		ia->ia_io[0].ir_size = 8;
227d3627477Sdyoung 
228d3627477Sdyoung 		ia->ia_niomem = 0;
229d3627477Sdyoung 		ia->ia_nirq = 0;
230d3627477Sdyoung 		ia->ia_ndrq = 0;
231d3627477Sdyoung 	}
232d3627477Sdyoung 
233d3627477Sdyoung 	return rv;
234d3627477Sdyoung }
235d3627477Sdyoung 
236d3627477Sdyoung 
237d3627477Sdyoung static void
isv_attach(device_t parent,device_t self,void * aux)238d3627477Sdyoung isv_attach(device_t parent, device_t self, void *aux)
239d3627477Sdyoung {
240d3627477Sdyoung 	struct isv_softc *sc = device_private(self);
241d3627477Sdyoung 	struct isv_regs *ir = &sc->sc_ir;
242d3627477Sdyoung 	struct isa_attach_args *ia = aux;
243d3627477Sdyoung 
244d3627477Sdyoung 	ir->ir_bt = ia->ia_iot;
245d3627477Sdyoung 
246d3627477Sdyoung 	if (bus_space_map(ir->ir_bt, ia->ia_io[0].ir_addr, 8, 0, &ir->ir_bh)) {
247d3627477Sdyoung 		aprint_error(": can't map i/o space\n");
248d3627477Sdyoung 		return;
249d3627477Sdyoung 	}
250d3627477Sdyoung 
251d3627477Sdyoung 	/* Bus-independent attachment */
252d3627477Sdyoung 	sc->sc_dev = self;
253d3627477Sdyoung 
254d3627477Sdyoung 	aprint_normal(": IDEC Supervision/16\n");
255d3627477Sdyoung 
256d3627477Sdyoung 	/* TBD */
257d3627477Sdyoung }
258d3627477Sdyoung 
259d3627477Sdyoung int
isv_open(dev_t dev,int flag,int devtype,lwp_t * l)260d3627477Sdyoung isv_open(dev_t dev, int flag, int devtype, lwp_t *l)
261d3627477Sdyoung {
262d3627477Sdyoung 	vaddr_t va;
263d3627477Sdyoung 	struct isv_softc *sc = device_lookup_private(&isv_cd, minor(dev));
264d3627477Sdyoung 
265d3627477Sdyoung 	if (sc == NULL)
266d3627477Sdyoung 		return ENXIO;
267d3627477Sdyoung 
268d3627477Sdyoung 	if (sc->sc_frame != NULL)
269d3627477Sdyoung 		return 0;
270d3627477Sdyoung 
271d3627477Sdyoung 	if ((va = uvm_km_alloc(kernel_map, ISV_WIDTH * ISV_LINES, PAGE_SIZE,
272d3627477Sdyoung 	    UVM_KMF_WIRED|UVM_KMF_ZERO|UVM_KMF_CANFAIL|UVM_KMF_WAITVA)) == 0)
273d3627477Sdyoung 		return ENOMEM;
274d3627477Sdyoung 
275d3627477Sdyoung 	sc->sc_frame = (uint16_t *)(void *)va;
276d3627477Sdyoung 	return 0;
277d3627477Sdyoung }
278d3627477Sdyoung 
279d3627477Sdyoung /* wait for retrace */
280d3627477Sdyoung static int
isv_retrace_wait(struct isv_regs * ir,int * state,const struct timeval * end)281d3627477Sdyoung isv_retrace_wait(struct isv_regs *ir, int *state, const struct timeval *end)
282d3627477Sdyoung {
283d3627477Sdyoung 	struct timeval now;
284d3627477Sdyoung 
285d3627477Sdyoung 	for (;;) {
286d3627477Sdyoung 		if (!isv_delta(state, isv_retrace(ir))) {
287d3627477Sdyoung 			microtime(&now);
288d3627477Sdyoung 			continue;
289d3627477Sdyoung 		}
290d3627477Sdyoung 		if (*state == ISV_S_RETRACE)
291d3627477Sdyoung 			break;
292d3627477Sdyoung 		if (*state != ISV_S_CAPTURE0)
293d3627477Sdyoung 			continue;
294d3627477Sdyoung 
295d3627477Sdyoung 		microtime(&now);
296d3627477Sdyoung 		if (timercmp(&now, end, >=))
297d3627477Sdyoung 			return EIO;
298d3627477Sdyoung 	}
299d3627477Sdyoung 	return 0;
300d3627477Sdyoung }
301d3627477Sdyoung 
302d3627477Sdyoung /* wait for capture mode */
303d3627477Sdyoung static int
isv_capture_wait(struct isv_regs * ir,int * state,const struct timeval * end)304d3627477Sdyoung isv_capture_wait(struct isv_regs *ir, int *state, const struct timeval *end)
305d3627477Sdyoung {
306d3627477Sdyoung 	struct timeval now;
307d3627477Sdyoung 
308d3627477Sdyoung 	for (;;) {
309d3627477Sdyoung 		if (!isv_delta(state, isv_retrace(ir))) {
310d3627477Sdyoung 			microtime(&now);
311d3627477Sdyoung 			continue;
312d3627477Sdyoung 		}
313d3627477Sdyoung 		if (*state != ISV_S_RETRACE)
314d3627477Sdyoung 			break;
315d3627477Sdyoung 
316d3627477Sdyoung 		microtime(&now);
317d3627477Sdyoung 		if (timercmp(&now, end, >=))
318d3627477Sdyoung 			return EIO;
319d3627477Sdyoung 	}
320d3627477Sdyoung 	return 0;
321d3627477Sdyoung }
322d3627477Sdyoung 
323d3627477Sdyoung 
324d3627477Sdyoung static int
isv_capture(struct isv_softc * sc)325d3627477Sdyoung isv_capture(struct isv_softc *sc)
326d3627477Sdyoung {
327d3627477Sdyoung 	int speed;
328d3627477Sdyoung 	int rc, state = ISV_S_CAPTURE0;
329d3627477Sdyoung 	struct timeval diff, end, start, stop;
330d3627477Sdyoung 	static const struct timeval wait = {.tv_sec = 0, .tv_usec = 200000};
331d3627477Sdyoung 	struct isv_regs *ir = &sc->sc_ir;
332d3627477Sdyoung 
333d3627477Sdyoung 	if (sc->sc_frame == NULL)
334d3627477Sdyoung 		return EAGAIN;
335d3627477Sdyoung 
336d3627477Sdyoung 	microtime(&start);
337d3627477Sdyoung 
338d3627477Sdyoung 	timeradd(&start, &wait, &end);
339d3627477Sdyoung 
340d3627477Sdyoung 	speed = sc->sc_speed;
341d3627477Sdyoung 	sc->sc_speed = 0;
342d3627477Sdyoung 
343d3627477Sdyoung 	if (speed < 1 && (rc = isv_retrace_wait(ir, &state, &end)) != 0)
344d3627477Sdyoung 		return rc;
345d3627477Sdyoung 
346d3627477Sdyoung 	if (speed < 2 && (rc = isv_capture_wait(ir, &state, &end)) != 0)
347d3627477Sdyoung 		return rc;
348d3627477Sdyoung 
349d3627477Sdyoung 	if ((rc = isv_retrace_wait(ir, &state, &end)) != 0)
350d3627477Sdyoung 		return rc;
351d3627477Sdyoung 
352d3627477Sdyoung 	microtime(&stop);
353d3627477Sdyoung 
354d3627477Sdyoung 	timersub(&stop, &start, &diff);
355d3627477Sdyoung 
3566e3de811Sdyoung 	aprint_debug_dev(sc->sc_dev, "%ssync in %" PRId64 ".%06d seconds\n",
357d3627477Sdyoung 	    (speed < 1) ? "" : ((speed < 2) ? "faster " : "fastest "),
358d3627477Sdyoung 	    diff.tv_sec, diff.tv_usec);
359d3627477Sdyoung 
360d3627477Sdyoung 	microtime(&start);
361d3627477Sdyoung 
362d3627477Sdyoung 	/* enter read mode, then toggle counter mode,
363d3627477Sdyoung 	 * autoinc -> reset -> autoinc, so that we start reading
364d3627477Sdyoung 	 * at the top of the frame.
365d3627477Sdyoung 	 */
366d3627477Sdyoung 	isv_write(ir, ISV_CONTROL,
367d3627477Sdyoung 	    ISV_CONTROL_MODE_READ|ISV_CONTROL_COUNTER_AUTOINC);
368d3627477Sdyoung 	isv_write(ir, ISV_CONTROL,
369d3627477Sdyoung 	    ISV_CONTROL_MODE_READ|ISV_CONTROL_COUNTER_RESET);
370d3627477Sdyoung 	isv_write(ir, ISV_CONTROL,
371d3627477Sdyoung 	    ISV_CONTROL_MODE_READ|ISV_CONTROL_COUNTER_AUTOINC);
372d3627477Sdyoung 	/* read one dummy word to prime the state machine on the
373d3627477Sdyoung 	 * image capture board
374d3627477Sdyoung 	 */
375025428b2Schristos 	isv_read(ir, ISV_DATA);
376d3627477Sdyoung 	bus_space_read_multi_stream_2(ir->ir_bt, ir->ir_bh, ISV_DATA,
377d3627477Sdyoung 	    sc->sc_frame, ISV_WIDTH * ISV_LINES / 2);
378d3627477Sdyoung 
379d3627477Sdyoung 	/* restore to initial conditions */
380d3627477Sdyoung 	isv_write(ir, ISV_CONTROL,
381d3627477Sdyoung 	    ISV_CONTROL_MODE_CAPTURE|ISV_CONTROL_COUNTER_AUTOINC);
382d3627477Sdyoung 
383d3627477Sdyoung 	microtime(&stop);
384d3627477Sdyoung 
385d3627477Sdyoung 	timersub(&stop, &start, &diff);
386d3627477Sdyoung 
3876e3de811Sdyoung 	aprint_debug_dev(sc->sc_dev, "read in %" PRId64 ".%06d seconds\n",
388d3627477Sdyoung 		diff.tv_sec, diff.tv_usec);
389d3627477Sdyoung 
390d3627477Sdyoung 	state = 0;
391d3627477Sdyoung 
392d3627477Sdyoung 	if (isv_retrace_wait(ir, &state, &end) != 0)
393d3627477Sdyoung 		return 0;
394d3627477Sdyoung 	sc->sc_speed++;
395d3627477Sdyoung 
396d3627477Sdyoung 	if (isv_capture_wait(ir, &state, &end) != 0)
397d3627477Sdyoung 		return 0;
398d3627477Sdyoung 	sc->sc_speed++;
399d3627477Sdyoung 
400d3627477Sdyoung 	return 0;
401d3627477Sdyoung }
402d3627477Sdyoung 
403d3627477Sdyoung int
isv_ioctl(dev_t dev,u_long cmd,void * data,int flag,lwp_t * l)404d3627477Sdyoung isv_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
405d3627477Sdyoung {
406d3627477Sdyoung 	struct isv_cmd ic;
407d3627477Sdyoung 	struct isv_softc *sc = device_lookup_private(&isv_cd, minor(dev));
408d3627477Sdyoung 
409d3627477Sdyoung 	if (cmd != ISV_CMD)
410d3627477Sdyoung 		return ENOTTY;
411d3627477Sdyoung 
412d3627477Sdyoung 	memcpy(&ic, data, sizeof(ic));
413d3627477Sdyoung 
414d3627477Sdyoung 	if (ic.c_cmd != ISV_CMD_READ)
415d3627477Sdyoung 		return EINVAL;
416d3627477Sdyoung 
417d3627477Sdyoung 	ic.c_frameno = 0;
418d3627477Sdyoung 
419d3627477Sdyoung 	return isv_capture(sc);
420d3627477Sdyoung }
421d3627477Sdyoung 
422d3627477Sdyoung paddr_t
isv_mmap(dev_t dev,off_t offset,int prot)423d3627477Sdyoung isv_mmap(dev_t dev, off_t offset, int prot)
424d3627477Sdyoung {
425d3627477Sdyoung 	struct isv_softc *sc = device_lookup_private(&isv_cd, minor(dev));
426d3627477Sdyoung 	paddr_t pa;
427d3627477Sdyoung 
428d3627477Sdyoung 	if ((prot & ~(VM_PROT_READ)) != 0)
429d3627477Sdyoung 		return -1;
430d3627477Sdyoung 
431d3627477Sdyoung 	if (sc->sc_frame == NULL)
432d3627477Sdyoung 		return -1;
433d3627477Sdyoung 
434d3627477Sdyoung 	if (offset >= ISV_WIDTH * ISV_LINES)
435d3627477Sdyoung 		return -1;
436d3627477Sdyoung 
437d3627477Sdyoung 	if (!pmap_extract(pmap_kernel(), (vaddr_t)&sc->sc_frame[offset/2], &pa))
438d3627477Sdyoung 		return -1;
439d3627477Sdyoung 
440d3627477Sdyoung 	return atop(pa);
441d3627477Sdyoung }
442d3627477Sdyoung 
443d3627477Sdyoung static int
isv_detach(device_t self,int flags)444d3627477Sdyoung isv_detach(device_t self, int flags)
445d3627477Sdyoung {
446d3627477Sdyoung 	struct isv_softc *sc = device_private(self);
447d3627477Sdyoung 	struct isv_regs *ir = &sc->sc_ir;
448d3627477Sdyoung 
449d3627477Sdyoung 	if (sc->sc_frame != NULL) {
450d3627477Sdyoung 		uvm_km_free(kernel_map, (vaddr_t)sc->sc_frame,
451d3627477Sdyoung 		    ISV_WIDTH * ISV_LINES, UVM_KMF_WIRED);
452d3627477Sdyoung 	}
453d3627477Sdyoung 	bus_space_unmap(ir->ir_bt, ir->ir_bh, 8);
454d3627477Sdyoung 	return 0;
455d3627477Sdyoung }
456