Home | History | Annotate | Line # | Download | only in i2c
xc5k.c revision 1.5
      1 /* $NetBSD: xc5k.c,v 1.5 2015/01/07 07:05:48 ozaki-r Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) 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.5 2015/01/07 07:05:48 ozaki-r 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, "iic");
    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