Module Name:    src
Committed By:   jakllsch
Date:           Mon Jul 11 00:30:23 UTC 2011

Modified Files:
        src/sys/dev/i2c: files.i2c
Added Files:
        src/sys/dev/i2c: nxt2k.c nxt2kvar.h

Log Message:
Add subdriver for Nextwave (subsequently acquired by ATI, then AMD, and
finally Broadcom) NXT200x series of 8VSB/QAM demodulator.

Still a bit crude and only supports the NXT2004 at this point.


To generate a diff of this commit:
cvs rdiff -u -r1.35 -r1.36 src/sys/dev/i2c/files.i2c
cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/nxt2k.c src/sys/dev/i2c/nxt2kvar.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.35 src/sys/dev/i2c/files.i2c:1.36
--- src/sys/dev/i2c/files.i2c:1.35	Mon Jul 11 00:01:51 2011
+++ src/sys/dev/i2c/files.i2c	Mon Jul 11 00:30:23 2011
@@ -1,4 +1,4 @@
-#	$NetBSD: files.i2c,v 1.35 2011/07/11 00:01:51 jakllsch Exp $
+#	$NetBSD: files.i2c,v 1.36 2011/07/11 00:30:23 jakllsch Exp $
 
 defflag	opt_i2cbus.h				I2C_SCAN
 define	i2cbus { }
@@ -26,6 +26,10 @@
 file	dev/i2c/tvpll.c				tvpll
 file	dev/i2c/tvpll_tuners.c			tvpll
 
+# Nextwave NXT200x demodulator
+define	nxt2k: i2cexec
+file	dev/i2c/nxt2k.c				nxt2k
+
 #
 # I2C master devices
 #

Added files:

Index: src/sys/dev/i2c/nxt2k.c
diff -u /dev/null src/sys/dev/i2c/nxt2k.c:1.1
--- /dev/null	Mon Jul 11 00:30:23 2011
+++ src/sys/dev/i2c/nxt2k.c	Mon Jul 11 00:30:23 2011
@@ -0,0 +1,830 @@
+/* $NetBSD: nxt2k.c,v 1.1 2011/07/11 00:30:23 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 2008, 2011 Jonathan A. Kollasch
+ * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: nxt2k.c,v 1.1 2011/07/11 00:30:23 jakllsch Exp $");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kmem.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
+
+#include <dev/firmload.h>
+
+#include <dev/i2c/nxt2kvar.h>
+
+struct nxt2k {
+	device_t        parent;
+	i2c_tag_t       tag;
+	i2c_addr_t      addr;
+	kcondvar_t      cv;
+	kmutex_t        mtx;
+	bool            loaded; /* firmware is loaded? */
+};
+
+static int nxt2k_init(struct nxt2k *);
+
+static int nxt2k_writedata(struct nxt2k *, uint8_t, uint8_t *, size_t);
+static int nxt2k_readdata(struct nxt2k *, uint8_t, uint8_t *, size_t);
+static int nxt2k_writereg(struct nxt2k *, uint8_t, uint8_t *, size_t);
+static int nxt2k_readreg(struct nxt2k*, uint8_t, uint8_t *, size_t);
+
+static int nxt2k4_init(struct nxt2k *);
+static bool nxt2k4_load_firmware(struct nxt2k *);
+static void nxt2k4_mc_init(struct nxt2k *);
+static void nxt2k_mc_start(struct nxt2k *);
+static void nxt2k_mc_stop(struct nxt2k *);
+static void nxt2k_agc_reset(struct nxt2k *);
+static uint16_t nxt2k_crc_ccit(uint16_t, uint8_t);
+
+static int
+nxt2k_writedata(struct nxt2k *nxt, uint8_t reg, uint8_t *data, size_t len)
+{
+	uint8_t buffer[384];
+	int error;
+
+	KASSERT((len + 1) <= 384);
+
+	if (iic_acquire_bus(nxt->tag, I2C_F_POLL) != 0)
+		return false;
+
+	buffer[0] = reg;
+	memcpy(&buffer[1], data, len);
+	
+	error = iic_exec(nxt->tag, I2C_OP_WRITE_WITH_STOP, nxt->addr,
+			 buffer, len + 1, NULL, 0, I2C_F_POLL);
+	
+	iic_release_bus(nxt->tag, I2C_F_POLL);
+
+	return error;
+}
+
+static int
+nxt2k_readdata(struct nxt2k *nxt, uint8_t reg, uint8_t *data, size_t len)
+{
+	int error;
+
+	if (iic_acquire_bus(nxt->tag, I2C_F_POLL) != 0)
+		return false;
+
+	error = iic_exec(nxt->tag, I2C_OP_READ_WITH_STOP, nxt->addr,
+			 &reg, 1, data, len, I2C_F_POLL);
+
+	iic_release_bus(nxt->tag, I2C_F_POLL);
+
+	return error;
+}
+
+static int
+nxt2k_writereg(struct nxt2k *nxt, uint8_t reg, uint8_t *data, size_t len)
+{
+	uint8_t attr, len2, buf;
+
+	nxt2k_writedata(nxt, 0x35, &reg, 1);
+
+	nxt2k_writedata(nxt, 0x36, data, len);
+
+	attr = 0x02;
+	if (reg & 0x80) {
+		attr = attr << 1;
+		if (reg & 0x04)
+			attr = attr >> 1;
+	}
+	len2 = ((attr << 4) | 0x10) | len;
+	buf = 0x80;
+
+	nxt2k_writedata(nxt, 0x34, &len2, 1);
+
+	nxt2k_writedata(nxt, 0x21, &buf, 1);
+
+	nxt2k_readdata(nxt, 0x21, &buf, 1);
+
+	if (buf == 0)
+		return 0;
+
+	return -1;
+}
+
+static int
+nxt2k_readreg(struct nxt2k *nxt, uint8_t reg, uint8_t *data, size_t len)
+{
+	uint8_t buf, len2, attr;
+	int i;
+
+	nxt2k_writedata(nxt, 0x35, &reg, 1);
+
+	attr = 0x02;
+	if (reg & 0x80) {
+		attr = attr << 1;
+		if (reg & 0x04)
+			attr = attr >> 1;
+	}
+
+	len2 = (attr << 4) | len;
+	nxt2k_writedata(nxt, 0x34, &len2, 1);
+	
+	buf = 0x80;
+	nxt2k_writedata(nxt, 0x21, &buf, 1);
+
+	for(i = 0; i < len; i++) {
+		nxt2k_readdata(nxt, 0x36+i, &data[i], 1);
+	}
+
+	return 0;
+}
+
+static void
+nxt2k_agc_reset(struct nxt2k *nxt)
+{
+	uint8_t byte;
+	nxt2k_readreg(nxt, 0x08, &byte, 1);
+	byte = 0x08;
+	nxt2k_writereg(nxt, 0x08, &byte, 1);
+	byte = 0x00;
+	nxt2k_writereg(nxt, 0x08, &byte, 1);
+	return;
+}
+
+static void
+nxt2k_mc_stop(struct nxt2k *nxt)
+{
+	int counter;
+	uint8_t stopval, buf;
+
+	/* 2k4 */
+	stopval = 0x10;
+
+	buf = 0x80;
+	nxt2k_writedata(nxt, 0x22, &buf, 1);
+
+	for(counter = 0; counter < 20; counter++) {
+		nxt2k_readdata(nxt, 0x31, &buf, 1);
+		if (buf & stopval)
+			return;
+		mutex_enter(&nxt->mtx);
+		cv_timedwait(&nxt->cv, &nxt->mtx, mstohz(10));
+		mutex_exit(&nxt->mtx);
+	}
+
+	printf("%s timeout\n", __func__);
+	
+	return;
+}
+
+static void
+nxt2k_mc_start(struct nxt2k *nxt)
+{
+	uint8_t buf;
+	
+	buf = 0x00;
+	nxt2k_writedata(nxt, 0x22, &buf, 1);
+}
+
+static void
+nxt2k4_mc_init(struct nxt2k *nxt)
+{
+	uint8_t byte;
+	uint8_t data[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xC0};
+	int counter;
+
+	byte = 0x00;
+	nxt2k_writedata(nxt, 0x2b, &byte, 1);
+	byte = 0x70;
+	nxt2k_writedata(nxt, 0x34, &byte, 1);
+	byte = 0x04;
+	nxt2k_writedata(nxt, 0x35, &byte, 1);
+
+	nxt2k_writedata(nxt, 0x36, data, 9);
+
+	byte = 0x80;
+	nxt2k_writedata(nxt, 0x21, &byte, 1);
+
+	for(counter = 0; counter < 20; counter++) {
+		nxt2k_readdata(nxt, 0x21, &byte, 1);
+		if ( byte == 0 )
+			return;
+		mutex_enter(&nxt->mtx);
+		cv_timedwait(&nxt->cv, &nxt->mtx, mstohz(25));
+		mutex_exit(&nxt->mtx);
+	}
+
+	printf("%s timeout\n", __func__);
+
+	return;
+}
+
+/* CRC-CCIT */
+static uint16_t
+nxt2k_crc_ccit(uint16_t crc, uint8_t byte)
+{
+	int i;
+	uint16_t input;
+	
+	input = byte << 8;
+
+	for(i = 0; i < 8; i++) {
+		if ((crc ^ input) & 0x8000)
+			crc = (crc << 1) ^ 0x1021;
+		else
+			crc = (crc << 1);
+		input = input << 1;
+	}
+	return crc;
+}
+
+static bool
+nxt2k4_load_firmware(struct nxt2k *nxt)
+{
+	firmware_handle_t fh;
+	uint8_t *blob;
+	size_t fwsize;
+	size_t position;
+	int error;
+	uint16_t crc;
+
+	error = firmware_open("nxt2k", "dvb-fe-nxt2004.fw", &fh);
+	if (error != 0) {
+		printf("nxt2k firmware_open fail %d\n", error);
+		return 0;
+	}
+
+	fwsize = firmware_get_size(fh);
+	printf("fwsize %zd\n", fwsize);
+	blob = firmware_malloc(fwsize);
+	if ( blob == NULL ) {
+		printf("nxt2k firmware_malloc fail\n");
+		firmware_close(fh);
+		return -1;
+	}
+
+	error = firmware_read(fh, 0, blob, fwsize);
+	if (error != 0) {
+		printf("nxt2k firmware_read fail %d\n", error);
+		firmware_free(blob, fwsize);
+		firmware_close(fh);
+		return -1;
+	}
+
+	/* calculate CRC */	
+	crc = 0;
+	for(position = 0; position < fwsize; position++) {
+		crc = nxt2k_crc_ccit(crc, blob[position]);
+	}
+	printf("nxt2k firmware crc is %02x\n", crc);
+
+	uint16_t rambase;
+	uint8_t buf[3];
+
+	rambase = 0x1000;
+
+	/* hold the micro in reset while loading firmware */
+	buf[0] = 0x80;
+	nxt2k_writedata(nxt, 0x2b, buf, 1);
+
+	buf[0] = rambase >> 8;
+	buf[1] = rambase & 0xFF;
+	buf[2] = 0x81;
+	/* write starting address */
+	nxt2k_writedata(nxt, 0x29, buf, 3);
+
+	position = 0;
+
+	size_t xfercnt;
+
+	while ( position < fwsize ) {
+		xfercnt = fwsize - position > 255 ? 255 : fwsize - position;
+		nxt2k_writedata(nxt, 0x2c, &blob[position], xfercnt);
+		position += xfercnt;
+	}
+
+	/* write crc */
+        buf[0] = crc >> 8;
+        buf[1] = crc & 0xFF;
+	nxt2k_writedata(nxt, 0x2c, buf, 2);
+
+	/* do a read to stop things */
+	nxt2k_readdata(nxt, 0x2c, buf, 1);
+	
+	/* set transfer mode to complete */
+	buf[0] = 0x80;
+	nxt2k_writedata(nxt, 0x2b, buf, 1);
+	
+	firmware_free(blob, fwsize);
+	firmware_close(fh);
+
+	return 1;
+}
+
+static int 
+nxt2k4_init(struct nxt2k *nxt)
+{
+	int success;
+	uint8_t buf[3];
+
+	buf[0] = 0x00;
+	nxt2k_writedata(nxt, 0x1e, buf, 1);
+
+	/* try to load firmware */
+	nxt->loaded = nxt2k4_load_firmware(nxt);
+	if (nxt->loaded == false)
+		return ECANCELED;
+
+	/* ensure transfer is complete */
+	buf[0] = 0x01;
+	nxt2k_writedata(nxt, 0x19, buf, 1);
+
+	nxt2k4_mc_init(nxt);
+	nxt2k_mc_stop(nxt);
+	nxt2k_mc_stop(nxt);
+	nxt2k4_mc_init(nxt);
+	nxt2k_mc_stop(nxt);
+
+	buf[0] = 0xff;
+	nxt2k_writereg(nxt, 0x08, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writereg(nxt, 0x08, buf, 1);
+
+	buf[0] = 0xD7;
+	nxt2k_writedata(nxt, 0xd7, buf, 1);
+
+	buf[0] = 0x07;
+	buf[1] = 0xfe;
+	nxt2k_writedata(nxt, 0x35, buf, 2);
+	buf[0] = 0x12;
+	nxt2k_writedata(nxt, 0x34, buf, 1);
+	buf[0] = 0x80;
+	nxt2k_writedata(nxt, 0x21, buf, 1);
+
+	buf[0] = 0x21;
+	nxt2k_writedata(nxt, 0x0a, buf, 1);
+
+	buf[0] = 0x01;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+
+	/* fec mpeg mode */
+	buf[0] = 0x7E;
+	buf[1] = 0x00;
+	nxt2k_writedata(nxt, 0xe9, buf, 2);
+
+	/* mux selection */
+	buf[0] = 0x00;
+	nxt2k_writedata(nxt, 0xcc, buf, 1);
+
+	/* */
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+
+	/* soft reset? */	
+	nxt2k_readreg(nxt, 0x08, buf, 1);
+	buf[0] = 0x10;
+	nxt2k_writereg(nxt, 0x08, buf, 1);
+	nxt2k_readreg(nxt, 0x08, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writereg(nxt, 0x08, buf, 1);
+	
+	/* */
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x01;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+	buf[0] = 0x70;
+	nxt2k_writereg(nxt, 0x81, buf, 1);
+	buf[0] = 0x31; buf[1] = 0x5E; buf[2] = 0x66;
+	nxt2k_writereg(nxt, 0x82, buf, 3);
+
+	nxt2k_readreg(nxt, 0x88, buf, 1);
+	buf[0] = 0x11;
+	nxt2k_writereg(nxt, 0x88, buf, 1);
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x40;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+
+	nxt2k_readdata(nxt, 0x10, buf, 1);
+	buf[0] = 0x10;
+	nxt2k_writedata(nxt, 0x10, buf, 1);
+	nxt2k_readdata(nxt, 0x0a, buf, 1);
+	buf[0] = 0x21;
+	nxt2k_writedata(nxt, 0x0a, buf, 1);
+	
+	nxt2k4_mc_init(nxt);
+
+	buf[0] = 0x21;
+	nxt2k_writedata(nxt, 0x0a, buf, 1);
+	buf[0] = 0x7e;
+	nxt2k_writedata(nxt, 0xe9, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writedata(nxt, 0xea, buf, 1);
+
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+
+	nxt2k_readreg(nxt, 0x08, buf, 1);
+	buf[0] = 0x10;
+	nxt2k_writereg(nxt, 0x08, buf, 1);
+	nxt2k_readreg(nxt, 0x08, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writereg(nxt, 0x08, buf, 1);
+
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x04;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writereg(nxt, 0x81, buf, 1);
+	buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00;
+	nxt2k_writereg(nxt, 0x82, buf, 3);
+
+	nxt2k_readreg(nxt, 0x88, buf, 1);
+	buf[0] = 0x11;
+	nxt2k_writereg(nxt, 0x88, buf, 1);
+
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x44;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+
+	/* init tuner */
+	nxt2k_readdata(nxt, 0x10, buf, 1);
+	buf[0] = 0x12;
+	nxt2k_writedata(nxt, 0x10, buf,1);
+	buf[0] = 0x04;
+	nxt2k_writedata(nxt, 0x13, buf,1);
+	buf[0] = 0x00;
+	nxt2k_writedata(nxt, 0x16, buf,1);
+	buf[0] = 0x04;
+	nxt2k_writedata(nxt, 0x14, buf,1);
+	buf[0] = 0x00;
+	nxt2k_writedata(nxt, 0x14, buf,1);
+	nxt2k_writedata(nxt, 0x17, buf,1);
+	nxt2k_writedata(nxt, 0x14, buf,1);
+	nxt2k_writedata(nxt, 0x17, buf,1);
+
+	success = 1;
+	return success;
+}
+
+uint16_t
+nxt2k_get_signal(struct nxt2k *nxt)
+{
+	uint16_t temp;
+	uint8_t b[2];
+	
+	b[0] = 0x00;
+	nxt2k_writedata(nxt, 0xa1, b, 1);
+
+	nxt2k_readreg(nxt, 0xa6, b, 2);
+
+	temp = (b[0] << 8) | b[1];
+
+	printf("a6: %04hx\n", temp);
+
+	return 0x7fff - temp * 16;
+}
+
+uint16_t
+nxt2k_get_snr(struct nxt2k *nxt)
+{
+	uint32_t tsnr;
+	uint16_t temp, temp2;
+	uint8_t b[2];
+
+	b[0] = 0x00;
+	nxt2k_writedata(nxt, 0xa1, b, 1);
+
+	nxt2k_readreg(nxt, 0xa6, b, 2);
+	
+	temp = (b[0] << 8) | b[1];
+
+	temp2 = 0x7fff - temp;
+
+	printf("snr temp2: %04hx\n", temp2);
+
+	if (temp2 > 0x7f00)
+		tsnr = 1000*24+(1000*(30-24)*(temp2-0x7f00)/(0x7fff-0x7f00));
+	else if ( temp2 > 0x7ec0)
+		tsnr = 1000*18+(1000*(24-18)*(temp2-0x7ec0)/(0x7f00-0x7ec0));
+	else if ( temp2 > 0x7c00)
+		tsnr = 1000*12+(1000*(18-12)*(temp2-0x7c00)/(0x7ec0-0x7c00));
+	else
+		tsnr = 1000*0+(1000*(12-0)*(temp2-0)/(0x7c00-0));
+
+	printf("snr tsnr: %08x\n", tsnr);
+
+	return ((tsnr * 0xffff)/32000);
+}
+
+fe_status_t
+nxt2k_get_dtv_status(struct nxt2k *nxt)
+{
+	uint8_t reg;
+	fe_status_t status = 0;
+
+	nxt2k_readdata(nxt, 0x31, &reg, 1);
+	if (reg & 0x20) {
+		status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+		    FE_HAS_SYNC | FE_HAS_LOCK;
+	}
+
+	return status;
+}
+
+#if notyet
+int
+nxt2k_fe_read_ucblocks(struct nxt2k *nxt, uint32_t *ucblk)
+{
+	uint8_t reg[3];
+	
+	nxt2k_readreg(nxt, 0xe6, reg, 3);
+	*ucblk = reg[2];
+
+	return 0;
+}
+
+int
+nxt2k_fe_read_ber(struct nxt2k *nxt, uint32_t *ber)
+{
+	uint8_t reg[3];
+
+	nxt2k_readreg(nxt, 0xe6, reg, 3);
+
+	*ber = ((reg[0] << 8) + reg[1]) * 8;
+
+	return 0;
+}
+#endif
+
+static int
+nxt2k_fe_set_frontend(struct nxt2k *nxt, fe_modulation_t modulation)
+{
+	uint8_t buf[5];
+
+	if (nxt->loaded != true)
+		nxt2k4_init(nxt);
+
+	nxt2k_mc_stop(nxt);
+
+	{ /* 2k4 */
+	/* make sure demod is set to digital */
+	buf[0] = 0x04;
+	nxt2k_writedata(nxt, 0x14, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writedata(nxt, 0x17, buf, 1);
+	}
+
+	/* QAM/VSB punctured/non-punctured goes here */
+
+	/* tune in */
+	/* maybe ensure tuner managed to tune in? */
+
+	/* tuning done, reset agc */
+	nxt2k_agc_reset(nxt);
+
+	/* set target power level */
+	switch (modulation) {
+	case VSB_8:
+		buf[0] = 0x70;
+		break;
+	case QAM_256:
+	case QAM_64:
+		buf[0] = 0x74;
+		break;
+	default:
+		return EINVAL;
+		/* NOTREACHED */
+	}
+	nxt2k_writedata(nxt, 0x42, buf, 1);
+
+	/* configure sdm */
+	buf[0] = 0x07; /* 2k4 */
+	nxt2k_writedata(nxt, 0x57, buf, 1);
+
+	/* write sdm1 input */
+        buf[0] = 0x10;
+        buf[1] = 0x00;
+	nxt2k_writedata(nxt, 0x58, buf, 2); /* 2k4 */
+
+	/* write sdmx input */
+	switch (modulation) {
+	case VSB_8:
+		buf[0] = 0x60;
+		break;
+	case QAM_256:
+		buf[0] = 0x64;
+		break;
+	case QAM_64:
+		buf[0] = 0x68;
+		break;
+	default:
+		return EINVAL;
+		/* NOTREACHED */
+	}
+        buf[1] = 0x00;
+	nxt2k_writedata(nxt, 0x5c, buf, 2); /* 2k4 */
+
+	/* write adc power lpf fc */
+	buf[0] = 0x05;
+	nxt2k_writedata(nxt, 0x43, buf, 1);
+	
+	{ /* 2k4 */
+	buf[0] = 0x00;
+	buf[1] = 0x00;
+	nxt2k_writedata(nxt, 0x46, buf, 2);
+	}
+
+	/* write accumulator2 input */
+	buf[0] = 0x80;
+	buf[1] = 0x00;
+	nxt2k_writedata(nxt, 0x4b, buf, 2); /* 2k4 */
+
+	/* write kg1 */
+	buf[0] = 0x00;
+	nxt2k_writedata(nxt, 0x4d, buf, 1);
+
+	/* write sdm12 lpf fc */
+	buf[0] = 0x44;
+	nxt2k_writedata(nxt, 0x55, buf, 1);
+
+	/* write agc control reg */
+	buf[0] = 0x04;
+	nxt2k_writedata(nxt, 0x41, buf, 1);
+	
+	{ /* 2k4 */
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x24;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+	
+	/* soft reset? */
+	nxt2k_readreg(nxt, 0x08, buf, 1);
+	buf[0] = 0x10;
+	nxt2k_writereg(nxt, 0x08, buf, 1);
+	nxt2k_readreg(nxt, 0x08, buf, 1);
+	buf[0] = 0x00;
+	nxt2k_writereg(nxt, 0x08, buf, 1);
+	
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x04;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+
+	buf[0] = 0x00;
+	nxt2k_writereg(nxt, 0x81, buf, 1);
+
+	buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00;
+	nxt2k_writereg(nxt, 0x82, buf, 3);
+	
+	nxt2k_readreg(nxt, 0x88, buf, 1);
+	buf[0] = 0x11;
+	nxt2k_writereg(nxt, 0x88, buf, 1);
+
+	nxt2k_readreg(nxt, 0x80, buf, 1);
+	buf[0] = 0x44;
+	nxt2k_writereg(nxt, 0x80, buf, 1);
+	}
+
+	/* write agc ucgp0 */
+	switch (modulation) {
+	case VSB_8:
+		buf[0] = 0x00;
+		break;
+	case QAM_64:
+		buf[0] = 0x02;
+		break;
+	case QAM_256:
+		buf[0] = 0x03;
+		break;
+	default:
+		return EINVAL;
+		/* NOTREACHED */
+	}
+	nxt2k_writedata(nxt, 0x30, buf, 1);
+	
+	/* write agc control reg */
+	buf[0] = 0x00;
+	nxt2k_writedata(nxt, 0x41, buf, 1);
+
+	/* write accumulator2 input */
+	buf[0] = 0x80;
+	buf[1] = 0x00;
+	{ /* 2k4 */
+	nxt2k_writedata(nxt, 0x49, buf, 2);
+	nxt2k_writedata(nxt, 0x4b, buf, 2);
+	}
+	
+	/* write agc control reg */
+	buf[0] = 0x04;
+	nxt2k_writedata(nxt, 0x41, buf, 1);
+	
+	nxt2k_mc_start(nxt);
+	
+	{ /* 2k4 */
+	nxt2k4_mc_init(nxt);
+	buf[0] = 0xf0;
+	buf[1] = 0x00;
+	nxt2k_writedata(nxt, 0x5c, buf, 2);
+	}
+
+	/* "adjacent channel detection" code would go here */
+
+	return 0;
+}
+
+static int
+nxt2k_init(struct nxt2k *nxt)
+{
+	int ret = 0;
+
+	printf("%s\n", __func__);
+
+	if (nxt->loaded != 1)
+		ret = nxt2k4_init(nxt);
+
+	return ret;
+}
+
+
+struct nxt2k *
+nxt2k_open(device_t parent, i2c_tag_t tag, i2c_addr_t addr, unsigned int if_freq)
+{
+	struct nxt2k *nxt;
+	int e;
+	uint8_t b[5];
+
+	nxt = kmem_alloc(sizeof(*nxt), KM_SLEEP);
+	if (nxt == NULL)
+		return NULL;
+
+	nxt->parent = parent;
+	nxt->tag = tag;
+	nxt->addr = addr;
+
+	/* read chip ids */
+	e = nxt2k_readdata(nxt, 0x00, b, 5);
+
+	if (e) {
+		printf("%s read failed %d\n", __func__, e);
+		kmem_free(nxt, sizeof(*nxt));
+		return NULL;
+	}
+
+	if (b[0] != 0x05) {
+		printf("%s unsupported %02x %02x %02x %02x %02x\n",
+		    __func__, b[0], b[1], b[2], b[3], b[4]);
+		kmem_free(nxt, sizeof(*nxt));
+		return NULL;
+	}
+
+	mutex_init(&nxt->mtx, MUTEX_DEFAULT, IPL_NONE);
+	cv_init(&nxt->cv, "nxtpl");
+
+	nxt->loaded = false;
+
+	return nxt;
+}
+
+void
+nxt2k_close(struct nxt2k *nxt)
+{
+	kmem_free(nxt, sizeof(*nxt));
+}
+
+void
+nxt2k_enable(struct nxt2k *nxt, bool enable)
+{
+	if (enable == true)
+		nxt2k_init(nxt);
+}
+
+int
+nxt2k_set_modulation(struct nxt2k *nxt, fe_modulation_t modulation)
+{
+	return nxt2k_fe_set_frontend(nxt, modulation);
+}
Index: src/sys/dev/i2c/nxt2kvar.h
diff -u /dev/null src/sys/dev/i2c/nxt2kvar.h:1.1
--- /dev/null	Mon Jul 11 00:30:23 2011
+++ src/sys/dev/i2c/nxt2kvar.h	Mon Jul 11 00:30:23 2011
@@ -0,0 +1,45 @@
+/* $NetBSD: nxt2kvar.h,v 1.1 2011/07/11 00:30:23 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 2008, 2011 Jonathan A. Kollasch
+ * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 _DEV_I2C_NXT2KVAR_H_
+#define _DEV_I2C_NXT2KVAR_H_
+
+#include <dev/i2c/i2cvar.h>
+#include <dev/dtv/dtvio.h>
+
+struct nxt2k;
+
+struct nxt2k * nxt2k_open(device_t, i2c_tag_t, i2c_addr_t, unsigned int);
+void nxt2k_close(struct nxt2k *);
+void nxt2k_enable(struct nxt2k *, bool);
+int nxt2k_set_modulation(struct nxt2k *, fe_modulation_t);
+fe_status_t nxt2k_get_dtv_status(struct nxt2k *);
+uint16_t nxt2k_get_signal(struct nxt2k *);
+uint16_t nxt2k_get_snr(struct nxt2k *);
+
+#endif /* _DEV_I2C_NXT2KVAR_H_ */

Reply via email to