1 /* $NetBSD: xc5k.c,v 1.6 2015/03/07 14:16:51 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 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 * Xceive XC5000 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: xc5k.c,v 1.6 2015/03/07 14:16:51 jmcneill 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/bus.h> 41 #include <sys/kmem.h> 42 #include <sys/mutex.h> 43 #include <sys/module.h> 44 45 #include <dev/firmload.h> 46 #include <dev/i2c/i2cvar.h> 47 48 #include <dev/i2c/xc5kreg.h> 49 #include <dev/i2c/xc5kvar.h> 50 51 #define XC5K_FIRMWARE_DRVNAME "xc5k" 52 #define XC5K_FIRMWARE_IMGNAME "dvb-fe-xc5000-1.6.114.fw" 53 54 #define XC5K_FREQ_MIN 1000000 55 #define XC5K_FREQ_MAX 1023000000 56 57 static kmutex_t xc5k_firmware_lock; 58 59 static int xc5k_reset(struct xc5k *); 60 static int xc5k_read_2(struct xc5k *, uint16_t, uint16_t *); 61 static int xc5k_write_buffer(struct xc5k *, const uint8_t *, size_t); 62 static int xc5k_firmware_open(struct xc5k *); 63 static int xc5k_firmware_upload(struct xc5k *, const uint8_t *, size_t); 64 65 static int 66 xc5k_reset(struct xc5k *xc) 67 { 68 int error = 0; 69 70 if (xc->reset) 71 error = xc->reset(xc->reset_priv); 72 73 return error; 74 } 75 76 static int 77 xc5k_firmware_upload(struct xc5k *xc, const uint8_t *fw, size_t fwlen) 78 { 79 const uint8_t *p; 80 uint8_t cmd[64]; 81 unsigned int i; 82 uint16_t len, rem; 83 size_t wrlen; 84 int error; 85 86 for (i = 0; i < fwlen - 1;) { 87 len = (fw[i] << 8) | fw[i + 1]; 88 i += 2; 89 if (len == 0xffff) 90 break; 91 92 /* reset command */ 93 if (len == 0x0000) { 94 error = xc5k_reset(xc); 95 if (error) 96 return error; 97 continue; 98 } 99 100 /* delay command */ 101 if (len & 0x8000) { 102 delay((len & 0x7fff) * 1000); 103 continue; 104 } 105 106 if (i + len >= fwlen) 107 break; 108 109 cmd[0] = fw[i]; 110 cmd[1] = fw[i + 1]; 111 p = &fw[i + 2]; 112 rem = len - 2; 113 while (rem > 0) { 114 wrlen = min(rem, __arraycount(cmd) - 2); 115 memcpy(&cmd[2], p, wrlen); 116 error = xc5k_write_buffer(xc, cmd, wrlen + 2); 117 if (error) 118 return error; 119 p += wrlen; 120 rem -= wrlen; 121 } 122 i += len; 123 } 124 125 return 0; 126 } 127 128 static int 129 xc5k_firmware_open(struct xc5k *xc) 130 { 131 firmware_handle_t fwh; 132 uint16_t product_id, xcversion, xcbuild; 133 uint8_t *fw = NULL; 134 size_t fwlen; 135 int error; 136 137 mutex_enter(&xc5k_firmware_lock); 138 139 error = xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id); 140 if (error || product_id != XC5K_PRODUCT_ID_NOFW) 141 goto done; 142 143 error = firmware_open(XC5K_FIRMWARE_DRVNAME, 144 XC5K_FIRMWARE_IMGNAME, &fwh); 145 if (error) 146 goto done; 147 fwlen = firmware_get_size(fwh); 148 fw = firmware_malloc(fwlen); 149 if (fw == NULL) { 150 firmware_close(fwh); 151 error = ENOMEM; 152 goto done; 153 } 154 error = firmware_read(fwh, 0, fw, fwlen); 155 firmware_close(fwh); 156 if (error) 157 goto done; 158 159 aprint_normal_dev(xc->parent, "xc5k: loading firmware '%s/%s'\n", 160 XC5K_FIRMWARE_DRVNAME, XC5K_FIRMWARE_IMGNAME); 161 error = xc5k_firmware_upload(xc, fw, fwlen); 162 if (!error) { 163 xc5k_read_2(xc, XC5K_REG_VERSION, &xcversion); 164 xc5k_read_2(xc, XC5K_REG_BUILD, &xcbuild); 165 if (!error) 166 aprint_normal_dev(xc->parent, 167 "xc5k: hw %d.%d, fw %d.%d.%d\n", 168 (xcversion >> 12) & 0xf, 169 (xcversion >> 8) & 0xf, 170 (xcversion >> 4) & 0xf, 171 xcversion & 0xf, 172 xcbuild); 173 } 174 175 done: 176 if (fw) 177 firmware_free(fw, fwlen); 178 mutex_exit(&xc5k_firmware_lock); 179 180 if (error) 181 aprint_error_dev(xc->parent, 182 "xc5k: couldn't open firmware '%s/%s' (error=%d)\n", 183 XC5K_FIRMWARE_DRVNAME, XC5K_FIRMWARE_IMGNAME, error); 184 185 return error; 186 } 187 188 static int 189 xc5k_read_2(struct xc5k *xc, uint16_t reg, uint16_t *val) 190 { 191 uint8_t cmd[2], resp[2]; 192 int error; 193 194 cmd[0] = reg >> 8; 195 cmd[1] = reg & 0xff; 196 error = iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr, 197 cmd, sizeof(cmd), NULL, 0, 0); 198 if (error) 199 return error; 200 resp[0] = resp[1] = 0; 201 error = iic_exec(xc->i2c, I2C_OP_READ, xc->i2c_addr, 202 NULL, 0, resp, sizeof(resp), 0); 203 if (error) 204 return error; 205 206 *val = (resp[0] << 8) | resp[1]; 207 208 return 0; 209 } 210 211 static int 212 xc5k_write_buffer(struct xc5k *xc, const uint8_t *data, size_t datalen) 213 { 214 return iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr, 215 data, datalen, NULL, 0, 0); 216 } 217 218 static int 219 xc5k_write_2(struct xc5k *xc, uint16_t reg, uint16_t val) 220 { 221 uint8_t data[4]; 222 uint16_t busy; 223 int error, retry; 224 225 data[0] = reg >> 8; 226 data[1] = reg & 0xff; 227 data[2] = val >> 8; 228 data[3] = val & 0xff; 229 error = xc5k_write_buffer(xc, data, sizeof(data)); 230 if (error) 231 return error; 232 233 retry = 1000; 234 while (--retry > 0) { 235 error = xc5k_read_2(xc, XC5K_REG_BUSY, &busy); 236 if (error || !busy) 237 break; 238 delay(5000); 239 } 240 241 return error; 242 } 243 244 struct xc5k * 245 xc5k_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr, 246 xc5k_reset_cb reset, void *reset_priv, unsigned int if_freq, 247 fe_type_t fe_type) 248 { 249 struct xc5k *xc; 250 uint16_t product_id; 251 252 xc = kmem_alloc(sizeof(*xc), KM_SLEEP); 253 if (xc == NULL) 254 return NULL; 255 xc->parent = parent; 256 xc->i2c = i2c; 257 xc->i2c_addr = addr; 258 xc->reset = reset; 259 xc->reset_priv = reset_priv; 260 xc->if_freq = if_freq; 261 xc->fe_type = fe_type; 262 263 if (xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id)) 264 goto failed; 265 266 aprint_debug_dev(parent, "xc5k: product=0x%04x\n", product_id); 267 268 if (product_id != XC5K_PRODUCT_ID_NOFW && product_id != XC5K_PRODUCT_ID) 269 goto failed; 270 271 if (xc5k_firmware_open(xc)) 272 goto failed; 273 if (xc5k_write_2(xc, XC5K_REG_INIT, 0)) 274 goto failed; 275 delay(100000); 276 277 if (xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id)) 278 goto failed; 279 280 aprint_debug_dev(parent, "xc5k: product=0x%04x\n", product_id); 281 282 return xc; 283 284 failed: 285 kmem_free(xc, sizeof(*xc)); 286 return NULL; 287 } 288 289 void 290 xc5k_close(struct xc5k *xc) 291 { 292 kmem_free(xc, sizeof(*xc)); 293 } 294 295 int 296 xc5k_tune_video(struct xc5k *xc, struct xc5k_params *params) 297 { 298 uint16_t amode, vmode; 299 uint16_t lock, freq; 300 int retry; 301 302 switch (params->standard) { 303 case VIDEO_STANDARD_NTSC_M: 304 case VIDEO_STANDARD_NTSC_M_JP: 305 case VIDEO_STANDARD_NTSC_M_KR: 306 amode = XC5K_AUDIO_MODE_BTSC; 307 vmode = XC5K_VIDEO_MODE_BTSC; 308 break; 309 default: 310 return EINVAL; 311 } 312 313 if (xc5k_write_2(xc, XC5K_REG_SIGNAL_SOURCE, params->signal_source)) 314 return EIO; 315 if (xc5k_write_2(xc, XC5K_REG_VIDEO_MODE, vmode)) 316 return EIO; 317 if (xc5k_write_2(xc, XC5K_REG_AUDIO_MODE, amode)) 318 return EIO; 319 if (xc5k_write_2(xc, XC5K_REG_OUTAMP, XC5K_OUTAMP_ANALOG)) 320 return EIO; 321 freq = (params->frequency * 62500) / 15625; 322 #ifdef XC5K_DEBUG 323 printf("xc5k_tune_video: frequency=%u (%u Hz)\n", params->frequency, 324 params->frequency * 62500); 325 printf(" freq=%u\n", freq); 326 #endif 327 if (xc5k_write_2(xc, XC5K_REG_FINER_FREQ, freq)) 328 return EIO; 329 330 retry = 100; 331 while (--retry > 0) { 332 if (xc5k_read_2(xc, XC5K_REG_LOCK, &lock)) 333 return EIO; 334 #ifdef XC5K_DEBUG 335 printf("xc5k_tune_video: lock=0x%04x\n", lock); 336 #endif 337 if (lock == 1) 338 break; 339 delay(5000); 340 } 341 342 return 0; 343 } 344 345 int 346 xc5k_tune_dtv(struct xc5k *xc, const struct dvb_frontend_parameters *params) 347 { 348 uint16_t amode, vmode; 349 uint32_t freq, ifout; 350 int signal_source; 351 fe_modulation_t modulation; 352 353 if (xc->fe_type == FE_ATSC) 354 modulation = params->u.vsb.modulation; 355 else if (xc->fe_type == FE_QAM) 356 modulation = params->u.qam.modulation; 357 else 358 return EINVAL; 359 360 switch (modulation) { 361 case VSB_8: 362 case VSB_16: 363 signal_source = XC5K_SIGNAL_SOURCE_AIR; 364 switch (xc->fe_type) { 365 case FE_ATSC: 366 amode = XC5K_AUDIO_MODE_DTV6; 367 vmode = XC5K_VIDEO_MODE_DTV6; 368 freq = params->frequency - 1750000; 369 break; 370 default: 371 return EINVAL; 372 } 373 break; 374 case QAM_16: 375 case QAM_32: 376 case QAM_64: 377 case QAM_128: 378 case QAM_256: 379 signal_source = XC5K_SIGNAL_SOURCE_CABLE; 380 switch (xc->fe_type) { 381 case FE_ATSC: 382 amode = XC5K_AUDIO_MODE_DTV6; 383 vmode = XC5K_VIDEO_MODE_DTV6; 384 freq = params->frequency - 1750000; 385 break; 386 case FE_QAM: 387 amode = XC5K_AUDIO_MODE_DTV78; 388 vmode = XC5K_VIDEO_MODE_DTV78; 389 freq = params->frequency - 2750000; 390 break; 391 default: 392 return EINVAL; 393 } 394 break; 395 default: 396 return EINVAL; 397 } 398 399 if (freq > XC5K_FREQ_MAX || freq < XC5K_FREQ_MIN) 400 return ERANGE; 401 402 if (xc5k_write_2(xc, XC5K_REG_SIGNAL_SOURCE, signal_source)) 403 return EIO; 404 if (xc5k_write_2(xc, XC5K_REG_VIDEO_MODE, vmode)) 405 return EIO; 406 if (xc5k_write_2(xc, XC5K_REG_AUDIO_MODE, amode)) 407 return EIO; 408 ifout = ((xc->if_freq / 1000) * 1024) / 1000; 409 if (xc5k_write_2(xc, XC5K_REG_IF_OUT, ifout)) 410 return EIO; 411 if (xc5k_write_2(xc, XC5K_REG_OUTAMP, XC5K_OUTAMP_DIGITAL)) 412 return EIO; 413 freq = (uint16_t)(freq / 15625); 414 if (xc5k_write_2(xc, XC5K_REG_FINER_FREQ, freq)) 415 return EIO; 416 417 return 0; 418 } 419 420 fe_status_t 421 xc5k_get_status(struct xc5k *xc) 422 { 423 uint16_t lock_status; 424 fe_status_t festatus = 0; 425 426 if (xc5k_read_2(xc, XC5K_REG_LOCK, &lock_status)) 427 return 0; 428 if (lock_status & XC5K_LOCK_LOCKED) { 429 festatus |= FE_HAS_LOCK; 430 if ((lock_status & XC5K_LOCK_NOSIGNAL) == 0) 431 festatus |= FE_HAS_SIGNAL; 432 } 433 434 return festatus; 435 } 436 437 MODULE(MODULE_CLASS_DRIVER, xc5k, "i2cexec"); 438 439 static int 440 xc5k_modcmd(modcmd_t cmd, void *opaque) 441 { 442 switch (cmd) { 443 case MODULE_CMD_INIT: 444 mutex_init(&xc5k_firmware_lock, MUTEX_DEFAULT, IPL_NONE); 445 return 0; 446 case MODULE_CMD_FINI: 447 mutex_destroy(&xc5k_firmware_lock); 448 return 0; 449 default: 450 return ENOTTY; 451 } 452 } 453