1/* $NetBSD$ */ 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$"); 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 54static void auvitek_dtv_get_devinfo(void *, 55 struct dvb_frontend_info *); 56static int auvitek_dtv_open(void *, int); 57static void auvitek_dtv_close(void *); 58static int auvitek_dtv_set_tuner(void *, 59 const struct dvb_frontend_parameters *); 60static fe_status_t auvitek_dtv_get_status(void *); 61static uint16_t auvitek_dtv_get_signal_strength(void *); 62static uint16_t auvitek_dtv_get_snr(void *); 63static int auvitek_dtv_start_transfer(void *, 64 void (*)(void *, const struct dtv_payload *), 65 void *); 66static int auvitek_dtv_stop_transfer(void *); 67 68static int auvitek_dtv_init_pipes(struct auvitek_softc *); 69static int auvitek_dtv_close_pipes(struct auvitek_softc *); 70 71static int auvitek_dtv_bulk_start(struct auvitek_softc *); 72static int auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *); 73static void auvitek_dtv_bulk_cb(usbd_xfer_handle, 74 usbd_private_handle, 75 usbd_status); 76 77static 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 89int 90auvitek_dtv_attach(struct auvitek_softc *sc) 91{ 92 93 auvitek_dtv_rescan(sc, NULL, NULL); 94 95 return (sc->sc_dtvdev != NULL); 96} 97 98int 99auvitek_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 109void 110auvitek_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 123void 124auvitek_dtv_childdet(struct auvitek_softc *sc, device_t child) 125{ 126 if (sc->sc_dtvdev == child) 127 sc->sc_dtvdev = NULL; 128} 129 130static void 131auvitek_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 144static int 145auvitek_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 return auvitek_dtv_init_pipes(sc); 157} 158 159static void 160auvitek_dtv_close(void *priv) 161{ 162 struct auvitek_softc *sc = priv; 163 164 auvitek_dtv_stop_transfer(sc); 165 auvitek_dtv_close_pipes(sc); 166 167 sc->sc_dtvsubmitcb = NULL; 168 sc->sc_dtvsubmitarg = NULL; 169} 170 171static int 172auvitek_dtv_set_tuner(void *priv, const struct dvb_frontend_parameters *params) 173{ 174 struct auvitek_softc *sc = priv; 175 int error; 176 177 error = au8522_set_modulation(sc->sc_au8522, params->u.vsb.modulation); 178 if (error) 179 return error; 180 181 delay(100000); 182 183 au8522_set_gate(sc->sc_au8522, true); 184 error = xc5k_tune_dtv(sc->sc_xc5k, params); 185 au8522_set_gate(sc->sc_au8522, false); 186 187 return error; 188} 189 190fe_status_t 191auvitek_dtv_get_status(void *priv) 192{ 193 struct auvitek_softc *sc = priv; 194 195 return au8522_get_dtv_status(sc->sc_au8522); 196} 197 198uint16_t 199auvitek_dtv_get_signal_strength(void *priv) 200{ 201 return auvitek_dtv_get_snr(priv); 202} 203 204uint16_t 205auvitek_dtv_get_snr(void *priv) 206{ 207 struct auvitek_softc *sc = priv; 208 209 return au8522_get_snr(sc->sc_au8522); 210} 211 212static int 213auvitek_dtv_start_transfer(void *priv, 214 void (*cb)(void *, const struct dtv_payload *), void *arg) 215{ 216 struct auvitek_softc *sc = priv; 217 int s; 218 219 if (sc->sc_ab.ab_running) { 220 return 0; 221 } 222 223 sc->sc_dtvsubmitcb = cb; 224 sc->sc_dtvsubmitarg = arg; 225 226 auvitek_write_1(sc, 0x608, 0x90); 227 auvitek_write_1(sc, 0x609, 0x72); 228 auvitek_write_1(sc, 0x60a, 0x71); 229 auvitek_write_1(sc, 0x60b, 0x01); 230 231 sc->sc_ab.ab_running = true; 232 233 s = splusb(); 234 auvitek_dtv_bulk_start(sc); 235 splx(s); 236 237 return 0; 238} 239 240static int 241auvitek_dtv_stop_transfer(void *priv) 242{ 243 struct auvitek_softc *sc = priv; 244 245 sc->sc_ab.ab_running = false; 246 247 auvitek_write_1(sc, 0x608, 0x00); 248 auvitek_write_1(sc, 0x609, 0x00); 249 auvitek_write_1(sc, 0x60a, 0x00); 250 auvitek_write_1(sc, 0x60b, 0x00); 251 252 return 0; 253} 254 255static int 256auvitek_dtv_init_pipes(struct auvitek_softc *sc) 257{ 258 usbd_status err; 259 260 KERNEL_LOCK(1, curlwp); 261 err = usbd_open_pipe(sc->sc_bulk_iface, sc->sc_ab.ab_endpt, 262 USBD_EXCLUSIVE_USE, &sc->sc_ab.ab_pipe); 263 KERNEL_UNLOCK_ONE(curlwp); 264 265 if (err) { 266 aprint_error_dev(sc->sc_dev, "couldn't open bulk-in pipe: %s\n", 267 usbd_errstr(err)); 268 return ENOMEM; 269 } 270 271 return 0; 272} 273 274static int 275auvitek_dtv_close_pipes(struct auvitek_softc *sc) 276{ 277 if (sc->sc_ab.ab_pipe != NULL) { 278 KERNEL_LOCK(1, curlwp); 279 usbd_abort_pipe(sc->sc_ab.ab_pipe); 280 usbd_close_pipe(sc->sc_ab.ab_pipe); 281 KERNEL_UNLOCK_ONE(curlwp); 282 sc->sc_ab.ab_pipe = NULL; 283 } 284 285 return 0; 286} 287 288static void 289auvitek_dtv_bulk_cb(usbd_xfer_handle xfer, usbd_private_handle priv, 290 usbd_status status) 291{ 292 struct auvitek_bulk_xfer *bx = priv; 293 struct auvitek_softc *sc = bx->bx_sc; 294 struct auvitek_bulk *ab = &sc->sc_ab; 295 struct dtv_payload payload; 296 uint32_t xferlen; 297 298 if (ab->ab_running == false || sc->sc_dtvsubmitcb == NULL) 299 return; 300 301 usbd_get_xfer_status(xfer, NULL, NULL, &xferlen, NULL); 302 303 //printf("%s: status=%d xferlen=%u\n", __func__, status, xferlen); 304 305 if (status != USBD_NORMAL_COMPLETION) { 306 printf("%s: USB error (%s)\n", __func__, usbd_errstr(status)); 307 if (status == USBD_STALLED) { 308 usbd_clear_endpoint_stall_async(ab->ab_pipe); 309 goto next; 310 } 311 if (status == USBD_SHORT_XFER) { 312 goto next; 313 } 314 return; 315 } 316 317 if (xferlen == 0) { 318 printf("%s: 0-length xfer\n", __func__); 319 goto next; 320 } 321 322 payload.data = bx->bx_buffer; 323 payload.size = xferlen; 324 sc->sc_dtvsubmitcb(sc->sc_dtvsubmitarg, &payload); 325 326next: 327 auvitek_dtv_bulk_start1(bx); 328} 329 330static int 331auvitek_dtv_bulk_start(struct auvitek_softc *sc) 332{ 333 int i, error; 334 335 for (i = 0; i < AUVITEK_NBULK_XFERS; i++) { 336 error = auvitek_dtv_bulk_start1(&sc->sc_ab.ab_bx[i]); 337 if (error) 338 return error; 339 } 340 341 return 0; 342} 343 344static int 345auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *bx) 346{ 347 struct auvitek_softc *sc = bx->bx_sc; 348 struct auvitek_bulk *ab = &sc->sc_ab; 349 int err; 350 351 usbd_setup_xfer(bx->bx_xfer, ab->ab_pipe, bx, 352 bx->bx_buffer, AUVITEK_BULK_BUFLEN, 353 //USBD_SHORT_XFER_OK|USBD_NO_COPY, USBD_NO_TIMEOUT, 354 USBD_NO_COPY, 100, 355 auvitek_dtv_bulk_cb); 356 357 KERNEL_LOCK(1, curlwp); 358 err = usbd_transfer(bx->bx_xfer); 359 KERNEL_UNLOCK_ONE(curlwp); 360 361 if (err != USBD_IN_PROGRESS) { 362 aprint_error_dev(sc->sc_dev, "USB error: %s\n", 363 usbd_errstr(err)); 364 return ENODEV; 365 } 366 367 return 0; 368} 369