Home | History | Annotate | Line # | Download | only in usb
      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, &params);
    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