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