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