Module Name:    src
Committed By:   jmcneill
Date:           Mon Jul 11 18:00:06 UTC 2011

Modified Files:
        src/sys/dev/i2c: files.i2c
Added Files:
        src/sys/dev/i2c: lg3303.c lg3303var.h xc3028.c xc3028reg.h xc3028var.h

Log Message:
add LGDT3303 tuner and XC3028L demod drivers


To generate a diff of this commit:
cvs rdiff -u -r1.36 -r1.37 src/sys/dev/i2c/files.i2c
cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/lg3303.c src/sys/dev/i2c/lg3303var.h \
    src/sys/dev/i2c/xc3028.c src/sys/dev/i2c/xc3028reg.h \
    src/sys/dev/i2c/xc3028var.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/i2c/files.i2c
diff -u src/sys/dev/i2c/files.i2c:1.36 src/sys/dev/i2c/files.i2c:1.37
--- src/sys/dev/i2c/files.i2c:1.36	Mon Jul 11 00:30:23 2011
+++ src/sys/dev/i2c/files.i2c	Mon Jul 11 18:00:06 2011
@@ -1,4 +1,4 @@
-#	$NetBSD: files.i2c,v 1.36 2011/07/11 00:30:23 jakllsch Exp $
+#	$NetBSD: files.i2c,v 1.37 2011/07/11 18:00:06 jmcneill Exp $
 
 defflag	opt_i2cbus.h				I2C_SCAN
 define	i2cbus { }
@@ -17,8 +17,16 @@
 define	au8522: i2cexec
 file	dev/i2c/au8522.c			au8522
 
+# LG DT3303 decoder
+define	lg3303: i2cexec
+file	dev/i2c/lg3303.c			lg3303
+
+# Xceive XC3028 tuner
+define	xc3028: i2cexec, firmload
+file	dev/i2c/xc3028.c			xc3028
+
 # Xceive XC5000 tuner
-define	xc5k: i2cexec
+define	xc5k: i2cexec, firmload
 file	dev/i2c/xc5k.c				xc5k
 
 # Generic PLL-based tuners

Added files:

Index: src/sys/dev/i2c/lg3303.c
diff -u /dev/null src/sys/dev/i2c/lg3303.c:1.1
--- /dev/null	Mon Jul 11 18:00:06 2011
+++ src/sys/dev/i2c/lg3303.c	Mon Jul 11 18:00:06 2011
@@ -0,0 +1,359 @@
+/* $NetBSD: lg3303.c,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
+
+/*-
+ * Copyright 2007 Jason Harmening
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+__KERNEL_RCSID(0, "$NetBSD: lg3303.c,v 1.1 2011/07/11 18:00:06 jmcneill Exp $");
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/module.h>
+
+#include <dev/i2c/i2cvar.h>
+#include <dev/i2c/lg3303var.h>
+#include <dev/dtv/dtvif.h>
+
+#define REG_TOP_CONTROL         0x00
+#define REG_IRQ_MASK            0x01
+#define REG_IRQ_STATUS          0x02
+#define REG_VSB_CARRIER_FREQ0   0x16
+#define REG_VSB_CARRIER_FREQ1   0x17
+#define REG_VSB_CARRIER_FREQ2   0x18
+#define REG_VSB_CARRIER_FREQ3   0x19
+#define REG_CARRIER_MSEQAM1     0x1a
+#define REG_CARRIER_MSEQAM2     0x1b
+#define REG_CARRIER_LOCK        0x1c
+#define REG_TIMING_RECOVERY     0x1d
+#define REG_AGC_DELAY0          0x2a
+#define REG_AGC_DELAY1          0x2b
+#define REG_AGC_DELAY2          0x2c
+#define REG_AGC_RF_BANDWIDTH0   0x2d
+#define REG_AGC_RF_BANDWIDTH1   0x2e
+#define REG_AGC_RF_BANDWIDTH2   0x2f
+#define REG_AGC_LOOP_BANDWIDTH0 0x30
+#define REG_AGC_LOOP_BANDWIDTH1 0x31
+#define REG_AGC_FUNC_CTRL1      0x32
+#define REG_AGC_FUNC_CTRL2      0x33
+#define REG_AGC_FUNC_CTRL3      0x34
+#define REG_AGC_RFIF_ACC0       0x39
+#define REG_AGC_RFIF_ACC1       0x3a
+#define REG_AGC_RFIF_ACC2       0x3b
+#define REG_AGC_STATUS          0x3f
+#define REG_SYNC_STATUS_VSB     0x43
+#define REG_DEMUX_CONTROL       0x66
+#define REG_EQPH_ERR0           0x6e
+#define REG_EQ_ERR1             0x6f
+#define REG_EQ_ERR2             0x70
+#define REG_PH_ERR1             0x71
+#define REG_PH_ERR2             0x72
+#define REG_PACKET_ERR_COUNTER1 0x8b
+#define REG_PACKET_ERR_COUNTER2 0x8c
+
+#define LG3303_DEFAULT_DELAY 250000
+
+static int	lg3303_reset(struct lg3303 *);
+static int	lg3303_init(struct lg3303 *);
+
+struct lg3303 *
+lg3303_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr)
+{
+	struct lg3303 *lg;
+
+	lg = kmem_alloc(sizeof(*lg), KM_SLEEP);
+	if (lg == NULL)
+		return NULL;
+	lg->parent = parent;
+	lg->i2c = i2c;
+	lg->i2c_addr = addr;
+	lg->current_modulation = -1;
+
+	if (lg3303_init(lg) != 0) {
+		kmem_free(lg, sizeof(*lg));
+		return NULL;
+	}
+
+	device_printf(lg->parent, "lg3303: found @ 0x%02x\n", addr);
+
+	return lg;
+}
+
+void
+lg3303_close(struct lg3303 *lg)
+{
+	kmem_free(lg, sizeof(*lg));
+}
+
+static int
+lg3303_write(struct lg3303 *lg, uint8_t *buf, size_t len)
+{
+	unsigned int i;
+	uint8_t *p = buf;
+	int error;
+
+	for (i = 0; i < len - 1; i += 2) {
+		error = iic_exec(lg->i2c, I2C_OP_WRITE_WITH_STOP, lg->i2c_addr,
+		    p, 2, NULL, 0, 0);
+		if (error)
+			return error;
+		p += 2;
+	}
+
+	return 0;
+}
+
+static int
+lg3303_read(struct lg3303 *lg, uint8_t reg, uint8_t *buf, size_t len)
+{
+	int error;
+
+	error = iic_exec(lg->i2c, I2C_OP_WRITE, lg->i2c_addr, 
+	    &reg, sizeof(reg), NULL, 0, 0);
+	if (error)
+		return error;
+	return iic_exec(lg->i2c, I2C_OP_READ, lg->i2c_addr,
+	    NULL, 0, buf, len, 0);
+}
+
+static int
+lg3303_reset(struct lg3303 *lg)
+{
+	uint8_t buffer[] = {REG_IRQ_STATUS, 0x00};
+	int error = lg3303_write(lg, buffer, 2);
+	if (error == 0) {
+		buffer[1] = 0x01;
+		error = lg3303_write(lg, buffer, 2);
+	}
+	return error;
+}
+      
+static int
+lg3303_init(struct lg3303 *lg)
+{
+	//static uint8_t init_data[] = {0x4c, 0x14, 0x87, 0xf3};
+	static uint8_t init_data[] = {0x4c, 0x14};
+	size_t len;
+	int error;
+
+#if notyet
+	if (clock_polarity == DVB_IFC_POS_POL)
+		len = 4;
+	else
+#endif
+	len = 2;
+
+	error = lg3303_write(lg, init_data, len);
+	if (error == 0)
+      		lg3303_reset(lg);
+
+	return error;
+}
+
+int
+lg3303_set_modulation(struct lg3303 *lg, fe_modulation_t modulation)
+{
+	int error;
+	static uint8_t vsb_data[] = {
+		0x04, 0x00,
+		0x0d, 0x40,
+		0x0e, 0x87,
+		0x0f, 0x8e,
+		0x10, 0x01,
+		0x47, 0x8b
+	};
+	static uint8_t qam_data[] = {
+		0x04, 0x00,
+		0x0d, 0x00,
+		0x0e, 0x00,
+		0x0f, 0x00,
+		0x10, 0x00,
+		0x51, 0x63,
+		0x47, 0x66,
+		0x48, 0x66,
+		0x4d, 0x1a,
+		0x49, 0x08,
+		0x4a, 0x9b   
+	};
+
+	error = lg3303_reset(lg);
+	if (error)
+		return error;
+
+	if (lg->current_modulation != modulation) {
+		uint8_t top_ctrl[] = {REG_TOP_CONTROL, 0x00};
+#if 0
+		if (m_input == DVB_INPUT_SERIAL)
+			top_ctrl[1] = 0x40;  
+#endif
+
+		switch (modulation) {
+		case VSB_8:
+			top_ctrl[1] |= 0x03;
+			error = lg3303_write(lg, vsb_data, sizeof(vsb_data));
+			if (error)
+				return error;
+			break;
+		case QAM_256:
+			top_ctrl[1] |= 0x01;
+			/* FALLTHROUGH */
+		case QAM_64:
+			error = lg3303_write(lg, qam_data, sizeof(qam_data));
+			if (error)
+				return error;
+			break;
+		default:
+			device_printf(lg->parent,
+			    "lg3303: unsupported modulation type (%d)\n",
+			    modulation);
+			return EINVAL;
+		}
+		error = lg3303_write(lg, top_ctrl, sizeof(top_ctrl));
+		if (error)
+			return error;
+		lg->current_modulation = modulation;
+		lg3303_reset(lg);
+	}
+
+	return error;
+}
+
+fe_status_t
+lg3303_get_dtv_status(struct lg3303 *lg)
+{
+	uint8_t reg = 0, value = 0x00;
+	fe_status_t festatus = 0;
+	int error = 0;
+
+	error = lg3303_read(lg, 0x58, &value, sizeof(value));
+	if (error)
+		return 0;
+
+	if (value & 0x01)
+		festatus |= FE_HAS_SIGNAL;
+
+	error = lg3303_read(lg, REG_CARRIER_LOCK, &value, sizeof(value));
+	if (error)
+		return 0;
+
+	switch (lg->current_modulation) {
+	case VSB_8:
+		if (value & 0x80)
+			festatus |= FE_HAS_CARRIER;
+		reg = 0x38;
+		break;
+	case QAM_64:
+	case QAM_256:
+		if ((value & 0x07) == 0x07)
+			festatus |= FE_HAS_CARRIER;
+		reg = 0x8a;
+		break;
+	default:
+		device_printf(lg->parent,
+		    "lg3303: unsupported modulation type (%d)\n",
+		    lg->current_modulation);
+		return EINVAL;
+	}
+
+	error = lg3303_read(lg, reg, &value, sizeof(value));
+	if (!error && (value & 0x01))
+		festatus |= FE_HAS_LOCK;
+
+	if (festatus & FE_HAS_LOCK)
+		festatus |= (FE_HAS_SYNC | FE_HAS_VITERBI);
+
+	return festatus;
+}
+
+#if notyet
+int lg3303::get_signal(dvb_signal &signal)
+{
+   int error = check_for_lock(signal.locked);
+   uint32_t noise, snr_const;
+   uint8_t buffer[5];
+   uint8_t reg;
+   if (error || !signal.locked)
+   {
+      return error;
+   }
+   signal.ber = 0;
+   switch(m_modulation)
+   {
+      case DVB_MOD_VSB_8:
+         reg = REG_EQPH_ERR0;
+         if ((error = m_device.transact(&reg, sizeof(reg), buffer, sizeof(buffer))))
+         {
+            LIBTUNERERR << "LG3303: Unable to retrieve 8-VSB noise value" << endl;
+            return error;
+         }
+         noise = ((buffer[0] & 7) << 16) | (buffer[3] << 8) | buffer[4];
+         snr_const = 25600;
+         break;
+      case DVB_MOD_QAM_64:
+      case DVB_MOD_QAM_256:
+         reg = REG_CARRIER_MSEQAM1;
+         if ((error = m_device.transact(&reg, sizeof(reg), buffer, 2)))
+         {
+            LIBTUNERERR << "LG3303: Unable to retrieve QAM noise value" << endl;
+            return error;
+         }
+         noise = (buffer[0] << 8) | buffer[1];
+         if (m_modulation == DVB_MOD_QAM_64)
+         {
+            snr_const = 688128;
+         }
+         else
+         {
+            snr_const = 696320;
+         }
+         break;
+      default:
+         LIBTUNERERR << "LG3303: Unsupported modulation type" << endl;
+         return EINVAL;
+   }
+   signal.snr = 10.0 * log10((double)snr_const / noise);
+   signal.strength = (signal.snr / 35) * 100;
+   reg = REG_PACKET_ERR_COUNTER1;
+   if ((error = m_device.transact(&reg, sizeof(reg), buffer, 2)))
+   {
+      LIBTUNERERR << "LG3303: Unable to retrieve packet error count" << endl;
+      return error;
+   }
+   signal.uncorrected_blocks = (buffer[0] << 8) | buffer[1];
+   return 0;
+}
+#endif
+
+
+MODULE(MODULE_CLASS_DRIVER, lg3303, NULL);
+
+static int
+lg3303_modcmd(modcmd_t cmd, void *opaque)
+{
+	if (cmd == MODULE_CMD_INIT || cmd == MODULE_CMD_FINI)
+		return 0;
+	return ENOTTY;
+}
Index: src/sys/dev/i2c/lg3303var.h
diff -u /dev/null src/sys/dev/i2c/lg3303var.h:1.1
--- /dev/null	Mon Jul 11 18:00:06 2011
+++ src/sys/dev/i2c/lg3303var.h	Mon Jul 11 18:00:06 2011
@@ -0,0 +1,48 @@
+/* $NetBSD: lg3303var.h,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LG3303VAR_H
+#define _LG3303VAR_H
+
+#include <dev/i2c/i2cvar.h>
+#include <dev/dtv/dtvio.h>
+
+struct lg3303 {
+	device_t	parent;
+	i2c_tag_t	i2c;
+	i2c_addr_t	i2c_addr;
+
+	fe_modulation_t	current_modulation;
+};
+
+struct lg3303 *	lg3303_open(device_t, i2c_tag_t, i2c_addr_t);
+void		lg3303_close(struct lg3303 *);
+int		lg3303_set_modulation(struct lg3303 *, fe_modulation_t);
+fe_status_t	lg3303_get_dtv_status(struct lg3303 *);
+
+#endif /* !_LG3303VAR_H */
Index: src/sys/dev/i2c/xc3028.c
diff -u /dev/null src/sys/dev/i2c/xc3028.c:1.1
--- /dev/null	Mon Jul 11 18:00:06 2011
+++ src/sys/dev/i2c/xc3028.c	Mon Jul 11 18:00:06 2011
@@ -0,0 +1,582 @@
+/* $NetBSD: xc3028.c,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Xceive XC3028L
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: xc3028.c,v 1.1 2011/07/11 18:00:06 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kmem.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+
+#include <dev/firmload.h>
+#include <dev/i2c/i2cvar.h>
+
+#include <dev/i2c/xc3028reg.h>
+#include <dev/i2c/xc3028var.h>
+
+#define	XC3028_FIRMWARE_DRVNAME	"xc3028"
+
+#define	XC3028_FREQ_MIN		1000000
+#define	XC3028_FREQ_MAX		1023000000
+
+#define	XC3028_FW_BASE		(1 << 0)
+#define	XC3028_FW_D2633		(1 << 4)
+#define	XC3028_FW_DTV6		(1 << 5)
+#define	XC3028_FW_QAM		(1 << 6)
+#define	XC3028_FW_ATSC		(1 << 16)
+#define	XC3028_FW_LG60		(1 << 18)
+#define	XC3028_FW_F6MHZ		(1 << 27)
+#define	XC3028_FW_SCODE		(1 << 29)
+#define	XC3028_FW_HAS_IF	(1 << 30)
+
+#define	XC3028_FW_DEFAULT	(XC3028_FW_ATSC|XC3028_FW_D2633|XC3028_FW_DTV6)
+
+static kmutex_t	xc3028_firmware_lock;
+
+static int	xc3028_reset(struct xc3028 *);
+static int	xc3028_read_2(struct xc3028 *, uint16_t, uint16_t *);
+static int	xc3028_write_buffer(struct xc3028 *, const uint8_t *, size_t);
+static int	xc3028_firmware_open(struct xc3028 *);
+static int	xc3028_firmware_parse(struct xc3028 *, const uint8_t *, size_t);
+static int	xc3028_firmware_upload(struct xc3028 *, struct xc3028_fw *);
+static int	xc3028_scode_upload(struct xc3028 *, struct xc3028_fw *);
+static void	xc3028_dump_fw(struct xc3028 *, struct xc3028_fw *,
+		    const char *);
+
+static const char *
+xc3028_name(struct xc3028 *xc)
+{
+	if (xc->type == XC3028L)
+		return "xc3028l";
+	else
+		return "xc3028";
+}
+
+static const char *
+xc3028_firmware_name(struct xc3028 *xc)
+{
+	if (xc->type == XC3028L)
+		return "xc3028L-v36.fw";
+	else
+		return "xc3028-v27.fw";
+}
+
+static int
+xc3028_reset(struct xc3028 *xc)
+{
+	int error = 0;
+
+	if (xc->reset)
+		error = xc->reset(xc->reset_priv);
+
+	return error;
+}
+
+static struct xc3028_fw *
+xc3028_get_basefw(struct xc3028 *xc)
+{
+	struct xc3028_fw *fw;
+	unsigned int i;
+
+	for (i = 0; i < xc->nfw; i++) {
+		fw = &xc->fw[i];
+		if (fw->type == XC3028_FW_BASE)
+			return fw;
+	}
+
+	return NULL;
+}
+
+static struct xc3028_fw *
+xc3028_get_stdfw(struct xc3028 *xc)
+{
+	struct xc3028_fw *fw;
+	unsigned int i;
+
+	for (i = 0; i < xc->nfw; i++) {
+		fw = &xc->fw[i];
+		if (fw->type == (XC3028_FW_D2633|XC3028_FW_DTV6|XC3028_FW_ATSC))
+			return fw;
+	}
+
+	return NULL;
+}
+
+static struct xc3028_fw *
+xc3028_get_scode(struct xc3028 *xc)
+{
+	struct xc3028_fw *fw;
+	unsigned int i;
+
+	for (i = 0; i < xc->nfw; i++) {
+		fw = &xc->fw[i];
+		if (fw->type ==
+		    (XC3028_FW_DTV6|XC3028_FW_QAM|XC3028_FW_ATSC|XC3028_FW_LG60|
+		     XC3028_FW_F6MHZ|XC3028_FW_SCODE|XC3028_FW_HAS_IF) &&
+		    fw->int_freq == 6200)
+			return fw;
+	}
+
+	return NULL;
+}
+
+static int
+xc3028_firmware_open(struct xc3028 *xc)
+{
+	firmware_handle_t fwh;
+	struct xc3028_fw *basefw, *stdfw, *scode;
+	uint8_t *fw = NULL;
+	uint16_t xcversion = 0;
+	size_t fwlen;
+	int error;
+
+	mutex_enter(&xc3028_firmware_lock);
+
+	error = firmware_open(XC3028_FIRMWARE_DRVNAME,
+	    xc3028_firmware_name(xc), &fwh);
+	if (error)
+		goto done;
+	fwlen = firmware_get_size(fwh);
+	fw = firmware_malloc(fwlen);
+	if (fw == NULL) {
+		firmware_close(fwh);
+		error = ENOMEM;
+		goto done;
+	}
+	error = firmware_read(fwh, 0, fw, fwlen);
+	firmware_close(fwh);
+	if (error)
+		goto done;
+
+	device_printf(xc->parent, "%s: loading firmware '%s/%s'\n",
+	    xc3028_name(xc), XC3028_FIRMWARE_DRVNAME, xc3028_firmware_name(xc));
+	error = xc3028_firmware_parse(xc, fw, fwlen);
+	if (!error) {
+		basefw = xc3028_get_basefw(xc);
+		stdfw = xc3028_get_stdfw(xc);
+		scode = xc3028_get_scode(xc);
+		if (basefw && stdfw) {
+			xc3028_reset(xc);
+			xc3028_dump_fw(xc, basefw, "base");
+			error = xc3028_firmware_upload(xc, basefw);
+			if (error)
+				return error;
+			xc3028_dump_fw(xc, stdfw, "std");
+			error = xc3028_firmware_upload(xc, stdfw);
+			if (error)
+				return error;
+			if (scode) {
+				xc3028_dump_fw(xc, scode, "scode");
+				error = xc3028_scode_upload(xc, scode);
+				if (error)
+					return error;
+			}
+		} else
+			error = ENODEV;
+	}
+	if (!error) {
+		xc3028_read_2(xc, XC3028_REG_VERSION, &xcversion);
+
+		device_printf(xc->parent, "%s: hw %d.%d, fw %d.%d\n",
+		    xc3028_name(xc),
+		    (xcversion >> 12) & 0xf, (xcversion >> 8) & 0xf,
+		    (xcversion >> 4) & 0xf, (xcversion >> 0) & 0xf);
+	}
+
+done:
+	if (fw)
+		firmware_free(fw, 0);
+	mutex_exit(&xc3028_firmware_lock);
+
+	if (error)
+		aprint_error_dev(xc->parent,
+		    "%s: couldn't open firmware '%s/%s' (error=%d)\n",
+		    xc3028_name(xc), XC3028_FIRMWARE_DRVNAME,
+		    xc3028_firmware_name(xc), error);
+
+	return error;
+}
+
+static const char *xc3028_fw_types[] = {
+	"BASE",
+	"F8MHZ",
+	"MTS",
+	"D2620",
+	"D2633",
+	"DTV6",
+	"QAM",
+	"DTV7",
+	"DTV78",
+	"DTV8",
+	"FM",
+	"INPUT1",
+	"LCD",
+	"NOGD",
+	"INIT1",
+	"MONO",
+	"ATSC",
+	"IF",
+	"LG60",
+	"ATI638",
+	"OREN538",
+	"OREN36",
+	"TOYOTA388",
+	"TOYOTA794",
+	"DIBCOM52",
+	"ZARLINK456",
+	"CHINA",
+	"F6MHZ",
+	"INPUT2",
+	"SCODE",
+	"HAS_IF",
+};
+
+static void
+xc3028_dump_fw(struct xc3028 *xc, struct xc3028_fw *xcfw, const char *type)
+{
+	unsigned int i;
+
+	device_printf(xc->parent, "%s: %s:", xc3028_name(xc), type);
+	if (xcfw == NULL) {
+		printf(" <none>\n");
+		return;
+	}
+	for (i = 0; i < __arraycount(xc3028_fw_types); i++) {
+		if (xcfw->type & (1 << i))
+			printf(" %s", xc3028_fw_types[i]);
+	}
+	if (xcfw->type & (1 << 30))
+		printf("_%d", xcfw->int_freq);
+	if (xcfw->id)
+		printf(" id=%llx", xcfw->id);
+	printf(" size=%u\n", xcfw->data_size);
+}
+
+static int
+xc3028_firmware_parse(struct xc3028 *xc, const uint8_t *fw, size_t fwlen)
+{
+	const uint8_t *p = fw, *endp = p + fwlen;
+	char fwname[32 + 1];
+	uint16_t fwver, narr;
+	unsigned int index;
+	struct xc3028_fw *xcfw;
+
+	if (fwlen < 36)
+		return EINVAL;
+
+	/* first 32 bytes are the firmware name string */
+	memset(fwname, 0, sizeof(fwname));
+	memcpy(fwname, p, sizeof(fwname) - 1);
+	p += (sizeof(fwname) - 1);
+
+	fwver = le16dec(p);
+	p += sizeof(fwver);
+	narr = le16dec(p);
+	p += sizeof(narr);
+
+	aprint_debug_dev(xc->parent, "%s: fw type %s, ver %d.%d, %d images\n",
+	    xc3028_name(xc), fwname, fwver >> 8, fwver & 0xff, narr);
+
+	xc->fw = kmem_zalloc(sizeof(*xc->fw) * narr, KM_SLEEP);
+	if (xc->fw == NULL)
+		return ENOMEM;
+	xc->nfw = narr;
+
+	for (index = 0; index < xc->nfw && p < endp; index++) {
+		xcfw = &xc->fw[index];
+
+		if (endp - p < 16)
+			goto corrupt;
+
+		xcfw->type = le32dec(p);
+		p += sizeof(xcfw->type);
+
+		xcfw->id = le64dec(p);
+		p += sizeof(xcfw->id);
+
+		if (xcfw->type & XC3028_FW_HAS_IF) {
+			xcfw->int_freq = le16dec(p);
+			p += sizeof(xcfw->int_freq);
+			if ((uint32_t)(endp - p) < sizeof(xcfw->data_size))
+				goto corrupt;
+		}
+
+		xcfw->data_size = le32dec(p);
+		p += sizeof(xcfw->data_size);
+
+		if (xcfw->data_size == 0 ||
+		    xcfw->data_size > (uint32_t)(endp - p))
+			goto corrupt;
+		xcfw->data = kmem_alloc(xcfw->data_size, KM_SLEEP);
+		if (xcfw->data == NULL)
+			goto corrupt;
+		memcpy(xcfw->data, p, xcfw->data_size);
+		p += xcfw->data_size;
+	}
+
+	return 0;
+
+corrupt:
+	aprint_error_dev(xc->parent, "%s: fw image corrupt\n", xc3028_name(xc));
+	for (index = 0; index < xc->nfw; index++) {
+		if (xc->fw[index].data)
+			kmem_free(xc->fw[index].data, xc->fw[index].data_size);
+	}
+	kmem_free(xc->fw, sizeof(*xc->fw) * xc->nfw);
+	xc->nfw = 0;
+
+	return ENXIO;
+}
+
+static int
+xc3028_firmware_upload(struct xc3028 *xc, struct xc3028_fw *xcfw)
+{
+	const uint8_t *fw = xcfw->data, *p;
+	uint32_t fwlen = xcfw->data_size;
+	uint8_t cmd[64];
+	unsigned int i;
+	uint16_t len, rem;
+	size_t wrlen;
+	int error;
+
+	for (i = 0; i < fwlen - 2;) {
+		len = le16dec(&fw[i]);
+		i += 2;
+		if (len == 0xffff)
+			break;
+
+		/* reset command */
+		if (len == 0x0000) {
+			error = xc3028_reset(xc);
+			if (error)
+				return error;
+			continue;
+		}
+		/* reset clk command */
+		if (len == 0xff00) {
+			continue;
+		}
+		/* delay command */
+		if (len & 0x8000) {
+			delay((len & 0x7fff) * 1000);
+			continue;
+		}
+
+		if (i + len > fwlen) {
+			printf("weird len, i=%u len=%u fwlen=%u'\n", i, len, fwlen);
+			return EINVAL;
+		}
+
+		cmd[0] = fw[i];
+		p = &fw[i + 1];
+		rem = len - 1;
+		while (rem > 0) {
+			wrlen = min(rem, __arraycount(cmd) - 1);
+			memcpy(&cmd[1], p, wrlen);
+			error = xc3028_write_buffer(xc, cmd, wrlen + 1);
+			if (error)
+				return error;
+			p += wrlen;
+			rem -= wrlen;
+		}
+		i += len;
+	}
+
+	return 0;
+}
+
+static int
+xc3028_scode_upload(struct xc3028 *xc, struct xc3028_fw *xcfw)
+{
+	static uint8_t scode_init[] = {	0xa0, 0x00, 0x00, 0x00 };
+	static uint8_t scode_fini[] = { 0x00, 0x8c };
+	int error;
+
+	if (xcfw->data_size < 12)
+		return EINVAL;
+	error = xc3028_write_buffer(xc, scode_init, sizeof(scode_init));
+	if (error)
+		return error;
+	error = xc3028_write_buffer(xc, xcfw->data, 12);
+	if (error)
+		return error;
+	error = xc3028_write_buffer(xc, scode_fini, sizeof(scode_fini));
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static int
+xc3028_read_2(struct xc3028 *xc, uint16_t reg, uint16_t *val)
+{
+	uint8_t cmd[2], resp[2];
+	int error;
+
+	cmd[0] = reg >> 8;
+	cmd[1] = reg & 0xff;
+	error = iic_exec(xc->i2c, I2C_OP_WRITE, xc->i2c_addr,
+	    cmd, sizeof(cmd), NULL, 0, 0);
+	if (error)
+		return error;
+	resp[0] = resp[1] = 0;
+	error = iic_exec(xc->i2c, I2C_OP_READ, xc->i2c_addr,
+	    NULL, 0, resp, sizeof(resp), 0);
+	if (error)
+		return error;
+
+	*val = (resp[0] << 8) | resp[1];
+
+	return 0;
+}
+
+static int
+xc3028_write_buffer(struct xc3028 *xc, const uint8_t *data, size_t datalen)
+{
+	return iic_exec(xc->i2c, I2C_OP_WRITE_WITH_STOP, xc->i2c_addr,
+	    data, datalen, NULL, 0, 0);
+}
+
+#if notyet
+static int
+xc3028_write_2(struct xc3028 *xc, uint16_t reg, uint16_t val)
+{
+	uint8_t data[4];
+
+	data[0] = reg >> 8;
+	data[1] = reg & 0xff;
+	data[2] = val >> 8;
+	data[3] = val & 0xff;
+
+	return xc3028_write_buffer(xc, data, sizeof(data));
+}
+#endif
+
+struct xc3028 *
+xc3028_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr,
+    xc3028_reset_cb reset, void *reset_priv, enum xc3028_type type)
+{
+	struct xc3028 *xc;
+
+	xc = kmem_alloc(sizeof(*xc), KM_SLEEP);
+	if (xc == NULL)
+		return NULL;
+	xc->parent = parent;
+	xc->i2c = i2c;
+	xc->i2c_addr = addr;
+	xc->reset = reset;
+	xc->reset_priv = reset_priv;
+	xc->type = type;
+
+	if (xc3028_firmware_open(xc)) {
+		aprint_error_dev(parent, "%s: fw open failed\n",
+		    xc3028_name(xc));
+		goto failed;
+	}
+
+	return xc;
+
+failed:
+	kmem_free(xc, sizeof(*xc));
+	return NULL;
+}
+
+void
+xc3028_close(struct xc3028 *xc)
+{
+	unsigned int index;
+
+	if (xc->fw) {
+		for (index = 0; index < xc->nfw; index++) {
+			if (xc->fw[index].data)
+				kmem_free(xc->fw[index].data,
+				    xc->fw[index].data_size);
+		}
+		kmem_free(xc->fw, sizeof(*xc->fw) * xc->nfw);
+	}
+	kmem_free(xc, sizeof(*xc));
+}
+
+int
+xc3028_tune_dtv(struct xc3028 *xc, const struct dvb_frontend_parameters *params)
+{
+	static uint8_t freq_init[] = { 0x80, 0x02, 0x00, 0x00 };
+	uint8_t freq_buf[4];
+	uint32_t div, offset = 0;
+	int error;
+
+	if (params->u.vsb.modulation == VSB_8) {
+		offset = 1750000;
+	} else {
+		return EINVAL;
+	}
+
+	div = (params->frequency - offset + 15625 / 2) / 15625;
+	
+	error = xc3028_write_buffer(xc, freq_init, sizeof(freq_init));
+	if (error)
+		return error;
+	delay(10000);
+
+	freq_buf[0] = (div >> 24) & 0xff;
+	freq_buf[1] = (div >> 16) & 0xff;
+	freq_buf[2] = (div >> 8) & 0xff;
+	freq_buf[3] = (div >> 0) & 0xff;
+	error = xc3028_write_buffer(xc, freq_buf, sizeof(freq_buf));
+	if (error)
+		return error;
+	delay(100000);
+
+	return 0;
+}
+
+MODULE(MODULE_CLASS_DRIVER, xc3028, NULL);
+
+static int
+xc3028_modcmd(modcmd_t cmd, void *opaque)
+{
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+		mutex_init(&xc3028_firmware_lock, MUTEX_DEFAULT, IPL_NONE);
+		return 0;
+	case MODULE_CMD_FINI:
+		mutex_destroy(&xc3028_firmware_lock);
+		return 0;
+	default:
+		return ENOTTY;
+	}
+}
Index: src/sys/dev/i2c/xc3028reg.h
diff -u /dev/null src/sys/dev/i2c/xc3028reg.h:1.1
--- /dev/null	Mon Jul 11 18:00:06 2011
+++ src/sys/dev/i2c/xc3028reg.h	Mon Jul 11 18:00:06 2011
@@ -0,0 +1,41 @@
+/* $NetBSD: xc3028reg.h,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XC3028REG_H
+#define _XC3028REG_H
+
+#define	XC3028_REG_FREQ_ERROR		0x0001
+#define	XC3028_REG_LOCK			0x0002
+#define	XC3028_REG_VERSION		0x0004
+#define XC3028_REG_PRODUCT_ID		0x0008
+#define	XC3028_REG_HSYNC_FREQ		0x0010
+#define	XC3028_REG_FRAME_LINES		0x0020
+#define	XC3028_REG_QUALITY		0x0040
+#define	XC3028_REG_ADC_ENVELOPE		0x0100
+
+#endif /* !_XC3028REG_H */
Index: src/sys/dev/i2c/xc3028var.h
diff -u /dev/null src/sys/dev/i2c/xc3028var.h:1.1
--- /dev/null	Mon Jul 11 18:00:06 2011
+++ src/sys/dev/i2c/xc3028var.h	Mon Jul 11 18:00:06 2011
@@ -0,0 +1,70 @@
+/* $NetBSD: xc3028var.h,v 1.1 2011/07/11 18:00:06 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XC3028VAR_H
+#define _XC3028VAR_H
+
+#include <dev/i2c/i2cvar.h>
+#include <dev/dtv/dtvio.h>
+
+typedef int (*xc3028_reset_cb)(void *);
+
+enum xc3028_type {
+	XC3028,
+	XC3028L,
+};
+
+struct xc3028_fw {
+	uint32_t	type;
+	uint64_t	id;
+	uint16_t	int_freq;
+	uint8_t		*data;
+	uint32_t	data_size;
+};
+
+struct xc3028 {
+	device_t	parent;
+	i2c_tag_t	i2c;
+	i2c_addr_t	i2c_addr;
+
+	xc3028_reset_cb reset;
+	void		*reset_priv;
+
+	enum xc3028_type type;
+
+	struct xc3028_fw *fw;
+	unsigned int	nfw;
+};
+
+struct xc3028 *	xc3028_open(device_t, i2c_tag_t, i2c_addr_t,
+		    xc3028_reset_cb, void *, enum xc3028_type);
+void		xc3028_close(struct xc3028 *);
+int		xc3028_tune_dtv(struct xc3028 *,
+		    const struct dvb_frontend_parameters *);
+
+#endif /* !_XC3028VAR_H */

Reply via email to