summaryrefslogtreecommitdiffstats
path: root/abs/extra/v4l-dvb-dvico2/xc-bluebird.patch
diff options
context:
space:
mode:
Diffstat (limited to 'abs/extra/v4l-dvb-dvico2/xc-bluebird.patch')
-rw-r--r--abs/extra/v4l-dvb-dvico2/xc-bluebird.patch896
1 files changed, 896 insertions, 0 deletions
diff --git a/abs/extra/v4l-dvb-dvico2/xc-bluebird.patch b/abs/extra/v4l-dvb-dvico2/xc-bluebird.patch
new file mode 100644
index 0000000..c54594d
--- /dev/null
+++ b/abs/extra/v4l-dvb-dvico2/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