From 356337137de663c8c2fba27f9d0ad88a5deb66c9 Mon Sep 17 00:00:00 2001
From: Cecil Hugh Watson <knoppmyth@gmail.com>
Date: Thu, 29 Jan 2009 00:10:56 -0800
Subject: For Dvico...

---
 abs/extra-testing/v4l-dvb-dvico/xc-bluebird.patch | 896 ++++++++++++++++++++++
 1 file changed, 896 insertions(+)
 create mode 100644 abs/extra-testing/v4l-dvb-dvico/xc-bluebird.patch

diff --git a/abs/extra-testing/v4l-dvb-dvico/xc-bluebird.patch b/abs/extra-testing/v4l-dvb-dvico/xc-bluebird.patch
new file mode 100644
index 0000000..c54594d
--- /dev/null
+++ b/abs/extra-testing/v4l-dvb-dvico/xc-bluebird.patch
@@ -0,0 +1,896 @@
+[PATCH] Add support for FusionHDTV DVB-T NANO 2 / Dual Digital 4
+
+Firmware required:
+
+Mirror 1: http://konstantin.filtschew.de/v4l-firmware/
+
+Mirror 2: http://www.tuxamito.com.es/em2880/
+
+This patch is for users, and NOT meant to be merged into the kernel.
+
+For AU support, apply Rogers patch afterwards:
+http://linuxtv.org/~mkrufky/pending/xc/dd4.au.patch
+
+From: Michael Krufky <mkrufky@linuxtv.org>
+---
+ linux/drivers/media/dvb/dvb-usb/cxusb.c       |  157 +++++++
+ linux/drivers/media/dvb/dvb-usb/cxusb.h       |    2 
+ linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h |    2 
+ linux/drivers/media/dvb/frontends/Kconfig     |    7 
+ linux/drivers/media/dvb/frontends/Makefile    |    1 
+ linux/drivers/media/dvb/frontends/xc3028-fe.c |  532 ++++++++++++++++++++++++++
+ linux/drivers/media/dvb/frontends/xc3028.h    |   56 ++
+ v4l/versions.txt                              |    1 
+ 8 files changed, 757 insertions(+), 1 deletion(-)
+
+--- v4l-dvb.orig/linux/drivers/media/dvb/dvb-usb/cxusb.c
++++ v4l-dvb/linux/drivers/media/dvb/dvb-usb/cxusb.c
+@@ -30,6 +30,7 @@
+ #include "mt352.h"
+ #include "mt352_priv.h"
+ #include "zl10353.h"
++#include "xc3028.h"
+ 
+ /* debug */
+ int dvb_usb_cxusb_debug;
+@@ -72,6 +73,28 @@
+ 	st->gpio_write_state[GPIO_TUNER] = onoff;
+ }
+ 
++static void cxusb_bluebird_gpio(struct dvb_usb_device *d, u8 pin)
++{
++	u8 o[2],i;
++
++	o[0] = 0xff & ~pin;
++	o[1] = 0x00;
++
++	cxusb_ctrl_msg(d, CMD_BLUEBIRD_GPIO_WRITE, o, 2, &i, 1);
++	msleep(140);
++
++	if ((i & pin) != 0x00)
++		deb_info("gpio_write failed.\n");
++
++	o[1] = pin;
++
++	cxusb_ctrl_msg(d, CMD_BLUEBIRD_GPIO_WRITE, o, 2, &i, 1);
++	msleep(140);
++
++	if ((i & pin) != pin)
++		deb_info("gpio_write failed.\n");
++}
++
+ /* I2C */
+ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ 			  int num)
+@@ -351,6 +374,64 @@
+ 	.demod_init    = cxusb_mt352_demod_init,
+ };
+ 
++static struct zl10353_config cxusb_zl10353_dualdig4_config = {
++	.demod_address = 0x0f,
++	.no_tuner = 1,
++	.parallel_ts = 1,
++};
++
++struct bcode {
++	int reg;
++	char *txt;
++	int len;
++	int delay;
++};
++
++static int cxusb_xc3028_zl353_gpio_reset(struct dvb_frontend* fe, int ptr)
++{
++	struct dvb_usb_adapter *adap = fe->dvb->priv;
++	struct dvb_usb_device *d = adap->dev;
++	int j;
++	struct bcode zlconf[] = {
++		/* borrowed from em2880-dvb
++		 * this should be fixed in zl10353.c instead */
++		{0x1e,"\x60\x00",2,0},
++		{0x1e,"\x61\x4d",2,0},
++
++		{0x1e,"\x50\x0b",2,0},
++		{0x1e,"\x51\x44",2,0},
++		{0x1e,"\x52\x46",2,0},
++		{0x1e,"\x53\x15",2,0},
++		{0x1e,"\x54\x0f",2,0},
++		{0x1e,"\x5e\x00",2,0},
++		{0x1e,"\x5f\x12",2,0},
++
++
++		{0x1e,"\x55\x80",2,0}, /* reset */
++		{0x1e,"\xea\x01",2,0},
++		{0x1e,"\xea\x00",2,0},
++		{0x1e,"\x5a\xcd",2,0},
++
++
++		{0x1e,"\x6c\xe6",2,0}, // set input frequency
++		{0x1e,"\x6d\x09",2,0},
++		{}
++	};
++
++	if (ptr == 0 || ptr == 1)
++		/* pulse the GPIO tuner reset pin  */
++		cxusb_bluebird_gpio(d,0x01);
++	else if (ptr == 2)
++		for(j = 0; zlconf[j].txt; j++)
++			d->adapter[0].fe->ops.write(d->adapter[0].fe, zlconf[j].txt,zlconf[j].len);
++
++	return 0;
++}
++
++static struct xc3028_config cxusb_xc3028_config = {
++	.gpio_reset = cxusb_xc3028_zl353_gpio_reset,
++};
++
+ /* Callbacks for DVB USB */
+ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
+ {
+@@ -386,6 +467,12 @@
+ 	return 0;
+ }
+ 
++static int cxusb_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
++{
++	dvb_attach(xc3028_attach,adap->fe, &adap->dev->i2c_adap, &cxusb_xc3028_config);
++	return 0;
++}
++
+ static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
+ {
+ 	u8 b;
+@@ -401,6 +488,24 @@
+ 	return -EIO;
+ }
+ 
++static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap)
++{
++	if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
++		err("set interface failed");
++
++	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
++
++	/* pulse the GPIO demod reset pin  */
++	cxusb_bluebird_gpio(adap->dev,0x02);
++
++	if ((adap->fe = dvb_attach(zl10353_attach,
++				   &cxusb_zl10353_dualdig4_config,
++				   &adap->dev->i2c_adap)) != NULL)
++		return 0;
++
++	return -EIO;
++}
++
+ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
+ {
+ 	if (usb_set_interface(adap->dev->udev, 0, 7) < 0)
+@@ -479,6 +584,7 @@
+ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties;
+ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties;
+ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
++static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties;
+ 
+ static int cxusb_probe(struct usb_interface *intf,
+ 		       const struct usb_device_id *id)
+@@ -487,7 +593,8 @@
+ 		dvb_usb_device_init(intf,&cxusb_bluebird_lgh064f_properties,THIS_MODULE,NULL) == 0 ||
+ 		dvb_usb_device_init(intf,&cxusb_bluebird_dee1601_properties,THIS_MODULE,NULL) == 0 ||
+ 		dvb_usb_device_init(intf,&cxusb_bluebird_lgz201_properties,THIS_MODULE,NULL) == 0 ||
+-		dvb_usb_device_init(intf,&cxusb_bluebird_dtt7579_properties,THIS_MODULE,NULL) == 0) {
++		dvb_usb_device_init(intf,&cxusb_bluebird_dtt7579_properties,THIS_MODULE,NULL) == 0 ||
++		dvb_usb_device_init(intf,&cxusb_bluebird_dualdig4_properties,THIS_MODULE,NULL) == 0) {
+ 		return 0;
+ 	}
+ 
+@@ -508,6 +615,8 @@
+ 	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
+ 	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
+ 	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
++	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4) },
++	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVBT_NANO_2) },
+ 	{}		/* Terminating entry */
+ };
+ MODULE_DEVICE_TABLE (usb, cxusb_table);
+@@ -766,6 +875,52 @@
+ 	}
+ };
+ 
++static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = {
++	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
++
++	.usb_ctrl          = CYPRESS_FX2,
++
++	.size_of_priv     = sizeof(struct cxusb_state),
++
++	.num_adapters = 1,
++	.adapter = {
++		{
++			.streaming_ctrl   = cxusb_streaming_ctrl,
++			.frontend_attach  = cxusb_dualdig4_frontend_attach,
++			.tuner_attach     = cxusb_xc3028_tuner_attach,
++			/* parameter for the MPEG2-data transfer */
++			.stream = {
++				.type = USB_BULK,
++				.count = 5,
++				.endpoint = 0x02,
++				.u = {
++					.bulk = {
++						.buffersize = 8192,
++					}
++				}
++			},
++		},
++	},
++
++	.power_ctrl       = cxusb_bluebird_power_ctrl,
++
++	.i2c_algo         = &cxusb_i2c_algo,
++
++	.generic_bulk_ctrl_endpoint = 0x01,
++
++	.num_device_descs = 2,
++	.devices = {
++		{   "DViCO FusionHDTV DVB-T Dual Digital 4",
++			{ NULL },
++			{ &cxusb_table[13], NULL },
++		},
++		{   "DViCO FusionHDTV DVB-T NANO2",
++			{ NULL },
++			{ &cxusb_table[14], NULL },
++		},
++	}
++};
++
+ static struct usb_driver cxusb_driver = {
+ #if LINUX_VERSION_CODE <=  KERNEL_VERSION(2,6,15)
+ 	.owner		= THIS_MODULE,
+--- v4l-dvb.orig/linux/drivers/media/dvb/dvb-usb/cxusb.h
++++ v4l-dvb/linux/drivers/media/dvb/dvb-usb/cxusb.h
+@@ -28,6 +28,8 @@
+ #define CMD_ANALOG        0x50
+ #define CMD_DIGITAL       0x51
+ 
++#define CMD_BLUEBIRD_GPIO_WRITE 0x05
++
+ struct cxusb_state {
+ 	u8 gpio_write_state[3];
+ };
+--- v4l-dvb.orig/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
++++ v4l-dvb/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+@@ -142,6 +142,8 @@
+ #define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM		0xdb51
+ #define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD		0xdb58
+ #define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM		0xdb59
++#define USB_PID_DVICO_BLUEBIRD_DUAL_4			0xdb78
++#define USB_PID_DVICO_BLUEBIRD_DVBT_NANO_2		0xdb70
+ #define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD		0xdb54
+ #define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM		0xdb55
+ #define USB_PID_MEDION_MD95700				0x0932
+--- v4l-dvb.orig/linux/drivers/media/dvb/frontends/Kconfig
++++ v4l-dvb/linux/drivers/media/dvb/frontends/Kconfig
+@@ -353,6 +353,13 @@
+ 	  This device is only used inside a SiP called togther with a
+ 	  demodulator for now.
+ 
++config DVB_XC3028
++	tristate "Xceive XC3028 silicon tuner"
++	depends on I2C
++	default m if DVB_FE_CUSTOMISE
++	help
++	  A driver for the silicon tuner XC3028 from Xceive.
++
+ comment "Miscellaneous devices"
+ 	depends on DVB_CORE
+ 
+--- v4l-dvb.orig/linux/drivers/media/dvb/frontends/Makefile
++++ v4l-dvb/linux/drivers/media/dvb/frontends/Makefile
+@@ -4,6 +4,7 @@
+ 
+ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
+ 
++obj-$(CONFIG_DVB_XC3028) += xc3028-fe.o
+ obj-$(CONFIG_DVB_PLL) += dvb-pll.o
+ obj-$(CONFIG_DVB_STV0299) += stv0299.o
+ obj-$(CONFIG_DVB_SP8870) += sp8870.o
+--- /dev/null
++++ v4l-dvb/linux/drivers/media/dvb/frontends/xc3028-fe.c
+@@ -0,0 +1,532 @@
++/*
++
++   Xceive -  xc3028 tuner interface (Firmware 2.7)
++
++   Copyright (c) 2007 Michael Krufky <mkrufky@linuxtv.org>
++   Copyright (c) 2006 Markus Rechberger <mrechberger@gmail.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/i2c.h>
++#include "compat.h"
++#include <linux/firmware.h>
++#include <linux/delay.h>
++#include <media/tuner.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#include "i2c-compat.h"
++#endif
++#include "dvb_frontend.h"
++#include "xc3028.h"
++
++
++struct xc3028_priv {
++	u8 tuning_code[12];
++	enum v4l2_tuner_type type;
++	v4l2_std_id std;
++	//	unsigned int mode;
++
++	struct xc3028_config *cfg;
++
++	struct i2c_adapter *i2c_adap;
++	u32 frequency;
++	int bandwidth;
++};
++
++MODULE_DESCRIPTION("Xceive xc3028 dvb frontend driver");
++MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
++MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
++MODULE_LICENSE("GPL");
++
++
++/* hack */
++#define V4L2_TUNER_DVBT_TV 19
++#define V4L2_TUNER_DVBC_TV 29
++#define V4L2_TUNER_ATSC_TV 39
++
++/* firmware functions */
++#define XC3028_BYTECODE 0
++#define XC3028_RESET 1
++#define XC3028_SLEEP 2
++
++#define XC3028_BW8MHZ 0
++#define XC3028_BW7MHZ 1
++#define XC3028_BW6MHZ 2
++#define XC3028_ATSC_BW6MHZ 3
++#define XC3028_RADIO  4
++
++
++#define XC3028_ANALOGUE_FW "xc3028_init0.i2c.fw"
++#define XC3028_DIGITAL_FW  "xc3028_8MHz_init0.i2c.fw"
++#define XC3028_RADIO_FW    "xc3028_FM_init0.i2c.fw"
++#define XC3028_SPECIFIC_RADIO_FW "xc3028_specific_radio.fw"
++
++#define TUNING_GRANULARITY      15625
++
++static int firmware_loader(struct dvb_frontend *fe, const struct firmware *fw);
++static int upload_firmware(struct dvb_frontend *fe);
++static int xc3028_set_mode(struct dvb_frontend *fe, enum v4l2_tuner_type type);
++
++static struct _analogue_standards{
++	v4l2_std_id standard;
++	u8 filename[50];
++} xc3028_standards[]={
++	{V4L2_STD_PAL_BG,"xc3028_BG_PAL_A2_A.i2c.fw"},
++	{V4L2_STD_PAL_I,"xc3028_I_PAL_NICAM.i2c.fw"},
++	{V4L2_STD_PAL_DK,"xc3028_DK_PAL_A2.i2c.fw"},
++	{V4L2_STD_MN,"xc3028_MN_NTSCPAL_A2.i2c.fw"},
++	{V4L2_STD_PAL,  "xc3028_BG_PAL_A2_A.i2c.fw"},
++
++	{V4L2_STD_SECAM_DK,"xc3028_DK_SECAM_A2_DK1.i2c.fw"},
++	{V4L2_STD_SECAM_L,"xc3028_L_SECAM_NICAM.i2c.fw"},
++	{V4L2_STD_SECAM_LC,"xc3028_L'_SECAM_NICAM.i2c.fw"},
++	{V4L2_STD_SECAM_K1,"xc3028_DK_SECAM_A2_DK1.i2c.fw"},
++};
++
++/* TODO: add the other standards here
++   The linux DVB framework sends us following values
++   from 0..2 to set up the correct channel bandwidth
++
++   0 ... 8 mhz
++   1 ... 7 mhz
++   2 ... 6 mhz
++
++   we do not have any settings for 6MHz at least I haven't found one,
++   feel free to complete this list
++
++ */
++
++static struct _digital_standards{
++	unsigned int dvb:1;
++	unsigned int atsc:1;
++	int bandwidth;
++	char filename[50];
++} xc3028_dtv_standards[]={
++	{1, 0, XC3028_BW8MHZ /* 8mhz  */, "xc3028_DTV8_2633.i2c.fw"     },
++	{1, 0, XC3028_BW7MHZ /* 7mhz  */, "xc3028_DTV7_2633.i2c.fw"  },
++	    /* 2    6mhz  */
++	{0, 1, 0 /* ????  */, "xc3028_DTV6_ATSC_2620.i2c.fw"},
++	{0, 1, 1 /* ????  */, "xc3028_DTV6_ATSC_2620.i2c.fw"},
++	{0, 1, XC3028_ATSC_BW6MHZ, "xc3028_DTV6_ATSC_2620.i2c.fw"},
++};
++
++/* ---------------------------------------------------------------------- */
++#define XC3028_I2C_ADDR 0x61
++
++static int xc3028_i2c_xfer(struct i2c_adapter* adap, char *buf, int len)
++{
++	int ret;
++	struct i2c_msg msg = { .addr = XC3028_I2C_ADDR, .flags = 0,
++			       .buf = buf, .len = len };
++
++	ret = i2c_transfer(adap, &msg, 1);
++
++	/* If everything went ok (i.e. 1 msg transmitted), return #bytes
++	   transmitted, else error code. */
++	return (ret == 1) ? len : ret;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int xc3028_set_params(struct dvb_frontend *fe,
++			     struct dvb_frontend_parameters *params)
++{
++	struct xc3028_priv *priv = fe->tuner_priv;
++	const struct firmware *fw = NULL;
++	unsigned char chanbuf[4];
++	unsigned long frequency=0;
++	unsigned long value;
++	int bandwidth;
++	int i;
++	enum v4l2_tuner_type type;
++
++	if (fe->ops.info.type == FE_ATSC) {
++		type = V4L2_TUNER_ATSC_TV;
++	} else { // if (fe->ops.info.type == FE_OFDM)
++		type = V4L2_TUNER_DVBT_TV;
++	}
++
++	xc3028_set_mode(fe, type);
++
++	if (priv->type == V4L2_TUNER_ATSC_TV) {
++		bandwidth = XC3028_ATSC_BW6MHZ;
++	} else {
++		bandwidth = params->u.ofdm.bandwidth;
++	}
++
++	if (priv->bandwidth != bandwidth) {
++		switch(bandwidth) {
++		case -1:
++			/* analogue */
++			priv->bandwidth = bandwidth;
++			break;
++		case XC3028_ATSC_BW6MHZ:
++			for (i = 0; i < ARRAY_SIZE(xc3028_dtv_standards); i++) {
++				if (xc3028_dtv_standards[i].bandwidth == XC3028_ATSC_BW6MHZ) {
++					printk("Loading 6MHz Bandwidth settings: %s\n",xc3028_dtv_standards[i].filename);
++					if (request_firmware(&fw, xc3028_dtv_standards[i].filename, &priv->i2c_adap->dev) == 0) {
++						if (firmware_loader(fe,fw) != 0) {
++							release_firmware(fw);
++							printk("xc3028-tuner.c: error uploading firmware!\n");
++							return -EINVAL;
++						}
++						release_firmware(fw);
++						break;
++					} else
++						printk("Loading firmware from file failed!\n");
++				}
++			}
++			priv->bandwidth = bandwidth;
++			break;
++		case XC3028_BW8MHZ: /* 8 MHz */
++			for (i = 0; i < ARRAY_SIZE(xc3028_dtv_standards); i++) {
++				if (xc3028_dtv_standards[i].bandwidth == XC3028_BW8MHZ) {
++					printk("Loading 8MHz Bandwidth settings: %s\n",xc3028_dtv_standards[i].filename);
++					if (request_firmware(&fw, xc3028_dtv_standards[i].filename, &priv->i2c_adap->dev) == 0) {
++						if (firmware_loader(fe,fw) != 0) {
++							release_firmware(fw);
++							printk("xc3028-tuner.c: error uploading firmware!\n");
++							return -EINVAL;
++						}
++						release_firmware(fw);
++						break;
++					} else
++						printk("Loading firmware from file failed!\n");
++				}
++			}
++			priv->bandwidth = bandwidth;
++			break;
++		case XC3028_BW7MHZ: /* 7 MHz */
++			for (i = 0; i < ARRAY_SIZE(xc3028_dtv_standards); i++) {
++				if (xc3028_dtv_standards[i].bandwidth == XC3028_BW7MHZ) {
++					printk("Loading 7MHz Bandwidth settings: %s\n",xc3028_dtv_standards[i].filename);
++					if (request_firmware(&fw, xc3028_dtv_standards[i].filename, &priv->i2c_adap->dev) == 0) {
++						if (firmware_loader(fe,fw) != 0) {
++							release_firmware(fw);
++							printk("xc3028-tuner.c: error uploading firmware!\n");
++							return -EINVAL;
++						}
++						release_firmware(fw);
++						break;
++					} else
++						printk("Loading firmware from file failed!\n");
++				}
++			}
++			priv->bandwidth = bandwidth;
++			break;
++		default:
++			printk("xc3028-tuner.c: sorry [%d] bandwidth isn't supported (please report)\n",bandwidth);
++		}
++	}
++	/* TODO: 7 MHz (1) has the same offset as 8 MHz -- this depends on the used firmware */
++#if 0
++	if(t->mode == V4L2_TUNER_RADIO){
++		frequency=(unsigned long long)f->frequency*1000/16;
++	} else { }
++#endif
++	switch(bandwidth) {
++	case XC3028_BW8MHZ:
++	case XC3028_BW7MHZ:
++		frequency=(unsigned long long)params->frequency-2750000;
++		break;
++	case XC3028_BW6MHZ:
++	case XC3028_ATSC_BW6MHZ:
++		frequency=(unsigned long long)params->frequency-1750000;
++		break;
++	default:
++		frequency=(unsigned long long)params->frequency;
++	}
++
++	value=(frequency+(TUNING_GRANULARITY/2))/TUNING_GRANULARITY;
++	chanbuf[0]=0;
++	chanbuf[1]=0;
++	chanbuf[2]=(value&0xff00)>>8;
++	chanbuf[3]=value&0x00ff;
++
++#if 0
++	/* seems like it's not needed! */
++	rc=i2c_master_send(c,"\xa0\x00\x00\x00",4);
++	if(priv && priv->tuning_code)
++		i2c_master_send(c,priv->tuning_code,12);
++	else
++		printk("ERROR: *** NO TUNING CODE SET **\n");
++	i2c_master_send(c,"\x00\x8c",2);
++#endif
++	xc3028_i2c_xfer(priv->i2c_adap,"\x80\x02\x00\x00",4);
++	xc3028_i2c_xfer(priv->i2c_adap,chanbuf,4);
++	return 0;
++}
++
++static int firmware_loader(struct dvb_frontend *fe, const struct firmware *fw)
++{
++	int txtlen=0;
++	int current_ptr=0;
++	int version;
++	int function;
++	int x;
++	struct xc3028_priv *priv = fe->tuner_priv;
++
++	if (fw->size == 0)
++		return -EINVAL;
++
++	version = fw->data[current_ptr++];
++
++	switch (version) {
++	case 1:
++		while (current_ptr < fw->size) {
++			function = fw->data[current_ptr++];
++			switch (function) {
++			case XC3028_BYTECODE:
++				txtlen = fw->data[current_ptr++];
++				if ((current_ptr + txtlen) > fw->size)
++					return -EINVAL;
++				if (fw->data[current_ptr]==0x1e && txtlen == 12){
++					memcpy(priv->tuning_code, &fw->data[current_ptr], 12);
++				}
++				if (txtlen != xc3028_i2c_xfer(priv->i2c_adap,&fw->data[current_ptr],txtlen)) {
++					printk("failed: %02x| ",txtlen);
++					for(x=0; x < txtlen; x++) {
++						printk("%02x ",(unsigned char)fw->data[current_ptr+x]);
++					}
++					printk("\n");
++				}
++
++				current_ptr += txtlen;
++				break;
++			case XC3028_RESET:
++				x = fw->data[current_ptr++];
++
++				if ((priv->cfg) && (priv->cfg->gpio_reset))
++					priv->cfg->gpio_reset(fe,x);
++				else
++					printk("XC3028: NO GPIO CALLBACK FUNCTION PROVIDED - SWITCHING MODES WON'T WORK (GPIO FW ARG: %d)!\n",x);
++				break;
++			case XC3028_SLEEP:
++				msleep(fw->data[current_ptr++]);
++				break;
++			default:
++				printk("xc3028-tuner.c: error while loading firmware!\n");
++				return -EINVAL;
++			}
++		}
++		break;
++	default:
++		printk("xc3028-tuner.c: Firmware Loader: Unknown firmware version (%d)\n",version);
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static int upload_firmware(struct dvb_frontend *fe) {
++	struct xc3028_priv *priv = fe->tuner_priv;
++	const struct firmware *fw = NULL;
++	int i;
++
++	switch (priv->type) {
++	case V4L2_TUNER_ANALOG_TV:
++		printk("Loading base firmware: %s\n", XC3028_ANALOGUE_FW);
++		if (request_firmware(&fw, XC3028_ANALOGUE_FW, &priv->i2c_adap->dev) == 0) {
++			firmware_loader(fe,fw);
++			release_firmware(fw);
++		} else {
++			printk("xc3028-tuner.c: Unable to load firmware\n");
++			printk("xc3028-tuner.c: ** PLEASE HAVE A LOOK AT **\n");
++			printk("xc3028-tuner.c: http://linuxtv.org/v4lwiki/index.php/Talk:Em2880#Firmware\n");
++			return -EINVAL;
++		}
++		for (i = 0; i < ARRAY_SIZE(xc3028_standards); i++) {
++			if (xc3028_standards[i].standard & priv->std) {
++				printk("%s, Loading specific analogue TV settings: %s\n",__FUNCTION__, xc3028_standards[i].filename);
++				if (request_firmware(&fw, xc3028_standards[i].filename, &priv->i2c_adap->dev) == 0){
++					if(firmware_loader(fe,fw)==0){
++						release_firmware(fw);
++						return 0;
++					}
++					release_firmware(fw);
++				} else {
++					printk("Loading configuration from file failed!\n");
++				}
++				break;
++			}
++		}
++		printk("Loading default analogue TV settings: %s\n",xc3028_standards[0].filename);
++		priv->std=xc3028_standards[0].standard;
++		if (request_firmware(&fw, xc3028_standards[0].filename, &priv->i2c_adap->dev) == 0) {
++			if (firmware_loader(fe,fw) == 0) {
++				release_firmware(fw);
++				return 0;
++			}
++			release_firmware(fw);
++		}
++		printk("xc3028-tuner.c: error loading firmware (analogue TV)! (please report -> mrechberger@gmail.com)\n");
++		break;
++	case V4L2_TUNER_DVBT_TV:
++	case V4L2_TUNER_DVBC_TV:
++	case V4L2_TUNER_ATSC_TV:
++		printk("Loading base firmware: %s\n", XC3028_DIGITAL_FW);
++		/* reset analog standard */
++		priv->std = 0;
++		if (request_firmware(&fw, XC3028_DIGITAL_FW, &priv->i2c_adap->dev) == 0) {
++			firmware_loader(fe,fw);
++			release_firmware(fw);
++		} else {
++			printk("xc3028-tuner.c: Unable to load firmware\n");
++			printk("xc3028-tuner.c: ** PLEASE HAVE A LOOK AT **\n");
++			printk("xc3028-tuner.c: http://linuxtv.org/v4lwiki/index.php/Talk:Em2880#Firmware\n");
++			return -EINVAL;
++		}
++		for( i = 0; i < ARRAY_SIZE(xc3028_dtv_standards); i++) {
++			if ((xc3028_dtv_standards[i].dvb==1 &&
++			     xc3028_dtv_standards[i].bandwidth == priv->bandwidth) ||
++			    (priv->type == V4L2_TUNER_ATSC_TV &&
++			     xc3028_dtv_standards[i].atsc == 1)) {
++				printk("Loading specific dtv settings: %s\n",xc3028_dtv_standards[i].filename);
++				if (request_firmware(&fw, xc3028_dtv_standards[i].filename, &priv->i2c_adap->dev) == 0) {
++					if (firmware_loader(fe,fw) == 0) {
++						release_firmware(fw);
++						return 0;
++					}
++					release_firmware(fw);
++				}
++			}
++			i++;
++		}
++
++		/* this gets accessed if a switchover occures, t->bandwidth will be set to -1 */
++		printk("Loading default dtv settings: %s\n",xc3028_dtv_standards[0].filename);
++		priv->bandwidth = xc3028_dtv_standards[0].bandwidth;
++		if (request_firmware(&fw, xc3028_dtv_standards[0].filename, &priv->i2c_adap->dev) == 0) {
++			if (firmware_loader(fe,fw) == 0) {
++				release_firmware(fw);
++				return 0;
++			}
++			release_firmware(fw);
++		}
++		printk("xc3028-tuner.c: error loading firmware (analogue TV)! (please report -> mrechberger@gmail.com)\n");
++		break;
++	case V4L2_TUNER_RADIO:
++		printk("Loading base firmware: %s\n", XC3028_RADIO_FW);
++		if (request_firmware(&fw, XC3028_RADIO_FW, &priv->i2c_adap->dev) == 0) {
++			firmware_loader(fe,fw);
++			release_firmware(fw);
++		} else {
++			printk("xc3028-tuner.c: Unable to load (radio) firmware\n");
++			printk("xc3028-tuner.c: ** PLEASE HAVE A LOOK AT **\n");
++			printk("xc3028-tuner.c: http://linuxtv.org/v4lwiki/index.php/Talk:Em2880#Firmware\n");
++			return -EINVAL;
++		}
++		printk("%s, Loading specific radio firmware: %s\n",__FUNCTION__, XC3028_SPECIFIC_RADIO_FW);
++		if (request_firmware(&fw, XC3028_SPECIFIC_RADIO_FW, &priv->i2c_adap->dev) == 0) {
++			if (firmware_loader(fe,fw) == 0) {
++				release_firmware(fw);
++				return 0;
++			}
++			release_firmware(fw);
++		} else {
++			printk("Loading configuration from file failed!\n");
++		}
++		return 0;
++	default:
++		printk("ERROR TUNER TYPE NOT SUPPORTED (%d)\n",priv->type);
++		return -EINVAL;
++	}
++
++	printk("xc3028-tuner.c: *********************************************************\n");
++	printk("xc3028-tuner.c: no firmware uploaded\n" );
++	printk("xc3028-tuner.c: ** please have a look at: **\n");
++	printk("xc3028-tuner.c: http://linuxtv.org/v4lwiki/index.php/Talk:Em2880#Firmware\n");
++	printk("xc3028-tuner.c: *********************************************************\n");
++
++	return -EINVAL;
++}
++
++static int xc3028_set_mode(struct dvb_frontend *fe, enum v4l2_tuner_type type)
++{
++	struct xc3028_priv *priv = fe->tuner_priv;
++
++	priv->type = type;
++
++	upload_firmware(fe);
++
++	if (priv->cfg && priv->cfg->gpio_reset && (priv->type == V4L2_TUNER_DVBT_TV ||
++						   priv->type == V4L2_TUNER_DVBC_TV ||
++						   priv->type == V4L2_TUNER_ATSC_TV )) {
++		printk("xc3028-tuner.c: sending extra call for DVB-T\n");
++		priv->cfg->gpio_reset(fe,2);
++	}
++
++	return 0;
++}
++
++/* dvb tuner api */
++static int xc3028_release(struct dvb_frontend *fe)
++{
++	kfree(fe->tuner_priv);
++	fe->tuner_priv = NULL;
++	return 0;
++}
++
++static int xc3028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
++{
++	struct xc3028_priv *priv = fe->tuner_priv;
++
++	*frequency = priv->frequency*1000/16*1000;
++	return 0;
++}
++
++static int xc3028_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
++{
++	struct xc3028_priv *priv = fe->tuner_priv;
++
++	*bandwidth = priv->bandwidth;
++	return 0;
++}
++
++static const struct dvb_tuner_ops xc3028_tuner_ops = {
++	.info = {
++		.name = "Xceive XC3028",
++#if 0
++		.frequency_min = ,
++		.frequency_max =
++#endif
++	},
++	.release       = xc3028_release,
++	.set_params    = xc3028_set_params,
++	.get_frequency = xc3028_get_frequency,
++	.get_bandwidth = xc3028_get_bandwidth,
++};
++
++struct dvb_frontend *xc3028_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct xc3028_config *cfg)
++{
++	struct xc3028_priv *priv = kzalloc(sizeof(struct xc3028_priv),GFP_KERNEL);
++
++	priv->i2c_adap = i2c;
++	priv->bandwidth=XC3028_BW8MHZ;
++	priv->cfg = cfg;
++
++	memcpy(&fe->ops.tuner_ops, &xc3028_tuner_ops, sizeof(struct dvb_tuner_ops));
++	fe->tuner_priv = priv;
++	return fe;
++}
++EXPORT_SYMBOL(xc3028_attach);
++
++/*
++ * Overrides for Emacs so that we follow Linus's tabbing style.
++ * ---------------------------------------------------------------------------
++ * Local variables:
++ * c-basic-offset: 8
++ * End:
++ */
+--- /dev/null
++++ v4l-dvb/linux/drivers/media/dvb/frontends/xc3028.h
+@@ -0,0 +1,56 @@
++  /*
++     Header for Xceive Silicon tuners
++
++     (c) 2007 Michael Krufky
++
++     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_XC3028_H__
++#define __DVB_XC3028_H__
++
++#include <linux/i2c.h>
++#include "dvb_frontend.h"
++
++/**
++ * Attach a xc3028 tuner to the supplied frontend structure.
++ *
++ * @param fe Frontend to attach to.
++ * @param i2c i2c adapter to use.
++ * @param cfg config struct with gpio reset callback.
++ * @return FE pointer on success, NULL on failure.
++ */
++
++struct xc3028_config {
++	int (*gpio_reset) (struct dvb_frontend *fe, int ptr);
++};
++
++#if defined(CONFIG_DVB_XC3028) || (defined(CONFIG_DVB_XC3028_MODULE) && defined(MODULE))
++extern struct dvb_frontend* xc3028_attach(struct dvb_frontend *fe,
++					  struct i2c_adapter *i2c,
++					  struct xc3028_config *cfg);
++#else
++static inline struct dvb_frontend* xc3028_attach(struct dvb_frontend *fe,
++						 struct i2c_adapter *i2c,
++						 struct xc3028_config *cfg)
++{
++	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
++	return NULL;
++}
++#endif // CONFIG_DVB_XC3028
++
++#endif // __DVB_XC3028_H__
+--- v4l-dvb.orig/v4l/versions.txt
++++ v4l-dvb/v4l/versions.txt
+@@ -228,3 +228,4 @@
+ USB_ZC0301
+ USB_ET61X251
+ USB_ZR364XX
++DVB_XC3028
-- 
cgit v0.12