[linux-dvb] PATCH: New driver for Zarlink ZL10313 based Compro S350/S300

Jan D. Louw jd.louw at mweb.co.za
Sat Dec 29 17:58:21 CET 2007


Hi List,

Attached is a new driver for the Zarlink ZL10313/ZL10039 based Compro
S350/S300 budget DVB-S cards. Everything except 2 way diseqc should work.



diff -r f637ac5a5898 linux/drivers/media/common/ir-keymaps.c
--- a/linux/drivers/media/common/ir-keymaps.c	Fri Dec 28 00:32:41 2007 -0200
+++ b/linux/drivers/media/common/ir-keymaps.c	Fri Dec 28 15:51:25 2007 +0200
@@ -1898,3 +1898,40 @@ IR_KEYTAB_TYPE ir_codes_fusionhdtv_mce[I
 };
 
 EXPORT_SYMBOL_GPL(ir_codes_fusionhdtv_mce);
+
+IR_KEYTAB_TYPE ir_codes_videomate_s350[IR_KEYTAB_SIZE] = {
+	[ 0x00 ] = KEY_TV,
+	[ 0x01 ] = KEY_DVD,
+	[ 0x04 ] = KEY_RECORD,
+	[ 0x05 ] = KEY_VIDEO, /* TV/Video */
+	[ 0x07 ] = KEY_STOP,
+	[ 0x08 ] = KEY_PLAYPAUSE,
+	[ 0x0a ] = KEY_REWIND,
+	[ 0x0f ] = KEY_FASTFORWARD,
+	[ 0x10 ] = KEY_CHANNELUP,
+	[ 0x12 ] = KEY_VOLUMEUP,
+	[ 0x13 ] = KEY_CHANNELDOWN,
+	[ 0x14 ] = KEY_MUTE,
+	[ 0x15 ] = KEY_VOLUMEDOWN,
+	[ 0x16 ] = KEY_1,
+	[ 0x17 ] = KEY_2,
+	[ 0x18 ] = KEY_3,
+	[ 0x19 ] = KEY_4,
+	[ 0x1a ] = KEY_5,
+	[ 0x1b ] = KEY_6,
+	[ 0x1c ] = KEY_7,
+	[ 0x1d ] = KEY_8,
+	[ 0x1e ] = KEY_9,
+	[ 0x1f ] = KEY_0,
+	[ 0x21 ] = KEY_SLEEP,
+	[ 0x24 ] = KEY_ZOOM,
+	[ 0x25 ] = KEY_LAST,	/* Recall */
+	[ 0x26 ] = KEY_SUBTITLE, /* CC */
+	[ 0x27 ] = KEY_LANGUAGE, /* MTS */
+	[ 0x29 ] = KEY_CHANNEL, /* SURF */
+	[ 0x2b ] = KEY_A,
+	[ 0x2c ] = KEY_B,
+	[ 0x2f ] = KEY_SHUFFLE, /* Snapshot */
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_videomate_s350);
diff -r f637ac5a5898 linux/drivers/media/dvb/frontends/Kconfig
--- a/linux/drivers/media/dvb/frontends/Kconfig	Fri Dec 28 00:32:41 2007 -0200
+++ b/linux/drivers/media/dvb/frontends/Kconfig	Sat Dec 29 16:34:08 2007 +0200
@@ -70,6 +70,13 @@ config DVB_TDA10086
 	default m if DVB_FE_CUSTOMISE
 	help
 	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_ZL10313
+	tristate "Zarlink ZL10313 DVB-S demodulator"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S demodulator module. Say Y when you want to support this frontend.
 
 comment "DVB-T (terrestrial) frontends"
 	depends on DVB_CORE
@@ -369,6 +376,14 @@ config DVB_TUNER_XC5000
 	  This device is only used inside a SiP called togther with a
 	  demodulator for now.
 
+config DVB_ZL10039
+	tristate "Zarlink ZL10039 DVB-S tuner"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A driver for the ZL10039 DVB-S tuner from Zarlink
+
+
 comment "Miscellaneous devices"
 	depends on DVB_CORE
 
diff -r f637ac5a5898 linux/drivers/media/dvb/frontends/Makefile
--- a/linux/drivers/media/dvb/frontends/Makefile	Fri Dec 28 00:32:41 2007 -0200
+++ b/linux/drivers/media/dvb/frontends/Makefile	Fri Dec 28 15:12:10 2007 +0200
@@ -51,3 +51,5 @@ obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131
 obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o
 obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
 obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o
+obj-$(CONFIG_DVB_ZL10313) += zl10313.o
+obj-$(CONFIG_DVB_ZL10039) += zl10039.o
diff -r f637ac5a5898 linux/drivers/media/dvb/frontends/zl10039.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/zl10039.c	Sat Dec 29 16:04:18 2007 +0200
@@ -0,0 +1,260 @@
+/*
+ *  Driver for Zarlink ZL10039 DVB-S tuner
+ *
+ *  Copyright 2007 Jan Daniël Louw <jd.louw at mweb.co.za>
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/dvb/frontend.h>
+
+#include "dvb_frontend.h"
+#include "zl10039.h"
+#include "zl10039_priv.h"
+
+
+static int zl10039_read(const struct zl10039_state *state,
+			const enum zl10039_reg_addr reg, u8 *buf,
+			const size_t count)
+{
+	struct i2c_msg msg[2];
+	u8 regbuf[1] = { reg };
+	int i;
+
+	io_printk("%s\n", __FUNCTION__);
+	/* Write register address */
+	msg[0].addr = state->config.tuner_address;
+	msg[0].flags = 0;
+	msg[0].buf = regbuf;
+	msg[0].len = 1;
+	/* Read count bytes */
+	msg[1].addr = state->config.tuner_address;
+	msg[1].flags = I2C_M_RD;
+	msg[1].buf = buf;
+	msg[1].len = count;
+	if (i2c_transfer(state->i2c, msg, 2) != 2) {
+		eprintk("%s: i2c read error\n", __FUNCTION__);
+		return -EREMOTEIO;
+	}
+	for (i = 0; i < count; i++) {
+		io_printk("R[%s] = 0x%x\n", zl10039_reg_names[reg + i], buf[i]);
+	}
+	return 0; /* Success */
+}
+
+static int zl10039_write(struct zl10039_state *state,
+			const enum zl10039_reg_addr reg, const u8 *src,
+			const size_t count)
+{
+	u8 buf[count + 1];
+	struct i2c_msg msg;
+	int i;
+
+	io_printk("%s\n", __FUNCTION__);
+	for (i = 0; i < count; i++) {
+		io_printk("W[%s] = 0x%x\n", zl10039_reg_names[reg + i], src[i]);
+	}
+	/* Write register address and data in one go */
+	buf[0] = reg;
+	memcpy(&buf[1], src, count);
+	msg.addr = state->config.tuner_address;
+	msg.flags = 0;
+	msg.buf = buf;
+	msg.len = count + 1;
+	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+		eprintk("%s: i2c write error\n", __FUNCTION__);
+		return -EREMOTEIO;
+	}
+	return 0; /* Success */
+}
+
+static inline int zl10039_readreg(struct zl10039_state *state,
+				const enum zl10039_reg_addr reg, u8 *val)
+{
+	return zl10039_read(state, reg, val, 1);
+}
+
+static inline int zl10039_writereg(struct zl10039_state *state,
+				const enum zl10039_reg_addr reg,
+				const u8 val)
+{
+	return zl10039_write(state, reg, &val, 1);
+}
+
+static int zl10039_init(struct dvb_frontend *fe)
+{
+	struct zl10039_state *state = fe->tuner_priv;
+	int ret;
+
+	trace_printk("%s\n", __FUNCTION__);
+	if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1);
+	/* Reset logic */
+	ret = zl10039_writereg(state, GENERAL, 0x40);
+	if (ret < 0) {
+		eprintk("Note: i2c write error normal when resetting the "
+			"tuner\n");
+	}
+	/* Wake up */
+	ret = zl10039_writereg(state, GENERAL, 0x01);
+	if (ret < 0) {
+		eprintk("Tuner power up failed\n");
+		return ret;
+	}
+	if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+	return 0;
+}
+
+static int zl10039_sleep(struct dvb_frontend *fe)
+{
+	struct zl10039_state *state = fe->tuner_priv;
+	int ret;
+
+	trace_printk("%s\n", __FUNCTION__);
+	if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1);
+	ret = zl10039_writereg(state, GENERAL, 0x80);
+	if (ret < 0) {
+		eprintk("Tuner sleep failed\n");
+		return ret;
+	}
+	if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+	return 0;
+}
+
+static int zl10039_set_params(struct dvb_frontend *fe,
+			struct dvb_frontend_parameters *params)
+{
+	struct zl10039_state *state = fe->tuner_priv;
+	u8 buf[6];
+	u8 bf;
+	u32 fbw;
+	u32 div;
+	int ret;
+
+	trace_printk("%s\n", __FUNCTION__);
+	params_printk("Set frequency = %d, symbol rate = %d\n",
+			params->frequency, params->u.qpsk.symbol_rate);
+
+	/* Assumed 10.111 MHz crystal oscillator */
+	/* Cancelled num/den 80 to prevent overflow */
+	div = (params->frequency * 1000) / 126387;
+	fbw = (params->u.qpsk.symbol_rate * 27) / 32000;
+	/* Cancelled num/den 10 to prevent overflow */
+	bf = ((fbw * 5088) / 1011100) - 1;
+
+	/*PLL divider*/
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = (div >> 0) & 0xff;
+	/*Reference divider*/
+	/* Select reference ratio of 80 */
+	buf[2] = 0x1D;
+	/*PLL test modes*/
+	buf[3] = 0x40;
+	/*RF Control register*/
+	buf[4] = 0x6E; /* Bypass enable */
+	/*Baseband filter cutoff */
+	buf[5] = bf;
+
+	/* Open i2c gate */
+	if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1);
+	/* BR = 10, Enable filter adjustment */
+	ret = zl10039_writereg(state, BASE1, 0x0A);
+	if (ret < 0) goto error;
+	/* Write new config values */
+	ret = zl10039_write(state, PLL0, buf, sizeof(buf));
+	if (ret < 0) goto error;
+	/* BR = 10, Disable filter adjustment */
+	ret = zl10039_writereg(state, BASE1, 0x6A);
+	if (ret < 0) goto error;
+
+	zl10039_dump_registers(state);
+	/* Close i2c gate */
+	if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+	return 0;
+error:
+	eprintk("Error setting tuner\n");
+	return ret;
+}
+
+static struct dvb_tuner_ops zl10039_ops;
+
+struct dvb_frontend * zl10039_attach(struct dvb_frontend *fe,
+		const struct zl10039_config *config, struct i2c_adapter *i2c)
+{
+	struct zl10039_state *state = NULL;
+
+	trace_printk("%s\n", __FUNCTION__);
+	state = kmalloc(sizeof(struct zl10039_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	state->i2c = i2c;
+	state->config.tuner_address = config->tuner_address;
+
+	/* Open i2c gate */
+	if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1);
+	/* check if this is a valid tuner */
+	if (zl10039_readreg(state, GENERAL, &state->id) < 0) {
+		/* Close i2c gate */
+		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+		goto error;
+	}
+	/* Close i2c gate */
+	if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+
+	state->id = state->id & 0x0F;
+	switch (state->id) {
+	case ID_ZL10039:
+		strcpy(fe->ops.tuner_ops.info.name,
+			"Zarlink ZL10039 DVB-S tuner");
+		break;
+	default:
+		eprintk("Chip ID does not match a known type\n");
+		goto error;
+	}
+	memcpy(&fe->ops.tuner_ops, &zl10039_ops, sizeof(struct dvb_tuner_ops));
+	fe->tuner_priv = state;
+	iprintk("Tuner attached @ i2c address 0x%02x\n", config->tuner_address);
+	return fe;
+error:
+	kfree(state);
+	return NULL;
+}
+
+static int zl10039_release(struct dvb_frontend *fe)
+{
+	struct zl10039_state *state = fe->tuner_priv;
+
+	trace_printk("%s\n", __FUNCTION__);
+	kfree(state);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static struct dvb_tuner_ops zl10039_ops = {
+	.release = zl10039_release,
+	.init = zl10039_init,
+	.sleep = zl10039_sleep,
+	.set_params = zl10039_set_params,
+};
+
+EXPORT_SYMBOL(zl10039_attach);
+
+MODULE_DESCRIPTION("Zarlink ZL10039 DVB-S tuner driver");
+MODULE_AUTHOR("Jan Daniël Louw <jd.louw at mweb.co.za>");
+MODULE_LICENSE("GPL");
diff -r f637ac5a5898 linux/drivers/media/dvb/frontends/zl10039.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/zl10039.h	Sat Dec 29 15:12:47 2007 +0200
@@ -0,0 +1,46 @@
+/*
+    Driver for Zarlink ZL10039 DVB-S tuner
+
+    Copyright (C) 2007 Jan Daniël Louw <jd.louw at mweb.co.za>
+
+    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.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef ZL10039_H
+#define ZL10039_H
+
+struct zl10039_config
+{
+	/* tuner's i2c address */
+	u8 tuner_address;
+};
+
+#if defined(CONFIG_DVB_ZL10039) || (defined(CONFIG_DVB_ZL10039_MODULE) \
+	    && defined(MODULE))
+struct dvb_frontend * zl10039_attach(struct dvb_frontend *fe,
+					const struct zl10039_config *config,
+					struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend * zl10039_attach(struct dvb_frontend *fe,
+					const struct zl10039_config *config,
+					struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_ZL10039 */
+
+#endif /* ZL10039_H */
diff -r f637ac5a5898 linux/drivers/media/dvb/frontends/zl10039_priv.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/zl10039_priv.h	Sat Dec 29 16:09:49 2007 +0200
@@ -0,0 +1,121 @@
+/*
+ *  Driver for Zarlink ZL10039 DVB-S tuner
+ *
+ *  Copyright 2007 Jan Daniël Louw <jd.louw at mweb.co.za>
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef DVB_FRONTENDS_ZL10039_PRIV
+#define DVB_FRONTENDS_ZL10039_PRIV
+
+/* Trace function calls */
+#define DEBUG_CALL_TRACE	0
+/* Trace read/write function calls - information overload */
+#define DEBUG_IO_TRACE		0
+/* Print register values at critical points */
+#define DEBUG_DUMP_REGISTERS	0
+/* Print important params passed to functions */
+#define DEBUG_PRINT_PARAMS	0
+
+#if DEBUG_CALL_TRACE
+	#define trace_printk(args...) printk(KERN_DEBUG "tuner: zl10039: " args)
+#else
+	#define trace_printk(args...)
+#endif
+
+#if DEBUG_IO_TRACE
+	#define io_printk(args...) printk(KERN_DEBUG "tuner: zl10039: " args)
+#else
+	#define io_printk(args...)
+#endif
+
+#if DEBUG_PRINT_PARAMS
+	#define params_printk(args...) printk(KERN_DEBUG "tuner: zl10039: " \
+						args)
+#else
+	#define params_printk(args...)
+#endif
+
+#define eprintk(args...) printk(KERN_ERR "tuner: zl10039: " args)
+#define iprintk(args...) printk(KERN_INFO "tuner: zl10039: " args)
+
+enum zl10039_model_id {
+	ID_ZL10039 = 1
+};
+
+struct zl10039_state {
+	struct i2c_adapter *i2c;
+	struct zl10039_config config;
+	u8 id;
+};
+
+enum zl10039_reg_addr {
+	PLL0 = 0,
+	PLL1,
+	PLL2,
+	PLL3,
+	RFFE,
+	BASE0,
+	BASE1,
+	BASE2,
+	LO0,
+	LO1,
+	LO2,
+	LO3,
+	LO4,
+	LO5,
+	LO6,
+	GENERAL
+};
+
+#if DEBUG_DUMP_REGISTERS || DEBUG_IO_TRACE
+static const char *zl10039_reg_names[] = {
+	"PLL_0", "PLL_1", "PLL_2", "PLL_3",
+	"RF_FRONT_END", "BASE_BAND_0", "BASE_BAND_1", "BASE_BAND_2",
+	"LOCAL_OSC_0", "LOCAL_OSC_1", "LOCAL_OSC_2", "LOCAL_OSC_3",
+	"LOCAL_OSC_4", "LOCAL_OSC_5", "LOCAL_OSC_6", "GENERAL"
+};
+#endif
+
+#if DEBUG_DUMP_REGISTERS
+static int zl10039_read(const struct zl10039_state *state,
+			const enum zl10039_reg_addr reg, u8 *buf,
+			const size_t count);
+
+static void zl10039_dump_registers(const struct zl10039_state *state)
+{
+	u8 buf[16];
+	int ret;
+	u8 reg;
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10039_read(state, PLL0, buf, sizeof(buf));
+	if (ret < 0) return;
+	for (reg = PLL0; reg <= GENERAL; reg += 4) {
+		printk(KERN_DEBUG "%03x: [%02x %13s] [%02x %13s] [%02x %13s] "
+			"[%02x %13s]\n", reg, buf[reg], zl10039_reg_names[reg],
+			buf[reg+1], zl10039_reg_names[reg+1], buf[reg+2],
+			zl10039_reg_names[reg+2], buf[reg+3],
+			zl10039_reg_names[reg+3]);
+	}
+}
+#else
+static inline void zl10039_dump_registers(const struct zl10039_state *state) {}
+#endif /* DEBUG_DUMP_REGISTERS */
+
+#endif /* DVB_FRONTENDS_ZL10039_PRIV */
+
diff -r f637ac5a5898 linux/drivers/media/dvb/frontends/zl10313.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/zl10313.c	Sat Dec 29 15:51:31 2007 +0200
@@ -0,0 +1,569 @@
+/*
+ *  Driver for Zarlink ZL10313 DVB-S demodulator
+ *
+ *  Copyright 2007 Jan Daniël Louw <jd.louw at mweb.co.za>
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/dvb/frontend.h>
+
+#include "dvb_frontend.h"
+#include "zl10313.h"
+#include "zl10313_priv.h"
+
+
+static int zl10313_read(const struct zl10313_state *state,
+			const enum zl10313_reg_addr reg,
+			u8 *buf, const size_t count)
+{
+	struct i2c_msg msg[2];
+	u8 regbuf[1] = { reg };
+	int i;
+
+	io_printk("%s\n", __FUNCTION__);
+	/* Write address to read */
+	msg[0].addr = state->config->demod_address;
+	msg[0].flags = 0;
+	msg[0].buf = regbuf;
+	msg[0].len = 1;
+	/* Read count bytes */
+	msg[1].addr = state->config->demod_address;
+	msg[1].flags = I2C_M_RD;
+	msg[1].buf = buf;
+	msg[1].len = count;
+
+	if (i2c_transfer(state->i2c, msg, 2) != 2) {
+		eprintk("%s: i2c read error\n", __FUNCTION__);
+		return -EREMOTEIO;
+	}
+	for (i = 0; i < count; i++) {
+		io_printk("R[%s] = 0x%x\n", zl10313_reg_names[reg + i], buf[i]);
+	}
+	return 0;
+}
+
+static int zl10313_write(struct zl10313_state *state,
+			const enum zl10313_reg_addr reg, const void *src,
+			const size_t count)
+{
+	u8 buf[count + 1];
+	struct i2c_msg msg;
+	int i;
+
+	io_printk("%s\n", __FUNCTION__);
+	for (i = 0; i < count; i++) {
+		io_printk("R[%s] = 0x%x\n", zl10313_reg_names[reg + i], buf[i]);
+	}
+	buf[0] = reg;
+	memcpy(&buf[1], src, count);
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.buf = buf;
+	msg.len = count + 1;
+
+	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+		eprintk("%s: i2c write error\n", __FUNCTION__);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static inline int zl10313_readreg(struct zl10313_state *state,
+				const enum zl10313_reg_addr reg, u8 *val)
+{
+	return zl10313_read(state, reg, val, 1);
+}
+
+static inline int zl10313_writereg(struct zl10313_state *state,
+				const enum zl10313_reg_addr reg, const u8 val)
+{
+	return zl10313_write(state, reg, &val, 1);
+}
+
+static inline u32 zl10313_div(u32 a, u32 b)
+{
+	return (a + (b / 2)) / b;
+}
+
+static int zl10313_read_status(struct dvb_frontend *fe, fe_status_t *s)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+	u8 status[3];
+
+	trace_printk("%s\n", __FUNCTION__);
+	*s = 0;
+	ret = zl10313_read(state, QPSK_STAT_H, status, sizeof(status));
+	if (ret < 0) return ret;
+	if (status[0] & 0xc0)
+		*s |= FE_HAS_SIGNAL;    /* signal noise ratio */
+	if (status[0] & 0x04)
+		*s |= FE_HAS_CARRIER;   /* qpsk carrier lock */
+	if (status[2] & 0x02)
+		*s |= FE_HAS_VITERBI;   /* viterbi lock */
+	if (status[2] & 0x04)
+		*s |= FE_HAS_SYNC;      /* byte align lock */
+	if (status[0] & 0x01)
+		*s |= FE_HAS_LOCK;      /* qpsk lock */
+
+	return 0;
+}
+
+/* Reports only Reed-Solomon error count (Viterbi BER) */
+/* IMPROVEMENT: Implement Viterbi error count (QPSK BER), a bit of a problem
+		without IRQs? */
+/* Viterbi BER = RS_BERCNT / (dt * CR * 2 * SR) */
+static int zl10313_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+	u8 buf[3];
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_read(state, RS_BERCNT_H, buf, 3);
+	if (ret < 0) return ret;
+	*ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]);
+	return 0;
+}
+
+/* Signal strength a number out of 1024 */
+static int zl10313_read_signal_strength(struct dvb_frontend *fe,
+					u16 * signal_strength)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+	u8 buf[3];
+	u16 agc;
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_read(state, AGC_H, buf, sizeof(buf));
+	if (ret < 0) return ret;
+	agc = (buf[0] << 2) | (buf[1] >> 6);
+	*signal_strength = agc;
+	return 0;
+}
+
+/* Holds for Eb/No between 0 and 12 dB */
+static int zl10313_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+	u8 buf[2];
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_read(state, M_SNR_H, buf, sizeof(buf));
+	if (ret < 0) return ret;
+	*snr = (13312 - (((buf[0] & 0x7f) << 8) | buf[1])) / 683;
+	return 0;
+}
+
+/* Block error rate = (ucblocks * 1632) / (dt * SR * 2 * CR) */
+static int zl10313_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+	u8 buf[2];
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_read(state, RS_UBC_H, buf, sizeof(buf));
+	if (ret < 0) return ret;
+	*ucblocks = (buf[0] << 8) | buf[1];
+	return 0;
+}
+
+static int zl10313_get_inversion(struct zl10313_state *state,
+		fe_spectral_inversion_t *i)
+{
+	int ret;
+	u8 vit_mode;
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_readreg(state, VIT_MODE, &vit_mode);
+	if (ret < 0) return ret;
+	*i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF;
+	return 0;
+}
+
+static int zl10313_get_symbol_rate(struct zl10313_state *state, u32 *sr)
+{
+	int ret;
+	u16 monitor;
+	u8 buf[2];
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_writereg(state, MON_CTRL, 0x03);
+	if (ret < 0) return ret;
+	ret = zl10313_read(state, MONITOR_H, buf, sizeof(buf));
+	if (ret < 0) return ret;
+	monitor = (buf[0] << 8) | buf[1];
+	/* SR = monitor * 1000000 / 1024 */
+	*sr = zl10313_div(monitor * 15625, 16);
+	return 0;
+}
+
+static int zl10313_get_code_rate(struct zl10313_state *state,
+					fe_code_rate_t *cr)
+{
+	const fe_code_rate_t fec_tab[8] = { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6,
+			FEC_6_7, FEC_7_8, FEC_AUTO, FEC_AUTO };
+	int ret;
+	u8 fec_status;
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_readreg(state, FEC_STATUS, &fec_status);
+	if (ret < 0) return ret;
+	*cr = fec_tab[(fec_status >> 4) & 0x07];
+	return 0;
+}
+
+static int zl10313_get_frontend(struct dvb_frontend *fe,
+				struct dvb_frontend_parameters *p)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_get_inversion(state, &p->inversion);
+	if (ret < 0) return ret;
+	ret = zl10313_get_symbol_rate(state, &p->u.qpsk.symbol_rate);
+	if (ret < 0) return ret;
+	ret = zl10313_get_code_rate(state, &p->u.qpsk.fec_inner);
+	if (ret < 0) return ret;
+	p->frequency = state->prev_frequency;
+	return 0;
+}
+
+static int zl10313_set_frontend(struct dvb_frontend *fe,
+				struct dvb_frontend_parameters *p)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+	u8 buf[5];
+	u16 sr;
+	const u8 fec_tab[10] = { 0x00, 0x01, 0x02, 0x04, 0x3f, 0x08, 0x10,
+				0x20, 0x3f, 0x3f };
+	const u8 inv_tab[3] = { 0x00, 0x40, 0x80 };
+
+	trace_printk("%s\n", __FUNCTION__);
+	params_printk("Setting freq: %d, fec: %s, inversion: %s\n",
+		p->frequency, fec_str[p->u.qpsk.fec_inner],
+		inv_str[p->inversion]);
+
+	if ((p->frequency < fe->ops.info.frequency_min) ||
+		(p->frequency > fe->ops.info.frequency_max)) return -EINVAL;
+
+	if ((p->inversion < INVERSION_OFF) || (p->inversion > INVERSION_AUTO))
+		return -EINVAL;
+
+	if ((p->u.qpsk.symbol_rate < fe->ops.info.symbol_rate_min) ||
+		(p->u.qpsk.symbol_rate > fe->ops.info.symbol_rate_max))
+		return -EINVAL;
+
+	if ((p->u.qpsk.fec_inner < FEC_NONE) ||
+		(p->u.qpsk.fec_inner > FEC_AUTO)) return -EINVAL;
+
+	if ((p->u.qpsk.fec_inner == FEC_4_5) ||
+		(p->u.qpsk.fec_inner == FEC_8_9)) return -EINVAL;
+
+	/* Set the tuner up first */
+	fe->ops.tuner_ops.set_params(fe, p);
+
+	/* sr = SR * 256 / 1000000 */
+	sr = zl10313_div(p->u.qpsk.symbol_rate * 4, 15625);
+	/* SYM_RATE */
+	buf[0] = (sr >> 8) & 0x3f;
+	buf[1] = (sr >> 0) & 0xff;
+	/* VIT_MODE */
+	buf[2] = inv_tab[p->inversion] | fec_tab[p->u.qpsk.fec_inner];
+	/* QPSK_CTRL */
+	buf[3] = 0x40; /* swap I and Q before QPSK demodulation */
+	if (p->u.qpsk.symbol_rate < 10000000)
+		buf[3] |= 0x04; /* use afc mode */
+	/* GO */
+	buf[4] = 0x01;
+
+	ret = zl10313_write(state, SYM_RATE_H, buf, sizeof(buf));
+	if (ret < 0) {
+		eprintk("Demodulator failed setup\n");
+		return ret;
+	}
+	state->prev_frequency = p->frequency;
+	zl10313_dump_registers(state);
+
+	return 0;
+}
+
+static int zl10313_sleep(struct dvb_frontend *fe)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+	u8 config;
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_readreg(state, CONFIG, &config);
+	if (ret < 0) return ret;
+	/* enter standby */
+	ret = zl10313_writereg(state, CONFIG, config & 0x7f);
+	if (ret < 0) return ret;
+	return 0;
+}
+
+static int zl10313_init(struct dvb_frontend *fe)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+	u8 buf[2];
+
+	trace_printk("%s\n", __FUNCTION__);
+	/* Set up for QPSK DVB-S with 10MHz crystal ref */
+	ret = zl10313_writereg(state, CONFIG, 0x8c);
+	if (ret < 0) return ret;
+	/* Wait 150 us to settle */
+	udelay(150);
+	/* Full reset */
+	ret = zl10313_writereg(state, RESET, 0x80);
+	if (ret < 0) return ret;
+	/* Crucial for correct operation - differs from mt312 datasheet */
+	ret = zl10313_writereg(state, GPP_CTRL, 0x80);
+	if (ret < 0) return ret;
+	/* Crucial for correct operation - unknown register in zl10313 */
+	/* Don't know what this register is for in the zl10313 */
+	ret = zl10313_writereg(state, VIT_ERRPER_M, 0x00);
+	if (ret < 0) return ret;
+	buf[0] = 182;
+	buf[1] = zl10313_div(10111000, 88000);
+	/*Set DISECQ ratio for 22KHz tone */
+	ret = zl10313_write(state, SYS_CLK, buf, sizeof(buf));
+	if (ret < 0) return ret;
+	/* Supposed to set register 49 to value 50 - can't see any difference */
+	ret = zl10313_writereg(state, SNR_THS_HIGH, 50);
+	if (ret < 0) return ret;
+
+	zl10313_dump_registers(state);
+
+	return 0;
+}
+
+static int zl10313_send_master_cmd(struct dvb_frontend *fe,
+				struct dvb_diseqc_master_cmd *c)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	int ret;
+	u8 diseqc_mode;
+
+	trace_printk("%s\n", __FUNCTION__);
+	if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg))) return -EINVAL;
+
+	ret = zl10313_readreg(state, DISEQC_MODE, &diseqc_mode);
+	if (ret < 0) return ret;
+
+	ret = zl10313_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len);
+	if (ret < 0) return ret;
+
+	ret = zl10313_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40) |
+				((c->msg_len - 1) << 3) | 0x04);
+	if (ret < 0) return ret;
+
+	msleep(100);
+
+	/* set DISEQC_MODE[2:0] to zero if a return message is expected */
+	if (c->msg[0] & 0x02) {
+		ret = zl10313_writereg(state, DISEQC_MODE, diseqc_mode & 0x40);
+		if (ret < 0) return ret;
+	}
+	return 0;
+}
+
+static int zl10313_send_burst(struct dvb_frontend *fe,
+				const fe_sec_mini_cmd_t c)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	const u8 mini_tab[2] = { 0x02, 0x03 };
+	int ret;
+	u8 diseqc_mode;
+
+	trace_printk("%s\n", __FUNCTION__);
+	params_printk("Set mini burst %s (enum %d)\n",  mini_cmd_name[c], c);
+	if (c > SEC_MINI_B) return -EINVAL;
+	ret = zl10313_readreg(state, DISEQC_MODE, &diseqc_mode);
+	if (ret < 0) return ret;
+	ret = zl10313_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40) |
+				mini_tab[c]);
+	if (ret < 0) return ret;
+	return 0;
+}
+
+static int zl10313_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	const u8 tone_tab[2] = { 0x01, 0x00 };
+	int ret;
+	u8 diseqc_mode;
+
+	trace_printk("%s\n", __FUNCTION__);
+	params_printk("Set tone %s (enum %d)\n", tone_name[tone], tone);
+	if (tone > SEC_TONE_OFF) return -EINVAL;
+	ret = zl10313_readreg(state, DISEQC_MODE, &diseqc_mode);
+	if (ret < 0) return ret;
+	ret = zl10313_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40) |
+				tone_tab[tone]);
+	if (ret < 0) return ret;
+	return 0;
+}
+
+static int zl10313_set_voltage(struct dvb_frontend *fe,
+				fe_sec_voltage_t voltage)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	const u8 volt_tab[3] = { 0x00, 0x40, 0x00 };
+	int ret;
+	u8 diseqc_mode;
+
+	trace_printk("%s\n", __FUNCTION__);
+	params_printk("Set voltage %s (enum %d)\n", volt_name[voltage],
+			voltage);
+	if (voltage > SEC_VOLTAGE_OFF) return -EINVAL;
+	ret = zl10313_readreg(state, DISEQC_MODE, &diseqc_mode);
+	if (ret < 0) return ret;
+	ret = zl10313_writereg(state, DISEQC_MODE, (diseqc_mode & 0x07) |
+				volt_tab[voltage]);
+	if (ret < 0) return ret;
+	return 0;
+}
+
+static void zl10313_release(struct dvb_frontend *fe)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+
+	trace_printk("%s\n", __FUNCTION__);
+	kfree(state);
+}
+
+static int zl10313_get_tune_settings(struct dvb_frontend *fe,
+				struct dvb_frontend_tune_settings *fesettings)
+{
+	fesettings->min_delay_ms = 100;
+	fesettings->step_size = 0;
+	fesettings->max_drift = 0;
+	return 0;
+}
+
+static int zl10313_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct zl10313_state *state = fe->demodulator_priv;
+	u8 gpp_ctrl;
+	int ret;
+
+	trace_printk("%s,\n", __FUNCTION__);
+	params_printk("Switching %d\n", enable);
+	ret = zl10313_readreg(state, GPP_CTRL, &gpp_ctrl);
+	if (ret < 0) return ret;
+	if (enable) {
+		return zl10313_writereg(state, GPP_CTRL, gpp_ctrl | 0x40);
+	} else {
+		return zl10313_writereg(state, GPP_CTRL, gpp_ctrl & ~0x40);
+	}
+}
+
+
+static struct dvb_frontend_ops zl10313_ops;
+
+struct dvb_frontend * zl10313_attach(const struct zl10313_config *config,
+					struct i2c_adapter *i2c)
+{
+	struct zl10313_state *state = NULL;
+
+	trace_printk("%s\n", __FUNCTION__);
+
+	state = kmalloc(sizeof(struct zl10313_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->frontend.ops, &zl10313_ops,
+		sizeof(struct dvb_frontend_ops));
+
+	/* check if this is a valid demodulator */
+	if (zl10313_readreg(state, ID, &state->id) < 0) goto error;
+
+	switch (state->id) {
+	case ID_ZL10313:
+		strcpy(state->frontend.ops.info.name, "Zarlink ZL10313 DVB-S");
+		break;
+	default:
+		eprintk("Chip ID does not match a known type\n");
+		goto error;
+	}
+	state->frontend.demodulator_priv = state;
+	iprintk("Demodulator attached @ i2c address 0x%02x\n",
+		config->demod_address);
+
+	zl10313_dump_registers(state);
+	return &state->frontend;
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops zl10313_ops = {
+
+	.info = {
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,
+		.frequency_max		= 2150000,
+		.frequency_stepsize	= 125,      /* kHz for QPSK frontends */
+		.frequency_tolerance	= 29500,
+		.symbol_rate_min	= 1000000,
+		.symbol_rate_max	= 45000000,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 |
+			FE_CAN_QPSK | FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO |
+			FE_CAN_RECOVER
+	},
+
+	.release = zl10313_release,
+
+	.init = zl10313_init,
+	.sleep = zl10313_sleep,
+
+	.set_frontend = zl10313_set_frontend,
+	.get_frontend = zl10313_get_frontend,
+	.get_tune_settings = zl10313_get_tune_settings,
+
+	.read_status = zl10313_read_status,
+	.read_ber = zl10313_read_ber,
+	.read_signal_strength = zl10313_read_signal_strength,
+	.read_snr = zl10313_read_snr,
+	.read_ucblocks = zl10313_read_ucblocks,
+
+	.diseqc_send_master_cmd = zl10313_send_master_cmd,
+	.diseqc_send_burst = zl10313_send_burst,
+	.set_voltage = zl10313_set_voltage,
+	.set_tone = zl10313_set_tone,
+	.i2c_gate_ctrl = zl10313_i2c_gate_ctrl,
+
+};
+
+MODULE_DESCRIPTION("Zarlink ZL10313 DVB-S demodulator driver");
+MODULE_AUTHOR("Jan Daniël Louw <jd.louw at mweb.co.za>");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(zl10313_attach);
diff -r f637ac5a5898 linux/drivers/media/dvb/frontends/zl10313.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/zl10313.h	Sat Dec 29 14:12:24 2007 +0200
@@ -0,0 +1,47 @@
+/*
+    Driver for Zarlink ZL10313 DVB-S demodulator
+
+    Copyright (C) 2007 Jan Daniël Louw <jd.louw at mweb.co.za>
+
+    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.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef ZL10313_H
+#define ZL10313_H
+
+
+struct zl10313_config
+{
+	/* demodulator's i2c address */
+	u8 demod_address;
+};
+
+
+#if defined(CONFIG_DVB_ZL10313) || (defined(CONFIG_DVB_ZL10313_MODULE) && \
+defined(MODULE))
+struct dvb_frontend * zl10313_attach(const struct zl10313_config *config,
+					struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend * zl10313_attach(
+					const struct zl10313_config *config,
+					struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_ZL10313 */
+
+#endif /* ZL10313_H */
diff -r f637ac5a5898 linux/drivers/media/dvb/frontends/zl10313_priv.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/dvb/frontends/zl10313_priv.h	Sat Dec 29 16:09:33 2007 +0200
@@ -0,0 +1,274 @@
+/*
+    Driver for Zarlink ZL10313 DVB-S Frontend
+
+    Copyright (C) 2007 Jan Daniël Louw <jd.louw at mweb.co.za>
+
+    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.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DVB_FRONTENDS_ZL10313_PRIV
+#define DVB_FRONTENDS_ZL10313_PRIV
+
+/* Trace function calls */
+#define DEBUG_CALL_TRACE	0
+/* Trace read/write function calls - information overload */
+#define DEBUG_IO_TRACE		0
+/* Print register values at critical points */
+#define DEBUG_DUMP_REGISTERS	0
+/* Print important params passed to functions */
+#define DEBUG_PRINT_PARAMS	0
+
+#if DEBUG_CALL_TRACE
+	#define trace_printk(args...) printk(KERN_DEBUG "demodulator: " \
+						"zl10313: " args)
+#else
+	#define trace_printk(args...)
+#endif
+
+#if DEBUG_IO_TRACE
+	#define io_printk(args...) printk(KERN_DEBUG "demodulator: zl10313: " \
+						args)
+#else
+	#define io_printk(args...)
+#endif
+
+#if DEBUG_PRINT_PARAMS
+	#define params_printk(args...) printk(KERN_DEBUG \
+					"demodulator: zl10313: " args)
+#else
+	#define params_printk(args...)
+#endif
+
+#define eprintk(args...) printk(KERN_ERR "demodulator: zl10313: " args)
+#define iprintk(args...) printk(KERN_INFO "demodulator: zl10313: " args)
+
+enum zl10313_model_id {
+	ID_VP310 = 1,
+	ID_MT312 = 3,
+	ID_ZL10313 = 5
+};
+
+struct zl10313_state
+{
+	struct i2c_adapter *i2c;
+	const struct zl10313_config *config;
+	u8 id;
+	u32 prev_frequency; /* Store previously set frequency */
+	struct dvb_frontend frontend;
+};
+
+enum zl10313_reg_addr {
+	QPSK_INT_H = 0,
+	QPSK_INT_M = 1,
+	QPSK_INT_L = 2,
+	FEC_INT = 3,
+	QPSK_STAT_H = 4,
+	QPSK_STAT_L = 5,
+	FEC_STATUS = 6,
+	LNB_FREQ_H = 7,
+	LNB_FREQ_L = 8,
+	M_SNR_H = 9,
+	M_SNR_L = 10,
+	VIT_ERRCNT_H = 11,
+	VIT_ERRCNT_M = 12,
+	VIT_ERRCNT_L = 13,
+	RS_BERCNT_H = 14,
+	RS_BERCNT_M = 15,
+	RS_BERCNT_L = 16,
+	RS_UBC_H = 17,
+	RS_UBC_L = 18,
+	SIG_LEVEL = 19,
+	GPP_CTRL = 20,
+	RESET = 21,
+	DISEQC_MODE = 22,
+	SYM_RATE_H = 23,
+	SYM_RATE_L = 24,
+	VIT_MODE = 25,
+	QPSK_CTRL = 26,
+	GO = 27,
+	IE_QPSK_H = 28,
+	IE_QPSK_M = 29,
+	IE_QPSK_L = 30,
+	IE_FEC = 31,
+	QPSK_STAT_EN = 32,
+	FEC_STAT_EN = 33,
+	SYS_CLK = 34,
+	DISEQC_RATIO = 35,
+	DISEQC_INSTR = 36,
+	FR_LIM = 37,
+	FR_OFF = 38,
+	AGC_CTRL = 39,
+	AGC_INIT = 40,
+	AGC_REF = 41,
+	AGC_MAX = 42,
+	AGC_MIN = 43,
+	AGC_LK_TH = 44,
+	TS_AGC_LK_TH = 45,
+	AGC_PWR_SET = 46,
+	QPSK_MISC = 47,
+	SNR_THS_LOW = 48,
+	SNR_THS_HIGH = 49,
+	TS_SW_RATE = 50,
+	TS_SW_LIM_L = 51,
+	TS_SW_LIM_H = 52,
+	CS_SW_RATE_1 = 53,
+	CS_SW_RATE_2 = 54,
+	CS_SW_RATE_3 = 55,
+	CS_SW_RATE_4 = 56,
+	CS_SW_LIM = 57,
+	TS_LPK = 58,
+	TS_LPK_M = 59,
+	TS_LPK_L = 60,
+	CS_KPROP_H = 61,
+	CS_KPROP_L = 62,
+	CS_KINT_H = 63,
+	CS_KINT_L = 64,
+	QPSK_SCALE = 65,
+	TLD_OUTCLK_TH = 66,
+	TLD_INCLK_TH = 67,
+	FLD_TH = 68,
+	PLD_OUTLK3 = 69,
+	PLD_OUTLK2 = 70,
+	PLD_OUTLK1 = 71,
+	PLD_OUTLK0 = 72,
+	PLD_INLK3 = 73,
+	PLD_INLK2 = 74,
+	PLD_INLK1 = 75,
+	PLD_INLK0 = 76,
+	PLD_ACC_TIME = 77,
+	SWEEP_PAR = 78,
+	STARTUP_TIME = 79,
+	LOSSLOCK_TH = 80,
+	FEC_LOCK_TM = 81,
+	LOSSLOCK_TM = 82,
+	VIT_ERRPER_H = 83,
+	VIT_ERRPER_M = 84,
+	VIT_ERRPER_L = 85,
+	VIT_SETUP = 86,
+	VIT_REF0 = 87,
+	VIT_REF1 = 88,
+	VIT_REF2 = 89,
+	VIT_REF3 = 90,
+	VIT_REF4 = 91,
+	VIT_REF5 = 92,
+	VIT_REF6 = 93,
+	VIT_MAXERR = 94,
+	BA_SETUPT = 95,
+	OP_CTRL = 96,
+	FEC_SETUP = 97,
+	PROG_SYNC = 98,
+	AFC_SEAR_TH = 99,
+	CSACC_DIF_TH = 100,
+	QPSK_LK_CT = 101,
+	QPSK_ST_CT = 102,
+	MON_CTRL = 103,
+	QPSK_RESET = 104,
+	QPSK_TST_CT = 105,
+	QPSK_TST_ST = 106,
+	TEST_R = 107,
+	AGC_H = 108,
+	AGC_M = 109,
+	AGC_L = 110,
+	FREQ_ERR1_H = 111,
+	FREQ_ERR1_M = 112,
+	FREQ_ERR1_L = 113,
+	FREQ_ERR2_H = 114,
+	FREQ_ERR2_L = 115,
+	SYM_RAT_OP_H = 116,
+	SYM_RAT_OP_L = 117,
+	DESEQC2_INT = 118,
+	DISEQC2_STAT = 119,
+	DISEQC2_FIFO = 120,
+	DISEQC2_CTRL1 = 121,
+	DISEQC2_CTRL2 = 122,
+	MONITOR_H = 123,
+	MONITOR_L = 124,
+	TEST_MODE = 125,
+	ID = 126,
+	CONFIG = 127
+};
+
+
+#if DEBUG_PRINT_PARAMS
+	static const char *inv_str[] = {"OFF", "ON", "AUTO"};
+	static const char *fec_str[] = {"NONE", "1/2", "2/3", "3/4", "4/5",
+					"5/6", "6/7", "7/8", "8/9", "AUTO"};
+	static const char *mini_cmd_name[] = {"SEC_MINI_A", "SEC_MINI_B"};
+	static const char *tone_name[] = {"SEC_TONE_ON", "SEC_TONE_OFF"};
+	static const char *volt_name[] = {"SEC_VOLTAGE_13", "SEC_VOLTAGE_18",
+						"SEC_VOLTAGE_OFF"};
+#endif
+
+#if DEBUG_DUMP_REGISTERS || DEBUG_IO_TRACE
+static const char *zl10313_reg_names[] = {
+	"QPSK_INT_H", "QPSK_INT_M", "QPSK_INT_L", "FEC_INT", "QPSK_STAT_H",
+	"QPSK_STAT_L", "FEC_STATUS", "LNB_FREQ_H", "LNB_FREQ_L", "M_SNR_H",
+	"M_SNR_L", "VIT_ERRCNT_H", "VIT_ERRCNT_M", "VIT_ERRCNT_L",
+	"RS_BERCNT_H", "RS_BERCNT_M", "RS_BERCNT_L", "RS_UBC_H", "RS_UBC_L",
+	"SIG_LEVEL", "GPP_CTRL", "RESET", "DISEQC_MODE", "SYM_RATE_H",
+	"SYM_RATE_L", "VIT_MODE", "QPSK_CTRL", "GO", "IE_QPSK_H", "IE_QPSK_M",
+	"IE_QPSK_L", "IE_FEC", "QPSK_STAT_EN", "FEC_STAT_EN", "SYS_CLK",
+	"DISEQC_RATIO", "DISEQC_INSTR", "FR_LIM", "FR_OFF", "AGC_CTRL",
+	"AGC_INIT", "AGC_REF", "AGC_MAX", "AGC_MIN", "AGC_LK_TH",
+	"TS_AGC_LK_TH", "AGC_PWR_SET", "QPSK_MISC", "SNR_THS_LOW",
+	"SNR_THS_HIGH", "TS_SW_RATE", "TS_SW_LIM_L", "TS_SW_LIM_H",
+	"CS_SW_RATE_1", "CS_SW_RATE_2", "CS_SW_RATE_3", "CS_SW_RATE_4",
+	"CS_SW_LIM", "TS_LPK", "TS_LPK_M", "TS_LPK_L", "CS_KPROP_H",
+	"CS_KPROP_L", "CS_KINT_H", "CS_KINT_L", "QPSK_SCALE", "TLD_OUTCLK_TH",
+	"TLD_INCLK_TH", "FLD_TH", "PLD_OUTLK3", "PLD_OUTLK2", "PLD_OUTLK1",
+	"PLD_OUTLK0", "PLD_INLK3", "PLD_INLK2", "PLD_INLK1", "PLD_INLK0",
+	"PLD_ACC_TIME", "SWEEP_PAR", "STARTUP_TIME", "LOSSLOCK_TH",
+	"FEC_LOCK_TM", "LOSSLOCK_TM", "VIT_ERRPER_H", "VIT_ERRPER_M",
+	"VIT_ERRPER_L", "VIT_SETUP", "VIT_REF0", "VIT_REF1", "VIT_REF2",
+	"VIT_REF3", "VIT_REF4", "VIT_REF5", "VIT_REF6", "VIT_MAXERR",
+	"BA_SETUPT", "OP_CTRL", "FEC_SETUP", "PROG_SYNC", "AFC_SEAR_TH",
+	"CSACC_DIF_TH", "QPSK_LK_CT", "QPSK_ST_CT", "MON_CTRL", "QPSK_RESET",
+	"QPSK_TST_CT", "QPSK_TST_ST", "TEST_R", "AGC_H", "AGC_M", "AGC_L",
+	"FREQ_ERR1_H", "FREQ_ERR1_M", "FREQ_ERR1_L", "FREQ_ERR2_H",
+	"FREQ_ERR2_L", "SYM_RAT_OP_H", "SYM_RAT_OP_L", "DESEQC2_INT",
+	"DISEQC2_STAT", "DISEQC2_FIFO", "DISEQC2_CTRL1", "DISEQC2_CTRL2",
+	"MONITOR_H", "MONITOR_L", "TEST_MODE", "ID", "CONFIG", ""
+};
+#endif
+
+#if DEBUG_DUMP_REGISTERS
+static int zl10313_read(const struct zl10313_state *state,
+			const enum zl10313_reg_addr reg, u8 *buf,
+			const size_t count);
+
+static void zl10313_dump_registers(const struct zl10313_state *state)
+{
+	int ret;
+	u8 buf[128];
+	u8 reg ;
+
+	trace_printk("%s\n", __FUNCTION__);
+	ret = zl10313_read(state, QPSK_INT_H, buf, sizeof(buf));
+	if (ret < 0) return;
+	for (reg = QPSK_INT_H; reg <= CONFIG; reg += 4) {
+		printk(KERN_DEBUG "%03x: [%02x %13s] [%02x %13s] [%02x %13s] "
+			"[%02x %13s]\n", reg, buf[reg], zl10313_reg_names[reg],
+			buf[reg+1], zl10313_reg_names[reg+1], buf[reg+2],
+			zl10313_reg_names[reg+2], buf[reg+3],
+			zl10313_reg_names[reg+3]);
+	}
+}
+#else
+static inline void zl10313_dump_registers(const struct zl10313_state *state) {}
+#endif /* DEBUG_DUMP_REGISTERS */
+
+#endif /* DVB_FRONTENDS_ZL10313_PRIV */
diff -r f637ac5a5898 linux/drivers/media/video/saa7134/Kconfig
--- a/linux/drivers/media/video/saa7134/Kconfig	Fri Dec 28 00:32:41 2007 -0200
+++ b/linux/drivers/media/video/saa7134/Kconfig	Fri Dec 28 17:41:56 2007 +0200
@@ -37,6 +37,8 @@ config VIDEO_SAA7134_DVB
 	select DVB_TDA826X if !DVB_FE_CUSTOMISE
 	select DVB_TDA827X if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
+	select DVB_ZL10313 if !DVB_FE_CUSTOMISE
+	select DVB_ZL10039 if !DVB_FE_CUSTOMISE
 	---help---
 	  This adds support for DVB cards based on the
 	  Philips saa7134 chip.
diff -r f637ac5a5898 linux/drivers/media/video/saa7134/saa7134-cards.c
--- a/linux/drivers/media/video/saa7134/saa7134-cards.c	Fri Dec 28 00:32:41 2007 -0200
+++ b/linux/drivers/media/video/saa7134/saa7134-cards.c	Fri Dec 28 15:34:49 2007 +0200
@@ -3601,6 +3601,26 @@ struct saa7134_board saa7134_boards[] = 
 			.tv     = 1,
 		}},
 	},
+	[SAA7134_BOARD_VIDEOMATE_S350] = {
+		/* Jan Daniël Louw <jd.louw at mweb.co.za */
+		.name		= "Compro VideoMate S350/S300",
+		.audio_clock	= 0x00187de7,
+		.tuner_type	= TUNER_ABSENT,
+		.radio_type	= UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg		= SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name	= name_comp1,
+			.vmux	= 0,
+			.amux	= LINE1,
+		},{
+			.name	= name_svideo,
+			.vmux	= 8, /* Not tested */
+			.amux	= LINE1
+		}},
+	},
+
 };
 
 const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -4371,6 +4391,12 @@ struct pci_device_id saa7134_pci_tbl[] =
 		.subvendor    = 0x4e42,
 		.subdevice    = 0x3502,
 		.driver_data  = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+		.subvendor    = 0x185b,
+		.subdevice    = 0xc900,
+		.driver_data  = SAA7134_BOARD_VIDEOMATE_S350,
 	},{
 		/* --- boards without eeprom + subsystem ID --- */
 		.vendor       = PCI_VENDOR_ID_PHILIPS,
@@ -4624,6 +4650,11 @@ int saa7134_board_init1(struct saa7134_d
 		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x8c040007, 0x8c040007);
 		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd);
 		break;
+	case SAA7134_BOARD_VIDEOMATE_S350:
+		saa_andorl(SAA7134_GPIO_GPMODE0 >> 2,   0x00008000, 0x00008000);
+		saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
+		dev->has_remote = SAA7134_REMOTE_GPIO;
+		break;
 	}
 	return 0;
 }
diff -r f637ac5a5898 linux/drivers/media/video/saa7134/saa7134-dvb.c
--- a/linux/drivers/media/video/saa7134/saa7134-dvb.c	Fri Dec 28 00:32:41 2007 -0200
+++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c	Sat Dec 29 15:58:59 2007 +0200
@@ -38,6 +38,8 @@
 #include "mt352_priv.h" /* FIXME */
 #include "tda1004x.h"
 #include "nxt200x.h"
+#include "zl10313.h"
+#include "zl10039.h"
 
 #include "tda10086.h"
 #include "tda826x.h"
@@ -841,6 +843,17 @@ static struct nxt200x_config kworldatsc1
 };
 
 /* ==================================================================
+ * ZL10313 based DVB-S cards
+ */
+static struct zl10313_config zl10313_compro_s350_config = {
+	.demod_address = 0x0E,
+};
+
+static struct zl10039_config zl10039_compro_s350_config = {
+	.tuner_address = 0x60
+};
+
+/* ==================================================================
  * Core code
  */
 
@@ -1044,6 +1057,15 @@ static int dvb_init(struct saa7134_dev *
 	case SAA7134_BOARD_AVERMEDIA_SUPER_007:
 		configure_tda827x_fe(dev, &avermedia_super_007_config);
 		break;
+	case SAA7134_BOARD_VIDEOMATE_S350:
+		dev->dvb.frontend = dvb_attach(zl10313_attach,
+				&zl10313_compro_s350_config, &dev->i2c_adap);
+		if (dev->dvb.frontend) {
+			dev->dvb.frontend = dvb_attach(zl10039_attach,
+			dev->dvb.frontend, &zl10039_compro_s350_config,
+			&dev->i2c_adap);
+		}
+		break;
 	default:
 		wprintk("Huh? unknown DVB card?\n");
 		break;
diff -r f637ac5a5898 linux/drivers/media/video/saa7134/saa7134-input.c
--- a/linux/drivers/media/video/saa7134/saa7134-input.c	Fri Dec 28 00:32:41 2007 -0200
+++ b/linux/drivers/media/video/saa7134/saa7134-input.c	Fri Dec 28 15:44:52 2007 +0200
@@ -352,6 +352,11 @@ int saa7134_input_init1(struct saa7134_d
 		mask_keyup   = 0x8000000;
 		polling      = 50; //ms
 		break;
+	case SAA7134_BOARD_VIDEOMATE_S350:
+		ir_codes     = ir_codes_videomate_s350;
+		mask_keycode = 0x003F00;
+		mask_keydown = 0x040000;
+		break;
 	}
 	if (NULL == ir_codes) {
 		printk("%s: Oops: IR config error [card=%d]\n",
diff -r f637ac5a5898 linux/drivers/media/video/saa7134/saa7134.h
--- a/linux/drivers/media/video/saa7134/saa7134.h	Fri Dec 28 00:32:41 2007 -0200
+++ b/linux/drivers/media/video/saa7134/saa7134.h	Fri Dec 28 15:48:46 2007 +0200
@@ -247,6 +247,7 @@ struct saa7134_format {
 #define SAA7134_BOARD_SABRENT_TV_PCB05     115
 #define SAA7134_BOARD_10MOONSTVMASTER3     116
 #define SAA7134_BOARD_AVERMEDIA_SUPER_007  117
+#define SAA7134_BOARD_VIDEOMATE_S350 118
 
 #define SAA7134_MAXBOARDS 8
 #define SAA7134_INPUT_MAX 8
diff -r f637ac5a5898 linux/include/media/ir-common.h
--- a/linux/include/media/ir-common.h	Fri Dec 28 00:32:41 2007 -0200
+++ b/linux/include/media/ir-common.h	Fri Dec 28 15:55:36 2007 +0200
@@ -140,6 +140,7 @@ extern IR_KEYTAB_TYPE ir_codes_encore_en
 extern IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE];
 extern IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE];
 extern IR_KEYTAB_TYPE ir_codes_fusionhdtv_mce[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_videomate_s350[IR_KEYTAB_SIZE];
 
 #endif
 



More information about the linux-dvb mailing list