1 /* $NetBSD: auvitek_dtv.c,v 1.7 2016/04/23 10:15:31 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 Jared D. McNeill <jmcneill@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 (Digital TV function) 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: auvitek_dtv.c,v 1.7 2016/04/23 10:15:31 skrll 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/dtv/dtvif.h> 50 51 #include <dev/usb/auvitekreg.h> 52 #include <dev/usb/auvitekvar.h> 53 54 static void auvitek_dtv_get_devinfo(void *, 55 struct dvb_frontend_info *); 56 static int auvitek_dtv_open(void *, int); 57 static void auvitek_dtv_close(void *); 58 static int auvitek_dtv_set_tuner(void *, 59 const struct dvb_frontend_parameters *); 60 static fe_status_t auvitek_dtv_get_status(void *); 61 static uint16_t auvitek_dtv_get_signal_strength(void *); 62 static uint16_t auvitek_dtv_get_snr(void *); 63 static int auvitek_dtv_start_transfer(void *, 64 void (*)(void *, const struct dtv_payload *), 65 void *); 66 static int auvitek_dtv_stop_transfer(void *); 67 68 static int auvitek_dtv_init_pipes(struct auvitek_softc *); 69 static int auvitek_dtv_abort_pipes(struct auvitek_softc *); 70 static int auvitek_dtv_close_pipes(struct auvitek_softc *); 71 72 static int auvitek_dtv_bulk_start(struct auvitek_softc *); 73 static int auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *); 74 static void auvitek_dtv_bulk_cb(struct usbd_xfer *, void *, 75 usbd_status); 76 77 static const struct dtv_hw_if auvitek_dtv_if = { 78 .get_devinfo = auvitek_dtv_get_devinfo, 79 .open = auvitek_dtv_open, 80 .close = auvitek_dtv_close, 81 .set_tuner = auvitek_dtv_set_tuner, 82 .get_status = auvitek_dtv_get_status, 83 .get_signal_strength = auvitek_dtv_get_signal_strength, 84 .get_snr = auvitek_dtv_get_snr, 85 .start_transfer = auvitek_dtv_start_transfer, 86 .stop_transfer = auvitek_dtv_stop_transfer, 87 }; 88 89 int 90 auvitek_dtv_attach(struct auvitek_softc *sc) 91 { 92 93 auvitek_dtv_rescan(sc, NULL, NULL); 94 95 return sc->sc_dtvdev != NULL; 96 } 97 98 int 99 auvitek_dtv_detach(struct auvitek_softc *sc, int flags) 100 { 101 if (sc->sc_dtvdev != NULL) { 102 config_detach(sc->sc_dtvdev, flags); 103 sc->sc_dtvdev = NULL; 104 } 105 106 return 0; 107 } 108 109 void 110 auvitek_dtv_rescan(struct auvitek_softc *sc, const char *ifattr, 111 const int *locs) 112 { 113 struct dtv_attach_args daa; 114 115 daa.hw = &auvitek_dtv_if; 116 daa.priv = sc; 117 118 if (ifattr_match(ifattr, "dtvbus") && sc->sc_dtvdev == NULL) 119 sc->sc_dtvdev = config_found_ia(sc->sc_dev, "dtvbus", 120 &daa, dtv_print); 121 } 122 123 void 124 auvitek_dtv_childdet(struct auvitek_softc *sc, device_t child) 125 { 126 if (sc->sc_dtvdev == child) 127 sc->sc_dtvdev = NULL; 128 } 129 130 static void 131 auvitek_dtv_get_devinfo(void *priv, struct dvb_frontend_info *info) 132 { 133 struct auvitek_softc *sc = priv; 134 135 memset(info, 0, sizeof(*info)); 136 strlcpy(info->name, sc->sc_descr, sizeof(info->name)); 137 info->type = FE_ATSC; 138 info->frequency_min = 54000000; 139 info->frequency_max = 858000000; 140 info->frequency_stepsize = 62500; 141 info->caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB; 142 } 143 144 static int 145 auvitek_dtv_open(void *priv, int flags) 146 { 147 struct auvitek_softc *sc = priv; 148 149 if (sc->sc_dying) 150 return EIO; 151 152 auvitek_attach_tuner(sc->sc_dev); 153 if (sc->sc_xc5k == NULL) 154 return ENXIO; 155 156 int err = auvitek_dtv_init_pipes(sc); 157 if (err) 158 return err; 159 160 for (size_t i = 0; i < AUVITEK_NBULK_XFERS; i++) { 161 sc->sc_ab.ab_bx[i].bx_sc = sc; 162 err = usbd_create_xfer(sc->sc_ab.ab_pipe, 163 AUVITEK_BULK_BUFLEN, 0, 0, &sc->sc_ab.ab_bx[i].bx_xfer); 164 if (err) { 165 aprint_error_dev(sc->sc_dev, 166 "couldn't allocate xfer\n"); 167 sc->sc_dying = 1; 168 return err; 169 } 170 sc->sc_ab.ab_bx[i].bx_buffer = usbd_get_buffer( 171 sc->sc_ab.ab_bx[i].bx_xfer); 172 } 173 174 175 return 0; 176 } 177 178 static void 179 auvitek_dtv_close(void *priv) 180 { 181 struct auvitek_softc *sc = priv; 182 183 auvitek_dtv_stop_transfer(sc); 184 auvitek_dtv_abort_pipes(sc); 185 186 for (size_t i = 0; i < AUVITEK_NBULK_XFERS; i++) { 187 if (sc->sc_ab.ab_bx[i].bx_xfer) 188 usbd_destroy_xfer(sc->sc_ab.ab_bx[i].bx_xfer); 189 } 190 191 auvitek_dtv_close_pipes(sc); 192 193 sc->sc_dtvsubmitcb = NULL; 194 sc->sc_dtvsubmitarg = NULL; 195 } 196 197 static int 198 auvitek_dtv_set_tuner(void *priv, const struct dvb_frontend_parameters *params) 199 { 200 struct auvitek_softc *sc = priv; 201 int error; 202 203 error = au8522_set_modulation(sc->sc_au8522, params->u.vsb.modulation); 204 if (error) 205 return error; 206 207 delay(100000); 208 209 au8522_set_gate(sc->sc_au8522, true); 210 error = xc5k_tune_dtv(sc->sc_xc5k, params); 211 au8522_set_gate(sc->sc_au8522, false); 212 213 return error; 214 } 215 216 fe_status_t 217 auvitek_dtv_get_status(void *priv) 218 { 219 struct auvitek_softc *sc = priv; 220 221 return au8522_get_dtv_status(sc->sc_au8522); 222 } 223 224 uint16_t 225 auvitek_dtv_get_signal_strength(void *priv) 226 { 227 return auvitek_dtv_get_snr(priv); 228 } 229 230 uint16_t 231 auvitek_dtv_get_snr(void *priv) 232 { 233 struct auvitek_softc *sc = priv; 234 235 return au8522_get_snr(sc->sc_au8522); 236 } 237 238 static int 239 auvitek_dtv_start_transfer(void *priv, 240 void (*cb)(void *, const struct dtv_payload *), void *arg) 241 { 242 struct auvitek_softc *sc = priv; 243 int s; 244 245 if (sc->sc_ab.ab_running) { 246 return 0; 247 } 248 249 sc->sc_dtvsubmitcb = cb; 250 sc->sc_dtvsubmitarg = arg; 251 252 auvitek_write_1(sc, 0x608, 0x90); 253 auvitek_write_1(sc, 0x609, 0x72); 254 auvitek_write_1(sc, 0x60a, 0x71); 255 auvitek_write_1(sc, 0x60b, 0x01); 256 257 sc->sc_ab.ab_running = true; 258 259 s = splusb(); 260 auvitek_dtv_bulk_start(sc); 261 splx(s); 262 263 return 0; 264 } 265 266 static int 267 auvitek_dtv_stop_transfer(void *priv) 268 { 269 struct auvitek_softc *sc = priv; 270 271 sc->sc_ab.ab_running = false; 272 273 auvitek_write_1(sc, 0x608, 0x00); 274 auvitek_write_1(sc, 0x609, 0x00); 275 auvitek_write_1(sc, 0x60a, 0x00); 276 auvitek_write_1(sc, 0x60b, 0x00); 277 278 return 0; 279 } 280 281 static int 282 auvitek_dtv_init_pipes(struct auvitek_softc *sc) 283 { 284 usbd_status err; 285 286 KERNEL_LOCK(1, curlwp); 287 err = usbd_open_pipe(sc->sc_bulk_iface, sc->sc_ab.ab_endpt, 288 USBD_EXCLUSIVE_USE|USBD_MPSAFE, &sc->sc_ab.ab_pipe); 289 KERNEL_UNLOCK_ONE(curlwp); 290 291 if (err) { 292 aprint_error_dev(sc->sc_dev, "couldn't open bulk-in pipe: %s\n", 293 usbd_errstr(err)); 294 return ENOMEM; 295 } 296 297 return 0; 298 } 299 300 static int 301 auvitek_dtv_abort_pipes(struct auvitek_softc *sc) 302 { 303 if (sc->sc_ab.ab_pipe != NULL) { 304 KERNEL_LOCK(1, curlwp); 305 usbd_abort_pipe(sc->sc_ab.ab_pipe); 306 KERNEL_UNLOCK_ONE(curlwp); 307 } 308 309 return 0; 310 } 311 312 static int 313 auvitek_dtv_close_pipes(struct auvitek_softc *sc) 314 { 315 if (sc->sc_ab.ab_pipe != NULL) { 316 KERNEL_LOCK(1, curlwp); 317 usbd_close_pipe(sc->sc_ab.ab_pipe); 318 KERNEL_UNLOCK_ONE(curlwp); 319 sc->sc_ab.ab_pipe = NULL; 320 } 321 322 return 0; 323 } 324 325 static void 326 auvitek_dtv_bulk_cb(struct usbd_xfer *xfer, void *priv, 327 usbd_status status) 328 { 329 struct auvitek_bulk_xfer *bx = priv; 330 struct auvitek_softc *sc = bx->bx_sc; 331 struct auvitek_bulk *ab = &sc->sc_ab; 332 struct dtv_payload payload; 333 uint32_t xferlen; 334 335 if (ab->ab_running == false || sc->sc_dtvsubmitcb == NULL) 336 return; 337 338 usbd_get_xfer_status(xfer, NULL, NULL, &xferlen, NULL); 339 340 //printf("%s: status=%d xferlen=%u\n", __func__, status, xferlen); 341 342 if (status != USBD_NORMAL_COMPLETION) { 343 printf("%s: USB error (%s)\n", __func__, usbd_errstr(status)); 344 if (status == USBD_STALLED) { 345 usbd_clear_endpoint_stall_async(ab->ab_pipe); 346 goto next; 347 } 348 if (status == USBD_SHORT_XFER) { 349 goto next; 350 } 351 return; 352 } 353 354 if (xferlen == 0) { 355 printf("%s: 0-length xfer\n", __func__); 356 goto next; 357 } 358 359 payload.data = bx->bx_buffer; 360 payload.size = xferlen; 361 sc->sc_dtvsubmitcb(sc->sc_dtvsubmitarg, &payload); 362 363 next: 364 auvitek_dtv_bulk_start1(bx); 365 } 366 367 static int 368 auvitek_dtv_bulk_start(struct auvitek_softc *sc) 369 { 370 int i, error; 371 372 for (i = 0; i < AUVITEK_NBULK_XFERS; i++) { 373 error = auvitek_dtv_bulk_start1(&sc->sc_ab.ab_bx[i]); 374 if (error) 375 return error; 376 } 377 378 return 0; 379 } 380 381 static int 382 auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *bx) 383 { 384 struct auvitek_softc *sc = bx->bx_sc; 385 usbd_status err; 386 387 usbd_setup_xfer(bx->bx_xfer, bx, bx->bx_buffer, AUVITEK_BULK_BUFLEN, 388 0 /* USBD_SHORT_XFER_OK */, 100 /* USBD_NO_TIMEOUT */, 389 auvitek_dtv_bulk_cb); 390 391 KERNEL_LOCK(1, curlwp); 392 err = usbd_transfer(bx->bx_xfer); 393 KERNEL_UNLOCK_ONE(curlwp); 394 395 if (err != USBD_IN_PROGRESS) { 396 aprint_error_dev(sc->sc_dev, "USB error: %s\n", 397 usbd_errstr(err)); 398 return ENODEV; 399 } 400 401 return 0; 402 } 403