1 1.10 riastrad /* $NetBSD: auvitek_dtv.c,v 1.10 2022/03/13 12:49:36 riastradh Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.1 jmcneill * Copyright (c) 2011 Jared D. McNeill <jmcneill (at) invisible.ca> 5 1.1 jmcneill * All rights reserved. 6 1.1 jmcneill * 7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 8 1.1 jmcneill * modification, are permitted provided that the following conditions 9 1.1 jmcneill * are met: 10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 11 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 14 1.1 jmcneill * documentation and/or other materials provided with the distribution. 15 1.1 jmcneill * 16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE. 27 1.1 jmcneill */ 28 1.1 jmcneill 29 1.1 jmcneill /* 30 1.1 jmcneill * Auvitek AU0828 USB controller (Digital TV function) 31 1.1 jmcneill */ 32 1.1 jmcneill 33 1.1 jmcneill #include <sys/cdefs.h> 34 1.10 riastrad __KERNEL_RCSID(0, "$NetBSD: auvitek_dtv.c,v 1.10 2022/03/13 12:49:36 riastradh Exp $"); 35 1.1 jmcneill 36 1.1 jmcneill #include <sys/param.h> 37 1.1 jmcneill #include <sys/systm.h> 38 1.1 jmcneill #include <sys/device.h> 39 1.1 jmcneill #include <sys/conf.h> 40 1.1 jmcneill #include <sys/kmem.h> 41 1.1 jmcneill #include <sys/bus.h> 42 1.1 jmcneill 43 1.1 jmcneill #include <dev/usb/usb.h> 44 1.1 jmcneill #include <dev/usb/usbdi.h> 45 1.1 jmcneill #include <dev/usb/usbdivar.h> 46 1.1 jmcneill #include <dev/usb/usbdi_util.h> 47 1.1 jmcneill #include <dev/usb/usbdevs.h> 48 1.1 jmcneill 49 1.1 jmcneill #include <dev/dtv/dtvif.h> 50 1.1 jmcneill 51 1.1 jmcneill #include <dev/usb/auvitekreg.h> 52 1.1 jmcneill #include <dev/usb/auvitekvar.h> 53 1.1 jmcneill 54 1.1 jmcneill static void auvitek_dtv_get_devinfo(void *, 55 1.1 jmcneill struct dvb_frontend_info *); 56 1.1 jmcneill static int auvitek_dtv_open(void *, int); 57 1.1 jmcneill static void auvitek_dtv_close(void *); 58 1.1 jmcneill static int auvitek_dtv_set_tuner(void *, 59 1.1 jmcneill const struct dvb_frontend_parameters *); 60 1.1 jmcneill static fe_status_t auvitek_dtv_get_status(void *); 61 1.1 jmcneill static uint16_t auvitek_dtv_get_signal_strength(void *); 62 1.1 jmcneill static uint16_t auvitek_dtv_get_snr(void *); 63 1.3 jmcneill static int auvitek_dtv_start_transfer(void *, 64 1.3 jmcneill void (*)(void *, const struct dtv_payload *), 65 1.3 jmcneill void *); 66 1.1 jmcneill static int auvitek_dtv_stop_transfer(void *); 67 1.1 jmcneill 68 1.1 jmcneill static int auvitek_dtv_init_pipes(struct auvitek_softc *); 69 1.7 skrll static int auvitek_dtv_abort_pipes(struct auvitek_softc *); 70 1.1 jmcneill static int auvitek_dtv_close_pipes(struct auvitek_softc *); 71 1.1 jmcneill 72 1.1 jmcneill static int auvitek_dtv_bulk_start(struct auvitek_softc *); 73 1.1 jmcneill static int auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *); 74 1.7 skrll static void auvitek_dtv_bulk_cb(struct usbd_xfer *, void *, 75 1.7 skrll usbd_status); 76 1.1 jmcneill 77 1.1 jmcneill static const struct dtv_hw_if auvitek_dtv_if = { 78 1.1 jmcneill .get_devinfo = auvitek_dtv_get_devinfo, 79 1.1 jmcneill .open = auvitek_dtv_open, 80 1.1 jmcneill .close = auvitek_dtv_close, 81 1.1 jmcneill .set_tuner = auvitek_dtv_set_tuner, 82 1.1 jmcneill .get_status = auvitek_dtv_get_status, 83 1.1 jmcneill .get_signal_strength = auvitek_dtv_get_signal_strength, 84 1.1 jmcneill .get_snr = auvitek_dtv_get_snr, 85 1.1 jmcneill .start_transfer = auvitek_dtv_start_transfer, 86 1.1 jmcneill .stop_transfer = auvitek_dtv_stop_transfer, 87 1.1 jmcneill }; 88 1.1 jmcneill 89 1.1 jmcneill int 90 1.1 jmcneill auvitek_dtv_attach(struct auvitek_softc *sc) 91 1.1 jmcneill { 92 1.1 jmcneill 93 1.3 jmcneill auvitek_dtv_rescan(sc, NULL, NULL); 94 1.1 jmcneill 95 1.7 skrll return sc->sc_dtvdev != NULL; 96 1.1 jmcneill } 97 1.1 jmcneill 98 1.1 jmcneill int 99 1.1 jmcneill auvitek_dtv_detach(struct auvitek_softc *sc, int flags) 100 1.1 jmcneill { 101 1.1 jmcneill 102 1.1 jmcneill return 0; 103 1.1 jmcneill } 104 1.1 jmcneill 105 1.1 jmcneill void 106 1.3 jmcneill auvitek_dtv_rescan(struct auvitek_softc *sc, const char *ifattr, 107 1.3 jmcneill const int *locs) 108 1.3 jmcneill { 109 1.3 jmcneill struct dtv_attach_args daa; 110 1.3 jmcneill 111 1.3 jmcneill daa.hw = &auvitek_dtv_if; 112 1.3 jmcneill daa.priv = sc; 113 1.3 jmcneill 114 1.3 jmcneill if (ifattr_match(ifattr, "dtvbus") && sc->sc_dtvdev == NULL) 115 1.8 thorpej sc->sc_dtvdev = config_found(sc->sc_dev, &daa, dtv_print, 116 1.9 thorpej CFARGS(.iattr = "dtvbus")); 117 1.3 jmcneill } 118 1.3 jmcneill 119 1.3 jmcneill void 120 1.1 jmcneill auvitek_dtv_childdet(struct auvitek_softc *sc, device_t child) 121 1.1 jmcneill { 122 1.1 jmcneill if (sc->sc_dtvdev == child) 123 1.1 jmcneill sc->sc_dtvdev = NULL; 124 1.1 jmcneill } 125 1.1 jmcneill 126 1.1 jmcneill static void 127 1.1 jmcneill auvitek_dtv_get_devinfo(void *priv, struct dvb_frontend_info *info) 128 1.1 jmcneill { 129 1.1 jmcneill struct auvitek_softc *sc = priv; 130 1.1 jmcneill 131 1.1 jmcneill memset(info, 0, sizeof(*info)); 132 1.1 jmcneill strlcpy(info->name, sc->sc_descr, sizeof(info->name)); 133 1.1 jmcneill info->type = FE_ATSC; 134 1.1 jmcneill info->frequency_min = 54000000; 135 1.1 jmcneill info->frequency_max = 858000000; 136 1.1 jmcneill info->frequency_stepsize = 62500; 137 1.1 jmcneill info->caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB; 138 1.1 jmcneill } 139 1.1 jmcneill 140 1.1 jmcneill static int 141 1.1 jmcneill auvitek_dtv_open(void *priv, int flags) 142 1.1 jmcneill { 143 1.1 jmcneill struct auvitek_softc *sc = priv; 144 1.1 jmcneill 145 1.1 jmcneill if (sc->sc_dying) 146 1.1 jmcneill return EIO; 147 1.1 jmcneill 148 1.4 jmcneill auvitek_attach_tuner(sc->sc_dev); 149 1.1 jmcneill if (sc->sc_xc5k == NULL) 150 1.1 jmcneill return ENXIO; 151 1.1 jmcneill 152 1.7 skrll int err = auvitek_dtv_init_pipes(sc); 153 1.7 skrll if (err) 154 1.7 skrll return err; 155 1.7 skrll 156 1.7 skrll for (size_t i = 0; i < AUVITEK_NBULK_XFERS; i++) { 157 1.7 skrll sc->sc_ab.ab_bx[i].bx_sc = sc; 158 1.7 skrll err = usbd_create_xfer(sc->sc_ab.ab_pipe, 159 1.7 skrll AUVITEK_BULK_BUFLEN, 0, 0, &sc->sc_ab.ab_bx[i].bx_xfer); 160 1.7 skrll if (err) { 161 1.7 skrll aprint_error_dev(sc->sc_dev, 162 1.7 skrll "couldn't allocate xfer\n"); 163 1.7 skrll sc->sc_dying = 1; 164 1.7 skrll return err; 165 1.7 skrll } 166 1.7 skrll sc->sc_ab.ab_bx[i].bx_buffer = usbd_get_buffer( 167 1.7 skrll sc->sc_ab.ab_bx[i].bx_xfer); 168 1.7 skrll } 169 1.7 skrll 170 1.7 skrll 171 1.7 skrll return 0; 172 1.1 jmcneill } 173 1.1 jmcneill 174 1.1 jmcneill static void 175 1.1 jmcneill auvitek_dtv_close(void *priv) 176 1.1 jmcneill { 177 1.1 jmcneill struct auvitek_softc *sc = priv; 178 1.1 jmcneill 179 1.1 jmcneill auvitek_dtv_stop_transfer(sc); 180 1.7 skrll auvitek_dtv_abort_pipes(sc); 181 1.7 skrll 182 1.7 skrll for (size_t i = 0; i < AUVITEK_NBULK_XFERS; i++) { 183 1.7 skrll if (sc->sc_ab.ab_bx[i].bx_xfer) 184 1.7 skrll usbd_destroy_xfer(sc->sc_ab.ab_bx[i].bx_xfer); 185 1.7 skrll } 186 1.7 skrll 187 1.1 jmcneill auvitek_dtv_close_pipes(sc); 188 1.3 jmcneill 189 1.3 jmcneill sc->sc_dtvsubmitcb = NULL; 190 1.3 jmcneill sc->sc_dtvsubmitarg = NULL; 191 1.1 jmcneill } 192 1.1 jmcneill 193 1.1 jmcneill static int 194 1.1 jmcneill auvitek_dtv_set_tuner(void *priv, const struct dvb_frontend_parameters *params) 195 1.1 jmcneill { 196 1.1 jmcneill struct auvitek_softc *sc = priv; 197 1.1 jmcneill int error; 198 1.1 jmcneill 199 1.1 jmcneill error = au8522_set_modulation(sc->sc_au8522, params->u.vsb.modulation); 200 1.1 jmcneill if (error) 201 1.1 jmcneill return error; 202 1.1 jmcneill 203 1.1 jmcneill delay(100000); 204 1.1 jmcneill 205 1.1 jmcneill au8522_set_gate(sc->sc_au8522, true); 206 1.1 jmcneill error = xc5k_tune_dtv(sc->sc_xc5k, params); 207 1.1 jmcneill au8522_set_gate(sc->sc_au8522, false); 208 1.1 jmcneill 209 1.1 jmcneill return error; 210 1.1 jmcneill } 211 1.1 jmcneill 212 1.1 jmcneill fe_status_t 213 1.1 jmcneill auvitek_dtv_get_status(void *priv) 214 1.1 jmcneill { 215 1.1 jmcneill struct auvitek_softc *sc = priv; 216 1.1 jmcneill 217 1.1 jmcneill return au8522_get_dtv_status(sc->sc_au8522); 218 1.1 jmcneill } 219 1.1 jmcneill 220 1.1 jmcneill uint16_t 221 1.1 jmcneill auvitek_dtv_get_signal_strength(void *priv) 222 1.1 jmcneill { 223 1.2 jmcneill return auvitek_dtv_get_snr(priv); 224 1.1 jmcneill } 225 1.1 jmcneill 226 1.1 jmcneill uint16_t 227 1.1 jmcneill auvitek_dtv_get_snr(void *priv) 228 1.1 jmcneill { 229 1.2 jmcneill struct auvitek_softc *sc = priv; 230 1.2 jmcneill 231 1.2 jmcneill return au8522_get_snr(sc->sc_au8522); 232 1.1 jmcneill } 233 1.1 jmcneill 234 1.1 jmcneill static int 235 1.3 jmcneill auvitek_dtv_start_transfer(void *priv, 236 1.3 jmcneill void (*cb)(void *, const struct dtv_payload *), void *arg) 237 1.1 jmcneill { 238 1.1 jmcneill struct auvitek_softc *sc = priv; 239 1.1 jmcneill int s; 240 1.1 jmcneill 241 1.1 jmcneill if (sc->sc_ab.ab_running) { 242 1.1 jmcneill return 0; 243 1.1 jmcneill } 244 1.1 jmcneill 245 1.3 jmcneill sc->sc_dtvsubmitcb = cb; 246 1.3 jmcneill sc->sc_dtvsubmitarg = arg; 247 1.3 jmcneill 248 1.1 jmcneill auvitek_write_1(sc, 0x608, 0x90); 249 1.1 jmcneill auvitek_write_1(sc, 0x609, 0x72); 250 1.1 jmcneill auvitek_write_1(sc, 0x60a, 0x71); 251 1.1 jmcneill auvitek_write_1(sc, 0x60b, 0x01); 252 1.1 jmcneill 253 1.1 jmcneill sc->sc_ab.ab_running = true; 254 1.1 jmcneill 255 1.1 jmcneill s = splusb(); 256 1.1 jmcneill auvitek_dtv_bulk_start(sc); 257 1.1 jmcneill splx(s); 258 1.1 jmcneill 259 1.1 jmcneill return 0; 260 1.1 jmcneill } 261 1.1 jmcneill 262 1.1 jmcneill static int 263 1.1 jmcneill auvitek_dtv_stop_transfer(void *priv) 264 1.1 jmcneill { 265 1.1 jmcneill struct auvitek_softc *sc = priv; 266 1.1 jmcneill 267 1.1 jmcneill sc->sc_ab.ab_running = false; 268 1.1 jmcneill 269 1.1 jmcneill auvitek_write_1(sc, 0x608, 0x00); 270 1.1 jmcneill auvitek_write_1(sc, 0x609, 0x00); 271 1.1 jmcneill auvitek_write_1(sc, 0x60a, 0x00); 272 1.1 jmcneill auvitek_write_1(sc, 0x60b, 0x00); 273 1.1 jmcneill 274 1.1 jmcneill return 0; 275 1.1 jmcneill } 276 1.1 jmcneill 277 1.1 jmcneill static int 278 1.1 jmcneill auvitek_dtv_init_pipes(struct auvitek_softc *sc) 279 1.1 jmcneill { 280 1.1 jmcneill usbd_status err; 281 1.1 jmcneill 282 1.5 jmcneill KERNEL_LOCK(1, curlwp); 283 1.1 jmcneill err = usbd_open_pipe(sc->sc_bulk_iface, sc->sc_ab.ab_endpt, 284 1.6 jmcneill USBD_EXCLUSIVE_USE|USBD_MPSAFE, &sc->sc_ab.ab_pipe); 285 1.5 jmcneill KERNEL_UNLOCK_ONE(curlwp); 286 1.5 jmcneill 287 1.1 jmcneill if (err) { 288 1.1 jmcneill aprint_error_dev(sc->sc_dev, "couldn't open bulk-in pipe: %s\n", 289 1.1 jmcneill usbd_errstr(err)); 290 1.1 jmcneill return ENOMEM; 291 1.1 jmcneill } 292 1.1 jmcneill 293 1.1 jmcneill return 0; 294 1.1 jmcneill } 295 1.1 jmcneill 296 1.1 jmcneill static int 297 1.7 skrll auvitek_dtv_abort_pipes(struct auvitek_softc *sc) 298 1.7 skrll { 299 1.7 skrll if (sc->sc_ab.ab_pipe != NULL) { 300 1.7 skrll KERNEL_LOCK(1, curlwp); 301 1.7 skrll usbd_abort_pipe(sc->sc_ab.ab_pipe); 302 1.7 skrll KERNEL_UNLOCK_ONE(curlwp); 303 1.7 skrll } 304 1.7 skrll 305 1.7 skrll return 0; 306 1.7 skrll } 307 1.7 skrll 308 1.7 skrll static int 309 1.1 jmcneill auvitek_dtv_close_pipes(struct auvitek_softc *sc) 310 1.1 jmcneill { 311 1.1 jmcneill if (sc->sc_ab.ab_pipe != NULL) { 312 1.5 jmcneill KERNEL_LOCK(1, curlwp); 313 1.1 jmcneill usbd_close_pipe(sc->sc_ab.ab_pipe); 314 1.5 jmcneill KERNEL_UNLOCK_ONE(curlwp); 315 1.1 jmcneill sc->sc_ab.ab_pipe = NULL; 316 1.1 jmcneill } 317 1.1 jmcneill 318 1.1 jmcneill return 0; 319 1.1 jmcneill } 320 1.1 jmcneill 321 1.1 jmcneill static void 322 1.7 skrll auvitek_dtv_bulk_cb(struct usbd_xfer *xfer, void *priv, 323 1.1 jmcneill usbd_status status) 324 1.1 jmcneill { 325 1.1 jmcneill struct auvitek_bulk_xfer *bx = priv; 326 1.1 jmcneill struct auvitek_softc *sc = bx->bx_sc; 327 1.1 jmcneill struct auvitek_bulk *ab = &sc->sc_ab; 328 1.1 jmcneill struct dtv_payload payload; 329 1.1 jmcneill uint32_t xferlen; 330 1.1 jmcneill 331 1.3 jmcneill if (ab->ab_running == false || sc->sc_dtvsubmitcb == NULL) 332 1.1 jmcneill return; 333 1.1 jmcneill 334 1.1 jmcneill usbd_get_xfer_status(xfer, NULL, NULL, &xferlen, NULL); 335 1.1 jmcneill 336 1.1 jmcneill //printf("%s: status=%d xferlen=%u\n", __func__, status, xferlen); 337 1.1 jmcneill 338 1.1 jmcneill if (status != USBD_NORMAL_COMPLETION) { 339 1.1 jmcneill printf("%s: USB error (%s)\n", __func__, usbd_errstr(status)); 340 1.1 jmcneill if (status == USBD_STALLED) { 341 1.1 jmcneill usbd_clear_endpoint_stall_async(ab->ab_pipe); 342 1.1 jmcneill goto next; 343 1.1 jmcneill } 344 1.1 jmcneill if (status == USBD_SHORT_XFER) { 345 1.1 jmcneill goto next; 346 1.1 jmcneill } 347 1.1 jmcneill return; 348 1.1 jmcneill } 349 1.1 jmcneill 350 1.1 jmcneill if (xferlen == 0) { 351 1.1 jmcneill printf("%s: 0-length xfer\n", __func__); 352 1.1 jmcneill goto next; 353 1.1 jmcneill } 354 1.1 jmcneill 355 1.1 jmcneill payload.data = bx->bx_buffer; 356 1.1 jmcneill payload.size = xferlen; 357 1.3 jmcneill sc->sc_dtvsubmitcb(sc->sc_dtvsubmitarg, &payload); 358 1.1 jmcneill 359 1.1 jmcneill next: 360 1.1 jmcneill auvitek_dtv_bulk_start1(bx); 361 1.1 jmcneill } 362 1.1 jmcneill 363 1.1 jmcneill static int 364 1.1 jmcneill auvitek_dtv_bulk_start(struct auvitek_softc *sc) 365 1.1 jmcneill { 366 1.1 jmcneill int i, error; 367 1.1 jmcneill 368 1.1 jmcneill for (i = 0; i < AUVITEK_NBULK_XFERS; i++) { 369 1.1 jmcneill error = auvitek_dtv_bulk_start1(&sc->sc_ab.ab_bx[i]); 370 1.1 jmcneill if (error) 371 1.1 jmcneill return error; 372 1.1 jmcneill } 373 1.1 jmcneill 374 1.1 jmcneill return 0; 375 1.1 jmcneill } 376 1.1 jmcneill 377 1.1 jmcneill static int 378 1.1 jmcneill auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *bx) 379 1.1 jmcneill { 380 1.1 jmcneill struct auvitek_softc *sc = bx->bx_sc; 381 1.7 skrll usbd_status err; 382 1.1 jmcneill 383 1.7 skrll usbd_setup_xfer(bx->bx_xfer, bx, bx->bx_buffer, AUVITEK_BULK_BUFLEN, 384 1.7 skrll 0 /* USBD_SHORT_XFER_OK */, 100 /* USBD_NO_TIMEOUT */, 385 1.1 jmcneill auvitek_dtv_bulk_cb); 386 1.5 jmcneill 387 1.5 jmcneill KERNEL_LOCK(1, curlwp); 388 1.1 jmcneill err = usbd_transfer(bx->bx_xfer); 389 1.5 jmcneill KERNEL_UNLOCK_ONE(curlwp); 390 1.5 jmcneill 391 1.1 jmcneill if (err != USBD_IN_PROGRESS) { 392 1.1 jmcneill aprint_error_dev(sc->sc_dev, "USB error: %s\n", 393 1.1 jmcneill usbd_errstr(err)); 394 1.1 jmcneill return ENODEV; 395 1.1 jmcneill } 396 1.1 jmcneill 397 1.1 jmcneill return 0; 398 1.1 jmcneill } 399