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