/* Xceive - xc3028 tuner interface Copyright (c) 2006 Markus Rechberger TODO: - remove em28xx dependency - add channel locking (requires some more reverse engineering) - try to get the datasheet from Xceive :) 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 #include "compat.h" #include #include "em28xx.h" #include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) #include "i2c-compat.h" #endif #define XC3028_DEFAULT_FIRMWARE "xceive_xc_3028.fw" int xceive_set_color(struct i2c_client *c); /* ---------------------------------------------------------------------- */ int xc3028_probe(struct i2c_client *c) { printk("xc3028: probe function unknown\n"); return -1; } static void xc3028_set_tv_freq(struct i2c_client *c, unsigned int freq){ /* the frequency is just shifted and there's a 1:1 relation for all frequencies E11 is Das Erste in Germany/Ulm all other channels match their frequency too */ unsigned char chanbuf[4]; freq<<=2; chanbuf[0]=0; chanbuf[1]=0; chanbuf[2]=(freq&0xff00)>>8; chanbuf[3]=freq&0x00ff; i2c_master_send(c,"\xa0\x00\x00\x00",4); i2c_master_send(c,"\x1e\x1f\x13\x87\x18\x02\x93\x91\x44\x86\x96\x8c",12); i2c_master_send(c,"\x00\x8c",2); i2c_master_send(c,"\x80\x02\x00\x00",4); i2c_master_send(c,chanbuf,4); } int xc3028_init(struct i2c_client *c) { struct tuner *t = i2c_get_clientdata(c); struct em28xx *dev; const struct firmware *fw = NULL; size_t firmware_size; int ret=-1; int i=0; int d=0; int txtlen; long fwoff; u8 *firmware; u8 linebuffer[100]; char *fwoffset; /* request firmware from /lib/firmware, note that the file got extracted by the convert application I wrote and which is available on linuxtv.org / xc3028 */ ret = request_firmware(&fw, XC3028_DEFAULT_FIRMWARE, &t->i2c.dev); if (ret) { printk("xc3028: no firmware uploaded please check %s\n",XC3028_DEFAULT_FIRMWARE); return ret; } firmware = fw->data; firmware_size = fw->size; /* small firmware check, both firmwares I have are between 6 and 7k bytes */ if(fw->size>7000||fw->size<6000){ printk("xc3028: wrong firmware provided!\n"); release_firmware(fw); return(ret); } for(i=0;i<8&&firmware[i]!='\n';i++); txtlen=i; firmware[i++]=0; fwoff=simple_strtol(firmware,&fwoffset,10); if(fwoff>fw->size){ printk("xc3028: firmware offset doesn't match!\n"); release_firmware(fw); return(-1); } linebuffer[d++]=0x2a; dev=c->adapter->algo_data; /* 0x08 is a GPIO address of the em28xx has to get replaced with something generic here */ dev->em28xx_write_regs(dev, 0x08, "\x6d", 1); dev->em28xx_write_regs(dev, 0x08, "\x7d", 1); /* the firmware always starts with 0x2a + 0x40 bytes payload I use to add the offset of the first part as the first line into the firmware binary */ while(i!=fw->size){ linebuffer[d++]=firmware[i]; if((d%64==0&&d!=0)||i==fwoff+txtlen){ i2c_master_send(c,linebuffer,d); if(i==(fwoff+txtlen)){ i2c_master_send(c,"\x02\x02",2); i2c_master_send(c,"\x02\x03",2); i2c_master_send(c,"\x00\x8c",2); i2c_master_send(c,"\x00\x00\x00\x00",4); /* at least 100 ms delay here, if less terratec FW won't work */ msleep(100); /* another reset here */ dev->em28xx_write_regs(dev, 0x08, "\x6d", 1); dev->em28xx_write_regs(dev, 0x08, "\x7d", 1); } linebuffer[0]=0x2a; d=1; } i++; } printk("xc3024: Firmware uploaded\n"); release_firmware(fw); /* MAGIC VALUES Hauppauge */ i2c_master_send(c,"\x13\x39",2); i2c_master_send(c,"\x0c\x80\xf0\xf7\x3e\x75\xc1\x8a\xe4\x02\x00",11); i2c_master_send(c,"\x05\x0f\xee\xaa\x5f\xea\x90",7); i2c_master_send(c,"\x06\x00\x0a\x4d\x8c\xf2\xd8\xcf\x30\x79\x9f",11); i2c_master_send(c,"\x0b\x0d\xa4\x6c",4); i2c_master_send(c,"\x0a\x01\x67\x24\x40\x08\xc3\x20\x10\x64\x3c\xfa\xf7\xe1\x0c\x2c",0x10); i2c_master_send(c,"\x09\x0b",0x2); i2c_master_send(c,"\x10\x13",0x2); i2c_master_send(c,"\x16\x12",0x2); i2c_master_send(c,"\x1f\x02",0x2); i2c_master_send(c,"\x21\x02",0x2); i2c_master_send(c,"\x01\x02",0x2); i2c_master_send(c,"\x2b\x10",0x2); i2c_master_send(c,"\x02\x02",0x2); i2c_master_send(c,"\x02\x03",0x2); i2c_master_send(c,"\x00\x8c",0x2); #if 0 /* MAGIC Values Terratec - dvb init*/ i2c_master_send(c,"\x13\x39",0x02); i2c_master_send(c,"\x0c\x80\xf0\xf7\x3e\x75\xc1\x8a\xe4\x02\x00",11); i2c_master_send(c,"\x05\x0f\xee\xaa\x5f\xea\x90",7); i2c_master_send(c,"\x06\x00\x0a\x4d\x8c\xf2\xd8\xcf\x30\x79\x9f",11); i2c_master_send(c,"\x0b\x0d\xa4\x6c",4); i2c_master_send(c,"\x0a\x01\x67\x24\x40\x08\xc3\x20\x10\x64\x3c\xfa\xf7\xe1\x0c\x2c",0x10); i2c_master_send(c,"\x09\x0b",0x02); i2c_master_send(c,"\x10\x13",0x02); i2c_master_send(c,"\x16\x12",0x02); i2c_master_send(c,"\x1f\x02",0x02); i2c_master_send(c,"\x21\x02",0x02); i2c_master_send(c,"\x01\x02",0x02); i2c_master_send(c,"\x2b\x10",0x02); i2c_master_send(c,"\x02\x02",0x02); i2c_master_send(c,"\x02\x03",0x02); i2c_master_send(c,"\x00\x8c",0x02); #endif #if 0 /* if set video will mostly be black/white - if set_color is called instead the video will have color */ i2c_master_send(c,"\x80\x01\x00\x00",0x4); i2c_master_send(c,"\x00\x5e\x00\x29",0x4); i2c_master_send(c,"\x2b\x1a",0x2); #endif xceive_set_color(c); t->set_tv_freq = xc3028_set_tv_freq; return(0); } int xceive_set_color(struct i2c_client *c){ /* I found this codeblock within the sniffed logfile it got called as it is a several times after 0x00 0x04 tuner settings are made */ /* codeblock hauppauge/terratec - analog */ i2c_master_send(c,"\x80\x01\x00\x00",4); i2c_master_send(c,"\x00\x5e\x00\x29",4); i2c_master_send(c,"\x2b\x1a",2); i2c_master_send(c,"\x2b\x1b",2); i2c_master_send(c,"\x14\x01\x6c\x25\x82\x38\xa4\x49\xa9\x24\x96\x69",0x0c); i2c_master_send(c,"\x13\x14\x08\x30\x10\x6c\x18\x12\x0d\x19\x32\xad",0x0c); i2c_master_send(c,"\x0d\x01\x4b\x03\x97\x55\xc7\xd7\x00\xa1\xeb\x8f\x5c",0x0d); i2c_master_send(c,"\x1a\x00\x00\x16\x8a\x40\x00\x00\x00\x20",0x0a); i2c_master_send(c,"\x2d\x01",2); i2c_master_send(c,"\x18\x01",2); i2c_master_send(c,"\x1b\x01\xb6\x15\x16\xb1\xa6\xd2\xa9\x12\x41\x66",0x0c); i2c_master_send(c,"\x1d\x00",2); i2c_master_send(c,"\x0f\x00\x29\x56\xb0\x00\xb6",0x07); i2c_master_send(c,"\x20\x00",0x02); i2c_master_send(c,"\x1e\x10\x32\x00\x00\x02\xe4\x81\x00\x06\xa9\x04",0x0c); i2c_master_send(c,"\x22\x29",0x02); i2c_master_send(c,"\x23\x06",0x02); i2c_master_send(c,"\x25\x00\x09\x90\x09\x06\x64\x02\x41",0x09); i2c_master_send(c,"\x26\xcc",0x02); i2c_master_send(c,"\x29\x40",0x02); i2c_master_send(c,"\x21\x03",0x02); i2c_master_send(c,"\x00\x8c",0x02); i2c_master_send(c,"\x00\x00\x00\x00",0x04); /* just wonder no sleep here regarding the logs */ i2c_master_send(c,"\x00\x04",0x02); return(0); } /* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- * Local variables: * c-basic-offset: 8 * End: */