1 /* $NetBSD: auvitek_video.c,v 1.6 2011/10/02 19:15:40 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Auvitek AU0828 USB controller 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: auvitek_video.c,v 1.6 2011/10/02 19:15:40 jmcneill Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/device.h> 39 #include <sys/conf.h> 40 #include <sys/kmem.h> 41 #include <sys/bus.h> 42 43 #include <dev/usb/usb.h> 44 #include <dev/usb/usbdi.h> 45 #include <dev/usb/usbdivar.h> 46 #include <dev/usb/usbdi_util.h> 47 #include <dev/usb/usbdevs.h> 48 49 #include <dev/video_if.h> 50 51 #include <dev/usb/auvitekreg.h> 52 #include <dev/usb/auvitekvar.h> 53 54 #define AUVITEK_FORMAT_DEFAULT 0 55 #define AUVITEK_STANDARD_NTSC_M 0 56 #define AUVITEK_TUNER_DEFAULT 0 57 #define AUVITEK_AUDIO_TELEVISION 0 58 #define AUVITEK_AUDIO_LINEIN 1 59 #define AUVITEK_INPUT_COMPOSITE 0 60 #define AUVITEK_INPUT_SVIDEO 1 61 #define AUVITEK_INPUT_TELEVISION 2 62 #define AUVITEK_INPUT_CABLE 3 63 64 static int auvitek_open(void *, int); 65 static void auvitek_close(void *); 66 static const char * auvitek_get_devname(void *); 67 static const char * auvitek_get_businfo(void *); 68 static int auvitek_enum_format(void *, uint32_t, 69 struct video_format *); 70 static int auvitek_get_format(void *, struct video_format *); 71 static int auvitek_set_format(void *, struct video_format *); 72 static int auvitek_try_format(void *, struct video_format *); 73 static int auvitek_enum_standard(void *, uint32_t, 74 enum video_standard *); 75 static int auvitek_get_standard(void *, enum video_standard *); 76 static int auvitek_set_standard(void *, enum video_standard); 77 static int auvitek_start_transfer(void *); 78 static int auvitek_stop_transfer(void *); 79 static int auvitek_get_tuner(void *, struct video_tuner *); 80 static int auvitek_set_tuner(void *, struct video_tuner *); 81 static int auvitek_enum_audio(void *, uint32_t, 82 struct video_audio *); 83 static int auvitek_get_audio(void *, struct video_audio *); 84 static int auvitek_set_audio(void *, struct video_audio *); 85 static int auvitek_enum_input(void *, uint32_t, 86 struct video_input *); 87 static int auvitek_get_input(void *, struct video_input *); 88 static int auvitek_set_input(void *, struct video_input *); 89 static int auvitek_get_frequency(void *, struct video_frequency *); 90 static int auvitek_set_frequency(void *, struct video_frequency *); 91 92 static int auvitek_start_xfer(struct auvitek_softc *); 93 static int auvitek_stop_xfer(struct auvitek_softc *); 94 static int auvitek_isoc_start(struct auvitek_softc *); 95 static int auvitek_isoc_start1(struct auvitek_isoc *); 96 static void auvitek_isoc_intr(usbd_xfer_handle, 97 usbd_private_handle, 98 usbd_status); 99 static int auvitek_isoc_process(struct auvitek_softc *, 100 uint8_t *, uint32_t); 101 static void auvitek_videobuf_weave(struct auvitek_softc *, 102 uint8_t *, uint32_t); 103 104 static const struct video_hw_if auvitek_video_if = { 105 .open = auvitek_open, 106 .close = auvitek_close, 107 .get_devname = auvitek_get_devname, 108 .get_businfo = auvitek_get_businfo, 109 .enum_format = auvitek_enum_format, 110 .get_format = auvitek_get_format, 111 .set_format = auvitek_set_format, 112 .try_format = auvitek_try_format, 113 .enum_standard = auvitek_enum_standard, 114 .get_standard = auvitek_get_standard, 115 .set_standard = auvitek_set_standard, 116 .start_transfer = auvitek_start_transfer, 117 .stop_transfer = auvitek_stop_transfer, 118 .get_tuner = auvitek_get_tuner, 119 .set_tuner = auvitek_set_tuner, 120 .enum_audio = auvitek_enum_audio, 121 .get_audio = auvitek_get_audio, 122 .set_audio = auvitek_set_audio, 123 .enum_input = auvitek_enum_input, 124 .get_input = auvitek_get_input, 125 .set_input = auvitek_set_input, 126 .get_frequency = auvitek_get_frequency, 127 .set_frequency = auvitek_set_frequency, 128 }; 129 130 int 131 auvitek_video_attach(struct auvitek_softc *sc) 132 { 133 snprintf(sc->sc_businfo, sizeof(sc->sc_businfo), "usb:%08x", 134 sc->sc_udev->cookie.cookie); 135 136 auvitek_video_rescan(sc, NULL, NULL); 137 138 return (sc->sc_videodev != NULL); 139 } 140 141 int 142 auvitek_video_detach(struct auvitek_softc *sc, int flags) 143 { 144 if (sc->sc_videodev != NULL) { 145 config_detach(sc->sc_videodev, flags); 146 sc->sc_videodev = NULL; 147 } 148 149 return 0; 150 } 151 152 void 153 auvitek_video_rescan(struct auvitek_softc *sc, const char *ifattr, 154 const int *locs) 155 { 156 if (ifattr_match(ifattr, "videobus") && sc->sc_videodev == NULL) 157 sc->sc_videodev = video_attach_mi(&auvitek_video_if, 158 sc->sc_dev); 159 } 160 161 void 162 auvitek_video_childdet(struct auvitek_softc *sc, device_t child) 163 { 164 if (sc->sc_videodev == child) 165 sc->sc_videodev = NULL; 166 } 167 168 static int 169 auvitek_open(void *opaque, int flags) 170 { 171 struct auvitek_softc *sc = opaque; 172 173 if (sc->sc_dying) 174 return EIO; 175 176 auvitek_attach_tuner(sc->sc_dev); 177 178 if (sc->sc_xc5k == NULL) 179 return ENXIO; 180 181 return 0; 182 } 183 184 static void 185 auvitek_close(void *opaque) 186 { 187 } 188 189 static const char * 190 auvitek_get_devname(void *opaque) 191 { 192 struct auvitek_softc *sc = opaque; 193 194 return sc->sc_descr; 195 } 196 197 static const char * 198 auvitek_get_businfo(void *opaque) 199 { 200 struct auvitek_softc *sc = opaque; 201 202 return sc->sc_businfo; 203 } 204 205 static int 206 auvitek_enum_format(void *opaque, uint32_t index, struct video_format *format) 207 { 208 if (index != AUVITEK_FORMAT_DEFAULT) 209 return EINVAL; 210 211 format->pixel_format = VIDEO_FORMAT_UYVY; 212 213 return 0; 214 } 215 216 static int 217 auvitek_get_format(void *opaque, struct video_format *format) 218 { 219 220 format->pixel_format = VIDEO_FORMAT_UYVY; 221 format->width = 720; 222 format->height = 480; 223 format->stride = format->width * 2; 224 format->sample_size = format->stride * format->height; 225 format->aspect_x = 4; 226 format->aspect_y = 3; 227 format->color.primaries = VIDEO_COLOR_PRIMARIES_SMPTE_170M; 228 format->color.gamma_function = VIDEO_GAMMA_FUNCTION_UNSPECIFIED; 229 format->color.matrix_coeff = VIDEO_MATRIX_COEFF_UNSPECIFIED; 230 format->interlace_flags = VIDEO_INTERLACE_ON; 231 format->priv = 0; 232 233 return 0; 234 } 235 236 static int 237 auvitek_set_format(void *opaque, struct video_format *format) 238 { 239 if (format->pixel_format != VIDEO_FORMAT_UYVY) 240 return EINVAL; 241 242 return auvitek_get_format(opaque, format); 243 } 244 245 static int 246 auvitek_try_format(void *opaque, struct video_format *format) 247 { 248 return auvitek_get_format(opaque, format); 249 } 250 251 static int 252 auvitek_enum_standard(void *opaque, uint32_t index, enum video_standard *vstd) 253 { 254 switch (index) { 255 case AUVITEK_STANDARD_NTSC_M: 256 *vstd = VIDEO_STANDARD_NTSC_M; 257 return 0; 258 default: 259 return EINVAL; 260 } 261 } 262 263 static int 264 auvitek_get_standard(void *opaque, enum video_standard *vstd) 265 { 266 *vstd = VIDEO_STANDARD_NTSC_M; 267 return 0; 268 } 269 270 static int 271 auvitek_set_standard(void *opaque, enum video_standard vstd) 272 { 273 switch (vstd) { 274 case VIDEO_STANDARD_NTSC_M: 275 return 0; 276 default: 277 return EINVAL; 278 } 279 } 280 281 static int 282 auvitek_start_transfer(void *opaque) 283 { 284 struct auvitek_softc *sc = opaque; 285 int error, s; 286 uint16_t vpos = 0, hpos = 0; 287 uint16_t hres = 720 * 2; 288 uint16_t vres = 484 / 2; 289 290 auvitek_write_1(sc, AU0828_REG_SENSORVBI_CTL, 0x00); 291 292 /* program video position and size */ 293 auvitek_write_1(sc, AU0828_REG_HPOS_LO, hpos & 0xff); 294 auvitek_write_1(sc, AU0828_REG_HPOS_HI, hpos >> 8); 295 auvitek_write_1(sc, AU0828_REG_VPOS_LO, vpos & 0xff); 296 auvitek_write_1(sc, AU0828_REG_VPOS_HI, vpos >> 8); 297 auvitek_write_1(sc, AU0828_REG_HRES_LO, hres & 0xff); 298 auvitek_write_1(sc, AU0828_REG_HRES_HI, hres >> 8); 299 auvitek_write_1(sc, AU0828_REG_VRES_LO, vres & 0xff); 300 auvitek_write_1(sc, AU0828_REG_VRES_HI, vres >> 8); 301 302 auvitek_write_1(sc, AU0828_REG_SENSOR_CTL, 0xb3); 303 304 auvitek_write_1(sc, AU0828_REG_AUDIOCTL, 0x01); 305 306 s = splusb(); 307 error = auvitek_start_xfer(sc); 308 splx(s); 309 310 if (error) 311 auvitek_stop_transfer(sc); 312 313 return error; 314 } 315 316 static int 317 auvitek_stop_transfer(void *opaque) 318 { 319 struct auvitek_softc *sc = opaque; 320 int error, s; 321 322 auvitek_write_1(sc, AU0828_REG_SENSOR_CTL, 0x00); 323 324 s = splusb(); 325 error = auvitek_stop_xfer(sc); 326 splx(s); 327 328 return error; 329 } 330 331 static int 332 auvitek_get_tuner(void *opaque, struct video_tuner *vt) 333 { 334 struct auvitek_softc *sc = opaque; 335 336 switch (vt->index) { 337 case AUVITEK_TUNER_DEFAULT: 338 strlcpy(vt->name, "XC5000", sizeof(vt->name)); 339 vt->freq_lo = 44000000 / 62500; 340 vt->freq_hi = 958000000 / 62500; 341 vt->caps = VIDEO_TUNER_F_STEREO; 342 vt->mode = VIDEO_TUNER_F_STEREO; 343 if (sc->sc_au8522) 344 vt->signal = au8522_get_signal(sc->sc_au8522); 345 else 346 vt->signal = 0; 347 break; 348 default: 349 return EINVAL; 350 } 351 352 return 0; 353 } 354 355 static int 356 auvitek_set_tuner(void *opaque, struct video_tuner *vt) 357 { 358 if (vt->index != AUVITEK_TUNER_DEFAULT) 359 return EINVAL; 360 return 0; 361 } 362 363 static int 364 auvitek_enum_audio(void *opaque, uint32_t index, struct video_audio *va) 365 { 366 switch (index) { 367 case AUVITEK_AUDIO_TELEVISION: 368 strlcpy(va->name, "Television", sizeof(va->name)); 369 va->caps = VIDEO_AUDIO_F_STEREO; 370 break; 371 case AUVITEK_AUDIO_LINEIN: 372 strlcpy(va->name, "Line In", sizeof(va->name)); 373 va->caps = VIDEO_AUDIO_F_STEREO; 374 break; 375 default: 376 return EINVAL; 377 } 378 379 return 0; 380 } 381 382 static int 383 auvitek_get_audio(void *opaque, struct video_audio *va) 384 { 385 struct auvitek_softc *sc = opaque; 386 387 return auvitek_enum_audio(opaque, sc->sc_ainput, va); 388 } 389 390 static int 391 auvitek_set_audio(void *opaque, struct video_audio *va) 392 { 393 struct auvitek_softc *sc = opaque; 394 395 if (va->index == sc->sc_ainput) 396 return 0; 397 398 return EINVAL; 399 } 400 401 static int 402 auvitek_enum_input(void *opaque, uint32_t index, struct video_input *vi) 403 { 404 switch (index) { 405 case AUVITEK_INPUT_COMPOSITE: 406 strlcpy(vi->name, "Composite", sizeof(vi->name)); 407 vi->type = VIDEO_INPUT_TYPE_BASEBAND; 408 vi->standards = VIDEO_STANDARD_NTSC_M; 409 break; 410 case AUVITEK_INPUT_SVIDEO: 411 strlcpy(vi->name, "S-Video", sizeof(vi->name)); 412 vi->type = VIDEO_INPUT_TYPE_BASEBAND; 413 vi->standards = VIDEO_STANDARD_NTSC_M; 414 break; 415 case AUVITEK_INPUT_TELEVISION: 416 strlcpy(vi->name, "Television", sizeof(vi->name)); 417 vi->type = VIDEO_INPUT_TYPE_TUNER; 418 vi->standards = VIDEO_STANDARD_NTSC_M; 419 break; 420 case AUVITEK_INPUT_CABLE: 421 strlcpy(vi->name, "Cable TV", sizeof(vi->name)); 422 vi->type = VIDEO_INPUT_TYPE_TUNER; 423 vi->standards = VIDEO_STANDARD_NTSC_M; 424 break; 425 default: 426 return EINVAL; 427 } 428 429 vi->index = index; 430 vi->tuner_index = AUVITEK_TUNER_DEFAULT; 431 432 return 0; 433 } 434 435 static int 436 auvitek_get_input(void *opaque, struct video_input *vi) 437 { 438 struct auvitek_softc *sc = opaque; 439 440 return auvitek_enum_input(opaque, sc->sc_vinput, vi); 441 } 442 443 static int 444 auvitek_set_input(void *opaque, struct video_input *vi) 445 { 446 struct auvitek_softc *sc = opaque; 447 struct video_frequency vf; 448 au8522_vinput_t vinput = AU8522_VINPUT_UNCONF; 449 au8522_ainput_t ainput = AU8522_AINPUT_UNCONF; 450 uint8_t r; 451 452 switch (vi->index) { 453 case AUVITEK_INPUT_COMPOSITE: 454 vinput = AU8522_VINPUT_CVBS; 455 ainput = AU8522_AINPUT_NONE; 456 sc->sc_ainput = AUVITEK_AUDIO_LINEIN; 457 break; 458 case AUVITEK_INPUT_SVIDEO: 459 vinput = AU8522_VINPUT_SVIDEO; 460 ainput = AU8522_AINPUT_NONE; 461 sc->sc_ainput = AUVITEK_AUDIO_LINEIN; 462 break; 463 case AUVITEK_INPUT_TELEVISION: 464 case AUVITEK_INPUT_CABLE: 465 vinput = AU8522_VINPUT_CVBS_TUNER; 466 ainput = AU8522_AINPUT_SIF; 467 sc->sc_ainput = AUVITEK_AUDIO_TELEVISION; 468 break; 469 default: 470 return EINVAL; 471 } 472 473 sc->sc_vinput = vi->index; 474 475 au8522_set_input(sc->sc_au8522, vinput, ainput); 476 477 /* XXX HVR-850/950Q specific */ 478 r = auvitek_read_1(sc, AU0828_REG_GPIO1_OUTEN); 479 if (ainput == AU8522_AINPUT_NONE) 480 r |= 0x10; 481 else 482 r &= ~0x10; 483 auvitek_write_1(sc, AU0828_REG_GPIO1_OUTEN, r); 484 485 if (vinput == AU8522_VINPUT_CVBS_TUNER && sc->sc_curfreq > 0) { 486 vf.tuner_index = AUVITEK_TUNER_DEFAULT; 487 vf.frequency = sc->sc_curfreq; 488 auvitek_set_frequency(sc, &vf); 489 } 490 491 return 0; 492 } 493 494 static int 495 auvitek_get_frequency(void *opaque, struct video_frequency *vf) 496 { 497 struct auvitek_softc *sc = opaque; 498 499 if (sc->sc_vinput != AUVITEK_INPUT_TELEVISION && 500 sc->sc_vinput != AUVITEK_INPUT_CABLE) 501 return EINVAL; 502 503 vf->tuner_index = AUVITEK_TUNER_DEFAULT; 504 vf->frequency = sc->sc_curfreq; 505 506 return 0; 507 } 508 509 static int 510 auvitek_set_frequency(void *opaque, struct video_frequency *vf) 511 { 512 struct auvitek_softc *sc = opaque; 513 struct xc5k_params params; 514 int error; 515 516 if (sc->sc_vinput != AUVITEK_INPUT_TELEVISION && 517 sc->sc_vinput != AUVITEK_INPUT_CABLE) 518 return EINVAL; 519 if (vf->tuner_index != AUVITEK_TUNER_DEFAULT) 520 return EINVAL; 521 if (sc->sc_xc5k == NULL) 522 return ENODEV; 523 524 params.standard = VIDEO_STANDARD_NTSC_M; 525 if (sc->sc_vinput == AUVITEK_INPUT_TELEVISION) 526 params.signal_source = XC5K_SIGNAL_SOURCE_AIR; 527 else 528 params.signal_source = XC5K_SIGNAL_SOURCE_CABLE; 529 params.frequency = vf->frequency; 530 if (sc->sc_au8522) 531 au8522_set_audio(sc->sc_au8522, false); 532 error = xc5k_tune_video(sc->sc_xc5k, ¶ms); 533 if (sc->sc_au8522) 534 au8522_set_audio(sc->sc_au8522, true); 535 if (error) 536 return error; 537 538 sc->sc_curfreq = vf->frequency; 539 540 auvitek_write_1(sc, AU0828_REG_SENSOR_CTL, 0x00); 541 delay(30000); 542 auvitek_write_1(sc, AU0828_REG_SENSOR_CTL, 0xb3); 543 544 return 0; 545 } 546 547 static int 548 auvitek_start_xfer(struct auvitek_softc *sc) 549 { 550 struct auvitek_xfer *ax = &sc->sc_ax; 551 uint32_t vframe_len, uframe_len, nframes; 552 usbd_status err; 553 int i; 554 555 err = usbd_set_interface(sc->sc_isoc_iface, AUVITEK_XFER_ALTNO); 556 if (err != USBD_NORMAL_COMPLETION) { 557 aprint_error_dev(sc->sc_dev, "couldn't set altno %d: %s\n", 558 AUVITEK_XFER_ALTNO, usbd_errstr(err)); 559 return EIO; 560 } 561 562 vframe_len = 720 * 480 * 2; 563 uframe_len = ax->ax_maxpktlen; 564 nframes = (vframe_len + uframe_len - 1) / uframe_len; 565 nframes = (nframes + 7) & ~7; 566 567 ax->ax_nframes = nframes; 568 ax->ax_uframe_len = uframe_len; 569 for (i = 0; i < AUVITEK_NISOC_XFERS; i++) { 570 struct auvitek_isoc *isoc = &ax->ax_i[i]; 571 isoc->i_ax = ax; 572 isoc->i_frlengths = 573 kmem_alloc(sizeof(isoc->i_frlengths[0]) * nframes, 574 KM_SLEEP); 575 } 576 577 err = usbd_open_pipe(sc->sc_isoc_iface, ax->ax_endpt, 578 USBD_EXCLUSIVE_USE, &ax->ax_pipe); 579 if (err != USBD_NORMAL_COMPLETION) { 580 aprint_error_dev(sc->sc_dev, "couldn't open pipe: %s\n", 581 usbd_errstr(err)); 582 return EIO; 583 } 584 585 for (i = 0; i < AUVITEK_NISOC_XFERS; i++) { 586 struct auvitek_isoc *isoc = &ax->ax_i[i]; 587 588 isoc->i_xfer = usbd_alloc_xfer(sc->sc_udev); 589 if (isoc->i_xfer == NULL) { 590 aprint_error_dev(sc->sc_dev, 591 "couldn't allocate usb xfer\n"); 592 return ENOMEM; 593 } 594 595 isoc->i_buf = usbd_alloc_buffer(isoc->i_xfer, 596 nframes * uframe_len); 597 if (isoc->i_buf == NULL) { 598 aprint_error_dev(sc->sc_dev, 599 "couldn't allocate usb xfer buffer\n"); 600 return ENOMEM; 601 } 602 } 603 604 return auvitek_isoc_start(sc); 605 } 606 607 static int 608 auvitek_stop_xfer(struct auvitek_softc *sc) 609 { 610 struct auvitek_xfer *ax = &sc->sc_ax; 611 usbd_status err; 612 int i; 613 614 if (ax->ax_pipe != NULL) { 615 usbd_abort_pipe(ax->ax_pipe); 616 usbd_close_pipe(ax->ax_pipe); 617 ax->ax_pipe = NULL; 618 } 619 620 for (i = 0; i < AUVITEK_NISOC_XFERS; i++) { 621 struct auvitek_isoc *isoc = &ax->ax_i[i]; 622 if (isoc->i_xfer != NULL) { 623 usbd_free_buffer(isoc->i_xfer); 624 usbd_free_xfer(isoc->i_xfer); 625 isoc->i_xfer = NULL; 626 } 627 if (isoc->i_frlengths != NULL) { 628 kmem_free(isoc->i_frlengths, 629 sizeof(isoc->i_frlengths[0]) * ax->ax_nframes); 630 isoc->i_frlengths = NULL; 631 } 632 } 633 634 usbd_delay_ms(sc->sc_udev, 1000); 635 err = usbd_set_interface(sc->sc_isoc_iface, 0); 636 if (err != USBD_NORMAL_COMPLETION) { 637 aprint_error_dev(sc->sc_dev, 638 "couldn't set zero bw interface: %s\n", 639 usbd_errstr(err)); 640 return EIO; 641 } 642 643 return 0; 644 } 645 646 static int 647 auvitek_isoc_start(struct auvitek_softc *sc) 648 { 649 struct auvitek_xfer *ax = &sc->sc_ax; 650 int i, error; 651 652 ax->ax_av.av_el = ax->ax_av.av_ol = 0; 653 ax->ax_av.av_eb = ax->ax_av.av_ob = 0; 654 ax->ax_av.av_stride = 720 * 2; 655 656 for (i = 0; i < AUVITEK_NISOC_XFERS; i++) { 657 error = auvitek_isoc_start1(&ax->ax_i[i]); 658 if (error) 659 return error; 660 } 661 662 return 0; 663 } 664 665 static int 666 auvitek_isoc_start1(struct auvitek_isoc *isoc) 667 { 668 struct auvitek_xfer *ax = isoc->i_ax; 669 struct auvitek_softc *sc = ax->ax_sc; 670 usbd_status err; 671 unsigned int i; 672 673 ax = isoc->i_ax; 674 675 for (i = 0; i < ax->ax_nframes; i++) 676 isoc->i_frlengths[i] = ax->ax_uframe_len; 677 678 usbd_setup_isoc_xfer(isoc->i_xfer, 679 ax->ax_pipe, 680 isoc, 681 isoc->i_frlengths, 682 ax->ax_nframes, 683 USBD_NO_COPY | USBD_SHORT_XFER_OK, 684 auvitek_isoc_intr); 685 686 err = usbd_transfer(isoc->i_xfer); 687 if (err != USBD_IN_PROGRESS) { 688 aprint_error_dev(sc->sc_dev, "couldn't start isoc xfer: %s\n", 689 usbd_errstr(err)); 690 return ENODEV; 691 } 692 693 return 0; 694 } 695 696 static void 697 auvitek_isoc_intr(usbd_xfer_handle xfer, usbd_private_handle priv, 698 usbd_status status) 699 { 700 struct auvitek_isoc *isoc = priv; 701 struct auvitek_xfer *ax = isoc->i_ax; 702 struct auvitek_softc *sc = ax->ax_sc; 703 uint32_t count; 704 uint8_t *buf; 705 unsigned int i; 706 707 if (sc->sc_dying) 708 return; 709 710 if (status != USBD_NORMAL_COMPLETION) { 711 if (status == USBD_STALLED) { 712 usbd_clear_endpoint_stall_async(ax->ax_pipe); 713 goto next; 714 } 715 return; 716 } 717 718 usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); 719 720 if (count == 0) 721 goto next; 722 723 for (i = 0, buf = isoc->i_buf; 724 i < ax->ax_nframes; 725 ++i, buf += ax->ax_uframe_len) { 726 status = auvitek_isoc_process(sc, buf, isoc->i_frlengths[i]); 727 if (status == USBD_IOERROR) 728 break; 729 } 730 731 next: 732 auvitek_isoc_start1(isoc); 733 } 734 735 static int 736 auvitek_isoc_process(struct auvitek_softc *sc, uint8_t *buf, uint32_t len) 737 { 738 struct video_payload payload; 739 bool submit = false; 740 741 if (buf[0] & 0x80) { 742 sc->sc_ax.ax_frinfo = buf[0]; 743 if (sc->sc_ax.ax_frinfo & 0x40) { 744 sc->sc_ax.ax_frno = !sc->sc_ax.ax_frno; 745 submit = true; 746 } 747 buf += 4; 748 len -= 4; 749 } 750 buf += 4; 751 len -= 4; 752 753 auvitek_videobuf_weave(sc, buf, len); 754 755 if (submit) { 756 payload.end_of_frame = 1; 757 payload.data = sc->sc_ax.ax_av.av_buf; 758 payload.size = sizeof(sc->sc_ax.ax_av.av_buf); 759 payload.frameno = -1; 760 761 video_submit_payload(sc->sc_videodev, &payload); 762 763 sc->sc_ax.ax_av.av_el = sc->sc_ax.ax_av.av_ol = 0; 764 sc->sc_ax.ax_av.av_eb = sc->sc_ax.ax_av.av_ob = 0; 765 } 766 767 return USBD_NORMAL_COMPLETION; 768 } 769 770 static void 771 auvitek_videobuf_weave(struct auvitek_softc *sc, uint8_t *buf, uint32_t len) 772 { 773 struct auvitek_videobuf *av = &sc->sc_ax.ax_av; 774 uint32_t resid, wlen; 775 uint32_t *l, *b; 776 uint8_t *vp; 777 778 if (sc->sc_ax.ax_frinfo & 0x40) { 779 l = &av->av_ol; 780 b = &av->av_ob; 781 vp = av->av_buf; 782 } else { 783 l = &av->av_el; 784 b = &av->av_eb; 785 vp = av->av_buf + av->av_stride; 786 } 787 788 resid = len; 789 while (resid > 0) { 790 if (*b == av->av_stride) { 791 *l = *l + 1; 792 *b = 0; 793 } 794 if (*l >= 240) { 795 break; 796 } 797 wlen = min(resid, av->av_stride - *b); 798 memcpy(vp + (av->av_stride * 2 * *l) + *b, buf, wlen); 799 *b += wlen; 800 buf += wlen; 801 resid -= wlen; 802 } 803 } 804