1 /* $NetBSD: xc5k.c,v 1.2 2010/12/28 00:11:50 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.2 2010/12/28 00:11:50 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 static kmutex_t xc5k_firmware_lock; 55 56 static int xc5k_reset(struct xc5k *); 57 static int xc5k_read_2(struct xc5k *, uint16_t, uint16_t *); 58 static int xc5k_write_buffer(struct xc5k *, const uint8_t *, size_t); 59 static int xc5k_firmware_open(struct xc5k *); 60 static int xc5k_firmware_upload(struct xc5k *, const uint8_t *, size_t); 61 62 static int 63 xc5k_reset(struct xc5k *xc) 64 { 65 int error = 0; 66 67 if (xc->reset) 68 error = xc->reset(xc->reset_priv); 69 70 return error; 71 } 72 73 static int 74 xc5k_firmware_upload(struct xc5k *xc, const uint8_t *fw, size_t fwlen) 75 { 76 const uint8_t *p; 77 uint8_t cmd[64]; 78 unsigned int i; 79 uint16_t len, rem; 80 size_t wrlen; 81 int error; 82 83 for (i = 0; i < fwlen - 1;) { 84 len = (fw[i] << 8) | fw[i + 1]; 85 i += 2; 86 if (len == 0xffff) 87 break; 88 89 /* reset command */ 90 if (len == 0x0000) { 91 error = xc5k_reset(xc); 92 if (error) 93 return error; 94 continue; 95 } 96 97 /* delay command */ 98 if (len & 0x8000) { 99 delay((len & 0x7fff) * 1000); 100 continue; 101 } 102 103 if (i + len >= fwlen) 104 break; 105 106 cmd[0] = fw[i]; 107 cmd[1] = fw[i + 1]; 108 p = &fw[i + 2]; 109 rem = len - 2; 110 while (rem > 0) { 111 wrlen = min(rem, __arraycount(cmd) - 2); 112 memcpy(&cmd[2], p, wrlen); 113 error = xc5k_write_buffer(xc, cmd, wrlen + 2); 114 if (error) 115 return error; 116 p += wrlen; 117 rem -= wrlen; 118 } 119 i += len; 120 } 121 122 return 0; 123 } 124 125 static int 126 xc5k_firmware_open(struct xc5k *xc) 127 { 128 firmware_handle_t fwh; 129 uint16_t product_id; 130 uint8_t *fw = NULL; 131 size_t fwlen; 132 int error; 133 134 mutex_enter(&xc5k_firmware_lock); 135 136 error = xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id); 137 if (error || product_id != XC5K_PRODUCT_ID_NOFW) 138 goto done; 139 140 error = firmware_open(XC5K_FIRMWARE_DRVNAME, 141 XC5K_FIRMWARE_IMGNAME, &fwh); 142 if (error) 143 goto done; 144 fwlen = firmware_get_size(fwh); 145 fw = firmware_malloc(fwlen); 146 if (fw == NULL) { 147 firmware_close(fwh); 148 error = ENOMEM; 149 goto done; 150 } 151 error = firmware_read(fwh, 0, fw, fwlen); 152 firmware_close(fwh); 153 if (error) 154 goto done; 155 156 aprint_normal_dev(xc->parent, "xc5k: loading firmware '%s/%s'\n", 157 XC5K_FIRMWARE_DRVNAME, XC5K_FIRMWARE_IMGNAME); 158 error = xc5k_firmware_upload(xc, fw, fwlen); 159 160 done: 161 if (fw) 162 firmware_free(fw, 0); 163 mutex_exit(&xc5k_firmware_lock); 164 165 if (error) 166 aprint_error_dev(xc->parent, 167 "xc5k: couldn't open firmware '%s/%s' (error=%d)\n", 168 XC5K_FIRMWARE_DRVNAME, XC5K_FIRMWARE_IMGNAME, error); 169 170 return error; 171 } 172 173 static int 174 xc5k_read_2(struct xc5k *xc, uint16_t reg, uint16_t *val) 175 { 176 uint8_t cmd[2], resp[2]; 177 int error; 178 179 cmd[0] = reg >> 8; 180 cmd[1] = reg & 0xff; 181 error = iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr, 182 cmd, sizeof(cmd), NULL, 0, 0); 183 if (error) 184 return error; 185 resp[0] = resp[1] = 0; 186 error = iic_exec(xc->i2c, I2C_OP_READ, xc->i2c_addr, 187 NULL, 0, resp, sizeof(resp), 0); 188 if (error) 189 return error; 190 191 *val = (resp[0] << 8) | resp[1]; 192 193 return 0; 194 } 195 196 static int 197 xc5k_write_buffer(struct xc5k *xc, const uint8_t *data, size_t datalen) 198 { 199 return iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr, 200 data, datalen, NULL, 0, 0); 201 } 202 203 static int 204 xc5k_write_2(struct xc5k *xc, uint16_t reg, uint16_t val) 205 { 206 uint8_t data[4]; 207 uint16_t busy; 208 int error, retry; 209 210 data[0] = reg >> 8; 211 data[1] = reg & 0xff; 212 data[2] = val >> 8; 213 data[3] = val & 0xff; 214 error = xc5k_write_buffer(xc, data, sizeof(data)); 215 if (error) 216 return error; 217 218 retry = 1000; 219 while (--retry > 0) { 220 error = xc5k_read_2(xc, XC5K_REG_BUSY, &busy); 221 if (error || !busy) 222 break; 223 delay(5000); 224 } 225 226 return error; 227 } 228 229 struct xc5k * 230 xc5k_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr, 231 xc5k_reset_cb reset, void *reset_priv) 232 { 233 struct xc5k *xc; 234 uint16_t product_id; 235 236 xc = kmem_alloc(sizeof(*xc), KM_SLEEP); 237 if (xc == NULL) 238 return NULL; 239 xc->parent = parent; 240 xc->i2c = i2c; 241 xc->i2c_addr = addr; 242 xc->reset = reset; 243 xc->reset_priv = reset_priv; 244 245 if (xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id)) 246 goto failed; 247 248 aprint_debug_dev(parent, "xc5k: product=0x%04x\n", product_id); 249 250 if (product_id != XC5K_PRODUCT_ID_NOFW && product_id != XC5K_PRODUCT_ID) 251 goto failed; 252 253 if (xc5k_firmware_open(xc)) 254 goto failed; 255 if (xc5k_write_2(xc, XC5K_REG_INIT, 0)) 256 goto failed; 257 delay(100000); 258 259 if (xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id)) 260 goto failed; 261 262 aprint_debug_dev(parent, "xc5k: product=0x%04x\n", product_id); 263 264 return xc; 265 266 failed: 267 kmem_free(xc, sizeof(*xc)); 268 return NULL; 269 } 270 271 void 272 xc5k_close(struct xc5k *xc) 273 { 274 kmem_free(xc, sizeof(*xc)); 275 } 276 277 int 278 xc5k_tune(struct xc5k *xc, struct xc5k_params *params) 279 { 280 uint16_t amode, vmode; 281 uint16_t lock, freq; 282 int retry; 283 284 switch (params->standard) { 285 case VIDEO_STANDARD_NTSC_M: 286 case VIDEO_STANDARD_NTSC_M_JP: 287 case VIDEO_STANDARD_NTSC_M_KR: 288 amode = XC5K_AUDIO_MODE_BTSC; 289 vmode = XC5K_VIDEO_MODE_BTSC; 290 break; 291 default: 292 return EINVAL; 293 } 294 295 if (xc5k_write_2(xc, XC5K_REG_SIGNAL_SOURCE, params->signal_source)) 296 return EIO; 297 if (xc5k_write_2(xc, XC5K_REG_VIDEO_MODE, vmode)) 298 return EIO; 299 if (xc5k_write_2(xc, XC5K_REG_AUDIO_MODE, amode)) 300 return EIO; 301 freq = (params->frequency * 62500) / 15625; 302 #ifdef XC5K_DEBUG 303 printf("xc5k_tune: frequency=%u (%u Hz)\n", params->frequency, 304 params->frequency * 62500); 305 printf(" freq=%u\n", freq); 306 #endif 307 if (xc5k_write_2(xc, XC5K_REG_FINER_FREQ, freq)) 308 return EIO; 309 310 retry = 100; 311 while (--retry > 0) { 312 if (xc5k_read_2(xc, XC5K_REG_LOCK, &lock)) 313 return EIO; 314 #ifdef XC5K_DEBUG 315 printf("xc5k_tune: lock=0x%04x\n", lock); 316 #endif 317 if (lock == 1) 318 break; 319 delay(5000); 320 } 321 322 return 0; 323 } 324 325 MODULE(MODULE_CLASS_DRIVER, xc5k, NULL); 326 327 static int 328 xc5k_modcmd(modcmd_t cmd, void *opaque) 329 { 330 switch (cmd) { 331 case MODULE_CMD_INIT: 332 mutex_init(&xc5k_firmware_lock, MUTEX_DEFAULT, IPL_VM); 333 return 0; 334 case MODULE_CMD_FINI: 335 mutex_destroy(&xc5k_firmware_lock); 336 return 0; 337 default: 338 return ENOTTY; 339 } 340 } 341