From 7e1e1091c96b13430fc1d36b3e47edffedb16656 Mon Sep 17 00:00:00 2001
From: Steve Kerrison <steve@stevekerrison.com>
Date: Sun, 27 Feb 2011 18:10:42 +0000
Subject: [PATCH] CXD2820 I2C passthrough, PCTV 290e board identification and modifications to em28xx to work with new board and em28174

---
 drivers/media/dvb/dvb-usb/dvb-usb-ids.h   |    1 +
 drivers/media/video/em28xx/Makefile       |    1 +
 drivers/media/video/em28xx/em28xx-cards.c |   32 +++
 drivers/media/video/em28xx/em28xx-core.c  |    8 +-
 drivers/media/video/em28xx/em28xx-dvb.c   |   24 ++
 drivers/media/video/em28xx/em28xx-i2c.c   |    3 +-
 drivers/media/video/em28xx/em28xx-reg.h   |    4 +
 drivers/media/video/em28xx/em28xx.h       |    1 +
 drivers/staging/Kconfig                   |    2 +
 drivers/staging/Makefile                  |    1 +
 drivers/staging/cxd2820/Kconfig           |    8 +
 drivers/staging/cxd2820/Makefile          |    5 +
 drivers/staging/cxd2820/TODO              |    2 +
 drivers/staging/cxd2820/cxd2820.c         |  331 +++++++++++++++++++++++++++++
 drivers/staging/cxd2820/cxd2820.h         |   59 +++++
 15 files changed, 479 insertions(+), 3 deletions(-)
 create mode 100644 drivers/staging/cxd2820/Kconfig
 create mode 100644 drivers/staging/cxd2820/Makefile
 create mode 100644 drivers/staging/cxd2820/TODO
 create mode 100644 drivers/staging/cxd2820/cxd2820.c
 create mode 100644 drivers/staging/cxd2820/cxd2820.h

diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index b71540d..cb35da8 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -237,6 +237,7 @@
 #define USB_PID_PCTV_200E				0x020e
 #define USB_PID_PCTV_400E				0x020f
 #define USB_PID_PCTV_450E				0x0222
+#define USB_PID_PCTV_290E				0x024f
 #define USB_PID_NEBULA_DIGITV				0x0201
 #define USB_PID_DVICO_BLUEBIRD_LGDT			0xd820
 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD		0xd500
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index d0f093d..1e76183 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -11,4 +11,5 @@ EXTRA_CFLAGS += -Idrivers/media/video
 EXTRA_CFLAGS += -Idrivers/media/common/tuners
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+EXTRA_CFLAGS += -Idrivers/staging/cxd2820
 
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 87f77a3..2d7ca57 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -282,6 +282,17 @@ static struct em28xx_reg_seq leadership_reset[] = {
 	{	-1,		-1,	-1,	-1},
 };
 
+/* 2013:024f PCTV Systems nanoStick T2 290e
+ * GPIO_6 - demod?
+ * GPIO_7 - LED
+ */
+static struct em28xx_reg_seq pctv_290e_gpio[] = {
+       {EM2874_R80_GPIO,       0x00,   0xff,           80},
+       {EM2874_R80_GPIO,       0x40,   0xff,           80}, /* GPIO_6 = 1 */
+       {EM2874_R80_GPIO,       0xc0,   0xff,           80}, /* GPIO_7 = 1 */
+       {-1,                    -1,     -1,             -1},
+};
+
 
 /*
  *  Board definitions
@@ -1749,6 +1760,18 @@ struct em28xx_board em28xx_boards[] = {
 		.dvb_gpio   = kworld_a340_digital,
 		.tuner_gpio = default_tuner_gpio,
 	},
+	/* 2013:024f - Empia EM28174, NXP TDA18271HD/C2 and Sony CXD2820R
+	 * sold as PCTV 290E, has DVB-T2 support */
+	[EM28174_BOARD_PCTV_290E] = {
+		.name	   	= "PCTV Nanostick T2 290e (DVB-T2)",
+		.tuner_type 	= TUNER_ABSENT, /* Digital-only TDA18271HD */
+		.has_dvb   	= 1, /* DVB-T2, in fact */
+		.dvb_gpio  	= default_digital,
+		.tuner_gpio 	= pctv_290e_gpio,
+		.i2c_speed   	= EM2874_I2C_SECONDARY_BUS_SELECT |
+	                               	EM28XX_I2C_CLK_WAIT_ENABLE |
+                                	EM28XX_I2C_FREQ_100_KHZ,
+	},
 };
 const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
 
@@ -1876,6 +1899,8 @@ struct usb_device_id em28xx_id_table[] = {
 			.driver_info = EM2860_BOARD_GADMEI_UTV330 },
 	{ USB_DEVICE(0x1b80, 0xa340),
 			.driver_info = EM2870_BOARD_KWORLD_A340 },
+	{ USB_DEVICE(0x2013, 0x024f),
+			.driver_info = EM28174_BOARD_PCTV_290E },
 	{ },
 };
 MODULE_DEVICE_TABLE(usb, em28xx_id_table);
@@ -2200,6 +2225,12 @@ void em28xx_pre_card_setup(struct em28xx *dev)
 		em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
 		msleep(70);
 		break;
+	case EM28174_BOARD_PCTV_290E:
+		em28xx_write_reg(dev, EM2874_R80_GPIO, 0x00);
+		msleep(70);
+		em28xx_write_reg(dev, 0x0d, 0xff);
+		msleep(70);
+		break;
 	}
 
 	em28xx_gpio_set(dev, dev->board.tuner_gpio);
@@ -2795,6 +2826,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
 			dev->wait_after_write = 0;
 			break;
 		case CHIP_ID_EM2874:
+		case CHIP_ID_EM28174: /* Seems to be the same so far */
 			em28xx_info("chip ID is em2874\n");
 			dev->reg_gpio_num = EM2874_R80_GPIO;
 			dev->wait_after_write = 0;
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 44c63cb..a68a82e 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -489,7 +489,7 @@ int em28xx_audio_setup(struct em28xx *dev)
 	int vid1, vid2, feat, cfg;
 	u32 vid;
 
-	if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874) {
+	if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) {
 		/* Digital only device - don't load any alsa module */
 		dev->audio_mode.has_audio = 0;
 		dev->has_audio_class = 0;
@@ -614,7 +614,7 @@ int em28xx_capture_start(struct em28xx *dev, int start)
 {
 	int rc;
 
-	if (dev->chip_id == CHIP_ID_EM2874) {
+	if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) {
 		/* The Transport Stream Enable Register moved in em2874 */
 		if (!start) {
 			rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
@@ -1111,6 +1111,10 @@ int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev)
 		/* FIXME - for now assume 564 like it was before, but the
 		   em2874 code should be added to return the proper value... */
 		packet_size = 564;
+	} else if (dev->chip_id == CHIP_ID_EM28174) {
+		/* FIXME - Not sure where this is stored in em28174, but it needs to be bigger
+		   than for em2874 to handle DVB-T2, I think */
+		packet_size = 752;
 	} else {
 		/* TS max packet size stored in bits 1-0 of R01 */
 		chip_cfg2 = em28xx_read_reg(dev, EM28XX_R01_CHIPCFG2);
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index c7c04bf..90172ac 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -38,6 +38,7 @@
 #include "tda1002x.h"
 #include "tda18271.h"
 #include "s921.h"
+#include "cxd2820.h"
 
 MODULE_DESCRIPTION("driver for em28xx based DVB cards");
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -292,6 +293,17 @@ static struct drx397xD_config em28xx_drx397xD_with_xc3028 = {
 };
 #endif
 
+/* Config for PCTV 290E w/ cxd2820 & TDA18271 */
+static struct cxd2820_config em28xx_cxd2820 = {
+	.demod_address = (0xd8 >> 1),
+};
+
+static struct tda18271_config em28xx_cxd2820_tda18271_config = {
+//     .std_map           = &em28xx_cxd2820r_tda18271_std_map,
+       .gate = TDA18271_GATE_DIGITAL,
+       .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
 static int mt352_terratec_xs_init(struct dvb_frontend *fe)
 {
 	/* Values extracted from a USB trace of the Terratec Windows driver */
@@ -621,6 +633,18 @@ static int dvb_init(struct em28xx *dev)
 			dvb_attach(tda18271_attach, dvb->frontend, 0x60,
 				   &dev->i2c_adap, &kworld_a340_config);
 		break;
+	case EM28174_BOARD_PCTV_290E:
+		dvb->frontend = dvb_attach(cxd2820_attach,
+					   &em28xx_cxd2820,
+					   &dev->i2c_adap);
+		em28xx_errdev("/2: CXD2820R stub attached\n");
+		em28xx_errdev("/2: Now attaching TDA18271...\n");
+		if (dvb->frontend != NULL)
+			dvb_attach(tda18271_attach, dvb->frontend, 0xc0>>1,
+				cxd2820_i2c_chain(dvb->frontend), &em28xx_cxd2820_tda18271_config);
+		em28xx_errdev("/2: This card has a TDA18271 tuner and a Sony CXD2820R,"
+				" but it's not supported yet. Please help!\n");
+		break;
 	default:
 		em28xx_errdev("/2: The frontend of your DVB/ATSC card"
 				" isn't supported yet\n");
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 71474d3..cd2a955 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -332,7 +332,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
 	struct em28xx_eeprom *em_eeprom = (void *)eedata;
 	int i, err, size = len, block;
 
-	if (dev->chip_id == CHIP_ID_EM2874) {
+	if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174) {
 		/* Empia switched to a 16-bit addressable eeprom in newer
 		   devices.  While we could certainly write a routine to read
 		   the eeprom, there is nothing of use in there that cannot be
@@ -487,6 +487,7 @@ static char *i2c_devs[128] = {
 	[0xc2 >> 1] = "tuner (analog)",
 	[0xc4 >> 1] = "tuner (analog)",
 	[0xc6 >> 1] = "tuner (analog)",
+	[0xd8 >> 1] = "cxd2820",
 };
 
 /*
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index 91e9055..6c5cf53 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -190,6 +190,9 @@
 #define EM28XX_AUDIO_SRC_TUNER	0xc0
 #define EM28XX_AUDIO_SRC_LINE	0x80
 
+/* em28174 registers */
+#define EM28174_R05_UNKNOWN		0x05 /* Possibly a status register, seems to contain 0x10 if the next i2c_master_read will be empty */
+
 /* FIXME: Need to be populated with the other chip ID's */
 enum em28xx_chip_id {
 	CHIP_ID_EM2800 = 7,
@@ -201,6 +204,7 @@ enum em28xx_chip_id {
 	CHIP_ID_EM2870 = 35,
 	CHIP_ID_EM2883 = 36,
 	CHIP_ID_EM2874 = 65,
+	CHIP_ID_EM28174 = 113,	/* Looks like an em2874 variant */
 };
 
 /*
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 6f2795a..1f713b7 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -118,6 +118,7 @@
 #define EM2882_BOARD_DIKOM_DK300		  75
 #define EM2870_BOARD_KWORLD_A340		  76
 #define EM2874_LEADERSHIP_ISDBT			  77
+#define EM28174_BOARD_PCTV_290E			  78
 
 
 /* Limits minimum and default number of buffers */
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 74a8b27..879a8bb 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -177,5 +177,7 @@ source "drivers/staging/ste_rmi4/Kconfig"
 
 source "drivers/staging/altera-stapl/Kconfig"
 
+source "drivers/staging/cxd2820/Kconfig"
+
 endif # !STAGING_EXCLUDE_BUILD
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 9f50ec9..5688fcd 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -69,3 +69,4 @@ obj-$(CONFIG_SPEAKUP)	+= speakup/
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217)	+= cptm1217/
 obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4)	+= ste_rmi4/
+obj-$(CONFIG_DVB_CXD2820)	+= cxd2820/
diff --git a/drivers/staging/cxd2820/Kconfig b/drivers/staging/cxd2820/Kconfig
new file mode 100644
index 0000000..0228f12
--- /dev/null
+++ b/drivers/staging/cxd2820/Kconfig
@@ -0,0 +1,8 @@
+config DVB_CXD2820
+        tristate "CXD2820R DVB-{C,T,T2} demodulator DVB frontend"
+        depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+        ---help---
+          Support for the Sony CXD2820R DVB demodulator with DVB-T2 support
+
+	  At the moment it doesn't work!
diff --git a/drivers/staging/cxd2820/Makefile b/drivers/staging/cxd2820/Makefile
new file mode 100644
index 0000000..66ab9bc
--- /dev/null
+++ b/drivers/staging/cxd2820/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_DVB_CXD2820) += cxd2820.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/
+EXTRA_CFLAGS += -Idrivers/media/common/tuners/
diff --git a/drivers/staging/cxd2820/TODO b/drivers/staging/cxd2820/TODO
new file mode 100644
index 0000000..70286d8
--- /dev/null
+++ b/drivers/staging/cxd2820/TODO
@@ -0,0 +1,2 @@
+1. Not kill anything or anyone.
+2. Make it work!
diff --git a/drivers/staging/cxd2820/cxd2820.c b/drivers/staging/cxd2820/cxd2820.c
new file mode 100644
index 0000000..4ff3908
--- /dev/null
+++ b/drivers/staging/cxd2820/cxd2820.c
@@ -0,0 +1,331 @@
+/*
+ *    Support for Sony CXD2820 DVB-{T,T2,C} demodulator
+ *
+ *    Copyright (C) 2010 Steve Kerrison <linux@stevekerrison.com>
+ *
+ *    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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+#include "cxd2820.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+struct cxd2820_state {
+	struct dvb_frontend frontend;
+	struct cxd2820_config *config;
+	struct i2c_adapter *i2c_passthrough; /* The CXD2820 has an I2C passthrough function */
+	struct i2c_adapter *i2c_real; /* This is the real I2C adapter to which the CXD2820 is attached */
+};
+
+/*
+ * functionality()
+ */
+static u32 functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm cxd2820_algo = {
+	.master_xfer   = cxd2820_i2c_xfer,
+	.functionality = functionality,
+#ifdef NEED_ALGO_CONTROL
+	.algo_control = dummy_algo_control,
+#endif
+};
+
+static struct i2c_adapter cxd2820_adap_template = {
+	.owner = THIS_MODULE,
+	.name = "cxd2820",
+	.algo = &cxd2820_algo,
+};
+
+/*
+ * cxd2820_i2c_xfer()
+ * Wrapper for I2C passthrough from the real I2C master to the slave behind
+ * the CXD2820
+ */
+int cxd2820_i2c_xfer(struct i2c_adapter *i2c_adap,
+			   struct i2c_msg msgs[], int num)
+{
+	int rc, i, j, k, new_num = num;
+	struct i2c_msg *new_msgs = NULL;
+	struct dvb_frontend *fe = NULL;
+	struct cxd2820_state *state = NULL;
+	u8 *buf = NULL;
+	
+	/* Writes that pass through here just need the message tweaking.
+	 * Reads need a write to the demod first, in order for it
+	 * to pass on the request. */
+	for (i = 0; i < num; i++)
+	{
+		new_num += (msgs[i].flags & I2C_M_RD) > 0;
+	}
+	
+	new_msgs = kzalloc(sizeof(struct i2c_msg) * new_num, GFP_KERNEL);
+	fe = i2c_adap->algo_data;
+	state = fe->demodulator_priv;
+	if (new_msgs == NULL || fe == NULL || state == NULL) goto error;
+
+	for (i = 0, j = 0; i < num; i++)
+	{
+		if (debug > 0)
+		{
+			printk("Trying to access 0x%02x for %s, length %d\n",
+				msgs[i].addr<<1,msgs[i].flags & I2C_M_RD ? "READ" : "WRITE",msgs[i].len);
+		}
+		if (msgs[i].flags & I2C_M_RD)
+		{
+			/* For a read op we write to the demod asking it to read along its chain */
+			new_msgs[j].len = 2;
+			new_msgs[j].flags = 0;
+			new_msgs[j].addr = state->config->demod_address;
+			buf = kzalloc(sizeof(u8) * 2, GFP_KERNEL);
+			if (buf == NULL)
+			{
+				kfree(buf);
+				goto error;
+			}
+			buf[0] = CXD2820_I2C_PASSTHROUGH;
+			buf[1] = (msgs[i].addr << 1) | 0x01;
+			new_msgs[j].buf = buf;
+			j++;
+			/* We've requested a read along the chain, so now do the readthrough */
+			memcpy(&new_msgs[j],&msgs[i],sizeof(struct i2c_msg));
+			new_msgs[j].addr = state->config->demod_address;
+		}
+		else 
+		{
+			if (debug > 0)
+			{
+				printk("Data: {");
+				for (k = 0; k < msgs[i].len; k++)
+				{
+					printk("0x%02x,",msgs[i].buf[k]);
+				}
+				printk("}\n");
+			}
+			/* For a write op we have to encapsulate the write */
+			memcpy(&new_msgs[j],&msgs[i],sizeof(struct i2c_msg));
+			new_msgs[j].len += 2;
+			new_msgs[j].buf = kmalloc(sizeof(u8) * new_msgs[j].len,GFP_KERNEL);
+			if (new_msgs[j].buf == NULL)
+			{
+				kfree(new_msgs[j].buf);
+				goto error;
+			}
+			new_msgs[j].buf[0] = CXD2820_I2C_PASSTHROUGH;
+			new_msgs[j].buf[1] = msgs[i].addr << 1;
+			new_msgs[j].addr = state->config->demod_address;
+			memcpy(&new_msgs[j].buf[2],msgs[i].buf,msgs[i].len);
+		}
+		j++;
+	}
+	/* Now despatch the commands via the I2C master */
+	rc = i2c_transfer(state->i2c_real, new_msgs, new_num);
+	/* Now to clean up the mess I've made */
+	for (i = 0, j = 0; i < num; i++, j++)
+	{
+		if (msgs[i].flags & I2C_M_RD)
+		{
+			kfree(new_msgs[j].buf);
+			j++;
+		}
+	}
+	if (debug > 0) printk("CXD2820: I2C xfer result: %d\n",rc);
+	kfree(new_msgs);
+	/* We may have done more transactions than expected,
+	 * remove injected transactions from the transfer count */
+	return rc > 0 ? rc - (new_num - num) : rc;
+error:
+	/* FIXME - Through cleanup? */
+	if (debug > 0) printk("CXD2820: I2C xfer error\n");
+	kfree(new_msgs);
+	return 0;
+}
+
+static int cxd2820_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	*status = FE_HAS_SIGNAL
+		| FE_HAS_CARRIER
+		| FE_HAS_VITERBI
+		| FE_HAS_SYNC
+		| FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int cxd2820_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	*ber = 0;
+	return 0;
+}
+
+static int cxd2820_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	*strength = 0;
+	return 0;
+}
+
+static int cxd2820_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	*snr = 0;
+	return 0;
+}
+
+static int cxd2820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	*ucblocks = 0;
+	return 0;
+}
+
+static int cxd2820_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	return 0;
+}
+
+static int cxd2820_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	if (fe->ops.tuner_ops.set_params) {
+		fe->ops.tuner_ops.set_params(fe, p);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+	}
+
+	return 0;
+}
+
+static int cxd2820_sleep(struct dvb_frontend* fe)
+{
+	return 0;
+}
+
+static int cxd2820_init(struct dvb_frontend* fe)
+{
+	return 0;
+}
+
+static int cxd2820_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	return 0;
+}
+
+static int cxd2820_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	return 0;
+}
+
+static void cxd2820_release(struct dvb_frontend* fe)
+{
+	struct cxd2820_state* state = fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops cxd2820_ops;
+
+struct i2c_adapter *cxd2820_i2c_chain(struct dvb_frontend* fe)
+{
+	struct cxd2820_state *state = fe->demodulator_priv;
+	if (state != NULL && state->i2c_passthrough != NULL)
+	{
+		return state->i2c_passthrough;
+	}
+	return 0;
+}
+
+struct dvb_frontend* cxd2820_attach(const struct cxd2820_config *config,
+					   struct i2c_adapter *i2c)
+{
+	struct cxd2820_state* state = NULL;
+	struct i2c_adapter *adap = NULL;
+
+	/* allocate memory for the internal state */
+	state = kzalloc(sizeof(struct cxd2820_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &cxd2820_ops, sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	state->config = (struct cxd2820_config *)config;
+	state->i2c_real = i2c;
+	
+	/* Allocate i2c passthrough adapter within cxd2820. */	
+	adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+	if (adap == NULL) 
+	{
+		kfree(adap);
+		goto error;
+	}
+	memcpy(adap, &cxd2820_adap_template, sizeof(struct i2c_adapter));
+	adap->algo_data = (void *)&state->frontend;
+	state->i2c_passthrough = adap;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+
+static struct dvb_frontend_ops cxd2820_ops = {
+
+	.info = {
+		.name			= "CXD2820 DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 0,
+		.frequency_max		= 863250000,
+		.frequency_stepsize	= 62500,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+				FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+				FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+				FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO |
+				FE_CAN_TRANSMISSION_MODE_AUTO |
+				FE_CAN_GUARD_INTERVAL_AUTO |
+				FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release = cxd2820_release,
+
+	.init = cxd2820_init,
+	.sleep = cxd2820_sleep,
+
+	.set_frontend = cxd2820_set_frontend,
+	.get_frontend = cxd2820_get_frontend,
+
+	.read_status = cxd2820_read_status,
+	.read_ber = cxd2820_read_ber,
+	.read_signal_strength = cxd2820_read_signal_strength,
+	.read_snr = cxd2820_read_snr,
+	.read_ucblocks = cxd2820_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("Sony CXD2820 DVB-{C,T,T2} demodulator");
+MODULE_AUTHOR("Steve Kerrison");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cxd2820_attach);
+EXPORT_SYMBOL(cxd2820_i2c_chain);
diff --git a/drivers/staging/cxd2820/cxd2820.h b/drivers/staging/cxd2820/cxd2820.h
new file mode 100644
index 0000000..129b094
--- /dev/null
+++ b/drivers/staging/cxd2820/cxd2820.h
@@ -0,0 +1,59 @@
+/*
+ *    Support for Sony CXD2820 DVB-{T,T2,C} demodulator
+ *
+ *    Copyright (C) 2010 Steve Kerrison <linux@stevekerrison.com>
+ *
+ *    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 _CXD2820_H
+#define _CXD2820_H
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+#define CXD2820_I2C_PASSTHROUGH		0x09
+
+extern int cxd2820_i2c_xfer(struct i2c_adapter *i2c_adap,
+			   struct i2c_msg msgs[], int num);
+
+/*struct i2c_adapter cxd2820_adap_template;*/
+
+struct cxd2820_config {
+	u8 demod_address;
+};
+
+#if defined(CONFIG_DVB_CXD2820) || (defined(CONFIG_DVB_CXD2820_MODULE) && defined(MODULE))
+extern struct dvb_frontend* cxd2820_attach(const struct cxd2820_config *config,
+					   struct i2c_adapter *i2c);
+extern struct i2c_adapter *cxd2820_i2c_chain(struct dvb_frontend *fe);
+#else
+static inline struct dvb_frontend* cxd2820_attach(const struct cxd2820_config *config,
+					   struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+
+static inline struct i2c_adapter *cxd2820_i2c_chain(struct dvb_frontend *fe)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_CXD2820 */
+
+
+#endif /* CXD2820_H */
-- 
1.7.1


