[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 --- 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 + Copyright (c) 2006 Markus Rechberger + + 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 +#include "compat.h" +#include +#include +#include +#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 "); +MODULE_AUTHOR("Markus Rechberger "); +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 +#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