diff -r 127f67dea087 linux/drivers/media/dvb/frontends/Kconfig --- a/linux/drivers/media/dvb/frontends/Kconfig Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/dvb/frontends/Kconfig Mon Mar 03 22:32:21 2008 +0000 @@ -14,6 +14,13 @@ config DVB_FE_CUSTOMISE comment "DVB-S (satellite) frontends" depends on DVB_CORE + +config DVB_CX24116 + tristate "Conexant CX24116 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. config DVB_STV0299 tristate "ST STV0299 based" diff -r 127f67dea087 linux/drivers/media/dvb/frontends/Makefile --- a/linux/drivers/media/dvb/frontends/Makefile Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/dvb/frontends/Makefile Mon Mar 03 22:32:21 2008 +0000 @@ -52,3 +52,4 @@ obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131 obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o obj-$(CONFIG_DVB_S5H1409) += s5h1409.o obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o +obj-$(CONFIG_DVB_CX24116) += cx24116.o diff -r 127f67dea087 linux/drivers/media/dvb/frontends/cx24116.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/dvb/frontends/cx24116.c Mon Mar 03 22:32:21 2008 +0000 @@ -0,0 +1,1508 @@ +/* + Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2006 Steven Toth + Copyright (C) 2006 Georg Acher + Copyright (C) 2007 Steven Toth, Georg Acher, Darron Broad + + 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. + + HISTORY (Darron): + + March + Removed DVB-S2 multiproto support. + Fixed legacy v4l-dvb support. + Fixed some bugs. + + August + Added diseqc support from patch set. + Added corrected signal strength support from patch set. + Some clean ups. + + November + Revert diseqc patch to include caching mechanism + as the default. Derived burst method (hack) must + now be enabled explicitly. + Inspection of reelbox driver reveals new data + concerning signal readings. This has been added. + Signal strength reading was previously calibrated + against dvb2000. Inspection of reelbox code and + calibration against a FORTEC receiver confirm that + it is indeed signal quality and not strength. This + is now returned as SNR. + Added detail gleaned from reelbox driver. + + December + Defaulted to diseqc burst hack again (now named tone cache). + Added option to never send a tone burst. + Added sysctl for toneburst switch. + Added firmware id dump as per updated reelbox driver. + Added DVB-S2 SYSCTLs for testing purposes + Updated ISL6421 resolving DiSEqC signalling with some setups. + Small tidy ups. + Fix newly introduced bug toggling pilot for DVB-S + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "cx24116.h" + +static int debug = 0; +#define dprintk(args...) \ + do { \ + if (debug) printk ("cx24116: " args); \ + } while (0) + +/* + * DVB-S2 SYSCTLs for tests + * + * modfec (modulation and FEC) + * =========================== + * + * MOD FEC modfec standard + * ---- -------- ------- -------- + * QPSK FEC_1_2 0x022+X DVB-S + * QPSK FEC_2_3 0x042+X DVB-S + * QPSK FEC_3_4 0x082+X DVB-S + * QPSK FEC_4_5 0x102+X DVB-S (?) + * QPSK FEC_5_6 0x202+X DVB-S + * QPSK FEC_6_7 0x402+X DVB-S + * QPSK FEC_7_8 0x802+X DVB-S + * QPSK FEC_8_9 0x012+X DVB-S (?) (NOT SUPPORTED?) + * QPSK AUTO 0xff2+X DVB-S + * + * For DVB-S high byte probably represents FEC + * and low byte selects the modulator. The high + * byte is search range mask. Bit 5 may turn + * on DVB-S and remaining bits represent some + * kind of calibration (how/what i do not know). + * + * Eg.(2/3) szap "Zone Horror" + * modfec=0x0420 + * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 00000000 | FE_HAS_LOCK + * modfec=0x0430 + * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 00000000 | FE_HAS_LOCK + * + * After tuning FECSTATUS contains actual FEC + * in use numbered 1 through to 8 for 1/2 .. 2/3 etc + * + * NBC-QPSK FEC_1_2 0x0004 DVB-S2 + * NBC-QPSK FEC_3_5 0x0005 DVB-S2 + * NBC-QPSK FEC_2_3 0x0006 DVB-S2 + * NBC-QPSK FEC_3_4 0x0007 DVB-S2 + * NBC-QPSK FEC_4_5 0x0008 DVB-S2 + * NBC-QPSK FEC_5_6 0x0009 DVB-S2 + * NBC-QPSK FEC_8_9 0x000a DVB-S2 + * NBC-QPSK FEC_9_10 0x000b DVB-S2 + * + * NBC-8PSK FEC_3_5 0x000c DVB-S2 + * NBC-8PSK FEC_2_3 0x000d DVB-S2 + * NBC-8PSK FEC_3_4 0x000e DVB-S2 + * NBC-8PSK FEC_5_6 0x000f DVB-S2 + * NBC-8PSK FEC_8_9 0x0010 DVB-S2 + * NBC-8PSK FEC_9_10 0x0011 DVB-S2 + * + * For DVB-S2 low bytes selects both modulator + * and FEC. High byte is meaningless here. To + * set pilot, bit 6 (0x40) is set. When inspecting + * FECSTATUS bit 7 (0x80) represents the pilot + * selection whilst not tuned. When tuned, actual FEC + * in use is found in FECSTATUS as per above. Pilot + * value is reset. + * + * dvb-s2 initial pilot on/off + * =========================== + * 0 OFF + * 1 ON + * + * dvb-s2 rolloff + * ============== + * 0=0.20 (DVB-S2) + * 1=0.25 (DVB-S2) + * 2=0.35 (DVB-S2/DVB-S) + */ +#define CX24116_DVBS_AUTOFEC (0xfe30) +static int modfec = CX24116_DVBS_AUTOFEC; /* DVB-S=0xfe30 ELSE DVB-S2 as per above */ +static int pilot = 1; /* DVB-S2 initial pilot */ +static int rolloff = 2; /* DVB-S2 rolloff */ + +/* + * Fetch firmware (version 1.20.79.0) in the following manner: + * + * #!/bin/sh + * wget ftp://167.206.143.11/outgoing/Oxford/88x_2_117_24275_1_INF.zip + * unzip 88x_2_117_24275_1_INF.zip + * dd if=Driver88/hcw88bda.sys of=dvb-fe-cx24116.fw skip=81768 bs=1 count=32522 + */ +#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw" +#define CX24116_SEARCH_RANGE_KHZ 5000 + +/* known registers */ +#define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */ +#define CX24116_REG_EXECUTE (0x1f) /* execute command */ +#define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */ +#define CX24116_REG_RESET (0x20) /* reset status > 0 */ +#define CX24116_REG_SIGNAL (0x9e) /* signal low */ +#define CX24116_REG_SSTATUS (0x9d) /* signal high / status */ +#define CX24116_REG_QSTATUS (0xbc) +#define CX24116_REG_QUALITY (0xd5) +#define CX24116_REG_BER0 (0xc9) +#define CX24116_REG_BER8 (0xc8) +#define CX24116_REG_BER16 (0xc7) +#define CX24116_REG_BER24 (0xc6) +#define CX24116_REG_UCB0 (0xcb) +#define CX24116_REG_UCB8 (0xca) + +#define CX24116_ARGLEN (0x1e) /* arg buffer size */ + +/* signal status */ +#define CX24116_HAS_SIGNAL (0x01) +#define CX24116_HAS_CARRIER (0x02) +#define CX24116_HAS_VITERBI (0x04) +#define CX24116_HAS_SYNCLOCK (0x08) +#define CX24116_HAS_UNKNOWN1 (0x10) +#define CX24116_HAS_UNKNOWN2 (0x20) +#define CX24116_STATUS_MASK (0x3f) +#define CX24116_SIGNAL_MASK (0xc0) + +/* unconfirmed or just wrong */ +#define CX24116_REG_FECSTATUS (0x9c) /* FEC in use (nb. for DVB-S range is 1..8, for DVB-S2 as configured) */ +#define CX24116_FEC_FECMASK (0x1f) /* mask to determine configured fec (not tuned) or actual fec (tuned) */ +#define CX24116_FEC_DVBS (0x20) /* Select DVB-S demodulator */ +#define CX24116_FEC_UNKNOWN2 (0x40) /* This bit represents ? */ +#define CX24116_FEC_PILOT (0x80) /* This bit represents the pilot mode requested when not tuned and is 0 when tuned */ + +/* DiSEqC tone burst */ +static int toneburst = 1; +static struct ctl_table_header *kernel_table_header; +static ctl_table toneburst_table[] = { + { + .ctl_name = 0, + .procname = "toneburst", + .data = &toneburst, + .maxlen = sizeof(int), + .mode = 0666, + .child = NULL, + .parent = NULL, + .proc_handler = &proc_dointvec, + .strategy = NULL, + .extra1 = NULL, + .extra2 = NULL, + }, + { + .ctl_name = 0, + .procname = "modfec", + .data = &modfec, + .maxlen = sizeof(int), + .mode = 0666, + .child = NULL, + .parent = NULL, + .proc_handler = &proc_dointvec, + .strategy = NULL, + .extra1 = NULL, + .extra2 = NULL, + }, + { + .ctl_name = 0, + .procname = "pilot", + .data = &pilot, + .maxlen = sizeof(int), + .mode = 0666, + .child = NULL, + .parent = NULL, + .proc_handler = &proc_dointvec, + .strategy = NULL, + .extra1 = NULL, + .extra2 = NULL, + }, + { + .ctl_name = 0, + .procname = "rolloff", + .data = &rolloff, + .maxlen = sizeof(int), + .mode = 0666, + .child = NULL, + .parent = NULL, + .proc_handler = &proc_dointvec, + .strategy = NULL, + .extra1 = NULL, + .extra2 = NULL, + }, + { + .ctl_name = 0, + .procname = "debug", + .data = &debug, + .maxlen = sizeof(int), + .mode = 0666, + .child = NULL, + .parent = NULL, + .proc_handler = &proc_dointvec, + .strategy = NULL, + .extra1 = NULL, + .extra2 = NULL, + }, + {0}, +}; +static ctl_table cx24116_table[] = { + { + .ctl_name = 0, + .procname = "cx24116", + .data = NULL, + .maxlen = 0, + .mode = 0555, + .child = toneburst_table, + .parent = NULL, + .proc_handler = NULL, + .strategy = NULL, + .extra1 = NULL, + .extra2 = NULL, + }, + {0}, +}; +static ctl_table kernel_table[] = { + { + .ctl_name = CTL_DEV, + .procname = "dev", + .data = NULL, + .maxlen = 0, + .mode = 0555, + .child = cx24116_table, + .parent = NULL, + .proc_handler = NULL, + .strategy = NULL, + .extra1 = NULL, + .extra2 = NULL, + }, + {0}, +}; +#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */ +#define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */ +#define CX24116_DISEQC_MESGCACHE (2) /* message cached */ + +/* arg offset for DiSEqC */ +#define CX24116_DISEQC_BURST (1) +#define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */ +#define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */ +#define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */ +#define CX24116_DISEQC_MSGLEN (5) +#define CX24116_DISEQC_MSGOFS (6) + +/* DiSEqC burst */ +#define CX24116_DISEQC_MINI_A (0) +#define CX24116_DISEQC_MINI_B (1) + +enum cmds +{ + CMD_SET_VCO = 0x10, + CMD_TUNEREQUEST = 0x11, + CMD_MPEGCONFIG = 0x13, + CMD_TUNERINIT = 0x14, + CMD_BANDWIDTH = 0x15, + CMD_GETAGC = 0x19, + CMD_LNBCONFIG = 0x20, + CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */ + CMD_SET_TONEPRE = 0x22, + CMD_SET_TONE = 0x23, + CMD_UPDFWVERS = 0x35, + CMD_TUNERSLEEP = 0x36, + CMD_AGCCONTROL = 0x3b, /* Unknown */ +}; + +/* The Demod/Tuner can't easily provide these, we cache them */ +struct cx24116_tuning +{ + u32 frequency; + u32 symbol_rate; + fe_spectral_inversion_t inversion; + enum fe_code_rate fec; + + /* Demod values */ + u8 fec_val; + u8 fec_mask; + u8 inversion_val; +}; + +/* Basic commands that are sent to the firmware */ +struct cx24116_cmd +{ + u8 len; + u8 args[CX24116_ARGLEN]; +}; + +struct cx24116_state +{ + struct i2c_adapter* i2c; + const struct cx24116_config* config; + + struct dvb_frontend frontend; + + struct cx24116_tuning dcur; + struct cx24116_tuning dnxt; + + u8 skip_fw_load; + struct cx24116_cmd dsec_cmd; +}; + +static int cx24116_writereg(struct cx24116_state* state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 2 }; + int err; + + if (debug>1) + printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n", + __FUNCTION__,reg, data); + + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { + printk("%s: writereg error(err == %i, reg == 0x%02x," + " value == 0x%02x)\n", __FUNCTION__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +/* Bulk byte writes to a single I2C address, for 32k firmware load */ +static int cx24116_writeregN(struct cx24116_state* state, int reg, u8 *data, u16 len) +{ + int ret = -EREMOTEIO; + struct i2c_msg msg; + u8 *buf; + + buf = kmalloc(len + 1, GFP_KERNEL); + if (buf == NULL) { + printk("Unable to kmalloc\n"); + ret = -ENOMEM; + goto error; + } + + *(buf) = reg; + memcpy(buf + 1, data, len); + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = buf; + msg.len = len + 1; + + if (debug>1) + printk("cx24116: %s: write regN 0x%02x, len = %d\n", + __FUNCTION__,reg, len); + + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) { + printk("%s: writereg error(err == %i, reg == 0x%02x\n", + __FUNCTION__, ret, reg); + ret = -EREMOTEIO; + } + +error: + kfree(buf); + + return ret; +} + +static int cx24116_readreg(struct cx24116_state* state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk("%s: reg=0x%x (error=%d)\n", __FUNCTION__, reg, ret); + return ret; + } + + if (debug>1) + printk("cx24116: read reg 0x%02x, value 0x%02x\n",reg, b1[0]); + + return b1[0]; +} + +static int cx24116_set_inversion(struct cx24116_state* state, fe_spectral_inversion_t inversion) +{ + dprintk("%s(%d)\n", __FUNCTION__, inversion); + + switch (inversion) { + case INVERSION_OFF: + state->dnxt.inversion_val = 0x00; + break; + case INVERSION_ON: + state->dnxt.inversion_val = 0x04; + break; + case INVERSION_AUTO: + state->dnxt.inversion_val = 0x0C; + break; + default: + return -EINVAL; + } + + state->dnxt.inversion = inversion; + + return 0; +} + +/* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ +struct cx24116_qpskfec { + enum fe_code_rate fec; + u16 modfec; +} CX24116_QPSKFEC[] = { + /* fec modfec */ + { FEC_NONE, CX24116_DVBS_AUTOFEC }, + { FEC_1_2, 0x022e }, /* 00000010 00101110 */ + { FEC_2_3, 0x042f }, /* 00000100 00101111 */ + { FEC_3_4, 0x0830 }, /* 00001000 00110000 */ + { FEC_4_5, CX24116_DVBS_AUTOFEC }, /* 000?0000 ? */ + { FEC_5_6, 0x2031 }, /* 00100000 00110001 */ + { FEC_6_7, CX24116_DVBS_AUTOFEC }, /* 0?000000 ? */ + { FEC_7_8, 0x8032 }, /* 10000000 00110010 */ + { FEC_8_9, CX24116_DVBS_AUTOFEC }, /* 0000000? ? */ + { FEC_AUTO, CX24116_DVBS_AUTOFEC }, + /* + * modfec&255 can be found in the FECSTATUS register when tuning. + * FECSTATUS will give the actual FEC in use if tuning was successful. + */ +}; + +static int cx24116_lookup_fec(struct cx24116_state* state, enum fe_code_rate f) +{ + int i, ret = -EOPNOTSUPP; + + dprintk("%s(0x%02x)\n", __FUNCTION__, f); + + for(i=0 ; i < sizeof(CX24116_QPSKFEC) / sizeof(struct cx24116_qpskfec) ; i++) + { + if( f == CX24116_QPSKFEC[i].fec ) + { + ret = i; + break; + } + } + + return ret; +} + +static int cx24116_set_fec(struct cx24116_state* state, struct dvb_frontend_parameters *p) +{ + int ret; + + dprintk("%s()\n", __FUNCTION__); + + ret = cx24116_lookup_fec(state, p->u.qpsk.fec_inner); + + if( ret == -EOPNOTSUPP ) + { + dprintk("%s() fec unsupported\n", __FUNCTION__); + } + else + { + state->dnxt.fec = p->u.qpsk.fec_inner; + state->dnxt.fec_mask = CX24116_QPSKFEC[ret].modfec >> 8; + state->dnxt.fec_val = CX24116_QPSKFEC[ret].modfec & 255; + dprintk("%s() fec supported fec/mask/val = 0x%02x/0x%02x/0x%02x\n", __FUNCTION__, + state->dnxt.fec, state->dnxt.fec_mask, state->dnxt.fec_val); + + ret = 0; /* OK */ + } + + return ret; +} + +static int cx24116_set_symbolrate(struct cx24116_state* state, struct dvb_frontend_parameters *p) +{ + int ret = 0; + + dprintk("%s()\n", __FUNCTION__); + + /* check if symbol rate is within limits */ + if ((p->u.qpsk.symbol_rate > state->frontend.ops.info.symbol_rate_max) || + (p->u.qpsk.symbol_rate < state->frontend.ops.info.symbol_rate_min)) + { + ret = -EOPNOTSUPP; + } + else + { + state->dnxt.symbol_rate = p->u.qpsk.symbol_rate; + dprintk("%s() symbol_rate = %d\n", __FUNCTION__, state->dnxt.symbol_rate); + } + + return ret; +} + +static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw); + +static int cx24116_firmware_ondemand(struct dvb_frontend* fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + const struct firmware *fw; + int ret = 0; + + dprintk("%s()\n",__FUNCTION__); + + if (cx24116_readreg(state, CX24116_REG_RESET) > 0) + { + if (state->skip_fw_load) + return 0; + + /* Load firmware */ + /* request the firmware, this will block until someone uploads it */ + printk("%s: Waiting for firmware upload (%s)...\n", __FUNCTION__, CX24116_DEFAULT_FIRMWARE); + ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, &state->i2c->dev); + printk("%s: Waiting for firmware upload(2)...\n", __FUNCTION__); + if (ret) { + printk("%s: No firmware uploaded (timeout or file not found?)\n", __FUNCTION__); + return ret; + } + + /* Make sure we don't recurse back through here during loading */ + state->skip_fw_load = 1; + + ret = cx24116_load_firmware(fe, fw); + if (ret) + printk("%s: Writing firmware to device failed\n", __FUNCTION__); + + release_firmware(fw); + + printk("%s: Firmware upload %s\n", __FUNCTION__, ret == 0 ? "complete" : "failed"); + + /* Ensure firmware is always loaded if required */ + state->skip_fw_load = 0; + } + + return ret; +} + +/* Take a basic firmware command structure, format it and forward it for processing */ +static int cx24116_cmd_execute(struct dvb_frontend* fe, struct cx24116_cmd *cmd) +{ + struct cx24116_state *state = fe->demodulator_priv; + int i, ret; + + dprintk("%s()\n", __FUNCTION__); + + /* Load the firmware if required */ + if ( (ret = cx24116_firmware_ondemand(fe)) != 0) + { + printk("%s(): Unable initialise the firmware\n", __FUNCTION__); + return ret; + } + + /* Write the command */ + for(i = CX24116_REG_COMMAND /* =0 */; (i < cmd->len) && (i <= CX24116_ARGLEN) ; i++) + { + dprintk("%s: 0x%02x == 0x%02x\n", __FUNCTION__, i, cmd->args[i]); + cx24116_writereg(state, i, cmd->args[i]); + } + + /* Start execution and wait for cmd to terminate */ + cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01); + while( cx24116_readreg(state, CX24116_REG_EXECUTE) ) + { + msleep(10); + if(i++ > 64) + { + /* Avoid looping forever if the firmware does no respond */ + printk("%s() Firmware not responding\n", __FUNCTION__); + return -EREMOTEIO; + } + } + return 0; +} + +static int cx24116_load_firmware(struct dvb_frontend* fe, const struct firmware *fw) +{ + struct cx24116_state* state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int i, ret; + unsigned char vers[4]; + + dprintk("%s\n", __FUNCTION__); + dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n" + ,fw->size + ,fw->data[0] + ,fw->data[1] + ,fw->data[ fw->size-2 ] + ,fw->data[ fw->size-1 ] + ); + + /* Toggle 88x SRST pin to reset demod */ + if (state->config->reset_device) + state->config->reset_device(fe); + + /* Begin the firmware load process */ + /* Prepare the demod, load the firmware, cleanup after load */ + + /* Init PLL */ + cx24116_writereg(state, 0xE5, 0x00); + cx24116_writereg(state, 0xF1, 0x08); + cx24116_writereg(state, 0xF2, 0x13); + + /* Start PLL */ + cx24116_writereg(state, 0xe0, 0x03); + cx24116_writereg(state, 0xe0, 0x00); + + /* Unknown */ + cx24116_writereg(state, 0xF3, 0x46); + cx24116_writereg(state, 0xF9, 0x00); + + /* Unknown */ + cx24116_writereg(state, 0xF0, 0x03); + cx24116_writereg(state, 0xF4, 0x81); + cx24116_writereg(state, 0xF5, 0x00); + cx24116_writereg(state, 0xF6, 0x00); + + /* write the entire firmware as one transaction */ + cx24116_writeregN(state, 0xF7, fw->data, fw->size); + + cx24116_writereg(state, 0xF4, 0x10); + cx24116_writereg(state, 0xF0, 0x00); + cx24116_writereg(state, 0xF8, 0x06); + + /* Firmware CMD 10: VCO config */ + cmd.args[0x00] = CMD_SET_VCO; + cmd.args[0x01] = 0x05; + cmd.args[0x02] = 0xdc; + cmd.args[0x03] = 0xda; + cmd.args[0x04] = 0xae; + cmd.args[0x05] = 0xaa; + cmd.args[0x06] = 0x04; + cmd.args[0x07] = 0x9d; + cmd.args[0x08] = 0xfc; + cmd.args[0x09] = 0x06; + cmd.len= 0x0a; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00); + + /* Firmware CMD 14: Tuner config */ + cmd.args[0x00] = CMD_TUNERINIT; + cmd.args[0x01] = 0x00; + cmd.args[0x02] = 0x00; + cmd.len= 0x03; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + cx24116_writereg(state, 0xe5, 0x00); + + /* Firmware CMD 13: MPEG config */ + cmd.args[0x00] = CMD_MPEGCONFIG; + cmd.args[0x01] = 0x01; + cmd.args[0x02] = 0x75; + cmd.args[0x03] = 0x00; + cmd.args[0x04] = 0x02; + cmd.args[0x05] = 0x00; + cmd.len= 0x06; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* Firmware CMD 35: Get firmware version */ + cmd.args[0x00] = CMD_UPDFWVERS; + cmd.len= 0x02; + for(i=0; i<4; i++) { + cmd.args[0x01] = i; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + vers[i]= cx24116_readreg(state, CX24116_REG_MAILBOX); + } + printk("%s: FW version %i.%i.%i.%i\n", __FUNCTION__, vers[0], vers[1], vers[2], vers[3]); + + return 0; +} + +static int cx24116_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + /* The isl6421 module will override this function in the fops. */ + dprintk("%s() This should never appear if the isl6421 module is loaded correctly\n",__FUNCTION__); + + return -EOPNOTSUPP; +} + +/* + * Registers affected by tuning (?) + * + * 0x2e DVB-S2 + * 0x2f DVB-S2 + * + * 0x70 DVB-S + * + * 0x83 DVB-S2 + * + * 0x97 DVB-S + * 0x98 DVB-S + * 0x99 DVB-S + * + * 0x9c BOTH + * 0x9d BOTH + * 0x9e BOTH + * 0x9f BOTH + * + * 0xa0 BOTH + * + * 0xb1 DVB-S + * 0xb2 DVB-S + * 0xb3 DVB-S + * 0xb7 DVB-S + * 0xb8 DVB-S + * 0xbc DVB-S + * + * 0xd4 DVB-S + * 0xd5 BOTH + * 0xda DVB-S + * + * 0xf5 DVB-S + * 0xfd DVB-S + */ +static void cx24116_dump_registers(struct dvb_frontend* fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + int x, y, reg=0, val; + + for(y=2 /* Ignore command mailbox */; y<16; y++) { + printk("%s: %1x0: ", __FUNCTION__, y); + for(x=0; x<16; x++) { + reg = (y << 4) + x; + val = cx24116_readreg(state, reg); + if(x!=15) + printk("%02x ", val); + else + printk("%02x\n", val); + } + } + printk("%s: -- MARK --\n", __FUNCTION__); +} + +#if 0 +static void cx24116_dump_reg(struct dvb_frontend *fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + + printk("%s: 9c=%02x 9d=%02x 9e=%02x 9f=%02x\n", __FUNCTION__, + cx24116_readreg(state, 0x9c), + cx24116_readreg(state, 0x9d), + cx24116_readreg(state, 0x9e), + cx24116_readreg(state, 0x9f)); +} +#endif + +static int cx24116_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct cx24116_state *state = fe->demodulator_priv; + + int lock = cx24116_readreg(state, CX24116_REG_SSTATUS); + + dprintk("%s: status = 0x%02x\n", __FUNCTION__, lock); + +#if 0 + if(debug) + cx24116_dump_registers(fe); +#endif + + *status = 0; + + if (lock & CX24116_HAS_SIGNAL) + *status |= FE_HAS_SIGNAL; + + if (lock & CX24116_HAS_CARRIER) + *status |= FE_HAS_CARRIER; + + if (lock & CX24116_HAS_VITERBI) + *status |= FE_HAS_VITERBI; + + if (lock & CX24116_HAS_SYNCLOCK) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + + return 0; +} + +#define FE_IS_TUNED (FE_HAS_SIGNAL + FE_HAS_LOCK) +static int cx24116_is_tuned(struct dvb_frontend* fe) +{ + fe_status_t tunerstat; + + cx24116_read_status(fe, &tunerstat); + + return ( (tunerstat & FE_IS_TUNED) == FE_IS_TUNED ); +} + +static int cx24116_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct cx24116_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __FUNCTION__); + + *ber = ( cx24116_readreg(state, CX24116_REG_BER24) << 24 ) | ( cx24116_readreg(state, CX24116_REG_BER16) << 16 ) | + ( cx24116_readreg(state, CX24116_REG_BER8 ) << 8 ) | cx24116_readreg(state, CX24116_REG_BER0 ); + + return 0; +} + +/* TODO Determine function and scale appropriately */ +static int cx24116_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int ret; + u16 sig_reading; + + dprintk("%s()\n", __FUNCTION__); + + /* Firmware CMD 19: Get AGC */ + cmd.args[0x00] = CMD_GETAGC; + cmd.len= 0x01; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + sig_reading = ( cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK ) | + ( cx24116_readreg(state, CX24116_REG_SIGNAL) << 6 ); + + *signal_strength= 0 - sig_reading; + + dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __FUNCTION__, sig_reading, *signal_strength); + + return 0; +} + +/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */ +static int cx24116_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct cx24116_state *state = fe->demodulator_priv; + u8 snr_reading; + static const u32 snr_tab[] = { /* 10 x Table (rounded up) */ + 0x00000,0x0199A,0x03333,0x04ccD,0x06667,0x08000,0x0999A,0x0b333,0x0cccD,0x0e667, + 0x10000,0x1199A,0x13333,0x14ccD,0x16667,0x18000 }; + + dprintk("%s()\n", __FUNCTION__); + + snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY); + + if(snr_reading >= 0xa0 /* 100% */) + *snr = 0xffff; + else + *snr = snr_tab [ ( snr_reading & 0xf0 ) >> 4 ] + + ( snr_tab [ ( snr_reading & 0x0f ) ] >> 4 ); + + dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __FUNCTION__, snr_reading, *snr); + + return 0; +} + +static int cx24116_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct cx24116_state *state = fe->demodulator_priv; + + dprintk("%s()\n", __FUNCTION__); + + *ucblocks = ( cx24116_readreg(state, CX24116_REG_UCB8) << 8 ) | cx24116_readreg(state, CX24116_REG_UCB0); + + return 0; +} + +/* Overwrite the current tuning params, we are about to tune */ +static void cx24116_clone_params(struct dvb_frontend* fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur)); +} + +/* Wait for LNB */ +static int cx24116_wait_for_lnb(struct dvb_frontend* fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + int i; + + dprintk("%s() qstatus = 0x%02x\n", __FUNCTION__, cx24116_readreg(state, CX24116_REG_QSTATUS)); + + /* Wait for up to 300 ms */ + for(i = 0; i < 30 ; i++) { + if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20) + return 0; + msleep(10); + } + + dprintk("%s(): LNB not ready\n", __FUNCTION__); + + return -ETIMEDOUT; /* -EBUSY ? */ +} + +static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct cx24116_cmd cmd; + int ret; + + dprintk("%s(%d)\n", __FUNCTION__, tone); + if ( (tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF) ) { + printk("%s: Invalid, tone=%d\n", __FUNCTION__, tone); + return -EINVAL; + } + + /* Wait for LNB ready */ + ret = cx24116_wait_for_lnb(fe); + if(ret != 0) + return ret; + + /* Min delay time after DiSEqC send */ + msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ + + /* This is always done before the tone is set */ + cmd.args[0x00] = CMD_SET_TONEPRE; + cmd.args[0x01] = 0x00; + cmd.len= 0x02; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* Now we set the tone */ + cmd.args[0x00] = CMD_SET_TONE; + cmd.args[0x01] = 0x00; + cmd.args[0x02] = 0x00; + switch (tone) { + case SEC_TONE_ON: + dprintk("%s: setting tone on\n", __FUNCTION__); + cmd.args[0x03] = 0x01; + break; + case SEC_TONE_OFF: + dprintk("%s: setting tone off\n",__FUNCTION__); + cmd.args[0x03] = 0x00; + break; + } + cmd.len= 0x04; + ret = cx24116_cmd_execute(fe, &cmd); + + /* Min delay time before DiSEqC send */ + msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ + + return ret; +} + +/* Initialise DiSEqC */ +static int cx24116_diseqc_init(struct dvb_frontend* fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int ret; + + /* Firmware CMD 20: LNB/DiSEqC config */ + cmd.args[0x00] = CMD_LNBCONFIG; + cmd.args[0x01] = 0x00; + cmd.args[0x02] = 0x10; + cmd.args[0x03] = 0x00; + cmd.args[0x04] = 0x8f; + cmd.args[0x05] = 0x28; + cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01; + cmd.args[0x07] = 0x01; + cmd.len= 0x08; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* Prepare a DiSEqC command */ + state->dsec_cmd.args[0x00] = CMD_LNBSEND; + + /* DiSEqC burst */ + state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; + + /* Unknown */ + state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; + state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; + state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; /* Continuation flag? */ + + /* DiSEqC message length */ + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; + + /* Command length */ + state->dsec_cmd.len= CX24116_DISEQC_MSGOFS; + + return 0; +} + +/* If toneburst enabled send with derived burst (hack) OR cache message until burst is known in send burst */ +static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *d) +{ + struct cx24116_state *state = fe->demodulator_priv; + int i, ret; + + /* Dump DiSEqC message */ + if (debug) { + printk("cx24116: %s(", __FUNCTION__); + for(i = 0 ; i < d->msg_len ;) { + printk("0x%02x", d->msg[i]); + if(++i < d->msg_len) + printk(", "); + } + printk(") toneburst=%d\n", toneburst); + } + + /* Validate length */ + if(d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS)) + return -EINVAL; + + /* DiSEqC message */ + for (i = 0; i < d->msg_len; i++) + state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i]; + + /* DiSEqC message length */ + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; + + /* Command length */ + state->dsec_cmd.len= CX24116_DISEQC_MSGOFS + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN]; + + /* DiSEqC toneburst */ + if(toneburst == CX24116_DISEQC_MESGCACHE) + /* Message is cached */ + return 0; + + else if(toneburst == CX24116_DISEQC_TONEOFF) + /* Message is sent without burst */ + state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0; + + else if(toneburst == CX24116_DISEQC_TONECACHE) { + /* + * Message is sent with derived else cached burst + * + * WRITE PORT GROUP COMMAND 38 + * + * 0/A/A: E0 10 38 F0..F3 + * 1/B/B: E0 10 38 F4..F7 + * 2/C/A: E0 10 38 F8..FB + * 3/D/B: E0 10 38 FC..FF + * + * datebyte[3]= 8421:8421 + * ABCD:WXYZ + * CLR :SET + * + * WX= PORT SELECT 0..3 (X=TONEBURST) + * Y = VOLTAGE (0=13V, 1=18V) + * Z = BAND (0=LOW, 1=HIGH(22K)) + */ + if(d->msg_len >= 4 && d->msg[2] == 0x38) + state->dsec_cmd.args[CX24116_DISEQC_BURST] = ((d->msg[3] & 4) >> 2); + if(debug) + dprintk("%s burst=%d\n", __FUNCTION__, state->dsec_cmd.args[CX24116_DISEQC_BURST]); + } + + /* Wait for LNB ready */ + ret = cx24116_wait_for_lnb(fe); + if(ret != 0) + return ret; + + /* Wait for voltage/min repeat delay */ + msleep(100); + + /* Command */ + ret = cx24116_cmd_execute(fe, &state->dsec_cmd); + if(ret != 0) + return ret; + + /* + * Wait for send + * + * Eutelsat spec: + * >15ms delay + (XXX determine if FW does this, see set_tone) + * 13.5ms per byte + + * >15ms delay + + * 12.5ms burst + + * >15ms delay (XXX determine if FW does this, see set_tone) + */ + msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60) ); + + return 0; +} + +/* Send DiSEqC burst */ +static int cx24116_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +{ + struct cx24116_state *state = fe->demodulator_priv; + int ret; + + dprintk("%s(%d) toneburst=%d\n",__FUNCTION__, burst, toneburst); + + /* DiSEqC burst */ + if (burst == SEC_MINI_A) + state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; + else if(burst == SEC_MINI_B) + state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B; + else + return -EINVAL; + + /* DiSEqC toneburst */ + if(toneburst != CX24116_DISEQC_MESGCACHE) + /* Burst is cached */ + return 0; + + /* Burst is to be sent with cached message */ + + /* Wait for LNB ready */ + ret = cx24116_wait_for_lnb(fe); + if(ret != 0) + return ret; + + /* Wait for voltage/min repeat delay */ + msleep(100); + + /* Command */ + ret = cx24116_cmd_execute(fe, &state->dsec_cmd); + if(ret != 0) + return ret; + + /* + * Wait for send + * + * Eutelsat spec: + * >15ms delay + (XXX determine if FW does this, see set_tone) + * 13.5ms per byte + + * >15ms delay + + * 12.5ms burst + + * >15ms delay (XXX determine if FW does this, see set_tone) + */ + msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 ); + + return 0; +} + +static void cx24116_release(struct dvb_frontend* fe) +{ + struct cx24116_state* state = fe->demodulator_priv; + dprintk("%s\n",__FUNCTION__); + kfree(state); + unregister_sysctl_table(kernel_table_header); +} + +static struct dvb_frontend_ops cx24116_ops; + +struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, + struct i2c_adapter* i2c) +{ + struct cx24116_state* state = NULL; + int ret; + + dprintk("%s\n",__FUNCTION__); + + kernel_table_header = register_sysctl_table(kernel_table); + if(!kernel_table_header) + goto error1; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL); + if (state == NULL) { + printk("Unable to kmalloc\n"); + goto error2; + } + + /* setup the state */ + memset(state, 0, sizeof(struct cx24116_state)); + + state->config = config; + state->i2c = i2c; + + /* check if the demod is present */ + ret = (cx24116_readreg(state, 0xFF) << 8) | cx24116_readreg(state, 0xFE); + if (ret != 0x0501) { + printk("Invalid probe, probably not a CX24116 device\n"); + goto error3; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &cx24116_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error3: kfree(state); +error2: unregister_sysctl_table(kernel_table_header); +error1: return NULL; +} + +static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int i, ret; + u8 status; + int retune, rolloff_factor, modfec_mask, modfec_val; + + dprintk("%s()\n",__FUNCTION__); + + state->dnxt.frequency = p->frequency; + + if ((ret = cx24116_set_inversion(state, p->inversion)) != 0) + return ret; + + if ((ret = cx24116_set_fec(state, p)) != 0) + return ret; + + if ((ret = cx24116_set_symbolrate(state, p)) != 0) + return ret; + + /* discard the 'current' tuning parameters and prepare to tune */ + cx24116_clone_params(fe); + + if(modfec == 0xfe30) { + /* DVB-S setup */ + retune = 3; /* Try 3 times */ + rolloff_factor = 2; /* DVB-S 0.35 */ + modfec_mask = state->dcur.fec_mask; + modfec_val = state->dcur.fec_val; + dprintk("%s: DVB-S: retune = %d, rolloff_factor = %d\n", + __FUNCTION__, retune, rolloff_factor); + } else { + /* DVB-S2 setup */ + retune = 6; /* Try 3 times with pilot toggle */ + rolloff_factor = rolloff; /* User defined rolloff */ + modfec_mask = (modfec >> 8); /* User defined mod/fec */ + modfec_val = (modfec & 255); + if(pilot) + modfec_val |= 0x40; /* Pilot on initially */ + else + modfec_val &= ~0x40; /* Pilot off initially */ + dprintk("%s: DVB-S2: retune = %d, rolloff_factor = %d, pilot = %d\n", + __FUNCTION__, retune, rolloff_factor, pilot); + } + + dprintk("%s: frequency = %d\n", __FUNCTION__, state->dcur.frequency); + dprintk("%s: symbol_rate = %d\n", __FUNCTION__, state->dcur.symbol_rate); + dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __FUNCTION__, + state->dcur.fec, modfec_mask, modfec_val); + dprintk("%s: Inversion = %d (val = 0x%02x)\n", __FUNCTION__, + state->dcur.inversion, state->dcur.inversion_val); + + /* XXX This is also done in advise/acquire on HVR4000 (not LITE) */ + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + + /* Set/Reset B/W */ + cmd.args[0x00] = CMD_BANDWIDTH; + cmd.args[0x01] = 0x01; + cmd.len= 0x02; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* Prepare a tune request */ + cmd.args[0x00] = CMD_TUNEREQUEST; + + /* Frequency */ + cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16; + cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8; + cmd.args[0x03] = (state->dcur.frequency & 0x0000ff); + + /* Symbol Rate */ + cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; + cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff); + + /* Automatic Inversion */ + cmd.args[0x06] = state->dcur.inversion_val; + + /* Modulation / FEC */ + cmd.args[0x07] = modfec_val; + + cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8; + cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff; + cmd.args[0x0a] = 0x00; + cmd.args[0x0b] = 0x00; + cmd.args[0x0c] = rolloff_factor; + cmd.args[0x0d] = modfec_mask; + cmd.args[0x0e] = 0x06; + cmd.args[0x0f] = 0x00; + cmd.args[0x10] = 0x00; + cmd.args[0x11] = 0xFA; + cmd.args[0x12] = 0x24; + cmd.len= 0x13; + + /* Set/Reset unknown */ + cx24116_writereg(state, 0xF9, 0x00); + cx24116_writereg(state, 0xF3, 0x46); + + do { + /* Reset status register */ + status = cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK; + cx24116_writereg(state, CX24116_REG_SSTATUS, status); + + /* Tune */ + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + /* + * Wait for up to 500 ms before retrying + * + * If we able to tune then generally it occurs within 100ms + * If it takes longer, try a different toneburst setting. + */ + for(i = 0; i < 50 ; i++) { + if( cx24116_is_tuned(fe) ) { + if(debug) { + printk("%s: Tuned\n", __FUNCTION__); + cx24116_dump_registers(fe); + } + goto tuned; + } + msleep(10); + } + + if(debug) { + printk("%s: Not tuned\n", __FUNCTION__); + cx24116_dump_registers(fe); + } + + if(modfec != 0xfe30) { + /* DVB-S2: Toggle pilot bit */ + cmd.args[0x07] ^= 0x40; + } + + } while(--retune); + +tuned: /* Set/Reset B/W */ + cmd.args[0x00] = CMD_BANDWIDTH; + cmd.args[0x01] = 0x00; + cmd.len= 0x02; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; + + return ret; +} + +static int cx24116_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct cx24116_state *state = fe->demodulator_priv; + int ret = 0; + + dprintk("%s()\n",__FUNCTION__); + + p->frequency = state->dcur.frequency; + p->inversion = state->dcur.inversion; + p->u.qpsk.fec_inner = state->dcur.fec; + p->u.qpsk.symbol_rate = state->dcur.symbol_rate; + + return ret; +} + +/* + * Initialise or wake up device + * + * Power config will reset and load initial firmware if required + */ +static int cx24116_initfe(struct dvb_frontend* fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int ret; + + dprintk("%s()\n",__FUNCTION__); + + /* Power on */ + cx24116_writereg(state, 0xe0, 0); + cx24116_writereg(state, 0xe1, 0); + cx24116_writereg(state, 0xea, 0); + + /* Firmware CMD 36: Power config */ + cmd.args[0x00] = CMD_TUNERSLEEP; + cmd.args[0x01] = 0; + cmd.len= 0x02; + ret = cx24116_cmd_execute(fe, &cmd); + if(ret != 0) + return ret; + + return cx24116_diseqc_init(fe); +} + +/* Put device to sleep */ +static int cx24116_sleep(struct dvb_frontend* fe) +{ + struct cx24116_state* state = fe->demodulator_priv; + struct cx24116_cmd cmd; + int ret; + + dprintk("%s()\n",__FUNCTION__); + + /* Firmware CMD 36: Power config */ + cmd.args[0x00] = CMD_TUNERSLEEP; + cmd.args[0x01] = 1; + cmd.len= 0x02; + ret = cx24116_cmd_execute(fe, &cmd); + if(ret != 0) + return ret; + + /* Power off (Shutdown clocks) */ + cx24116_writereg(state, 0xea, 0xff); + cx24116_writereg(state, 0xe1, 1); + cx24116_writereg(state, 0xe0, 1); + + return 0; +} + +static struct dvb_frontend_ops cx24116_ops = { + + .info = { + .name = "Conexant CX24116/CX24118", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1011, /* kHz for QPSK frontends */ + .frequency_tolerance = 5000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = cx24116_release, + + .init = cx24116_initfe, + .sleep = cx24116_sleep, + .set_frontend = cx24116_set_frontend, + .get_frontend = cx24116_get_frontend, + .read_status = cx24116_read_status, + .read_ber = cx24116_read_ber, + .read_signal_strength = cx24116_read_signal_strength, + .read_snr = cx24116_read_snr, + .read_ucblocks = cx24116_read_ucblocks, + .set_tone = cx24116_set_tone, + .set_voltage = cx24116_set_voltage, + .diseqc_send_master_cmd = cx24116_send_diseqc_msg, + .diseqc_send_burst = cx24116_diseqc_send_burst, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +module_param(toneburst, int, 0644); +MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, 2=MESSAGE CACHE (default:1)"); + +MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(cx24116_attach); diff -r 127f67dea087 linux/drivers/media/dvb/frontends/cx24116.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/dvb/frontends/cx24116.h Mon Mar 03 22:32:21 2008 +0000 @@ -0,0 +1,41 @@ +/* + Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2006 Steven Toth + + 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 CX24116_H +#define CX24116_H + +#include + +struct cx24116_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* Need to set device param for start_dma */ + int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); + + /* Need to reset device during firmware loading */ + int (*reset_device)(struct dvb_frontend* fe); +}; + +extern struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, + struct i2c_adapter* i2c); + +#endif /* CX24116_H */ diff -r 127f67dea087 linux/drivers/media/video/cx88/Kconfig --- a/linux/drivers/media/video/cx88/Kconfig Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/video/cx88/Kconfig Mon Mar 03 22:32:21 2008 +0000 @@ -57,6 +57,7 @@ config VIDEO_CX88_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE + select DVB_CX24116 if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. diff -r 127f67dea087 linux/drivers/media/video/cx88/cx88-cards.c --- a/linux/drivers/media/video/cx88/cx88-cards.c Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/video/cx88/cx88-cards.c Mon Mar 03 22:32:21 2008 +0000 @@ -1386,6 +1386,9 @@ static const struct cx88_board cx88_boar }}, }, [CX88_BOARD_HAUPPAUGE_HVR1300] = { + /* + * gpio0 as reported by Mike Crash on linux-dvb ML. + */ .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder", .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, .radio_type = UNSET, @@ -1396,17 +1399,17 @@ static const struct cx88_board cx88_boar .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, - .gpio0 = 0xe780, + .gpio0 = 0xef88, /* Formerly e780 */ .audioroute = 1, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - .gpio0 = 0xe780, + .gpio0 = 0xef88, /* Formerly e780 */ .audioroute = 2, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, - .gpio0 = 0xe780, + .gpio0 = 0xef88, /* Formerly e780 */ .audioroute = 2, }}, /* fixme: Add radio support */ @@ -1431,6 +1434,71 @@ static const struct cx88_board cx88_boar .vmux = 2, .gpio0 = 0x07fa, }}, + }, + [CX88_BOARD_HAUPPAUGE_HVR4000] = { + .name = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid", + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + /* + * GPIO0 as found in WINTV2000 (dmb) + * + * Analogue DVB-S/S2 DVB-T + * Antenna 0xc4bf 0xc4bb + * Composite 0xc4bf 0xc4bb + * S-Video 0xc4bf 0xc4bb + * Composite1 0xc4ff 0xc4fb + * S-Video1 0xc4ff 0xc4fb + * + * BIT VALUE FUNCTION GP{x}_IO + * 0 1 I:? + * 1 1 I:? + * 2 1 O:DVB-T DEMOD ENABLE LOW/ANALOG DEMOD ENABLE HIGH + * 3 1 I:? + * 4 1 I:? + * 5 1 I:? + * 6 0 O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION + * 7 1 O:DVB-T DEMOD RESET LOW + * + * BIT VALUE FUNCTION GP{x}_OE + * 8 0 I + * 9 0 I + * a 1 O + * b 0 I + * c 0 I + * d 0 I + * e 1 O + * f 1 O + */ + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0xc4bf, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0xc4bf, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0xc4bf, + }}, + /* fixme: Add radio support */ + .mpeg = CX88_MPEG_DVB, + }, + [CX88_BOARD_HAUPPAUGE_HVR4000LITE] = { + .name = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + }}, + .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PINNACLE_PCTV_HD_800i] = { .name = "Pinnacle PCTV HD 800i", @@ -1935,6 +2003,26 @@ static const struct cx88_subid cx88_subi .subdevice = 0x0390, .card = CX88_BOARD_ADSTECH_PTV_390, },{ + .subvendor = 0x0070, + .subdevice = 0x6900, + .card = CX88_BOARD_HAUPPAUGE_HVR4000, + },{ + .subvendor = 0x0070, + .subdevice = 0x6904, + .card = CX88_BOARD_HAUPPAUGE_HVR4000, + },{ + .subvendor = 0x0070, + .subdevice = 0x6902, + .card = CX88_BOARD_HAUPPAUGE_HVR4000, + },{ + .subvendor = 0x0070, + .subdevice = 0x6905, + .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE, + },{ + .subvendor = 0x0070, + .subdevice = 0x6906, + .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE, + },{ .subvendor = 0x11bd, .subdevice = 0x0051, .card = CX88_BOARD_PINNACLE_PCTV_HD_800i, @@ -2010,6 +2098,11 @@ static void hauppauge_eeprom(struct cx88 case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */ case 28552: /* WinTV-PVR 'Roslyn' (No IR) */ case 34519: /* WinTV-PCI-FM */ + case 69009: /* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */ + case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */ + case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */ + case 69559: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */ + case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */ case 90002: /* Nova-T-PCI (9002) */ case 92001: /* Nova-S-Plus (Video and IR) */ case 92002: /* Nova-S-Plus (Video and IR) */ @@ -2298,12 +2391,24 @@ static void cx88_card_setup_pre_i2c(stru case CX88_BOARD_HAUPPAUGE_HVR1300: /* Bring the 702 demod up before i2c scanning/attach or devices are hidden */ /* We leave here with the 702 on the bus */ - cx_write(MO_GP0_IO, 0x0000e780); + + /* + * "Also reset the IR receiver on GPIO[3]" + * + * Reported by Mike Crash on linux-dvb ML. + * + */ + cx_write(MO_GP0_IO, 0x0000ef88); udelay(1000); - cx_clear(MO_GP0_IO, 0x00000080); + cx_clear(MO_GP0_IO, 0x00000088); udelay(50); - cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */ + cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */ udelay(1000); + break; + case CX88_BOARD_HAUPPAUGE_HVR3000: /* ? */ + case CX88_BOARD_HAUPPAUGE_HVR4000: + /* Init GPIO for DVB-S/S2/Analog */ + cx_write(MO_GP0_IO, core->board.input[0].gpio0); break; } } @@ -2338,6 +2443,8 @@ static void cx88_card_setup(struct cx88_ case CX88_BOARD_HAUPPAUGE_HVR1100LP: case CX88_BOARD_HAUPPAUGE_HVR3000: case CX88_BOARD_HAUPPAUGE_HVR1300: + case CX88_BOARD_HAUPPAUGE_HVR4000: + case CX88_BOARD_HAUPPAUGE_HVR4000LITE: if (0 == core->i2c_rc) hauppauge_eeprom(core,eeprom); break; diff -r 127f67dea087 linux/drivers/media/video/cx88/cx88-dvb.c --- a/linux/drivers/media/video/cx88/cx88-dvb.c Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/video/cx88/cx88-dvb.c Mon Mar 03 22:32:21 2008 +0000 @@ -48,6 +48,7 @@ #include "isl6421.h" #include "tuner-xc2028.h" #include "tuner-xc2028-types.h" +#include "cx24116.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe "); @@ -60,6 +61,10 @@ MODULE_PARM_DESC(debug,"enable debug mes #define dprintk(level,fmt, arg...) if (debug >= level) \ printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg) + +static unsigned int frontend = 0; +module_param(frontend, int, 0644); +MODULE_PARM_DESC(frontend,"frontend to load [dvb]"); /* ------------------------------------------------------------------ */ @@ -372,6 +377,28 @@ static int geniatech_dvbs_set_voltage(st return core->prev_set_voltage(fe, voltage); return 0; } + +static int cx24116_reset_device(struct dvb_frontend* fe) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct cx88_core *core = dev->core; + + /* Put the cx24116 into reset */ + cx_write(MO_SRST_IO, 0); + msleep(10); + + /* Take the cx24116 out of reset */ + cx_write(MO_SRST_IO, 1); + msleep(10); + + return 0; +} + +static struct cx24116_config hauppauge_hvr4000_config = { + .demod_address = 0x05, + .set_ts_params = cx24123_set_ts_param, + .reset_device = cx24116_reset_device, +}; static int cx88_pci_nano_callback(void *ptr, int command, int arg) { @@ -519,13 +546,43 @@ static int dvb_register(struct cx8802_de case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: case CX88_BOARD_HAUPPAUGE_HVR1300: + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, DVB_PLL_FMD1216ME); + } + break; case CX88_BOARD_HAUPPAUGE_HVR3000: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, DVB_PLL_FMD1216ME); + if(frontend == 0) { + /* DVB-S Enabled */ + dev->dvb.frontend = dvb_attach(cx24123_attach, + &hauppauge_novas_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend) { + /* + * ISL6421_DCL turns off dynamic current protection + * and enforces static protection. + * + * This is a requirement for 4x1 DiSEqC switches + * and/or Rotors. + * + * This is also how the Windows driver configures + * the LNB voltage controller. (dmb). + */ + dvb_attach(isl6421_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00); + } + } else { + /* DVB-T Enabled */ + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, DVB_PLL_FMD1216ME); + } } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: @@ -734,8 +791,18 @@ static int dvb_register(struct cx8802_de &hauppauge_novas_config, &dev->core->i2c_adap); if (dev->dvb.frontend) { + /* + * ISL6421_DCL turns off dynamic current protection + * and enforces static protection. + * + * This is a requirement for 4x1 DiSEqC switches + * and/or Rotors. + * + * This is also how the Windows driver configures + * the LNB voltage controller. (dmb). + */ dvb_attach(isl6421_attach, dev->dvb.frontend, - &dev->core->i2c_adap, 0x08, 0x00, 0x00); + &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00); } break; case CX88_BOARD_KWORLD_DVBS_100: @@ -754,6 +821,56 @@ static int dvb_register(struct cx8802_de if (dev->dvb.frontend) { dev->core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage; dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; + } + break; + case CX88_BOARD_HAUPPAUGE_HVR4000: + if(frontend == 0) { + /* DVB-S/S2 Enabled */ + dev->dvb.frontend = dvb_attach(cx24116_attach, + &hauppauge_hvr4000_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend) { + /* + * ISL6421_DCL turns off dynamic current protection + * and enforces static protection. + * + * This is a requirement for 4x1 DiSEqC switches + * and/or Rotors. + * + * This is also how the Windows driver configures + * the LNB voltage controller. (dmb). + */ + dvb_attach(isl6421_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00); + } + } else { + /* DVB-T Enabled */ + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, DVB_PLL_FMD1216ME); + } + } + break; + case CX88_BOARD_HAUPPAUGE_HVR4000LITE: + dev->dvb.frontend = dvb_attach(cx24116_attach, + &hauppauge_hvr4000_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend) { + /* + * ISL6421_DCL turns off dynamic current protection + * and enforces static protection. + * + * This is a requirement for 4x1 DiSEqC switches + * and/or Rotors. + * + * This is also how the Windows driver configures + * the LNB voltage controller. (dmb). + */ + dvb_attach(isl6421_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00); } break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: @@ -840,6 +957,7 @@ static int cx8802_dvb_advise_acquire(str { struct cx88_core *core = drv->core; int err = 0; + dprintk( 1, "%s\n", __FUNCTION__); switch (core->boardnr) { @@ -852,6 +970,35 @@ static int cx8802_dvb_advise_acquire(str cx_clear(MO_GP0_IO, 0x00000004); udelay(1000); break; + case CX88_BOARD_HAUPPAUGE_HVR3000: /* ? */ + case CX88_BOARD_HAUPPAUGE_HVR4000: + if(frontend == 0) { + /* DVB-S/S2 Enabled */ + + /* Toggle reset on cx22702 leaving i2c active */ + cx_write(MO_GP0_IO, (core->board.input[0].gpio0 & 0x0000ff00) | 0x00000080); + udelay(1000); + cx_clear(MO_GP0_IO, 0x00000080); + udelay(50); + cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset */ + cx_set(MO_GP0_IO, 0x00000004); /* tri-state the cx22702 pins */ + udelay(1000); + + cx_write(MO_SRST_IO, 1); /* Take the cx24116/cx24123 out of reset */ + drv->core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */ + } else { + /* DVB-T Enabled */ + + /* Put the cx24116/cx24123 into reset */ + cx_write(MO_SRST_IO, 0); + + /* cx22702 out of reset and enable it */ + cx_set(MO_GP0_IO, 0x00000080); + cx_clear(MO_GP0_IO, 0x00000004); + drv->core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */ + udelay(1000); + } + break; default: err = -ENODEV; } @@ -871,6 +1018,9 @@ static int cx8802_dvb_advise_release(str #if 0 cx_set(MO_GP0_IO, 0x00000004); #endif + break; + case CX88_BOARD_HAUPPAUGE_HVR3000: + case CX88_BOARD_HAUPPAUGE_HVR4000: break; default: err = -ENODEV; diff -r 127f67dea087 linux/drivers/media/video/cx88/cx88-i2c.c --- a/linux/drivers/media/video/cx88/cx88-i2c.c Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/video/cx88/cx88-i2c.c Mon Mar 03 22:32:21 2008 +0000 @@ -249,7 +249,32 @@ int cx88_i2c_init(struct cx88_core *core core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap); if (0 == core->i2c_rc) { + static u8 tuner_data[] = + { 0x0b, 0xdc, 0x86, 0x52 }; + static struct i2c_msg tuner_msg = + { .flags = 0, .addr = 0xc2 >> 1, .buf = tuner_data, .len = 4 }; + dprintk(1, "i2c register ok\n"); + + switch( core->boardnr ) + { + case CX88_BOARD_HAUPPAUGE_HVR1300: + case CX88_BOARD_HAUPPAUGE_HVR3000: /* ? */ + case CX88_BOARD_HAUPPAUGE_HVR4000: + /* + * The tda9887 2-0043: tda988[5/6/7] found @ 0x43 (tuner') + * is disabled after a cold boot on the HVR devices. + * + * We enable it here prior to probing for successful detection. + * + * This has been tested on the HVR1300 and HVR4000. (dmb). + */ + printk("%s: i2c init: enabling analog demod on HVR1300/3000/4000 tuner\n", + core->name); + i2c_transfer(core->i2c_client.adapter, &tuner_msg, 1); + break; + } + if (i2c_scan) do_i2c_scan(core->name,&core->i2c_client); } else diff -r 127f67dea087 linux/drivers/media/video/cx88/cx88-input.c --- a/linux/drivers/media/video/cx88/cx88-input.c Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/video/cx88/cx88-input.c Mon Mar 03 22:32:21 2008 +0000 @@ -237,6 +237,8 @@ int cx88_ir_init(struct cx88_core *core, case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR3000: + case CX88_BOARD_HAUPPAUGE_HVR4000: + case CX88_BOARD_HAUPPAUGE_HVR4000LITE: ir_codes = ir_codes_hauppauge_new; ir_type = IR_TYPE_RC5; ir->sampling = 1; @@ -402,7 +404,7 @@ void cx88_ir_irq(struct cx88_core *core) { struct cx88_IR *ir = core->ir; u32 samples, ircode; - int i; + int i, start, range, toggle, dev, code; if (NULL == ir) return; @@ -471,12 +473,32 @@ void cx88_ir_irq(struct cx88_core *core) case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR3000: + case CX88_BOARD_HAUPPAUGE_HVR4000: + case CX88_BOARD_HAUPPAUGE_HVR4000LITE: case CX88_BOARD_PINNACLE_PCTV_HD_800i: ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); ir_dprintk("biphase decoded: %x\n", ircode); - if ((ircode & 0xfffff000) != 0x3000) - break; - ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f, ircode); + /* + * RC5 has an extension bit which adds a new range + * of available codes, this is detected here. Also + * hauppauge remotes (black/silver) always use + * specific device ids. If we do not filter the + * device ids then messages destined for devices + * such as TVs (id=0) will get through. (dmb). + */ + /* split rc5 data block ... */ + start = (ircode & 0x2000) >> 13; + range = (ircode & 0x1000) >> 12; + toggle= (ircode & 0x0800) >> 11; + dev = (ircode & 0x07c0) >> 6; + code = (ircode & 0x003f) | ((range << 6) ^ 0x0040); + if( start != 1) + /* no key pressed */ + break; + if ( dev != 0x1e && dev != 0x1f ) + /* not a hauppauge remote */ + break; + ir_input_keydown(ir->input, &ir->ir, code, ircode); ir->release = jiffies + msecs_to_jiffies(120); break; } diff -r 127f67dea087 linux/drivers/media/video/cx88/cx88.h --- a/linux/drivers/media/video/cx88/cx88.h Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/video/cx88/cx88.h Mon Mar 03 22:32:21 2008 +0000 @@ -220,6 +220,8 @@ extern struct sram_channel cx88_sram_cha #define CX88_BOARD_POWERCOLOR_REAL_ANGEL 62 #define CX88_BOARD_GENIATECH_X8000_MT 63 #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64 +#define CX88_BOARD_HAUPPAUGE_HVR4000 65 +#define CX88_BOARD_HAUPPAUGE_HVR4000LITE 66 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff -r 127f67dea087 linux/drivers/media/video/ir-kbd-i2c.c --- a/linux/drivers/media/video/ir-kbd-i2c.c Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/video/ir-kbd-i2c.c Mon Mar 03 22:32:21 2008 +0000 @@ -67,7 +67,7 @@ static int get_key_haup_common(struct IR int size, int offset) { unsigned char buf[6]; - int start, range, toggle, dev, code; + int start, range, toggle, dev, code, ircode; /* poll IR chip */ if (size != i2c_master_recv(&ir->c,buf,size)) @@ -88,6 +88,28 @@ static int get_key_haup_common(struct IR /* no key pressed */ return 0; + /* + * Hauppauge remotes (black/silver) always use + * specific device ids. If we do not filter the + * device ids then messages destined for devices + * such as TVs (id=0) will get through. + * + * We also filter out invalid key presses which + * produce annoying debug log entries. + * + * These changes have been tested on both the PVR-350 + * and HVR-1300. (dmb). + */ + + ircode= (start << 12) | (toggle << 11) | (dev << 6) | code; + if ((ircode & 0x1fff)==0x1fff) + /* invalid key press */ + return 0; + + if (dev!=0x1e && dev!=0x1f) + /* not a hauppauge remote */ + return 0; + if (!range) code += 64; @@ -96,7 +118,7 @@ static int get_key_haup_common(struct IR /* return key */ *ir_key = code; - *ir_raw = (start << 12) | (toggle << 11) | (dev << 6) | code; + *ir_raw = ircode; return 1; } diff -r 127f67dea087 linux/drivers/media/video/tveeprom.c --- a/linux/drivers/media/video/tveeprom.c Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/drivers/media/video/tveeprom.c Mon Mar 03 22:32:21 2008 +0000 @@ -246,7 +246,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "TCL M2523_3DBH_E"}, { TUNER_ABSENT, "TCL M2523_3DIH_E"}, { TUNER_ABSENT, "TCL MFPE05_2_U"}, - { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216MEX"}, + { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216MEX"}, { TUNER_ABSENT, "Philips FRH2036B"}, { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, { TUNER_ABSENT, "MaxLinear MXL5005"}, diff -r 127f67dea087 linux/sound/pci/bt87x.c --- a/linux/sound/pci/bt87x.c Tue Feb 26 20:43:56 2008 +0000 +++ b/linux/sound/pci/bt87x.c Mon Mar 03 22:32:21 2008 +0000 @@ -28,9 +28,7 @@ #include #include #include -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24) #include -#endif #include #include #include