Home | History | Annotate | Line # | Download | only in i2c
      1 /* $NetBSD: xc5k.c,v 1.9 2018/09/03 16:29:31 riastradh 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.9 2018/09/03 16:29:31 riastradh 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 = uimin(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 
    162 	error = xc5k_firmware_upload(xc, fw, fwlen);
    163 	if (error)
    164 		goto done;
    165 
    166 	error = xc5k_read_2(xc, XC5K_REG_VERSION, &xcversion);
    167 	if (error) {
    168 		error = 0;
    169 		goto done;
    170 	}
    171 
    172 	error = xc5k_read_2(xc, XC5K_REG_BUILD, &xcbuild);
    173 	if (error) {
    174 		error = 0;
    175 		xcbuild = 0;
    176 	}
    177 
    178 	aprint_normal_dev(xc->parent, "xc5k: hw %d.%d, fw %d.%d.%d\n",
    179 	    (xcversion >> 12) & 0xf, (xcversion >> 8) & 0xf,
    180 	    (xcversion >> 4) & 0xf, xcversion & 0xf, xcbuild);
    181 
    182 done:
    183 	if (fw)
    184 		firmware_free(fw, fwlen);
    185 	mutex_exit(&xc5k_firmware_lock);
    186 
    187 	if (error)
    188 		aprint_error_dev(xc->parent,
    189 		    "xc5k: couldn't open firmware '%s/%s' (error=%d)\n",
    190 		    XC5K_FIRMWARE_DRVNAME, XC5K_FIRMWARE_IMGNAME, error);
    191 
    192 	return error;
    193 }
    194 
    195 static int
    196 xc5k_read_2(struct xc5k *xc, uint16_t reg, uint16_t *val)
    197 {
    198 	uint8_t cmd[2], resp[2];
    199 	int error;
    200 
    201 	cmd[0] = reg >> 8;
    202 	cmd[1] = reg & 0xff;
    203 	error = iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr,
    204 	    cmd, sizeof(cmd), NULL, 0, 0);
    205 	if (error)
    206 		return error;
    207 	resp[0] = resp[1] = 0;
    208 	error = iic_exec(xc->i2c, I2C_OP_READ, xc->i2c_addr,
    209 	    NULL, 0, resp, sizeof(resp), 0);
    210 	if (error)
    211 		return error;
    212 
    213 	*val = (resp[0] << 8) | resp[1];
    214 
    215 	return 0;
    216 }
    217 
    218 static int
    219 xc5k_write_buffer(struct xc5k *xc, const uint8_t *data, size_t datalen)
    220 {
    221 	return iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr,
    222 	    data, datalen, NULL, 0, 0);
    223 }
    224 
    225 static int
    226 xc5k_write_2(struct xc5k *xc, uint16_t reg, uint16_t val)
    227 {
    228 	uint8_t data[4];
    229 	uint16_t busy;
    230 	int error, retry;
    231 
    232 	data[0] = reg >> 8;
    233 	data[1] = reg & 0xff;
    234 	data[2] = val >> 8;
    235 	data[3] = val & 0xff;
    236 	error = xc5k_write_buffer(xc, data, sizeof(data));
    237 	if (error)
    238 		return error;
    239 
    240 	retry = 1000;
    241 	while (--retry > 0) {
    242 		error = xc5k_read_2(xc, XC5K_REG_BUSY, &busy);
    243 		if (error || !busy)
    244 			break;
    245 		delay(5000);
    246 	}
    247 
    248 	return error;
    249 }
    250 
    251 struct xc5k *
    252 xc5k_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr,
    253     xc5k_reset_cb reset, void *reset_priv, unsigned int if_freq,
    254     fe_type_t fe_type)
    255 {
    256 	struct xc5k *xc;
    257 	uint16_t product_id;
    258 
    259 	xc = kmem_alloc(sizeof(*xc), KM_SLEEP);
    260 	xc->parent = parent;
    261 	xc->i2c = i2c;
    262 	xc->i2c_addr = addr;
    263 	xc->reset = reset;
    264 	xc->reset_priv = reset_priv;
    265 	xc->if_freq = if_freq;
    266 	xc->fe_type = fe_type;
    267 
    268 	if (xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id))
    269 		goto failed;
    270 
    271 	aprint_debug_dev(parent, "xc5k: product=0x%04x\n", product_id);
    272 
    273 	if (product_id != XC5K_PRODUCT_ID_NOFW && product_id != XC5K_PRODUCT_ID)
    274 		goto failed;
    275 
    276 	if (xc5k_firmware_open(xc))
    277 		goto failed;
    278 	if (xc5k_write_2(xc, XC5K_REG_INIT, 0))
    279 		goto failed;
    280 	delay(100000);
    281 
    282 	if (xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id))
    283 		goto failed;
    284 
    285 	aprint_debug_dev(parent, "xc5k: product=0x%04x\n", product_id);
    286 
    287 	return xc;
    288 
    289 failed:
    290 	kmem_free(xc, sizeof(*xc));
    291 	return NULL;
    292 }
    293 
    294 void
    295 xc5k_close(struct xc5k *xc)
    296 {
    297 	kmem_free(xc, sizeof(*xc));
    298 }
    299 
    300 int
    301 xc5k_tune_video(struct xc5k *xc, struct xc5k_params *params)
    302 {
    303 	uint16_t amode, vmode;
    304 	uint16_t lock, freq;
    305 	int retry;
    306 
    307 	switch (params->standard) {
    308 	case VIDEO_STANDARD_NTSC_M:
    309 	case VIDEO_STANDARD_NTSC_M_JP:
    310 	case VIDEO_STANDARD_NTSC_M_KR:
    311 		amode = XC5K_AUDIO_MODE_BTSC;
    312 		vmode = XC5K_VIDEO_MODE_BTSC;
    313 		break;
    314 	default:
    315 		return EINVAL;
    316 	}
    317 
    318 	if (xc5k_write_2(xc, XC5K_REG_SIGNAL_SOURCE, params->signal_source))
    319 		return EIO;
    320 	if (xc5k_write_2(xc, XC5K_REG_VIDEO_MODE, vmode))
    321 		return EIO;
    322 	if (xc5k_write_2(xc, XC5K_REG_AUDIO_MODE, amode))
    323 		return EIO;
    324 	if (xc5k_write_2(xc, XC5K_REG_OUTAMP, XC5K_OUTAMP_ANALOG))
    325 		return EIO;
    326 	freq = (params->frequency * 62500) / 15625;
    327 #ifdef XC5K_DEBUG
    328 	printf("xc5k_tune_video: frequency=%u (%u Hz)\n", params->frequency,
    329 	    params->frequency * 62500);
    330 	printf("           freq=%u\n", freq);
    331 #endif
    332 	if (xc5k_write_2(xc, XC5K_REG_FINER_FREQ, freq))
    333 		return EIO;
    334 
    335 	retry = 100;
    336 	while (--retry > 0) {
    337 		if (xc5k_read_2(xc, XC5K_REG_LOCK, &lock))
    338 			return EIO;
    339 #ifdef XC5K_DEBUG
    340 		printf("xc5k_tune_video: lock=0x%04x\n", lock);
    341 #endif
    342 		if (lock == 1)
    343 			break;
    344 		delay(5000);
    345 	}
    346 
    347 	return 0;
    348 }
    349 
    350 int
    351 xc5k_tune_dtv(struct xc5k *xc, const struct dvb_frontend_parameters *params)
    352 {
    353 	uint16_t amode, vmode;
    354 	uint32_t freq, ifout;
    355 	int signal_source;
    356 	fe_modulation_t modulation;
    357 
    358 	if (xc->fe_type == FE_ATSC)
    359 		modulation = params->u.vsb.modulation;
    360 	else if (xc->fe_type == FE_QAM)
    361 		modulation = params->u.qam.modulation;
    362 	else
    363 		return EINVAL;
    364 
    365 	switch (modulation) {
    366 	case VSB_8:
    367 	case VSB_16:
    368 		signal_source = XC5K_SIGNAL_SOURCE_AIR;
    369 		switch (xc->fe_type) {
    370 		case FE_ATSC:
    371 			amode = XC5K_AUDIO_MODE_DTV6;
    372 			vmode = XC5K_VIDEO_MODE_DTV6;
    373 			freq = params->frequency - 1750000;
    374 			break;
    375 		default:
    376 			return EINVAL;
    377 		}
    378 		break;
    379 	case QAM_16:
    380 	case QAM_32:
    381 	case QAM_64:
    382 	case QAM_128:
    383 	case QAM_256:
    384 		signal_source = XC5K_SIGNAL_SOURCE_CABLE;
    385 		switch (xc->fe_type) {
    386 		case FE_ATSC:
    387 			amode = XC5K_AUDIO_MODE_DTV6;
    388 			vmode = XC5K_VIDEO_MODE_DTV6;
    389 			freq = params->frequency - 1750000;
    390 			break;
    391 		case FE_QAM:
    392 			amode = XC5K_AUDIO_MODE_DTV78;
    393 			vmode = XC5K_VIDEO_MODE_DTV78;
    394 			freq = params->frequency - 2750000;
    395 			break;
    396 		default:
    397 			return EINVAL;
    398 		}
    399 		break;
    400 	default:
    401 		return EINVAL;
    402 	}
    403 
    404 	if (freq > XC5K_FREQ_MAX || freq < XC5K_FREQ_MIN)
    405 		return ERANGE;
    406 
    407 	if (xc5k_write_2(xc, XC5K_REG_SIGNAL_SOURCE, signal_source))
    408 		return EIO;
    409 	if (xc5k_write_2(xc, XC5K_REG_VIDEO_MODE, vmode))
    410 		return EIO;
    411 	if (xc5k_write_2(xc, XC5K_REG_AUDIO_MODE, amode))
    412 		return EIO;
    413 	ifout = ((xc->if_freq / 1000) * 1024) / 1000;
    414 	if (xc5k_write_2(xc, XC5K_REG_IF_OUT, ifout))
    415 		return EIO;
    416 	if (xc5k_write_2(xc, XC5K_REG_OUTAMP, XC5K_OUTAMP_DIGITAL))
    417 		return EIO;
    418 	freq = (uint16_t)(freq / 15625);
    419 	if (xc5k_write_2(xc, XC5K_REG_FINER_FREQ, freq))
    420 		return EIO;
    421 
    422 	return 0;
    423 }
    424 
    425 fe_status_t
    426 xc5k_get_status(struct xc5k *xc)
    427 {
    428 	uint16_t lock_status;
    429 	fe_status_t festatus = 0;
    430 
    431 	if (xc5k_read_2(xc, XC5K_REG_LOCK, &lock_status))
    432 		return 0;
    433 	if (lock_status & XC5K_LOCK_LOCKED) {
    434 		festatus |= FE_HAS_LOCK;
    435 		if ((lock_status & XC5K_LOCK_NOSIGNAL) == 0)
    436 			festatus |= FE_HAS_SIGNAL;
    437 	}
    438 
    439 	return festatus;
    440 }
    441 
    442 MODULE(MODULE_CLASS_DRIVER, xc5k, "i2cexec");
    443 
    444 static int
    445 xc5k_modcmd(modcmd_t cmd, void *opaque)
    446 {
    447 	switch (cmd) {
    448 	case MODULE_CMD_INIT:
    449 		mutex_init(&xc5k_firmware_lock, MUTEX_DEFAULT, IPL_NONE);
    450 		return 0;
    451 	case MODULE_CMD_FINI:
    452 		mutex_destroy(&xc5k_firmware_lock);
    453 		return 0;
    454 	default:
    455 		return ENOTTY;
    456 	}
    457 }
    458