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