linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] wintv 930c-hd: Add basic support
@ 2014-07-01 19:55 Matthias Schwarzott
  2014-07-01 19:55 ` [PATCH 1/4] get_dvb_firmware: Add firmware extractor for si2165 Matthias Schwarzott
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Matthias Schwarzott @ 2014-07-01 19:55 UTC (permalink / raw)
  To: linux-media, crope

This patch series is the third version of my si2165 driver.
It supports only DVB-T and was tested on 8MHz channels in germany.

Maybe the si2165 driver also works on other si2165/si2163/si2161 based cards.

Regards
Matthias



 Documentation/dvb/get_dvb_firmware         |   33 +-
 drivers/media/dvb-frontends/Kconfig        |    9 +
 drivers/media/dvb-frontends/Makefile       |    1 +
 drivers/media/dvb-frontends/si2165.c       | 1036 ++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/si2165.h       |   61 ++
 drivers/media/dvb-frontends/si2165_priv.h  |   23 +
 drivers/media/pci/cx23885/Kconfig          |    1 +
 drivers/media/pci/cx23885/cx23885-cards.c  |   17 +-
 drivers/media/pci/cx23885/cx23885-dvb.c    |   43 +-
 drivers/media/usb/cx231xx/Kconfig          |    1 +
 drivers/media/usb/cx231xx/cx231xx-avcore.c |    1 +
 drivers/media/usb/cx231xx/cx231xx-cards.c  |   92 +++
 drivers/media/usb/cx231xx/cx231xx-core.c   |    3 +
 drivers/media/usb/cx231xx/cx231xx-dvb.c    |   32 +
 drivers/media/usb/cx231xx/cx231xx.h        |    1 +
 15 files changed, 1345 insertions(+), 9 deletions(-)


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] get_dvb_firmware: Add firmware extractor for si2165
  2014-07-01 19:55 [PATCH 0/4] wintv 930c-hd: Add basic support Matthias Schwarzott
@ 2014-07-01 19:55 ` Matthias Schwarzott
  2014-07-01 19:55 ` [PATCH 2/4] si2165: Add demod driver for DVB-T only Matthias Schwarzott
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Matthias Schwarzott @ 2014-07-01 19:55 UTC (permalink / raw)
  To: linux-media, crope; +Cc: Matthias Schwarzott

Signed-off-by: Matthias Schwarzott <zzam@gentoo.org>
---
 Documentation/dvb/get_dvb_firmware | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
index d91b8be..26c623d 100755
--- a/Documentation/dvb/get_dvb_firmware
+++ b/Documentation/dvb/get_dvb_firmware
@@ -29,7 +29,7 @@ use IO::Handle;
 		"af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395",
 		"lme2510c_s7395_old", "drxk", "drxk_terratec_h5",
 		"drxk_hauppauge_hvr930c", "tda10071", "it9135", "drxk_pctv",
-		"drxk_terratec_htc_stick", "sms1xxx_hcw");
+		"drxk_terratec_htc_stick", "sms1xxx_hcw", "si2165");
 
 # Check args
 syntax() if (scalar(@ARGV) != 1);
@@ -783,6 +783,37 @@ sub sms1xxx_hcw {
     $allfiles;
 }
 
+sub si2165 {
+    my $sourcefile = "model_111xxx_122xxx_driver_6_0_119_31191_WHQL.zip";
+    my $url = "http://www.hauppauge.de/files/drivers/";
+    my $hash = "76633e7c76b0edee47c3ba18ded99336";
+    my $fwfile = "dvb-demod-si2165.fw";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+    checkstandard();
+
+    wgetfile($sourcefile, $url . $sourcefile);
+    verify($sourcefile, $hash);
+    unzip($sourcefile, $tmpdir);
+    extract("$tmpdir/Driver10/Hcw10bda.sys", 0x80788, 0x81E08-0x80788, "$tmpdir/fw1");
+
+    delzero("$tmpdir/fw1","$tmpdir/fw1-1");
+    #verify("$tmpdir/fw1","5e0909858fdf0b5b09ad48b9fe622e70");
+
+    my $CRC="\x0A\xCC";
+    my $BLOCKS_MAIN="\x27";
+    open FW,">$fwfile";
+    print FW "\x01\x00"; # just a version id for the driver itself
+    print FW "\x9A"; # fw version
+    print FW "\x00"; # padding
+    print FW "$BLOCKS_MAIN"; # number of blocks of main part
+    print FW "\x00"; # padding
+    print FW "$CRC"; # 16bit crc value of main part
+    appendfile(FW,"$tmpdir/fw1");
+
+    "$fwfile";
+}
+
 # ---------------------------------------------------------------
 # Utilities
 
-- 
2.0.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] si2165: Add demod driver for DVB-T only
  2014-07-01 19:55 [PATCH 0/4] wintv 930c-hd: Add basic support Matthias Schwarzott
  2014-07-01 19:55 ` [PATCH 1/4] get_dvb_firmware: Add firmware extractor for si2165 Matthias Schwarzott
@ 2014-07-01 19:55 ` Matthias Schwarzott
  2014-07-01 19:55 ` [PATCH 3/4] cx231xx: Add [2040:b130] Hauppauge WinTV 930C-hd 1113xx Matthias Schwarzott
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Matthias Schwarzott @ 2014-07-01 19:55 UTC (permalink / raw)
  To: linux-media, crope; +Cc: Matthias Schwarzott

DVB-T was tested  with 8MHz BW channels in germany
This driver is the simplest possible, it uses automatic mode for all
parameters (TPS).

Signed-off-by: Matthias Schwarzott <zzam@gentoo.org>
---
 drivers/media/dvb-frontends/Kconfig       |    9 +
 drivers/media/dvb-frontends/Makefile      |    1 +
 drivers/media/dvb-frontends/si2165.c      | 1036 +++++++++++++++++++++++++++++
 drivers/media/dvb-frontends/si2165.h      |   61 ++
 drivers/media/dvb-frontends/si2165_priv.h |   23 +
 5 files changed, 1130 insertions(+)
 create mode 100644 drivers/media/dvb-frontends/si2165.c
 create mode 100644 drivers/media/dvb-frontends/si2165.h
 create mode 100644 drivers/media/dvb-frontends/si2165_priv.h

diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index 1469d44..0da53c2 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -63,6 +63,15 @@ config DVB_TDA18271C2DD
 
 	  Say Y when you want to support this tuner.
 
+config DVB_SI2165
+	tristate "Silicon Labs si2165 based"
+	depends on DVB_CORE && I2C
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+	help
+	  A DVB-C/T demodulator.
+
+	  Say Y when you want to support this frontend.
+
 comment "DVB-S (satellite) frontends"
 	depends on DVB_CORE
 
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index dda0bee..595dd8d 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_DVB_STV0367) += stv0367.o
 obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
 obj-$(CONFIG_DVB_DRXK) += drxk.o
 obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
+obj-$(CONFIG_DVB_SI2165) += si2165.o
 obj-$(CONFIG_DVB_A8293) += a8293.o
 obj-$(CONFIG_DVB_TDA10071) += tda10071.o
 obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c
new file mode 100644
index 0000000..bc122e4
--- /dev/null
+++ b/drivers/media/dvb-frontends/si2165.c
@@ -0,0 +1,1036 @@
+/*
+    Driver for Silicon Labs SI2165 DVB-C/-T Demodulator
+
+    Copyright (C) 2013-2014 Matthias Schwarzott <zzam@gentoo.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    References:
+    http://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf
+*/
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "dvb_math.h"
+#include "si2165_priv.h"
+#include "si2165.h"
+
+/* Hauppauge WinTV-HVR-930C-HD B130 / PCTV QuatroStick 521e 1113xx
+ * uses 16 MHz xtal */
+
+/* Hauppauge WinTV-HVR-930C-HD B131 / PCTV QuatroStick 522e 1114xx
+ * uses 24 MHz clock provided by tuner */
+
+struct si2165_state {
+	struct i2c_adapter *i2c;
+
+	struct dvb_frontend frontend;
+
+	struct si2165_config config;
+
+	/* chip revision */
+	u8 revcode;
+	/* chip type */
+	u8 chip_type;
+
+	/* calculated by xtal and div settings */
+	u32 fvco_hz;
+	u32 sys_clk;
+	u32 adc_clk;
+
+	bool has_dvbc;
+	bool has_dvbt;
+	bool firmware_loaded;
+};
+
+#define DEBUG_OTHER	0x01
+#define DEBUG_I2C_WRITE	0x02
+#define DEBUG_I2C_READ	0x04
+#define DEBUG_REG_READ	0x08
+#define DEBUG_REG_WRITE	0x10
+#define DEBUG_FW_LOAD	0x20
+
+static int debug = 0x00;
+
+#define dprintk(args...) \
+	do { \
+		if (debug & DEBUG_OTHER) \
+			printk(KERN_DEBUG "si2165: " args); \
+	} while (0)
+
+#define deb_i2c_write(args...) \
+	do { \
+		if (debug & DEBUG_I2C_WRITE) \
+			printk(KERN_DEBUG "si2165: i2c write: " args); \
+	} while (0)
+
+#define deb_i2c_read(args...) \
+	do { \
+		if (debug & DEBUG_I2C_READ) \
+			printk(KERN_DEBUG "si2165: i2c read: " args); \
+	} while (0)
+
+#define deb_readreg(args...) \
+	do { \
+		if (debug & DEBUG_REG_READ) \
+			printk(KERN_DEBUG "si2165: reg read: " args); \
+	} while (0)
+
+#define deb_writereg(args...) \
+	do { \
+		if (debug & DEBUG_REG_WRITE) \
+			printk(KERN_DEBUG "si2165: reg write: " args); \
+	} while (0)
+
+#define deb_fw_load(args...) \
+	do { \
+		if (debug & DEBUG_FW_LOAD) \
+			printk(KERN_DEBUG "si2165: fw load: " args); \
+	} while (0)
+
+static int si2165_write(struct si2165_state *state, const u16 reg,
+		       const u8 *src, const int count)
+{
+	int ret;
+	struct i2c_msg msg;
+	u8 buf[2 + 4]; /* write a maximum of 4 bytes of data */
+
+	if (count + 2 > sizeof(buf)) {
+		dev_warn(&state->i2c->dev,
+			  "%s: i2c wr reg=%04x: count=%d is too big!\n",
+			  KBUILD_MODNAME, reg, count);
+		return -EINVAL;
+	}
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+	memcpy(buf + 2, src, count);
+
+	msg.addr = state->config.i2c_addr;
+	msg.flags = 0;
+	msg.buf = buf;
+	msg.len = count + 2;
+
+	if (debug & DEBUG_I2C_WRITE)
+		deb_i2c_write("reg: 0x%04x, data: %*ph\n", reg, count, src);
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1) {
+		dev_err(&state->i2c->dev, "%s: ret == %d\n", __func__, ret);
+		if (ret < 0)
+			return ret;
+		else
+			return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int si2165_read(struct si2165_state *state,
+		       const u16 reg, u8 *val, const int count)
+{
+	int ret;
+	u8 reg_buf[] = { reg >> 8, reg & 0xff };
+	struct i2c_msg msg[] = {
+		{ .addr = state->config.i2c_addr,
+		  .flags = 0, .buf = reg_buf, .len = 2 },
+		{ .addr = state->config.i2c_addr,
+		  .flags = I2C_M_RD, .buf = val, .len = count },
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		dev_err(&state->i2c->dev, "%s: error (addr %02x reg %04x error (ret == %i)\n",
+			__func__, state->config.i2c_addr, reg, ret);
+		if (ret < 0)
+			return ret;
+		else
+			return -EREMOTEIO;
+	}
+
+	if (debug & DEBUG_I2C_READ)
+		deb_i2c_read("reg: 0x%04x, data: %*ph\n", reg, count, val);
+
+	return 0;
+}
+
+static int si2165_readreg8(struct si2165_state *state,
+		       const u16 reg, u8 *val)
+{
+	int ret;
+
+	ret = si2165_read(state, reg, val, 1);
+	deb_readreg("R(0x%04x)=0x%02x\n", reg, *val);
+	return ret;
+}
+
+static int si2165_readreg16(struct si2165_state *state,
+		       const u16 reg, u16 *val)
+{
+	u8 buf[2];
+
+	int ret = si2165_read(state, reg, buf, 2);
+	*val = buf[0] | buf[1] << 8;
+	deb_readreg("R(0x%04x)=0x%04x\n", reg, *val);
+	return ret;
+}
+
+static int si2165_writereg8(struct si2165_state *state, const u16 reg, u8 val)
+{
+	return si2165_write(state, reg, &val, 1);
+}
+
+static int si2165_writereg16(struct si2165_state *state, const u16 reg, u16 val)
+{
+	u8 buf[2] = { val & 0xff, (val >> 8) & 0xff };
+
+	return si2165_write(state, reg, buf, 2);
+}
+
+static int si2165_writereg24(struct si2165_state *state, const u16 reg, u32 val)
+{
+	u8 buf[3] = { val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff };
+
+	return si2165_write(state, reg, buf, 3);
+}
+
+static int si2165_writereg32(struct si2165_state *state, const u16 reg, u32 val)
+{
+	u8 buf[4] = {
+		val & 0xff,
+		(val >> 8) & 0xff,
+		(val >> 16) & 0xff,
+		(val >> 24) & 0xff
+	};
+	return si2165_write(state, reg, buf, 4);
+}
+
+static int si2165_writereg_mask8(struct si2165_state *state, const u16 reg,
+				 u8 val, u8 mask)
+{
+	int ret;
+	u8 tmp;
+
+	if (mask != 0xff) {
+		ret = si2165_readreg8(state, reg, &tmp);
+		if (ret < 0)
+			goto err;
+
+		val &= mask;
+		tmp &= ~mask;
+		val |= tmp;
+	}
+
+	ret = si2165_writereg8(state, reg, val);
+err:
+	return ret;
+}
+
+static int si2165_get_tune_settings(struct dvb_frontend *fe,
+				    struct dvb_frontend_tune_settings *s)
+{
+	s->min_delay_ms = 1000;
+	return 0;
+}
+
+static int si2165_init_pll(struct si2165_state *state)
+{
+	u32 ref_freq_Hz = state->config.ref_freq_Hz;
+	u8 divr = 1; /* 1..7 */
+	u8 divp = 1; /* only 1 or 4 */
+	u8 divn = 56; /* 1..63 */
+	u8 divm = 8;
+	u8 divl = 12;
+	u8 buf[4];
+
+	/* hardcoded values can be deleted if calculation is verified
+	 * or it yields the same values as the windows driver */
+	switch (ref_freq_Hz) {
+	case 16000000u:
+		divn = 56;
+		break;
+	case 24000000u:
+		divr = 2;
+		divp = 4;
+		divn = 19;
+		break;
+	default:
+		/* ref_freq / divr must be between 4 and 16 MHz */
+		if (ref_freq_Hz > 16000000u)
+			divr = 2;
+
+		/* now select divn and divp such that
+		 * fvco is in 1624..1824 MHz */
+		if (1624000000u * divr > ref_freq_Hz * 2u * 63u)
+			divp = 4;
+
+		/* is this already correct regarding rounding? */
+		divn = 1624000000u * divr / (ref_freq_Hz * 2u * divp);
+		break;
+	}
+
+	/* adc_clk and sys_clk depend on xtal and pll settings */
+	state->fvco_hz = ref_freq_Hz / divr
+			* 2u * divn * divp;
+	state->adc_clk = state->fvco_hz / (divm * 4u);
+	state->sys_clk = state->fvco_hz / (divl * 2u);
+
+	/* write pll registers 0x00a0..0x00a3 at once */
+	buf[0] = divl;
+	buf[1] = divm;
+	buf[2] = (divn & 0x3f) | ((divp == 1) ? 0x40 : 0x00) | 0x80;
+	buf[3] = divr;
+	return si2165_write(state, 0x00a0, buf, 4);
+}
+
+static int si2165_adjust_pll_divl(struct si2165_state *state, u8 divl)
+{
+	state->sys_clk = state->fvco_hz / (divl * 2u);
+	return si2165_writereg8(state, 0x00a0, divl); /* pll_divl */
+}
+
+static u32 si2165_get_fe_clk(struct si2165_state *state)
+{
+	/* assume Oversampling mode Ovr4 is used */
+	return state->adc_clk;
+}
+
+static bool si2165_wait_init_done(struct si2165_state *state)
+{
+	int ret = -EINVAL;
+	u8 val = 0;
+	int i;
+
+	for (i = 0; i < 3; ++i) {
+		si2165_readreg8(state, 0x0054, &val);
+		if (val == 0x01)
+			return 0;
+		usleep_range(1000, 50000);
+	}
+	dev_err(&state->i2c->dev, "%s: init_done was not set\n",
+		KBUILD_MODNAME);
+	return ret;
+}
+
+static int si2165_upload_firmware_block(struct si2165_state *state,
+	const u8 *data, u32 len, u32 *poffset, u32 block_count)
+{
+	int ret;
+	u8 buf_ctrl[4] = { 0x00, 0x00, 0x00, 0xc0 };
+	u8 wordcount;
+	u32 cur_block = 0;
+	u32 offset = poffset ? *poffset : 0;
+
+	if (len < 4)
+		return -EINVAL;
+	if (len % 4 != 0)
+		return -EINVAL;
+
+	deb_fw_load("si2165_upload_firmware_block called with len=0x%x offset=0x%x blockcount=0x%x\n",
+				len, offset, block_count);
+	while (offset+12 <= len && cur_block < block_count) {
+		deb_fw_load("si2165_upload_firmware_block in while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
+					len, offset, cur_block, block_count);
+		wordcount = data[offset];
+		if (wordcount < 1 || data[offset+1] ||
+		    data[offset+2] || data[offset+3]) {
+			dev_warn(&state->i2c->dev,
+				 "%s: bad fw data[0..3] = %*ph\n",
+				KBUILD_MODNAME, 4, data);
+			return -EINVAL;
+		}
+
+		if (offset + 8 + wordcount * 4 > len) {
+			dev_warn(&state->i2c->dev,
+				 "%s: len is too small for block len=%d, wordcount=%d\n",
+				KBUILD_MODNAME, len, wordcount);
+			return -EINVAL;
+		}
+
+		buf_ctrl[0] = wordcount - 1;
+
+		ret = si2165_write(state, 0x0364, buf_ctrl, 4);
+		if (ret < 0)
+			goto error;
+		ret = si2165_write(state, 0x0368, data+offset+4, 4);
+		if (ret < 0)
+			goto error;
+
+		offset += 8;
+
+		while (wordcount > 0) {
+			ret = si2165_write(state, 0x36c, data+offset, 4);
+			if (ret < 0)
+				goto error;
+			wordcount--;
+			offset += 4;
+		}
+		cur_block++;
+	}
+
+	deb_fw_load("si2165_upload_firmware_block after while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
+				len, offset, cur_block, block_count);
+
+	if (poffset)
+		*poffset = offset;
+
+	deb_fw_load("si2165_upload_firmware_block returned offset=0x%x\n",
+				offset);
+
+	return 0;
+error:
+	return ret;
+}
+
+static int si2165_upload_firmware(struct si2165_state *state)
+{
+	/* int ret; */
+	u8 val[3];
+	u16 val16;
+	int ret;
+
+	const struct firmware *fw = NULL;
+	u8 *fw_file = SI2165_FIRMWARE;
+	const u8 *data;
+	u32 len;
+	u32 offset;
+	u8 patch_version;
+	u8 block_count;
+	u16 crc_expected;
+
+	/* request the firmware, this will block and timeout */
+	ret = request_firmware(&fw, fw_file, state->i2c->dev.parent);
+	if (ret) {
+		dev_warn(&state->i2c->dev, "%s: firmare file '%s' not found\n",
+				KBUILD_MODNAME, fw_file);
+		goto error;
+	}
+
+	data = fw->data;
+	len = fw->size;
+
+	dev_info(&state->i2c->dev, "%s: downloading firmware from file '%s' size=%d\n",
+			KBUILD_MODNAME, fw_file, len);
+
+	if (len % 4 != 0) {
+		dev_warn(&state->i2c->dev, "%s: firmware size is not multiple of 4\n",
+				KBUILD_MODNAME);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	/* check header (8 bytes) */
+	if (len < 8) {
+		dev_warn(&state->i2c->dev, "%s: firmware header is missing\n",
+				KBUILD_MODNAME);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	if (data[0] != 1 || data[1] != 0) {
+		dev_warn(&state->i2c->dev, "%s: firmware file version is wrong\n",
+				KBUILD_MODNAME);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	patch_version = data[2];
+	block_count = data[4];
+	crc_expected = data[7] << 8 | data[6];
+
+	/* start uploading fw */
+	/* boot/wdog status */
+	ret = si2165_writereg8(state, 0x0341, 0x00);
+	if (ret < 0)
+		goto error;
+	/* reset */
+	ret = si2165_writereg8(state, 0x00c0, 0x00);
+	if (ret < 0)
+		goto error;
+	/* boot/wdog status */
+	ret = si2165_readreg8(state, 0x0341, val);
+	if (ret < 0)
+		goto error;
+
+	/* enable reset on error */
+	ret = si2165_readreg8(state, 0x035c, val);
+	if (ret < 0)
+		goto error;
+	ret = si2165_readreg8(state, 0x035c, val);
+	if (ret < 0)
+		goto error;
+	ret = si2165_writereg8(state, 0x035c, 0x02);
+	if (ret < 0)
+		goto error;
+
+	/* start right after the header */
+	offset = 8;
+
+	dev_info(&state->i2c->dev, "%s: si2165_upload_firmware extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n",
+		KBUILD_MODNAME, patch_version, block_count, crc_expected);
+
+	ret = si2165_upload_firmware_block(state, data, len, &offset, 1);
+	if (ret < 0)
+		goto error;
+
+	ret = si2165_writereg8(state, 0x0344, patch_version);
+	if (ret < 0)
+		goto error;
+
+	/* reset crc */
+	ret = si2165_writereg8(state, 0x0379, 0x01);
+	if (ret)
+		return ret;
+
+	ret = si2165_upload_firmware_block(state, data, len,
+					   &offset, block_count);
+	if (ret < 0) {
+		dev_err(&state->i2c->dev,
+			"%s: firmare could not be uploaded\n",
+			KBUILD_MODNAME);
+		goto error;
+	}
+
+	/* read crc */
+	ret = si2165_readreg16(state, 0x037a, &val16);
+	if (ret)
+		goto error;
+
+	if (val16 != crc_expected) {
+		dev_err(&state->i2c->dev,
+			"%s: firmware crc mismatch %04x != %04x\n",
+			KBUILD_MODNAME, val16, crc_expected);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	ret = si2165_upload_firmware_block(state, data, len, &offset, 5);
+	if (ret)
+		goto error;
+
+	if (len != offset) {
+		dev_err(&state->i2c->dev,
+			"%s: firmare len mismatch %04x != %04x\n",
+			KBUILD_MODNAME, len, offset);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	/* reset watchdog error register */
+	ret = si2165_writereg_mask8(state, 0x0341, 0x02, 0x02);
+	if (ret < 0)
+		goto error;
+
+	/* enable reset on error */
+	ret = si2165_writereg_mask8(state, 0x035c, 0x01, 0x01);
+	if (ret < 0)
+		goto error;
+
+	dev_info(&state->i2c->dev, "%s: fw load finished\n", KBUILD_MODNAME);
+
+	ret = 0;
+	state->firmware_loaded = true;
+error:
+	if (fw) {
+		release_firmware(fw);
+		fw = NULL;
+	}
+
+	return ret;
+}
+
+static int si2165_init(struct dvb_frontend *fe)
+{
+	int ret = 0;
+	struct si2165_state *state = fe->demodulator_priv;
+	u8 val;
+	u8 patch_version = 0x00;
+
+	dprintk("%s: called\n", __func__);
+
+	/* powerup */
+	ret = si2165_writereg8(state, 0x0000, state->config.chip_mode);
+	if (ret < 0)
+		goto error;
+	/* dsp_clock_enable */
+	ret = si2165_writereg8(state, 0x0104, 0x01);
+	if (ret < 0)
+		goto error;
+	ret = si2165_readreg8(state, 0x0000, &val); /* verify chip_mode */
+	if (ret < 0)
+		goto error;
+	if (val != state->config.chip_mode) {
+		dev_err(&state->i2c->dev, "%s: could not set chip_mode\n",
+			KBUILD_MODNAME);
+		return -EINVAL;
+	}
+
+	/* agc */
+	ret = si2165_writereg8(state, 0x018b, 0x00);
+	if (ret < 0)
+		goto error;
+	ret = si2165_writereg8(state, 0x0190, 0x01);
+	if (ret < 0)
+		goto error;
+	ret = si2165_writereg8(state, 0x0170, 0x00);
+	if (ret < 0)
+		goto error;
+	ret = si2165_writereg8(state, 0x0171, 0x07);
+	if (ret < 0)
+		goto error;
+	/* rssi pad */
+	ret = si2165_writereg8(state, 0x0646, 0x00);
+	if (ret < 0)
+		goto error;
+	ret = si2165_writereg8(state, 0x0641, 0x00);
+	if (ret < 0)
+		goto error;
+
+	ret = si2165_init_pll(state);
+	if (ret < 0)
+		goto error;
+
+	/* enable chip_init */
+	ret = si2165_writereg8(state, 0x0050, 0x01);
+	if (ret < 0)
+		goto error;
+	/* set start_init */
+	ret = si2165_writereg8(state, 0x0096, 0x01);
+	if (ret < 0)
+		goto error;
+	ret = si2165_wait_init_done(state);
+	if (ret < 0)
+		goto error;
+
+	/* disable chip_init */
+	ret = si2165_writereg8(state, 0x0050, 0x00);
+	if (ret < 0)
+		goto error;
+
+	/* ber_pkt */
+	ret = si2165_writereg16(state, 0x0470 , 0x7530);
+	if (ret < 0)
+		goto error;
+
+	ret = si2165_readreg8(state, 0x0344, &patch_version);
+	if (ret < 0)
+		goto error;
+
+	ret = si2165_writereg8(state, 0x00cb, 0x00);
+	if (ret < 0)
+		goto error;
+
+	/* dsp_addr_jump */
+	ret = si2165_writereg32(state, 0x0348, 0xf4000000);
+	if (ret < 0)
+		goto error;
+	/* boot/wdog status */
+	ret = si2165_readreg8(state, 0x0341, &val);
+	if (ret < 0)
+		goto error;
+
+	if (patch_version == 0x00) {
+		ret = si2165_upload_firmware(state);
+		if (ret < 0)
+			goto error;
+	}
+
+	/* write adc values after each reset*/
+	ret = si2165_writereg8(state, 0x012a, 0x46);
+	if (ret < 0)
+		goto error;
+	ret = si2165_writereg8(state, 0x012c, 0x00);
+	if (ret < 0)
+		goto error;
+	ret = si2165_writereg8(state, 0x012e, 0x0a);
+	if (ret < 0)
+		goto error;
+	ret = si2165_writereg8(state, 0x012f, 0xff);
+	if (ret < 0)
+		goto error;
+	ret = si2165_writereg8(state, 0x0123, 0x70);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+error:
+	return ret;
+}
+
+static int si2165_sleep(struct dvb_frontend *fe)
+{
+	int ret;
+	struct si2165_state *state = fe->demodulator_priv;
+
+	/* dsp clock disable */
+	ret = si2165_writereg8(state, 0x0104, 0x00);
+	if (ret < 0)
+		return ret;
+	/* chip mode */
+	ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int si2165_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	int ret;
+	u8 fec_lock = 0;
+	struct si2165_state *state = fe->demodulator_priv;
+
+	if (!state->has_dvbt)
+		return -EINVAL;
+
+	/* check fec_lock */
+	ret = si2165_readreg8(state, 0x4e0, &fec_lock);
+	if (ret < 0)
+		return ret;
+	*status = 0;
+	if (fec_lock & 0x01) {
+		*status |= FE_HAS_SIGNAL;
+		*status |= FE_HAS_CARRIER;
+		*status |= FE_HAS_VITERBI;
+		*status |= FE_HAS_SYNC;
+		*status |= FE_HAS_LOCK;
+	}
+
+	return 0;
+}
+
+static int si2165_set_oversamp(struct si2165_state *state, u32 dvb_rate)
+{
+	u64 oversamp;
+	u32 reg_value;
+
+	oversamp = si2165_get_fe_clk(state);
+	oversamp <<= 23;
+	if (dvb_rate > 0)
+		oversamp /= (u64)dvb_rate;
+	reg_value = oversamp & 0x3fffffff;
+
+	/* oversamp, usbdump contained 0x03100000; */
+	return si2165_writereg32(state, 0x00e4, reg_value);
+}
+
+static int si2165_set_if_freq_shift(struct si2165_state *state, u32 IF)
+{
+	s64 if_freq_shift;
+	u32 reg_value;
+	u32 fe_clk = si2165_get_fe_clk(state);
+
+	if_freq_shift = IF;
+	if_freq_shift <<= 29;
+	if (fe_clk > 0)
+		if_freq_shift /= (u64)fe_clk;
+	reg_value = ((u32)if_freq_shift) & 0x1fffffff;
+
+	/* if_freq_shift, usbdump contained 0x023ee08f; */
+	return si2165_writereg32(state, 0x00e8, reg_value);
+}
+
+static int si2165_set_parameters(struct dvb_frontend *fe)
+{
+	int ret;
+	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+	struct si2165_state *state = fe->demodulator_priv;
+	u8 val[3];
+	u32 IF;
+	u32 dvb_rate = 0;
+	u16 bw10k;
+
+	dprintk("%s: called\n", __func__);
+
+	if (!fe->ops.tuner_ops.get_if_frequency) {
+		dev_err(&state->i2c->dev,
+			"%s: Error: get_if_frequency() not defined at tuner. Can't work without it!\n",
+			KBUILD_MODNAME);
+		return -EINVAL;
+	}
+
+	if (!state->has_dvbt)
+		return -EINVAL;
+
+	if (p->bandwidth_hz > 0) {
+		dvb_rate = p->bandwidth_hz * 8 / 7;
+		bw10k = p->bandwidth_hz / 10000;
+	} else {
+		dvb_rate = 8 * 8 / 7;
+		bw10k = 800;
+	}
+
+	/* standard = DVB-T */
+	ret = si2165_writereg8(state, 0x00ec, 0x01);
+	if (ret < 0)
+		return ret;
+	ret = si2165_adjust_pll_divl(state, 12);
+	if (ret < 0)
+		return ret;
+
+	fe->ops.tuner_ops.get_if_frequency(fe, &IF);
+	ret = si2165_set_if_freq_shift(state, IF);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg8(state, 0x08f8, 0x00);
+	if (ret < 0)
+		return ret;
+	/* ts output config */
+	ret = si2165_writereg8(state, 0x04e4, 0x20);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg16(state, 0x04ef, 0x00fe);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg24(state, 0x04f4, 0x555555);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg8(state, 0x04e5, 0x01);
+	if (ret < 0)
+		return ret;
+	/* bandwidth in 10KHz steps */
+	ret = si2165_writereg16(state, 0x0308, bw10k);
+	if (ret < 0)
+		return ret;
+	ret = si2165_set_oversamp(state, dvb_rate);
+	if (ret < 0)
+		return ret;
+	/* impulsive_noise_remover */
+	ret = si2165_writereg8(state, 0x031c, 0x01);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg8(state, 0x00cb, 0x00);
+	if (ret < 0)
+		return ret;
+	/* agc2 */
+	ret = si2165_writereg8(state, 0x016e, 0x41);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg8(state, 0x016c, 0x0e);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg8(state, 0x016d, 0x10);
+	if (ret < 0)
+		return ret;
+	/* agc */
+	ret = si2165_writereg8(state, 0x015b, 0x03);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg8(state, 0x0150, 0x78);
+	if (ret < 0)
+		return ret;
+	/* agc */
+	ret = si2165_writereg8(state, 0x01a0, 0x78);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg8(state, 0x01c8, 0x68);
+	if (ret < 0)
+		return ret;
+	/* freq_sync_range */
+	ret = si2165_writereg16(state, 0x030c, 0x0064);
+	if (ret < 0)
+		return ret;
+	/* gp_reg0 */
+	ret = si2165_readreg8(state, 0x0387, val);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg8(state, 0x0387, 0x00);
+	if (ret < 0)
+		return ret;
+	/* dsp_addr_jump */
+	ret = si2165_writereg32(state, 0x0348, 0xf4000000);
+	if (ret < 0)
+		return ret;
+
+	if (fe->ops.tuner_ops.set_params)
+		fe->ops.tuner_ops.set_params(fe);
+
+	/* recalc if_freq_shift if IF might has changed */
+	fe->ops.tuner_ops.get_if_frequency(fe, &IF);
+	ret = si2165_set_if_freq_shift(state, IF);
+	if (ret < 0)
+		return ret;
+
+	/* boot/wdog status */
+	ret = si2165_readreg8(state, 0x0341, val);
+	if (ret < 0)
+		return ret;
+	ret = si2165_writereg8(state, 0x0341, 0x00);
+	if (ret < 0)
+		return ret;
+	/* reset all */
+	ret = si2165_writereg8(state, 0x00c0, 0x00);
+	if (ret < 0)
+		return ret;
+	/* gp_reg0 */
+	ret = si2165_writereg32(state, 0x0384, 0x00000000);
+	if (ret < 0)
+		return ret;
+	/* start_synchro */
+	ret = si2165_writereg8(state, 0x02e0, 0x01);
+	if (ret < 0)
+		return ret;
+	/* boot/wdog status */
+	ret = si2165_readreg8(state, 0x0341, val);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void si2165_release(struct dvb_frontend *fe)
+{
+	struct si2165_state *state = fe->demodulator_priv;
+
+	dprintk("%s: called\n", __func__);
+	kfree(state);
+}
+
+static struct dvb_frontend_ops si2165_ops = {
+	.info = {
+		.name = "Silicon Labs Si2165",
+		.caps =	FE_CAN_FEC_1_2 |
+			FE_CAN_FEC_2_3 |
+			FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 |
+			FE_CAN_FEC_7_8 |
+			FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK |
+			FE_CAN_QAM_16 |
+			FE_CAN_QAM_32 |
+			FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 |
+			FE_CAN_QAM_256 |
+			FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_HIERARCHY_AUTO |
+			FE_CAN_MUTE_TS |
+			FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_RECOVER
+	},
+
+	.get_tune_settings = si2165_get_tune_settings,
+
+	.init = si2165_init,
+	.sleep = si2165_sleep,
+
+	.set_frontend      = si2165_set_parameters,
+	.read_status       = si2165_read_status,
+
+	.release = si2165_release,
+};
+
+struct dvb_frontend *si2165_attach(const struct si2165_config *config,
+				   struct i2c_adapter *i2c)
+{
+	struct si2165_state *state = NULL;
+	int n;
+	int io_ret;
+	u8 val;
+
+	if (config == NULL || i2c == NULL)
+		goto error;
+
+	/* allocate memory for the internal state */
+	state = kzalloc(sizeof(struct si2165_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	state->i2c = i2c;
+	state->config = *config;
+
+	if (state->config.ref_freq_Hz < 4000000
+	    || state->config.ref_freq_Hz > 27000000) {
+		dev_err(&state->i2c->dev, "%s: ref_freq of %d Hz not supported by this driver\n",
+			 KBUILD_MODNAME, state->config.ref_freq_Hz);
+		goto error;
+	}
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &si2165_ops,
+		sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+
+	/* powerup */
+	io_ret = si2165_writereg8(state, 0x0000, state->config.chip_mode);
+	if (io_ret < 0)
+		goto error;
+
+	io_ret = si2165_readreg8(state, 0x0000, &val);
+	if (io_ret < 0)
+		goto error;
+	if (val != state->config.chip_mode)
+		goto error;
+
+	io_ret = si2165_readreg8(state, 0x0023 , &state->revcode);
+	if (io_ret < 0)
+		goto error;
+
+	io_ret = si2165_readreg8(state, 0x0118, &state->chip_type);
+	if (io_ret < 0)
+		goto error;
+
+	/* powerdown */
+	io_ret = si2165_writereg8(state, 0x0000, SI2165_MODE_OFF);
+	if (io_ret < 0)
+		goto error;
+
+	dev_info(&state->i2c->dev, "%s: hardware revision 0x%02x, chip type 0x%02x\n",
+		 KBUILD_MODNAME, state->revcode, state->chip_type);
+
+	/* It is a guess that register 0x0118 (chip type?) can be used to
+	 * differ between si2161, si2163 and si2165
+	 * Only si2165 has been tested.
+	 */
+	if (state->revcode == 0x03 && state->chip_type == 0x07) {
+		state->has_dvbt = true;
+		state->has_dvbc = true;
+	} else {
+		dev_err(&state->i2c->dev, "%s: Unsupported chip.\n",
+			KBUILD_MODNAME);
+		goto error;
+	}
+
+	n = 0;
+	if (state->has_dvbt) {
+		state->frontend.ops.delsys[n++] = SYS_DVBT;
+		strlcat(state->frontend.ops.info.name, " DVB-T",
+			sizeof(state->frontend.ops.info.name));
+	}
+	if (state->has_dvbc)
+		dev_warn(&state->i2c->dev, "%s: DVB-C is not yet supported.\n",
+		       KBUILD_MODNAME);
+
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+EXPORT_SYMBOL(si2165_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Silicon Labs Si2165 DVB-C/-T Demodulator driver");
+MODULE_AUTHOR("Matthias Schwarzott <zzam@gentoo.org>");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(SI2165_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/si2165.h b/drivers/media/dvb-frontends/si2165.h
new file mode 100644
index 0000000..3aae834
--- /dev/null
+++ b/drivers/media/dvb-frontends/si2165.h
@@ -0,0 +1,61 @@
+/*
+    Driver for Silicon Labs SI2165 DVB-C/-T Demodulator
+
+    Copyright (C) 2013-2014 Matthias Schwarzott <zzam@gentoo.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    References:
+    http://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf
+*/
+
+#ifndef _DVB_SI2165_H
+#define _DVB_SI2165_H
+
+#include <linux/dvb/frontend.h>
+
+#if IS_ENABLED(CONFIG_DVB_SI2165)
+
+enum {
+	SI2165_MODE_OFF = 0x00,
+	SI2165_MODE_PLL_EXT = 0x20,
+	SI2165_MODE_PLL_XTAL = 0x21
+};
+
+struct si2165_config {
+	/* i2c addr
+	 * possible values: 0x64,0x65,0x66,0x67 */
+	u8 i2c_addr;
+
+	/* external clock or XTAL */
+	u8 chip_mode;
+
+	/* frequency of external clock or xtal in Hz
+	 * possible values: 4000000, 16000000, 20000000, 240000000, 27000000
+	 */
+	u32 ref_freq_Hz;
+};
+
+/* Addresses: 0x64,0x65,0x66,0x67 */
+struct dvb_frontend *si2165_attach(
+	const struct si2165_config *config,
+	struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *si2165_attach(
+	const struct si2165_config *config,
+	struct i2c_adapter *i2c)
+{
+	pr_warn("%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_SI2165 */
+
+#endif /* _DVB_SI2165_H */
diff --git a/drivers/media/dvb-frontends/si2165_priv.h b/drivers/media/dvb-frontends/si2165_priv.h
new file mode 100644
index 0000000..d4cc93f
--- /dev/null
+++ b/drivers/media/dvb-frontends/si2165_priv.h
@@ -0,0 +1,23 @@
+/*
+    Driver for Silicon Labs SI2165 DVB-C/-T Demodulator
+
+    Copyright (C) 2013-2014 Matthias Schwarzott <zzam@gentoo.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+*/
+
+#ifndef _DVB_SI2165_PRIV
+#define _DVB_SI2165_PRIV
+
+#define SI2165_FIRMWARE "dvb-demod-si2165.fw"
+
+#endif /* _DVB_SI2165_PRIV */
-- 
2.0.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] cx231xx: Add [2040:b130] Hauppauge WinTV 930C-hd 1113xx
  2014-07-01 19:55 [PATCH 0/4] wintv 930c-hd: Add basic support Matthias Schwarzott
  2014-07-01 19:55 ` [PATCH 1/4] get_dvb_firmware: Add firmware extractor for si2165 Matthias Schwarzott
  2014-07-01 19:55 ` [PATCH 2/4] si2165: Add demod driver for DVB-T only Matthias Schwarzott
@ 2014-07-01 19:55 ` Matthias Schwarzott
  2014-07-01 19:55 ` [PATCH 4/4] cx23885: Add si2165 support for HVR-5500 Matthias Schwarzott
  2014-07-02  5:27 ` [PATCH 0/4] wintv 930c-hd: Add basic support Matthias Schwarzott
  4 siblings, 0 replies; 6+ messages in thread
From: Matthias Schwarzott @ 2014-07-01 19:55 UTC (permalink / raw)
  To: linux-media, crope; +Cc: Matthias Schwarzott

Reading eeprom works

TODO:
* Analog is untested
* after loading the driver the first open to dvb device node fails

Signed-off-by: Matthias Schwarzott <zzam@gentoo.org>
---
 drivers/media/usb/cx231xx/Kconfig          |  1 +
 drivers/media/usb/cx231xx/cx231xx-avcore.c |  1 +
 drivers/media/usb/cx231xx/cx231xx-cards.c  | 92 ++++++++++++++++++++++++++++++
 drivers/media/usb/cx231xx/cx231xx-core.c   |  3 +
 drivers/media/usb/cx231xx/cx231xx-dvb.c    | 32 +++++++++++
 drivers/media/usb/cx231xx/cx231xx.h        |  1 +
 6 files changed, 130 insertions(+)

diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig
index f14c5e8..036454e 100644
--- a/drivers/media/usb/cx231xx/Kconfig
+++ b/drivers/media/usb/cx231xx/Kconfig
@@ -47,6 +47,7 @@ config VIDEO_CX231XX_DVB
 	select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT
 
 	---help---
 	  This adds support for DVB cards based on the
diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index 89de00b..a428c10 100644
--- a/drivers/media/usb/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c
@@ -352,6 +352,7 @@ int cx231xx_afe_update_power_control(struct cx231xx *dev,
 	case CX231XX_BOARD_CNXT_RDU_253S:
 	case CX231XX_BOARD_CNXT_VIDEO_GRABBER:
 	case CX231XX_BOARD_HAUPPAUGE_EXETER:
+	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
 	case CX231XX_BOARD_HAUPPAUGE_USBLIVE2:
 	case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
 	case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 2ee03e4..8b80f88 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -704,6 +704,45 @@ struct cx231xx_board cx231xx_boards[] = {
 			}
 		},
 	},
+	[CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx] = {
+		.name = "Hauppauge WinTV 930C-HD (1113xx)",
+		.tuner_type = TUNER_NXP_TDA18271,
+		.tuner_addr = 0x60,
+		.tuner_gpio = RDE250_XCV_TUNER,
+		.tuner_sif_gpio = 0x05,
+		.tuner_scl_gpio = 0x1a,
+		.tuner_sda_gpio = 0x1b,
+		.decoder = CX231XX_AVDECODER,
+		.output_mode = OUT_MODE_VIP11,
+		.demod_xfer_mode = 0,
+		.ctl_pin_status_mask = 0xFFFFFFC4,
+		.agc_analog_digital_select_gpio = 0x0c,
+		.gpio_pin_status_mask = 0x4001000,
+		.tuner_i2c_master = 1,
+		.demod_i2c_master = 2,
+		.has_dvb = 1,
+		.demod_addr = 0x0e,
+		.norm = V4L2_STD_PAL,
+
+		.input = {{
+			.type = CX231XX_VMUX_TELEVISION,
+			.vmux = CX231XX_VIN_3_1,
+			.amux = CX231XX_AMUX_VIDEO,
+			.gpio = NULL,
+		}, {
+			.type = CX231XX_VMUX_COMPOSITE1,
+			.vmux = CX231XX_VIN_2_1,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = NULL,
+		}, {
+			.type = CX231XX_VMUX_SVIDEO,
+			.vmux = CX231XX_VIN_1_1 |
+				(CX231XX_VIN_1_2 << 8) |
+				CX25840_SVIDEO_ON,
+			.amux = CX231XX_AMUX_LINE_IN,
+			.gpio = NULL,
+		} },
+	},
 };
 const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
 
@@ -733,6 +772,8 @@ struct usb_device_id cx231xx_id_table[] = {
 	 .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC},
 	{USB_DEVICE(0x2040, 0xb120),
 	 .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
+	{USB_DEVICE(0x2040, 0xb130),
+	 .driver_info = CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx},
 	{USB_DEVICE(0x2040, 0xb140),
 	 .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
 	{USB_DEVICE(0x2040, 0xc200),
@@ -886,6 +927,43 @@ static void cx231xx_config_tuner(struct cx231xx *dev)
 
 }
 
+static int read_eeprom(struct cx231xx *dev, u8 *eedata, int len)
+{
+	int ret = 0;
+	u8 addr = 0xa0 >> 1;
+	u8 start_offset = 0;
+	int len_todo = len;
+	u8 *eedata_cur = eedata;
+	int i;
+	struct i2c_msg msg_write = { .addr = addr, .flags = 0,
+		.buf = &start_offset, .len = 1 };
+	struct i2c_msg msg_read = { .addr = addr, .flags = I2C_M_RD };
+
+	/* mutex_lock(&dev->i2c_lock); */
+	cx231xx_enable_i2c_port_3(dev, false);
+
+	/* start reading at offset 0 */
+	ret = i2c_transfer(&dev->i2c_bus[1].i2c_adap, &msg_write, 1);
+
+	while (len_todo > 0) {
+		msg_read.len = (len_todo > 64) ? 64 : len_todo;
+		msg_read.buf = eedata_cur;
+
+		ret = i2c_transfer(&dev->i2c_bus[1].i2c_adap, &msg_read, 1);
+
+		eedata_cur += msg_read.len;
+		len_todo -= msg_read.len;
+	}
+
+	cx231xx_enable_i2c_port_3(dev, true);
+	/* mutex_unlock(&dev->i2c_lock); */
+
+	for (i = 0; i + 15 < len; i += 16)
+		cx231xx_info("i2c eeprom %02x: %*ph\n", i, 16, &eedata[i]);
+
+	return 0;
+}
+
 void cx231xx_card_setup(struct cx231xx *dev)
 {
 
@@ -917,6 +995,20 @@ void cx231xx_card_setup(struct cx231xx *dev)
 		else
 			cx231xx_config_tuner(dev);
 	}
+
+	switch (dev->model) {
+	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
+		{
+			struct tveeprom tvee;
+			static u8 eeprom[256];
+
+			read_eeprom(dev, eeprom, sizeof(eeprom));
+			tveeprom_hauppauge_analog(&dev->i2c_bus[1].i2c_client,
+						&tvee, eeprom + 0xc0);
+			break;
+		}
+	}
+
 }
 
 /*
diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index 4ba3ce0..513194a 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
@@ -726,6 +726,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
 			errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
 			break;
 		case CX231XX_BOARD_HAUPPAUGE_EXETER:
+		case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
 			errCode = cx231xx_set_power_mode(dev,
 						POLARIS_AVMODE_DIGITAL);
 			break;
@@ -744,6 +745,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
 		case CX231XX_BOARD_CNXT_RDE_253S:
 		case CX231XX_BOARD_CNXT_RDU_253S:
 		case CX231XX_BOARD_HAUPPAUGE_EXETER:
+		case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
 		case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
 		case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
 		case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
@@ -1379,6 +1381,7 @@ int cx231xx_dev_init(struct cx231xx *dev)
 	case CX231XX_BOARD_CNXT_RDE_253S:
 	case CX231XX_BOARD_CNXT_RDU_253S:
 	case CX231XX_BOARD_HAUPPAUGE_EXETER:
+	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
 	case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
 	case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
 	case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index 4504bc6..cbc63e7 100644
--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
@@ -32,6 +32,7 @@
 #include "tda18271.h"
 #include "s5h1411.h"
 #include "lgdt3305.h"
+#include "si2165.h"
 #include "mb86a20s.h"
 
 MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
@@ -151,6 +152,12 @@ static struct tda18271_config pv_tda18271_config = {
 	.small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
 };
 
+static const struct si2165_config hauppauge_930C_HD_1113xx_si2165_config = {
+	.i2c_addr	= 0x64,
+	.chip_mode	= SI2165_MODE_PLL_XTAL,
+	.ref_freq_Hz	= 16000000,
+};
+
 static inline void print_err_status(struct cx231xx *dev, int packet, int status)
 {
 	char *errmsg = "Unknown";
@@ -704,6 +711,31 @@ static int dvb_init(struct cx231xx *dev)
 			   &hcw_tda18271_config);
 		break;
 
+	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
+
+		dev->dvb->frontend = dvb_attach(si2165_attach,
+			&hauppauge_930C_HD_1113xx_si2165_config,
+			&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap
+			);
+
+		if (dev->dvb->frontend == NULL) {
+			printk(DRIVER_NAME
+			       ": Failed to attach SI2165 front end\n");
+			result = -EINVAL;
+			goto out_free;
+		}
+
+		dev->dvb->frontend->ops.i2c_gate_ctrl = 0;
+
+		/* define general-purpose callback pointer */
+		dvb->frontend->callback = cx231xx_tuner_callback;
+
+		dvb_attach(tda18271_attach, dev->dvb->frontend,
+			0x60,
+			&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+			&hcw_tda18271_config);
+		break;
+
 	case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
 	case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID:
 
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index babca7f..a6373ba 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -73,6 +73,7 @@
 #define CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2 16
 #define CX231XX_BOARD_OTG102 17
 #define CX231XX_BOARD_KWORLD_UB445_USB_HYBRID 18
+#define CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx 19
 
 /* Limits minimum and default number of buffers */
 #define CX231XX_MIN_BUF                 4
-- 
2.0.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] cx23885: Add si2165 support for HVR-5500
  2014-07-01 19:55 [PATCH 0/4] wintv 930c-hd: Add basic support Matthias Schwarzott
                   ` (2 preceding siblings ...)
  2014-07-01 19:55 ` [PATCH 3/4] cx231xx: Add [2040:b130] Hauppauge WinTV 930C-hd 1113xx Matthias Schwarzott
@ 2014-07-01 19:55 ` Matthias Schwarzott
  2014-07-02  5:27 ` [PATCH 0/4] wintv 930c-hd: Add basic support Matthias Schwarzott
  4 siblings, 0 replies; 6+ messages in thread
From: Matthias Schwarzott @ 2014-07-01 19:55 UTC (permalink / raw)
  To: linux-media, crope; +Cc: Matthias Schwarzott

The same card entry is used for HVR-4400 and HVR-5500.
Only HVR-5500 has been tested.

Signed-off-by: Matthias Schwarzott <zzam@gentoo.org>
---
 drivers/media/pci/cx23885/Kconfig         |  1 +
 drivers/media/pci/cx23885/cx23885-cards.c | 17 +++++++++---
 drivers/media/pci/cx23885/cx23885-dvb.c   | 43 +++++++++++++++++++++++++++----
 3 files changed, 53 insertions(+), 8 deletions(-)

diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig
index d1dcb1d..6cd1db2 100644
--- a/drivers/media/pci/cx23885/Kconfig
+++ b/drivers/media/pci/cx23885/Kconfig
@@ -31,6 +31,7 @@ config VIDEO_CX23885
 	select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT
 	select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
+	select DVB_SI2165 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index 79f20c8..6ed0551 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -619,7 +619,12 @@ struct cx23885_board cx23885_boards[] = {
 	},
 	[CX23885_BOARD_HAUPPAUGE_HVR4400] = {
 		.name		= "Hauppauge WinTV-HVR4400",
+		.porta		= CX23885_ANALOG_VIDEO,
 		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+		.tuner_type	= TUNER_NXP_TDA18271,
+		.tuner_addr	= 0x60, /* 0xc0 >> 1 */
+		.tuner_bus	= 1,
 	},
 	[CX23885_BOARD_AVERMEDIA_HC81R] = {
 		.name		= "AVerTV Hybrid Express Slim HC81R",
@@ -1449,13 +1454,16 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR4400:
 		/* GPIO-8 tda10071 demod reset */
+		/* GPIO-9 si2165 demod reset */
 
 		/* Put the parts into reset and back */
-		cx23885_gpio_enable(dev, GPIO_8, 1);
-		cx23885_gpio_clear(dev, GPIO_8);
+		cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1);
+
+		cx23885_gpio_clear(dev, GPIO_8 | GPIO_9);
 		mdelay(100);
-		cx23885_gpio_set(dev, GPIO_8);
+		cx23885_gpio_set(dev, GPIO_8 | GPIO_9);
 		mdelay(100);
+
 		break;
 	case CX23885_BOARD_AVERMEDIA_HC81R:
 		cx_clear(MC417_CTL, 1);
@@ -1799,6 +1807,9 @@ void cx23885_card_setup(struct cx23885_dev *dev)
 		ts1->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
 		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
 		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500:
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index d037459..4822776 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -71,6 +71,7 @@
 #include "tda10071.h"
 #include "a8293.h"
 #include "mb86a20s.h"
+#include "si2165.h"
 
 static unsigned int debug;
 
@@ -302,6 +303,11 @@ static struct tda18271_config hauppauge_hvr1210_tuner_config = {
 	.output_opt = TDA18271_OUTPUT_LT_OFF,
 };
 
+static struct tda18271_config hauppauge_hvr4400_tuner_config = {
+	.gate    = TDA18271_GATE_DIGITAL,
+	.output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
 static struct tda18271_std_map hauppauge_hvr127x_std_map = {
 	.atsc_6   = { .if_freq = 3250, .agc_mode = 3, .std = 4,
 		      .if_lvl = 1, .rfagc_top = 0x58 },
@@ -702,6 +708,12 @@ static const struct a8293_config hauppauge_a8293_config = {
 	.i2c_addr = 0x0b,
 };
 
+static const struct si2165_config hauppauge_hvr4400_si2165_config = {
+	.i2c_addr	= 0x64,
+	.chip_mode	= SI2165_MODE_PLL_XTAL,
+	.ref_freq_Hz	= 16000000,
+};
+
 static int netup_altera_fpga_rw(void *device, int flag, int data, int read)
 {
 	struct cx23885_dev *dev = (struct cx23885_dev *)device;
@@ -1335,13 +1347,34 @@ static int dvb_register(struct cx23885_tsport *port)
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR4400:
 		i2c_bus = &dev->i2c_bus[0];
-		fe0->dvb.frontend = dvb_attach(tda10071_attach,
+		i2c_bus2 = &dev->i2c_bus[1];
+		switch (port->nr) {
+		/* port b */
+		case 1:
+			fe0->dvb.frontend = dvb_attach(tda10071_attach,
 						&hauppauge_tda10071_config,
 						&i2c_bus->i2c_adap);
-		if (fe0->dvb.frontend != NULL) {
-			dvb_attach(a8293_attach, fe0->dvb.frontend,
-				   &i2c_bus->i2c_adap,
-				   &hauppauge_a8293_config);
+			if (fe0->dvb.frontend != NULL) {
+				if (!dvb_attach(a8293_attach, fe0->dvb.frontend,
+						&i2c_bus->i2c_adap,
+						&hauppauge_a8293_config))
+					goto frontend_detach;
+			}
+			break;
+		/* port c */
+		case 2:
+			fe0->dvb.frontend = dvb_attach(si2165_attach,
+					&hauppauge_hvr4400_si2165_config,
+					&i2c_bus->i2c_adap);
+			if (fe0->dvb.frontend != NULL) {
+				fe0->dvb.frontend->ops.i2c_gate_ctrl = 0;
+				if (!dvb_attach(tda18271_attach,
+						fe0->dvb.frontend,
+						0x60, &i2c_bus2->i2c_adap,
+					  &hauppauge_hvr4400_tuner_config))
+					goto frontend_detach;
+			}
+			break;
 		}
 		break;
 	default:
-- 
2.0.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/4] wintv 930c-hd: Add basic support
  2014-07-01 19:55 [PATCH 0/4] wintv 930c-hd: Add basic support Matthias Schwarzott
                   ` (3 preceding siblings ...)
  2014-07-01 19:55 ` [PATCH 4/4] cx23885: Add si2165 support for HVR-5500 Matthias Schwarzott
@ 2014-07-02  5:27 ` Matthias Schwarzott
  4 siblings, 0 replies; 6+ messages in thread
From: Matthias Schwarzott @ 2014-07-02  5:27 UTC (permalink / raw)
  To: linux-media, crope

On 01.07.2014 21:55, Matthias Schwarzott wrote:
> This patch series is the third version of my si2165 driver.
> It supports only DVB-T and was tested on 8MHz channels in germany.
> 
> Maybe the si2165 driver also works on other si2165/si2163/si2161 based cards.
> 
I forgot to mention, that support for HVR-5500 is also added.

The card entry is shared with HVR-4400.
It would be interesting to hear what happens when testing this on a
HVR-4400 card, because as far as I know there is an si2161 chip (that
has only DVB-T instead of DVB-T and DVB-C support).
Most likely only some numbers are to be adjusted and it could work also.

The driver should also work for the PCTV QuatroStick 521e - only the usb
ids are not yet added.

Regards
Matthias


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2014-07-02  5:27 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-01 19:55 [PATCH 0/4] wintv 930c-hd: Add basic support Matthias Schwarzott
2014-07-01 19:55 ` [PATCH 1/4] get_dvb_firmware: Add firmware extractor for si2165 Matthias Schwarzott
2014-07-01 19:55 ` [PATCH 2/4] si2165: Add demod driver for DVB-T only Matthias Schwarzott
2014-07-01 19:55 ` [PATCH 3/4] cx231xx: Add [2040:b130] Hauppauge WinTV 930C-hd 1113xx Matthias Schwarzott
2014-07-01 19:55 ` [PATCH 4/4] cx23885: Add si2165 support for HVR-5500 Matthias Schwarzott
2014-07-02  5:27 ` [PATCH 0/4] wintv 930c-hd: Add basic support Matthias Schwarzott

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).