xref: /netbsd/sys/dev/usb/emdtv_dtv.c (revision b1252f77)
1*b1252f77Sriastradh /* $NetBSD: emdtv_dtv.c,v 1.17 2022/03/29 09:08:44 riastradh Exp $ */
296a186d7Sjmcneill 
396a186d7Sjmcneill /*-
496a186d7Sjmcneill  * Copyright (c) 2008, 2011 Jared D. McNeill <jmcneill@invisible.ca>
596a186d7Sjmcneill  * All rights reserved.
696a186d7Sjmcneill  *
796a186d7Sjmcneill  * Redistribution and use in source and binary forms, with or without
896a186d7Sjmcneill  * modification, are permitted provided that the following conditions
996a186d7Sjmcneill  * are met:
1096a186d7Sjmcneill  * 1. Redistributions of source code must retain the above copyright
1196a186d7Sjmcneill  *    notice, this list of conditions and the following disclaimer.
1296a186d7Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1396a186d7Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
1496a186d7Sjmcneill  *    documentation and/or other materials provided with the distribution.
1596a186d7Sjmcneill  *
1696a186d7Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1796a186d7Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1896a186d7Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1996a186d7Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2096a186d7Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2196a186d7Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2296a186d7Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2396a186d7Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2496a186d7Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2596a186d7Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2696a186d7Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
2796a186d7Sjmcneill  */
2896a186d7Sjmcneill 
2996a186d7Sjmcneill #include <sys/cdefs.h>
30*b1252f77Sriastradh __KERNEL_RCSID(0, "$NetBSD: emdtv_dtv.c,v 1.17 2022/03/29 09:08:44 riastradh Exp $");
3196a186d7Sjmcneill 
3296a186d7Sjmcneill #include <sys/param.h>
3396a186d7Sjmcneill #include <sys/systm.h>
3496a186d7Sjmcneill #include <sys/device.h>
3540df89e0Sskrll #include <sys/lwp.h>
3696a186d7Sjmcneill #include <sys/conf.h>
3796a186d7Sjmcneill 
3896a186d7Sjmcneill #include <dev/usb/usb.h>
3996a186d7Sjmcneill #include <dev/usb/usbdi.h>
4096a186d7Sjmcneill #include <dev/usb/usbdi_util.h>
4140d1aa7cSmrg #include <dev/usb/usbdivar.h>
4296a186d7Sjmcneill #include <dev/usb/usbdevs.h>
4396a186d7Sjmcneill 
4496a186d7Sjmcneill #include <dev/i2c/i2cvar.h>
4596a186d7Sjmcneill 
4696a186d7Sjmcneill #include <dev/usb/emdtvvar.h>
4796a186d7Sjmcneill #include <dev/usb/emdtvreg.h>
4896a186d7Sjmcneill 
4996a186d7Sjmcneill static void		emdtv_dtv_get_devinfo(void *,
5096a186d7Sjmcneill 			    struct dvb_frontend_info *);
5196a186d7Sjmcneill static int		emdtv_dtv_open(void *, int);
5296a186d7Sjmcneill static void		emdtv_dtv_close(void *);
5396a186d7Sjmcneill static int		emdtv_dtv_set_tuner(void *,
5496a186d7Sjmcneill 			    const struct dvb_frontend_parameters *);
5596a186d7Sjmcneill static fe_status_t	emdtv_dtv_get_status(void *);
5696a186d7Sjmcneill static uint16_t		emdtv_dtv_get_signal_strength(void *);
5796a186d7Sjmcneill static uint16_t		emdtv_dtv_get_snr(void *);
588f316c9dSjmcneill static int		emdtv_dtv_start_transfer(void *,
598f316c9dSjmcneill 			    void (*)(void *, const struct dtv_payload *),
608f316c9dSjmcneill 			    void *);
6196a186d7Sjmcneill static int		emdtv_dtv_stop_transfer(void *);
6296a186d7Sjmcneill 
6396a186d7Sjmcneill static int		emdtv_dtv_tuner_reset(void *);
6496a186d7Sjmcneill 
6596a186d7Sjmcneill static void		emdtv_dtv_isoc_startall(struct emdtv_softc *);
6696a186d7Sjmcneill static int		emdtv_dtv_isoc_start(struct emdtv_softc *,
6796a186d7Sjmcneill 			    struct emdtv_isoc_xfer *);
6871112a2eSskrll static void		emdtv_dtv_isoc(struct usbd_xfer *, void *,
6996a186d7Sjmcneill 			    usbd_status);
7096a186d7Sjmcneill 
7196a186d7Sjmcneill static const struct dtv_hw_if emdtv_dtv_if = {
7296a186d7Sjmcneill 	.get_devinfo = emdtv_dtv_get_devinfo,
7396a186d7Sjmcneill 	.open = emdtv_dtv_open,
7496a186d7Sjmcneill 	.close = emdtv_dtv_close,
7596a186d7Sjmcneill 	.set_tuner = emdtv_dtv_set_tuner,
7696a186d7Sjmcneill 	.get_status = emdtv_dtv_get_status,
7796a186d7Sjmcneill 	.get_signal_strength = emdtv_dtv_get_signal_strength,
7896a186d7Sjmcneill 	.get_snr = emdtv_dtv_get_snr,
7996a186d7Sjmcneill 	.start_transfer = emdtv_dtv_start_transfer,
8096a186d7Sjmcneill 	.stop_transfer = emdtv_dtv_stop_transfer,
8196a186d7Sjmcneill };
8296a186d7Sjmcneill 
8396a186d7Sjmcneill void
emdtv_dtv_attach(struct emdtv_softc * sc)8496a186d7Sjmcneill emdtv_dtv_attach(struct emdtv_softc *sc)
8596a186d7Sjmcneill {
8696a186d7Sjmcneill 	usb_endpoint_descriptor_t *ed;
8796a186d7Sjmcneill 	usbd_status status;
8896a186d7Sjmcneill 	int i;
8996a186d7Sjmcneill 
9096a186d7Sjmcneill 	for (i = 0; i < EMDTV_NXFERS; i++) {
9196a186d7Sjmcneill 		sc->sc_ix[i].ix_altix = (i & 1) ?
9296a186d7Sjmcneill 		    &sc->sc_ix[i - 1] : &sc->sc_ix[i + 1];
9396a186d7Sjmcneill 		sc->sc_ix[i].ix_sc = sc;
9496a186d7Sjmcneill 	}
9596a186d7Sjmcneill 
9696a186d7Sjmcneill 	ed = usbd_interface2endpoint_descriptor(sc->sc_iface, 3);
9796a186d7Sjmcneill 	if (ed == NULL) {
9896a186d7Sjmcneill 		aprint_error_dev(sc->sc_dev, "couldn't find endpoint 3\n");
9996a186d7Sjmcneill 		return;
10096a186d7Sjmcneill 	}
10196a186d7Sjmcneill 	sc->sc_isoc_maxpacketsize = UGETW(ed->wMaxPacketSize);
10296a186d7Sjmcneill 	sc->sc_isoc_buflen = sc->sc_isoc_maxpacketsize * EMDTV_NFRAMES;
10396a186d7Sjmcneill 
1044f8d1b77Schristos 	aprint_debug_dev(sc->sc_dev, "calling usbd_open_pipe, ep 0x%02x\n",
10596a186d7Sjmcneill 	    ed->bEndpointAddress);
10696a186d7Sjmcneill 	status = usbd_open_pipe(sc->sc_iface,
107e2eaccd2Sjmcneill 	    ed->bEndpointAddress, USBD_EXCLUSIVE_USE|USBD_MPSAFE,
10896a186d7Sjmcneill 	    &sc->sc_isoc_pipe);
10996a186d7Sjmcneill 	if (status != USBD_NORMAL_COMPLETION) {
11096a186d7Sjmcneill 		aprint_error_dev(sc->sc_dev, "couldn't open isoc pipe\n");
11196a186d7Sjmcneill 		usbd_set_interface(sc->sc_iface, 0);
11296a186d7Sjmcneill 		return;
11396a186d7Sjmcneill 	}
11496a186d7Sjmcneill 
11596a186d7Sjmcneill 	emdtv_write_1(sc, UR_GET_STATUS, 0x48, 0x00);
11696a186d7Sjmcneill 	emdtv_write_1(sc, UR_GET_STATUS, 0x12, 0x77);
11796a186d7Sjmcneill 	usbd_delay_ms(sc->sc_udev, 6);
11896a186d7Sjmcneill 
11996a186d7Sjmcneill 	emdtv_gpio_ctl(sc, EMDTV_GPIO_ANALOG_ON, false);
12096a186d7Sjmcneill 	emdtv_gpio_ctl(sc, EMDTV_GPIO_TS1_ON, true);
12196a186d7Sjmcneill 	emdtv_gpio_ctl(sc, EMDTV_GPIO_TUNER1_ON, true);
12296a186d7Sjmcneill 	emdtv_gpio_ctl(sc, EMDTV_GPIO_DEMOD1_RESET, true);
12396a186d7Sjmcneill 	usbd_delay_ms(sc->sc_udev, 100);
12496a186d7Sjmcneill 
1258f316c9dSjmcneill 	emdtv_dtv_rescan(sc, NULL, NULL);
12696a186d7Sjmcneill }
12796a186d7Sjmcneill 
12871112a2eSskrll static void
emdtv_dtv_free_xfers(struct emdtv_softc * sc)12971112a2eSskrll emdtv_dtv_free_xfers(struct emdtv_softc *sc)
13071112a2eSskrll {
13171112a2eSskrll 
13271112a2eSskrll 	for (size_t i = 0; i < EMDTV_NXFERS; i++)
13371112a2eSskrll 		if (sc->sc_ix[i].ix_xfer) {
13471112a2eSskrll 			usbd_destroy_xfer(sc->sc_ix[i].ix_xfer);
13571112a2eSskrll 			sc->sc_ix[i].ix_xfer = NULL;
13671112a2eSskrll 			sc->sc_ix[i].ix_buf = NULL;
13771112a2eSskrll 		}
13871112a2eSskrll 
13971112a2eSskrll 	return;
14071112a2eSskrll }
14171112a2eSskrll 
14296a186d7Sjmcneill void
emdtv_dtv_detach(struct emdtv_softc * sc,int flags)14396a186d7Sjmcneill emdtv_dtv_detach(struct emdtv_softc *sc, int flags)
14496a186d7Sjmcneill {
14596a186d7Sjmcneill 
146*b1252f77Sriastradh 	sc->sc_streaming = false;
14796a186d7Sjmcneill 
14896a186d7Sjmcneill 	if (sc->sc_xc3028)
14996a186d7Sjmcneill 		xc3028_close(sc->sc_xc3028);
15096a186d7Sjmcneill 	if (sc->sc_lg3303)
15196a186d7Sjmcneill 		lg3303_close(sc->sc_lg3303);
15296a186d7Sjmcneill 
15396a186d7Sjmcneill 	if (sc->sc_isoc_pipe) {
15496a186d7Sjmcneill 		usbd_abort_pipe(sc->sc_isoc_pipe);
15571112a2eSskrll 		emdtv_dtv_free_xfers(sc);
15696a186d7Sjmcneill 		usbd_close_pipe(sc->sc_isoc_pipe);
15796a186d7Sjmcneill 		sc->sc_isoc_pipe = NULL;
15896a186d7Sjmcneill 	}
15996a186d7Sjmcneill }
16096a186d7Sjmcneill 
1618f316c9dSjmcneill void
emdtv_dtv_rescan(struct emdtv_softc * sc,const char * ifattr,const int * locs)1628f316c9dSjmcneill emdtv_dtv_rescan(struct emdtv_softc *sc, const char *ifattr, const int *locs)
1638f316c9dSjmcneill {
1648f316c9dSjmcneill 	struct dtv_attach_args daa;
1658f316c9dSjmcneill 
1668f316c9dSjmcneill 	daa.hw = &emdtv_dtv_if;
1678f316c9dSjmcneill 	daa.priv = sc;
1688f316c9dSjmcneill 
1698f316c9dSjmcneill 	if (ifattr_match(ifattr, "dtvbus") && sc->sc_dtvdev == NULL)
1703bee0c11Sthorpej 		sc->sc_dtvdev = config_found(sc->sc_dev, &daa, dtv_print,
171beecddb6Sthorpej 		    CFARGS(.iattr = "dtvbus"));
1728f316c9dSjmcneill }
1738f316c9dSjmcneill 
17496a186d7Sjmcneill static void
emdtv_dtv_get_devinfo(void * priv,struct dvb_frontend_info * info)17596a186d7Sjmcneill emdtv_dtv_get_devinfo(void *priv, struct dvb_frontend_info *info)
17696a186d7Sjmcneill {
17796a186d7Sjmcneill 	struct emdtv_softc *sc = priv;
17896a186d7Sjmcneill 
17996a186d7Sjmcneill 	memset(info, 0, sizeof(*info));
18096a186d7Sjmcneill 	strlcpy(info->name, sc->sc_board->eb_name, sizeof(info->name));
18196a186d7Sjmcneill 	info->type = FE_ATSC;
18296a186d7Sjmcneill 	info->frequency_min = 54000000;
18396a186d7Sjmcneill 	info->frequency_max = 858000000;
18496a186d7Sjmcneill 	info->frequency_stepsize = 62500;
18596a186d7Sjmcneill 	info->caps = FE_CAN_8VSB;
18696a186d7Sjmcneill }
18796a186d7Sjmcneill 
18896a186d7Sjmcneill static int
emdtv_dtv_open(void * priv,int flags)18996a186d7Sjmcneill emdtv_dtv_open(void *priv, int flags)
19096a186d7Sjmcneill {
19196a186d7Sjmcneill 	struct emdtv_softc *sc = priv;
19296a186d7Sjmcneill 
19396a186d7Sjmcneill 	if (sc->sc_dying)
19496a186d7Sjmcneill 		return ENXIO;
19596a186d7Sjmcneill 
19671112a2eSskrll 	aprint_debug_dev(sc->sc_dev, "allocating isoc xfers (pktsz %d)\n",
19771112a2eSskrll 	    sc->sc_isoc_maxpacketsize);
19871112a2eSskrll 
19971112a2eSskrll 	for (size_t i = 0; i < EMDTV_NXFERS; i++) {
20071112a2eSskrll 		int error = usbd_create_xfer(sc->sc_isoc_pipe,
20171112a2eSskrll 		    sc->sc_isoc_buflen, USBD_SHORT_XFER_OK, EMDTV_NFRAMES,
20271112a2eSskrll 		    &sc->sc_ix[i].ix_xfer);
20371112a2eSskrll 		if (error)
20471112a2eSskrll 			return error;
20571112a2eSskrll 		sc->sc_ix[i].ix_buf = usbd_get_buffer(sc->sc_ix[i].ix_xfer);
20671112a2eSskrll 		aprint_debug_dev(sc->sc_dev, "  ix[%zu] xfer %p buf %p\n",
20771112a2eSskrll 		    i, sc->sc_ix[i].ix_xfer, sc->sc_ix[i].ix_buf);
20871112a2eSskrll 	}
20971112a2eSskrll 
21096a186d7Sjmcneill 	switch (sc->sc_board->eb_tuner) {
211d1a09809Sjakllsch 	case EMDTV_TUNER_XC3028:
212d1a09809Sjakllsch 		if (sc->sc_xc3028 == NULL) {
213d1a09809Sjakllsch 			sc->sc_xc3028 = xc3028_open(sc->sc_dev,
214d1a09809Sjakllsch 			    &sc->sc_i2c, 0x61 << 1, emdtv_dtv_tuner_reset, sc,
215d1a09809Sjakllsch 			    XC3028);
216d1a09809Sjakllsch 		}
217d1a09809Sjakllsch 		if (sc->sc_xc3028 == NULL) {
218d1a09809Sjakllsch 			aprint_error_dev(sc->sc_dev, "couldn't open xc3028\n");
219d1a09809Sjakllsch 			return ENXIO;
220d1a09809Sjakllsch 		}
221d1a09809Sjakllsch 		break;
22296a186d7Sjmcneill 	case EMDTV_TUNER_XC3028L:
22396a186d7Sjmcneill 		if (sc->sc_xc3028 == NULL) {
22496a186d7Sjmcneill 			sc->sc_xc3028 = xc3028_open(sc->sc_dev,
22596a186d7Sjmcneill 			    &sc->sc_i2c, 0x61 << 1, emdtv_dtv_tuner_reset, sc,
22696a186d7Sjmcneill 			    XC3028L);
22796a186d7Sjmcneill 		}
22896a186d7Sjmcneill 		if (sc->sc_xc3028 == NULL) {
22996a186d7Sjmcneill 			aprint_error_dev(sc->sc_dev, "couldn't open xc3028l\n");
23096a186d7Sjmcneill 			return ENXIO;
23196a186d7Sjmcneill 		}
23296a186d7Sjmcneill 		break;
23396a186d7Sjmcneill 	default:
23496a186d7Sjmcneill 		aprint_error_dev(sc->sc_dev, "unsupported tuner (%d)\n",
23596a186d7Sjmcneill 		    sc->sc_board->eb_tuner);
23696a186d7Sjmcneill 		return EIO;
23796a186d7Sjmcneill 	}
23896a186d7Sjmcneill 
23996a186d7Sjmcneill 	switch (sc->sc_board->eb_demod) {
24096a186d7Sjmcneill 	case EMDTV_DEMOD_LG3303:
24196a186d7Sjmcneill 		if (sc->sc_lg3303 == NULL) {
24296a186d7Sjmcneill 			sc->sc_lg3303 = lg3303_open(sc->sc_dev,
243ff321f6fSjmcneill 			    &sc->sc_i2c, 0x1c, 0);
24496a186d7Sjmcneill 		}
24596a186d7Sjmcneill 		if (sc->sc_lg3303 == NULL) {
24696a186d7Sjmcneill 			aprint_error_dev(sc->sc_dev, "couldn't open lg3303\n");
24796a186d7Sjmcneill 			return ENXIO;
24896a186d7Sjmcneill 		}
24996a186d7Sjmcneill 		break;
25096a186d7Sjmcneill 	default:
25196a186d7Sjmcneill 		aprint_error_dev(sc->sc_dev, "unsupported demod (%d)\n",
25296a186d7Sjmcneill 		    sc->sc_board->eb_demod);
25396a186d7Sjmcneill 		return EIO;
25496a186d7Sjmcneill 	}
25596a186d7Sjmcneill 
25696a186d7Sjmcneill 	return 0;
25796a186d7Sjmcneill }
25896a186d7Sjmcneill 
25996a186d7Sjmcneill static void
emdtv_dtv_close(void * priv)26096a186d7Sjmcneill emdtv_dtv_close(void *priv)
26196a186d7Sjmcneill {
26271112a2eSskrll 	struct emdtv_softc *sc = priv;
26371112a2eSskrll 
26471112a2eSskrll 	emdtv_dtv_free_xfers(sc);
26596a186d7Sjmcneill }
26696a186d7Sjmcneill 
26796a186d7Sjmcneill static int
emdtv_dtv_set_tuner(void * priv,const struct dvb_frontend_parameters * params)26896a186d7Sjmcneill emdtv_dtv_set_tuner(void *priv, const struct dvb_frontend_parameters *params)
26996a186d7Sjmcneill {
27096a186d7Sjmcneill 	struct emdtv_softc *sc = priv;
27196a186d7Sjmcneill 	int error;
27296a186d7Sjmcneill 
27396a186d7Sjmcneill 	/* Setup demod */
27496a186d7Sjmcneill 	error = ENXIO;
27596a186d7Sjmcneill 	if (sc->sc_lg3303)
27696a186d7Sjmcneill 		error = lg3303_set_modulation(sc->sc_lg3303,
27796a186d7Sjmcneill 		    params->u.vsb.modulation);
27896a186d7Sjmcneill 	if (error)
27996a186d7Sjmcneill 		return error;
28096a186d7Sjmcneill 
28196a186d7Sjmcneill 	/* Setup tuner */
28296a186d7Sjmcneill 	error = ENXIO;
28396a186d7Sjmcneill 	if (sc->sc_xc3028)
28496a186d7Sjmcneill 		error = xc3028_tune_dtv(sc->sc_xc3028, params);
28596a186d7Sjmcneill 
28696a186d7Sjmcneill 	return error;
28796a186d7Sjmcneill }
28896a186d7Sjmcneill 
28996a186d7Sjmcneill static fe_status_t
emdtv_dtv_get_status(void * priv)29096a186d7Sjmcneill emdtv_dtv_get_status(void *priv)
29196a186d7Sjmcneill {
29296a186d7Sjmcneill 	struct emdtv_softc *sc = priv;
29396a186d7Sjmcneill 
29496a186d7Sjmcneill 	if (sc->sc_lg3303)
29596a186d7Sjmcneill 		return lg3303_get_dtv_status(sc->sc_lg3303);
29696a186d7Sjmcneill 
29796a186d7Sjmcneill 	return 0;
29896a186d7Sjmcneill }
29996a186d7Sjmcneill 
30096a186d7Sjmcneill uint16_t
emdtv_dtv_get_signal_strength(void * priv)30196a186d7Sjmcneill emdtv_dtv_get_signal_strength(void *priv)
30296a186d7Sjmcneill {
303b698a2d3Sjmcneill 	struct emdtv_softc *sc = priv;
304b698a2d3Sjmcneill 
305b698a2d3Sjmcneill 	if (sc->sc_lg3303)
306b698a2d3Sjmcneill 		return lg3303_get_signal_strength(sc->sc_lg3303);
307b698a2d3Sjmcneill 
30896a186d7Sjmcneill 	return 0;
30996a186d7Sjmcneill }
31096a186d7Sjmcneill 
31196a186d7Sjmcneill uint16_t
emdtv_dtv_get_snr(void * priv)31296a186d7Sjmcneill emdtv_dtv_get_snr(void *priv)
31396a186d7Sjmcneill {
314b698a2d3Sjmcneill 	struct emdtv_softc *sc = priv;
315b698a2d3Sjmcneill 
316b698a2d3Sjmcneill 	if (sc->sc_lg3303)
317b698a2d3Sjmcneill 		return lg3303_get_snr(sc->sc_lg3303);
318b698a2d3Sjmcneill 
31996a186d7Sjmcneill 	return 0;
32096a186d7Sjmcneill }
32196a186d7Sjmcneill 
32296a186d7Sjmcneill static int
emdtv_dtv_start_transfer(void * priv,void (* cb)(void *,const struct dtv_payload *),void * arg)3238f316c9dSjmcneill emdtv_dtv_start_transfer(void *priv,
3248f316c9dSjmcneill     void (*cb)(void *, const struct dtv_payload *), void *arg)
32596a186d7Sjmcneill {
32696a186d7Sjmcneill 	struct emdtv_softc *sc = priv;
32771112a2eSskrll 	int s;
32896a186d7Sjmcneill 
32996a186d7Sjmcneill 	s = splusb();
33096a186d7Sjmcneill 
33196a186d7Sjmcneill 	sc->sc_streaming = true;
3328f316c9dSjmcneill 	sc->sc_dtvsubmitcb = cb;
3338f316c9dSjmcneill 	sc->sc_dtvsubmitarg = arg;
33496a186d7Sjmcneill 
33596a186d7Sjmcneill 	aprint_debug_dev(sc->sc_dev, "starting isoc transactions\n");
33696a186d7Sjmcneill 
33796a186d7Sjmcneill 	emdtv_dtv_isoc_startall(sc);
33896a186d7Sjmcneill 	splx(s);
33996a186d7Sjmcneill 
34096a186d7Sjmcneill 	return 0;
34196a186d7Sjmcneill }
34296a186d7Sjmcneill 
34396a186d7Sjmcneill static int
emdtv_dtv_stop_transfer(void * priv)34496a186d7Sjmcneill emdtv_dtv_stop_transfer(void *priv)
34596a186d7Sjmcneill {
34696a186d7Sjmcneill 	struct emdtv_softc *sc = priv;
34796a186d7Sjmcneill 
34896a186d7Sjmcneill 	aprint_debug_dev(sc->sc_dev, "stopping stream\n");
34996a186d7Sjmcneill 
35096a186d7Sjmcneill 	sc->sc_streaming = false;
35196a186d7Sjmcneill 
352cb477d62Sjmcneill 	KERNEL_LOCK(1, curlwp);
35396a186d7Sjmcneill 	if (sc->sc_isoc_pipe != NULL)
35496a186d7Sjmcneill 		usbd_abort_pipe(sc->sc_isoc_pipe);
355cb477d62Sjmcneill 	KERNEL_UNLOCK_ONE(curlwp);
35696a186d7Sjmcneill 
3578f316c9dSjmcneill 	sc->sc_dtvsubmitcb = NULL;
3588f316c9dSjmcneill 	sc->sc_dtvsubmitarg = NULL;
3598f316c9dSjmcneill 
36096a186d7Sjmcneill 	return 0;
36196a186d7Sjmcneill }
36296a186d7Sjmcneill 
36396a186d7Sjmcneill static void
emdtv_dtv_isoc_startall(struct emdtv_softc * sc)36496a186d7Sjmcneill emdtv_dtv_isoc_startall(struct emdtv_softc *sc)
36596a186d7Sjmcneill {
36696a186d7Sjmcneill 	int i;
36796a186d7Sjmcneill 
36896a186d7Sjmcneill 	if (sc->sc_streaming == false || sc->sc_dying == true)
36996a186d7Sjmcneill 		return;
37096a186d7Sjmcneill 
37196a186d7Sjmcneill 	for (i = 0; i < EMDTV_NXFERS; i += 2)
37296a186d7Sjmcneill 		emdtv_dtv_isoc_start(sc, &sc->sc_ix[i]);
37396a186d7Sjmcneill }
37496a186d7Sjmcneill 
37596a186d7Sjmcneill static int
emdtv_dtv_isoc_start(struct emdtv_softc * sc,struct emdtv_isoc_xfer * ix)37696a186d7Sjmcneill emdtv_dtv_isoc_start(struct emdtv_softc *sc, struct emdtv_isoc_xfer *ix)
37796a186d7Sjmcneill {
37896a186d7Sjmcneill 	int i;
37996a186d7Sjmcneill 
38096a186d7Sjmcneill 	if (sc->sc_isoc_pipe == NULL)
38196a186d7Sjmcneill 		return EIO;
38296a186d7Sjmcneill 
38396a186d7Sjmcneill 	for (i = 0; i < EMDTV_NFRAMES; i++)
38496a186d7Sjmcneill 		ix->ix_frlengths[i] = sc->sc_isoc_maxpacketsize;
38596a186d7Sjmcneill 
38696a186d7Sjmcneill 	usbd_setup_isoc_xfer(ix->ix_xfer,
38796a186d7Sjmcneill 			     ix,
38896a186d7Sjmcneill 			     ix->ix_frlengths,
38996a186d7Sjmcneill 			     EMDTV_NFRAMES,
39071112a2eSskrll 			     USBD_SHORT_XFER_OK,
39196a186d7Sjmcneill 			     emdtv_dtv_isoc);
392cb477d62Sjmcneill 
393cb477d62Sjmcneill 	KERNEL_LOCK(1, curlwp);
39496a186d7Sjmcneill 	usbd_transfer(ix->ix_xfer);
395cb477d62Sjmcneill 	KERNEL_UNLOCK_ONE(curlwp);
39696a186d7Sjmcneill 
39796a186d7Sjmcneill 	return 0;
39896a186d7Sjmcneill }
39996a186d7Sjmcneill 
40096a186d7Sjmcneill static void
emdtv_dtv_isoc(struct usbd_xfer * xfer,void * priv,usbd_status err)40171112a2eSskrll emdtv_dtv_isoc(struct usbd_xfer *xfer, void * priv,
40296a186d7Sjmcneill     usbd_status err)
40396a186d7Sjmcneill {
40496a186d7Sjmcneill 	struct emdtv_isoc_xfer *ix = priv;
40596a186d7Sjmcneill 	struct emdtv_softc *sc = ix->ix_sc;
40696a186d7Sjmcneill 	struct dtv_payload payload;
40771112a2eSskrll 	struct usbd_pipe *isoc = sc->sc_isoc_pipe;
40896a186d7Sjmcneill 	uint32_t len;
40996a186d7Sjmcneill 	uint8_t *buf;
41096a186d7Sjmcneill 	int i;
41196a186d7Sjmcneill 
41296a186d7Sjmcneill 	KASSERT(xfer == ix->ix_xfer);
41396a186d7Sjmcneill 
4148f316c9dSjmcneill 	if (sc->sc_dying || sc->sc_dtvsubmitcb == NULL)
41596a186d7Sjmcneill 		return;
41696a186d7Sjmcneill 
41796a186d7Sjmcneill 	if (err) {
41896a186d7Sjmcneill 		if (err == USBD_STALLED) {
41996a186d7Sjmcneill 			usbd_clear_endpoint_stall_async(isoc);
42096a186d7Sjmcneill 			goto resched;
42196a186d7Sjmcneill 		}
42296a186d7Sjmcneill 		return;
42396a186d7Sjmcneill 	}
42496a186d7Sjmcneill 
42596a186d7Sjmcneill 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
42696a186d7Sjmcneill 
42796a186d7Sjmcneill 	if (len == 0)
42896a186d7Sjmcneill 		goto resched;
42996a186d7Sjmcneill 
43096a186d7Sjmcneill 	buf = usbd_get_buffer(xfer);
43196a186d7Sjmcneill 	if (buf == NULL)
43296a186d7Sjmcneill 		goto resched;
43396a186d7Sjmcneill 
43496a186d7Sjmcneill 	for (i = 0; i < EMDTV_NFRAMES; i++, buf += sc->sc_isoc_maxpacketsize) {
43596a186d7Sjmcneill 		if (ix->ix_frlengths[i] == 0)
43696a186d7Sjmcneill 			continue;
43796a186d7Sjmcneill 		payload.data = buf;
43896a186d7Sjmcneill 		payload.size = ix->ix_frlengths[i];
4398f316c9dSjmcneill 		sc->sc_dtvsubmitcb(sc->sc_dtvsubmitarg, &payload);
44096a186d7Sjmcneill 	}
44196a186d7Sjmcneill 
44296a186d7Sjmcneill resched:
44396a186d7Sjmcneill 	emdtv_dtv_isoc_start(sc, ix->ix_altix);
44496a186d7Sjmcneill }
44596a186d7Sjmcneill 
44696a186d7Sjmcneill static int
emdtv_dtv_tuner_reset(void * opaque)44796a186d7Sjmcneill emdtv_dtv_tuner_reset(void *opaque)
44896a186d7Sjmcneill {
44996a186d7Sjmcneill 	struct emdtv_softc *sc = opaque;
45096a186d7Sjmcneill 	emdtv_gpio_ctl(sc, EMDTV_GPIO_TUNER1_RESET, true);
45196a186d7Sjmcneill 	return 0;
45296a186d7Sjmcneill }
453