Dear Darron, I hope you are fine, someone on linux-dvb ("gimli") wrote a patch against http://jusst.de/hg/multiproto which add HVR-4000 support to it. With this patch I am able to follow http://www.vdr-wiki.de/wiki/index.php/OpenSuSE_DVB-S2_-_Step_by_Step_Installationsanleitung_%28Achtung_Beta%29 and have the HVR-4000 card working with my DVB-S2's VDR :-) Unfortunately, as with the repo from Steven Stoth and with some of your patches against it, diseqc don't work. Could you give a look at it ? Thank you very much :-) I hope it's fine to put the patch here again in case you missed it. -- Grégoire FAVRE http://gregoire.favre.googlepages.com http://www.gnupg.org http://picasaweb.google.com/Gregoire.Favre
diff -uNr multiproto/linux/drivers/media/dvb/frontends/cx24116.c multiproto-test-hvr400/linux/drivers/media/dvb/frontends/cx24116.c --- multiproto/linux/drivers/media/dvb/frontends/cx24116.c 1970-01-01 01:00:00.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/dvb/frontends/cx24116.c 2007-11-05 22:55:49.000000000 +0100 @@ -0,0 +1,1213 @@ +/* + Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2006 Steven Toth <stoth@xxxxxxxxxxxxx> + + 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. +*/ + +/* + * Updates by Darron Broad 2007. + * + * March + * Fixed some bugs. + * Added diseqc support. + * Added corrected signal strength support. + * + * August + * Sync with legacy version. + * Some clean ups. + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/firmware.h> + +#include "dvb_frontend.h" +#include "cx24116.h" + +/* + * Fetch firmware 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 + +/* registers (TO BE COMPLETED) */ +#define CX24116_REG_SIGNAL (0xd5) + +/* arg buffer size */ +#define CX24116_ARGLEN (0x1e) + +/* 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) + +static int debug = 0; +#define dprintk(args...) \ + do { \ + if (debug) printk ("cx24116: " args); \ + } while (0) + +enum cmds +{ + CMD_INIT_CMD10 = 0x10, + CMD_TUNEREQUEST = 0x11, + CMD_INIT_CMD13 = 0x13, + CMD_INIT_CMD14 = 0x14, + CMD_SEND_DISEQC = 0x21, + CMD_SET_TONEPRE = 0x22, + CMD_SET_TONE = 0x23, +}; + +/* 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 dvbfe_fec fec; + + enum dvbfe_delsys delivery; + + /* 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; + u8 burst; +}; + +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; +} + +static int cx24116_get_inversion(struct cx24116_state* state, fe_spectral_inversion_t *inversion) +{ + dprintk("%s()\n", __FUNCTION__); + *inversion = state->dcur.inversion; + return 0; +} + +/* A table of modulation, fec and configuration bytes for the demod. + * Not all S2 mmodulation schemes are support and not all rates with + * a scheme are support. Especially, no auto detect when in S2 mode. + */ +struct cx24116_modfec { + enum dvbfe_delsys delsys; + enum dvbfe_modulation modulation; + enum dvbfe_fec fec; + u8 mask; /* In DVBS mode this is used to autodetect */ + u8 val; /* Passed to the firmware to indicate mode selection */ +} CX24116_MODFEC_MODES[] = { + /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_NONE, 0xfe, 0x30 }, + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_1_2, 0x02, 0x2e }, + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_2_3, 0x04, 0x2f }, + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_3_4, 0x08, 0x30 }, + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_4_5, 0xfe, 0x30 }, + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_5_6, 0x20, 0x31 }, + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_6_7, 0xfe, 0x30 }, + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_7_8, 0x80, 0x32 }, + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_8_9, 0xfe, 0x30 }, + { DVBFE_DELSYS_DVBS, DVBFE_MOD_QPSK, DVBFE_FEC_AUTO, 0xfe, 0x30 }, + /* NBC-QPSK */ +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_NONE, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_1_4, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_1_3, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_2_5, 0x00, 0x00 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_1_2, 0x00, 0x04 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_3_5, 0x00, 0x05 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_2_3, 0x00, 0x06 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_3_4, 0x00, 0x07 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_4_5, 0x00, 0x08 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_5_6, 0x00, 0x09 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_6_7, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_7_8, 0x00, 0x00 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_8_9, 0x00, 0x0a }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_9_10, 0x00, 0x0b }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_QPSK, DVBFE_FEC_AUTO, 0x00, 0x00 }, + /* 8PSK */ +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_NONE, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_1_4, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_1_3, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_2_5, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_1_2, 0x00, 0x00 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_3_5, 0x00, 0x0c }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_2_3, 0x00, 0x0d }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_3_4, 0x00, 0x0e }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_4_5, 0x00, 0x00 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_5_6, 0x00, 0x0f }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_6_7, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_7_8, 0x00, 0x00 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_8_9, 0x00, 0x10 }, + { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_9_10, 0x00, 0x11 }, +// { DVBFE_DELSYS_DVBS2, DVBFE_MOD_8PSK, DVBFE_FEC_AUTO, 0x00, 0x00 }, +}; + +static int cx24116_lookup_fecmod(struct cx24116_state* state, + enum dvbfe_delsys d, enum dvbfe_modulation m, enum dvbfe_fec f) +{ + int i, ret = -EOPNOTSUPP; + + for(i=0 ; i < sizeof(CX24116_MODFEC_MODES) / sizeof(struct cx24116_modfec) ; i++) + { + if( (d == CX24116_MODFEC_MODES[i].delsys) && + (m == CX24116_MODFEC_MODES[i].modulation) && + (f == CX24116_MODFEC_MODES[i].fec) ) + { + ret = i; + break; + } + } + + return ret; +} + +static int cx24116_set_fec(struct cx24116_state* state, struct dvbfe_params *p) +{ + int ret = 0; + dprintk("%s()\n", __FUNCTION__); + + switch(p->delivery) + { + case DVBFE_DELSYS_DVBS: + ret = cx24116_lookup_fecmod(state, + p->delivery, p->delsys.dvbs.modulation, p->delsys.dvbs.fec); + state->dnxt.fec = p->delsys.dvbs.fec; + break; + case DVBFE_DELSYS_DVBS2: + ret = cx24116_lookup_fecmod(state, + p->delivery, p->delsys.dvbs2.modulation, p->delsys.dvbs2.fec); + state->dnxt.fec = p->delsys.dvbs2.fec; + break; + default: + ret = -EOPNOTSUPP; + } + + if(ret < 0) + return ret; + + state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val; + state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask; + dprintk("%s() fec_val/mask = 0x%02x/0x%02x\n", __FUNCTION__, + state->dnxt.fec_val, state->dnxt.fec_mask); + + return 0; +} + +static int cx24116_get_fec(struct cx24116_state* state, + enum dvbfe_fec *fec) +{ + dprintk("%s()\n", __FUNCTION__); + *fec = state->dcur.fec; + return 0; +} + +static int cx24116_set_symbolrate(struct cx24116_state* state, struct dvbfe_params *p) +{ + int ret = 0; + + dprintk("%s()\n", __FUNCTION__); + + switch(p->delivery) + { + case DVBFE_DELSYS_DVBS: + state->dnxt.symbol_rate = p->delsys.dvbs.symbol_rate; + break; + case DVBFE_DELSYS_DVBS2: + state->dnxt.symbol_rate = p->delsys.dvbs2.symbol_rate; + break; + default: + ret = -EOPNOTSUPP; + } + + dprintk("%s() symbol_rate = %d\n", __FUNCTION__, state->dnxt.symbol_rate); + + /* check if symbol rate is within limits */ + if ((state->dnxt.symbol_rate > state->frontend.ops.info.symbol_rate_max) || + (state->dnxt.symbol_rate < state->frontend.ops.info.symbol_rate_min)) + ret = -EOPNOTSUPP; + + 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, 0x20) > 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 = 0; i < cmd->len ; 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, 0x1f, 0x01); + while( cx24116_readreg(state, 0x1f) ) + { + 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 ret; + + 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 */ + cx24116_writereg(state, 0xF1, 0x08); + cx24116_writereg(state, 0xF2, cx24116_readreg(state, 0xF2) | 0x03); + cx24116_writereg(state, 0xF3, 0x46); + cx24116_writereg(state, 0xF9, 0x00); + + 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: Chip config? */ + cmd.args[0x00] = CMD_INIT_CMD10; + 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, 0x9d, 0x00); + + /* Firmware CMD 14: Unknown */ + cmd.args[0x00] = CMD_INIT_CMD14; + 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: Unknown - Firmware config? */ + cmd.args[0x00] = CMD_INIT_CMD13; + 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; + + 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; +} + +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, 0x9d); + + dprintk("%s: status = 0x%02x\n", __FUNCTION__, lock); + + *status = 0; + + if (lock & 0x01) + *status |= FE_HAS_SIGNAL; + if (lock & 0x02) + *status |= FE_HAS_CARRIER; + if (lock & 0x04) + *status |= FE_HAS_VITERBI; + if (lock & 0x08) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + + return 0; +} + +/* TODO: Not clear how we do this */ +static int cx24116_read_ber(struct dvb_frontend* fe, u32* ber) +{ + //struct cx24116_state *state = fe->demodulator_priv; + dprintk("%s()\n", __FUNCTION__); + *ber = 0; + + return 0; +} + +/* Signal strength (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */ +static int cx24116_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct cx24116_state *state = fe->demodulator_priv; + u8 strength_reg; + static const u32 strength_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__); + + strength_reg = cx24116_readreg(state, CX24116_REG_SIGNAL); + + if(strength_reg < 0xa0) + *signal_strength = strength_tab [ ( strength_reg & 0xf0 ) >> 4 ] + + ( strength_tab [ ( strength_reg & 0x0f ) ] >> 4 ); + else + *signal_strength = 0xffff; + + dprintk("%s: Signal strength (raw / cooked) = (0x%02x / 0x%04x)\n", + __FUNCTION__,strength_reg,*signal_strength); + + return 0; +} + +/* TODO: Not clear how we do this */ +static int cx24116_read_snr(struct dvb_frontend* fe, u16* snr) +{ + //struct cx24116_state *state = fe->demodulator_priv; + dprintk("%s()\n", __FUNCTION__); + *snr = 0; + + return 0; +} + +/* TODO: Not clear how we do this */ +static int cx24116_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + //struct cx24116_state *state = fe->demodulator_priv; + dprintk("%s()\n", __FUNCTION__); + *ucblocks = 0; + + 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)); +} + +/* Why isn't this a generic frontend core function? */ +static enum dvbfe_fec cx24116_convert_oldfec_to_new(enum fe_code_rate c) +{ + enum dvbfe_fec fec = DVBFE_FEC_NONE; + + switch(c) + { + case FEC_NONE: fec = DVBFE_FEC_NONE; break; + case FEC_1_2 : fec = DVBFE_FEC_1_2 ; break; + case FEC_2_3 : fec = DVBFE_FEC_2_3 ; break; + case FEC_3_4 : fec = DVBFE_FEC_3_4 ; break; + case FEC_4_5 : fec = DVBFE_FEC_4_5 ; break; + case FEC_5_6 : fec = DVBFE_FEC_5_6 ; break; + case FEC_6_7 : fec = DVBFE_FEC_6_7 ; break; + case FEC_7_8 : fec = DVBFE_FEC_7_8 ; break; + case FEC_8_9 : fec = DVBFE_FEC_8_9 ; break; + case FEC_AUTO: fec = DVBFE_FEC_AUTO; break; + } + + return fec; +} + +/* Why isn't this a generic frontend core function? */ +static enum dvbfe_fec cx24116_convert_newfec_to_old(enum dvbfe_fec c) +{ + fe_code_rate_t fec; + + switch(c) + { + case DVBFE_FEC_NONE: fec = FEC_NONE; break; + case DVBFE_FEC_1_2 : fec = FEC_1_2 ; break; + case DVBFE_FEC_2_3 : fec = FEC_2_3 ; break; + case DVBFE_FEC_3_4 : fec = FEC_3_4 ; break; + case DVBFE_FEC_4_5 : fec = FEC_4_5 ; break; + case DVBFE_FEC_5_6 : fec = FEC_5_6 ; break; + case DVBFE_FEC_6_7 : fec = FEC_6_7 ; break; + case DVBFE_FEC_7_8 : fec = FEC_7_8 ; break; + case DVBFE_FEC_8_9 : fec = FEC_8_9 ; break; + case DVBFE_FEC_AUTO: fec = FEC_AUTO; break; + default: fec = FEC_NONE; break; + } + + return fec; +} + +/* Why isn't this a generic frontend core function? */ +static int cx24116_create_new_qpsk_feparams(struct dvb_frontend* fe, + struct dvb_frontend_parameters *pFrom, + struct dvbfe_params *pTo) +{ + int ret = 0; + + dprintk("%s\n", __FUNCTION__); + + memset(pTo, 0, sizeof(struct dvbfe_params)); + + pTo->frequency = pFrom->frequency; + pTo->inversion = pFrom->inversion; + pTo->delivery = DVBFE_DELSYS_DVBS; + pTo->delsys.dvbs.symbol_rate = pFrom->u.qpsk.symbol_rate; + pTo->delsys.dvbs.modulation = DVBFE_MOD_QPSK; + pTo->delsys.dvbs.fec = cx24116_convert_oldfec_to_new(pFrom->u.qpsk.fec_inner); + + /* I guess we could do more validation on the old fe params and return error */ + return ret; +} + +/* Why isn't this a generic frontend core function? */ +static int cx24116_create_old_qpsk_feparams(struct dvb_frontend* fe, + struct dvbfe_params *pFrom, + struct dvb_frontend_parameters *pTo) +{ + int ret = 0; + + dprintk("%s\n", __FUNCTION__); + + memset(pTo, 0, sizeof(struct dvb_frontend_parameters)); + + pTo->frequency = pFrom->frequency; + pTo->inversion = pFrom->inversion; + + switch(pFrom->delivery) + { + case DVBFE_DELSYS_DVBS: + pTo->u.qpsk.fec_inner = cx24116_convert_newfec_to_old(pFrom->delsys.dvbs.fec); + pTo->u.qpsk.symbol_rate = pFrom->delsys.dvbs.symbol_rate; + break; + default: + ret = -1; + } + + /* I guess we could do more validation on the old fe params and return error */ + return ret; +} + +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; + } + + /* 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; + + return cx24116_cmd_execute(fe, &cmd); +} + +/* Initialise DiSEqC */ +static int cx24116_diseqc_init(struct dvb_frontend* fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + + /* Default DiSEqC burst state */ + state->burst = CX24116_DISEQC_MINI_A; + + return 0; +} + +/* Send DiSEqC message with derived burst (hack) || previous burst */ +static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *d) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + 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(")\n"); + } + + if(d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS)) + return -EINVAL; + + cmd.args[0x00] = CMD_SEND_DISEQC; + cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; + cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; + cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; + + /* DiSEqC message */ + for (i = 0; i < d->msg_len; i++) + cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i]; + + /* Hack: Derive burst from command else use previous burst */ + if(d->msg_len >= 4 && d->msg[2] == 0x38) + cmd.args[CX24116_DISEQC_BURST] = (d->msg[3] >> 2) & 1; + else + cmd.args[CX24116_DISEQC_BURST] = state->burst; + + cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; + cmd.len = CX24116_DISEQC_MSGOFS + d->msg_len; + + ret = cx24116_cmd_execute(fe, &cmd); + + /* Firmware command duration is unknown, so guess... + * + * Eutelsat spec: + * >15ms delay + + * 13.5ms per byte + + * >15ms delay + + * 12.5ms burst + + * >15ms delay + */ + if(ret == 0) + msleep( (cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 ); + + return ret; +} + +/* 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; + struct cx24116_cmd cmd; + int ret; + + dprintk("%s(%d)\n",__FUNCTION__,(int)burst); + + cmd.args[0x00] = CMD_SEND_DISEQC; + cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; + cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; + cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; + + if (burst == SEC_MINI_A) + cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; + else if(burst == SEC_MINI_B) + cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B; + else + return -EINVAL; + + /* Cache as previous burst state */ + state->burst= cmd.args[CX24116_DISEQC_BURST]; + + cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; + cmd.len= CX24116_DISEQC_MSGOFS; + + ret= cx24116_cmd_execute(fe, &cmd); + + /* Firmware command duration is unknown, so guess... */ + if(ret == 0) + msleep(60); + + return ret; +} + +static void cx24116_release(struct dvb_frontend* fe) +{ + struct cx24116_state* state = fe->demodulator_priv; + dprintk("%s\n",__FUNCTION__); + kfree(state); +} + +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__); + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL); + if (state == NULL) { + printk("Unable to kmalloc\n"); + goto error; + } + + /* 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 error; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &cx24116_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + + return NULL; +} + +static struct dvbfe_info dvbs_info = { + .name = "Conexant CX24116 DVB-S", + .delivery = DVBFE_DELSYS_DVBS, + .delsys = { + .dvbs.modulation = DVBFE_MOD_QPSK, + .dvbs.fec = DVBFE_FEC_1_2 | DVBFE_FEC_2_3 | + DVBFE_FEC_3_4 | DVBFE_FEC_4_5 | + DVBFE_FEC_5_6 | DVBFE_FEC_6_7 | + DVBFE_FEC_7_8 | DVBFE_FEC_AUTO + }, + + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 1011, + .frequency_tolerance = 5000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000 +}; + +static const struct dvbfe_info dvbs2_info = { + .name = "Conexant CX24116 DVB-S2", + .delivery = DVBFE_DELSYS_DVBS2, + .delsys = { + .dvbs2.modulation = DVBFE_MOD_QPSK | DVBFE_MOD_8PSK, + + /* TODO: Review these */ + .dvbs2.fec = DVBFE_FEC_1_4 | DVBFE_FEC_1_3 | + DVBFE_FEC_2_5 | DVBFE_FEC_1_2 | + DVBFE_FEC_3_5 | DVBFE_FEC_2_3 | + DVBFE_FEC_3_4 | DVBFE_FEC_4_5 | + DVBFE_FEC_5_6 | DVBFE_FEC_8_9 | + DVBFE_FEC_9_10, + + }, + + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 0, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 0 +}; + +static int cx24116_set_params(struct dvb_frontend* fe, struct dvbfe_params *p) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct cx24116_cmd cmd; + fe_status_t tunerstat; + int ret; + u8 retune=4; + + dprintk("%s()\n",__FUNCTION__); + + state->dnxt.delivery = p->delivery; + 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); + + 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, state->dcur.fec_mask, state->dcur.fec_val); + dprintk("%s: Inversion = %d (val = 0x%02x)\n", __FUNCTION__, + state->dcur.inversion, state->dcur.inversion_val); + + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + + /* 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] = state->dcur.fec_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] = 0x02; + cmd.args[0x0d] = state->dcur.fec_mask; + cmd.args[0x0e] = 0x06; + cmd.args[0x0f] = 0x00; + cmd.args[0x10] = 0x00; + cmd.args[0x11] = 0xFA; + cmd.args[0x12] = 0x24; + cmd.len= 0x13; + + do { + /* Reset status register? */ + cx24116_writereg(state, 0x9d, 0xc1); + + /* Tune */ + ret = cx24116_cmd_execute(fe, &cmd); + if( ret != 0 ) + break; + + /* The hardware can take time to lock, wait a while */ + msleep(500); + + cx24116_read_status(fe, &tunerstat); + if(tunerstat & FE_HAS_SIGNAL) { + if(tunerstat & FE_HAS_SYNC) + /* Tuned */ + break; + else if(p->delivery == DVBFE_DELSYS_DVBS2) + /* Toggle pilot bit */ + cmd.args[0x07] ^= 0x40; + } + } + while(--retune); + + return ret; +} + +/* Why isn't this part of the frontend core? */ +/* This could be generic and set_params gets callvia via the ops */ +static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct dvbfe_params newfe; + int ret = 0; + + dprintk("%s\n", __FUNCTION__); + + ret = cx24116_create_new_qpsk_feparams(fe, p, &newfe); + if(ret != 0) + return ret; + + return cx24116_set_params(fe, &newfe); +} + +static int cx24116_get_params(struct dvb_frontend* fe, + struct dvbfe_params *p) +{ + struct cx24116_state *state = fe->demodulator_priv; + int ret = 0; + + dprintk("%s()\n",__FUNCTION__); + + p->frequency = state->dcur.frequency; + if (cx24116_get_inversion(state, &p->inversion) != 0) { + printk("%s: Failed to get inversion status\n",__FUNCTION__); + return -EREMOTEIO; + } + + p->delivery = state->dcur.delivery; + + switch(p->delivery) + { + case DVBFE_DELSYS_DVBS2: + cx24116_get_fec(state, &p->delsys.dvbs2.fec); + p->delsys.dvbs2.symbol_rate = state->dcur.symbol_rate; + break; + case DVBFE_DELSYS_DVBS: + cx24116_get_fec(state, &p->delsys.dvbs.fec); + p->delsys.dvbs.symbol_rate = state->dcur.symbol_rate; + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* Why isn't this a generic frontend core function? */ +/* This could be generic and get_params gets callvia via the ops */ +static int cx24116_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct dvbfe_params feparams; + int ret; + dprintk("%s()\n",__FUNCTION__); + + ret = cx24116_get_params(fe, &feparams); + if(ret != 0) + return ret; + + return cx24116_create_old_qpsk_feparams(fe, &feparams, p); +} + +static int cx24116_get_info(struct dvb_frontend *fe, struct dvbfe_info *fe_info) +{ + dprintk("%s()\n",__FUNCTION__); + + switch (fe_info->delivery) + { + case DVBFE_DELSYS_DVBS: + dprintk("%s() DVBFE_DELSYS_DVBS\n",__FUNCTION__); + memcpy(fe_info, &dvbs_info, sizeof (dvbs_info)); + break; + case DVBFE_DELSYS_DVBS2: + dprintk("%s() DVBFE_DELSYS_DVBS2\n",__FUNCTION__); + memcpy(fe_info, &dvbs2_info, sizeof (dvbs2_info)); + break; + default: + dprintk("%s() invalid arg\n",__FUNCTION__); + return -EINVAL; + } + + return 0; +} + +/* TODO: The hardware does DSS too, how does the kernel demux handle this? */ +static int cx24116_get_delsys(struct dvb_frontend *fe, + enum dvbfe_delsys *fe_delsys) +{ + dprintk("%s()\n",__FUNCTION__); + *fe_delsys = DVBFE_DELSYS_DVBS | DVBFE_DELSYS_DVBS2; + + return 0; +} + +/* TODO: is this necessary? */ +static enum dvbfe_algo cx24116_get_algo(struct dvb_frontend *fe) +{ + dprintk("%s()\n",__FUNCTION__); + return DVBFE_ALGO_SW; +} + +static int cx24116_initfe(struct dvb_frontend* fe) +{ + dprintk("%s()\n",__FUNCTION__); + + return cx24116_diseqc_init(fe); +} + +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, + .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, + .set_params = cx24116_set_params, + .get_params = cx24116_get_params, + .get_info = cx24116_get_info, + .get_delsys = cx24116_get_delsys, + .get_frontend_algo = cx24116_get_algo, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware"); +MODULE_AUTHOR("Steven Toth"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(cx24116_attach); diff -uNr multiproto/linux/drivers/media/dvb/frontends/cx24116.h multiproto-test-hvr400/linux/drivers/media/dvb/frontends/cx24116.h --- multiproto/linux/drivers/media/dvb/frontends/cx24116.h 1970-01-01 01:00:00.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/dvb/frontends/cx24116.h 2007-11-05 21:56:48.000000000 +0100 @@ -0,0 +1,50 @@ +/* + Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2006 Steven Toth <stoth@xxxxxxxxxxxxx> + + 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 <linux/dvb/frontend.h> + +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); +}; + +#if defined(CONFIG_DVB_CX24116) || defined(CONFIG_DVB_CX24116_MODULE) +extern struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, + struct i2c_adapter* i2c); +#else +static inline struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, + struct i2c_adapter* i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_CX24116 + +#endif /* CX24116_H */ diff -uNr multiproto/linux/drivers/media/dvb/frontends/Kconfig multiproto-test-hvr400/linux/drivers/media/dvb/frontends/Kconfig --- multiproto/linux/drivers/media/dvb/frontends/Kconfig 2007-11-05 10:26:03.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/dvb/frontends/Kconfig 2007-11-05 12:36:14.000000000 +0100 @@ -34,6 +34,13 @@ 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" depends on DVB_CORE && I2C diff -uNr multiproto/linux/drivers/media/dvb/frontends/Makefile multiproto-test-hvr400/linux/drivers/media/dvb/frontends/Makefile --- multiproto/linux/drivers/media/dvb/frontends/Makefile 2007-11-05 10:26:03.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/dvb/frontends/Makefile 2007-11-05 12:36:33.000000000 +0100 @@ -47,3 +47,4 @@ obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o +obj-$(CONFIG_DVB_CX24116) += cx24116.o diff -uNr multiproto/linux/drivers/media/video/cx88/cx88-cards.c multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88-cards.c --- multiproto/linux/drivers/media/video/cx88/cx88-cards.c 2007-11-05 10:26:03.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88-cards.c 2007-11-05 12:36:14.000000000 +0100 @@ -1388,6 +1388,51 @@ .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 (WINTV2000) + * + * Analogue SAT DVB-T + * Antenna 0xc4bf 0xc4bb + * Composite 0xc4bf 0xc4bb + * S-Video 0xc4bf 0xc4bb + * Composite1 0xc4ff 0xc4fb + * S-Video1 0xc4ff 0xc4fb + */ + .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, + }, }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); @@ -1698,6 +1743,26 @@ .subvendor = 0x1421, .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, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); @@ -1752,6 +1817,11 @@ 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) */ @@ -1905,6 +1975,11 @@ cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */ udelay(1000); break; + case CX88_BOARD_HAUPPAUGE_HVR4000: + /* Init GPIO to allow tuner to attach? */ + cx_write(MO_GP0_IO, 0x0000c4bf); + udelay(1000); + break; } } @@ -1938,6 +2013,8 @@ 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 -uNr multiproto/linux/drivers/media/video/cx88/cx88-dvb.c multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88-dvb.c --- multiproto/linux/drivers/media/video/cx88/cx88-dvb.c 2007-11-05 10:26:03.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88-dvb.c 2007-11-05 12:36:14.000000000 +0100 @@ -44,6 +44,7 @@ #include "nxt200x.h" #include "cx24123.h" #include "isl6421.h" +#include "cx24116.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@xxxxxxxxxxxxxx>"); @@ -372,6 +373,35 @@ .lnb_polarity = 1, }; +static int cx24116_set_ts_param(struct dvb_frontend* fe, + int is_punctured) +{ + struct cx8802_dev *dev= fe->dvb->priv; + dev->ts_gen_cntrl = 0x2; + + return 0; +} + +static int cx24116_reset_device(struct dvb_frontend* fe) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct cx88_core *core = dev->core; + + /* Reset the part */ + cx_write(MO_SRST_IO, 0); + msleep(10); + cx_write(MO_SRST_IO, 1); + msleep(10); + + return 0; +} + +static struct cx24116_config hauppauge_hvr4000_config = { + .demod_address = 0x05, + .set_ts_params = cx24116_set_ts_param, + .reset_device = cx24116_reset_device, +}; + static int dvb_register(struct cx8802_dev *dev) { /* init struct videobuf_dvb */ @@ -625,6 +655,16 @@ dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; } break; + case CX88_BOARD_HAUPPAUGE_HVR4000: + case CX88_BOARD_HAUPPAUGE_HVR4000LITE: + dev->dvb.frontend = dvb_attach(cx24116_attach, + &hauppauge_hvr4000_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend) { + dvb_attach(isl6421_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x08, 0x00, 0x00); + } + break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->core->name); diff -uNr multiproto/linux/drivers/media/video/cx88/cx88.h multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88.h --- multiproto/linux/drivers/media/video/cx88/cx88.h 2007-11-05 10:26:03.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88.h 2007-11-05 12:39:01.000000000 +0100 @@ -213,6 +213,8 @@ #define CX88_BOARD_TE_DTV_250_OEM_SWANN 55 #define CX88_BOARD_HAUPPAUGE_HVR1300 56 #define CX88_BOARD_ADSTECH_PTV_390 57 +#define CX88_BOARD_HAUPPAUGE_HVR4000 58 +#define CX88_BOARD_HAUPPAUGE_HVR4000LITE 59 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, @@ -310,6 +312,7 @@ u32 i2c_state, i2c_rc; /* config info -- analog */ + unsigned int boardnr; unsigned int board; unsigned int tuner_type; unsigned int radio_type; diff -uNr multiproto/linux/drivers/media/video/cx88/cx88-i2c.c multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88-i2c.c --- multiproto/linux/drivers/media/video/cx88/cx88-i2c.c 2007-11-05 10:26:03.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88-i2c.c 2007-11-05 12:36:14.000000000 +0100 @@ -243,7 +243,23 @@ 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_HVR4000: + printk("%s: i2c init: enabling analog demod on HVR1300/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 -uNr multiproto/linux/drivers/media/video/cx88/cx88-input.c multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88-input.c --- multiproto/linux/drivers/media/video/cx88/cx88-input.c 2007-11-05 10:26:03.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/video/cx88/cx88-input.c 2007-11-05 12:36:14.000000000 +0100 @@ -238,6 +238,8 @@ 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; @@ -399,7 +401,7 @@ { struct cx88_IR *ir = core->ir; u32 samples, ircode; - int i; + int i, start, range, toggle, dev, code; if (NULL == ir) return; @@ -468,11 +470,23 @@ 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: ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); ir_dprintk("biphase decoded: %x\n", ircode); - if ((ircode & 0xfffff000) != 0x3000) + /* 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; - ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f, ircode); + 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 -uNr multiproto/linux/drivers/media/video/cx88/Kconfig multiproto-test-hvr400/linux/drivers/media/video/cx88/Kconfig --- multiproto/linux/drivers/media/video/cx88/Kconfig 2007-11-05 10:26:03.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/video/cx88/Kconfig 2007-11-05 12:36:14.000000000 +0100 @@ -56,6 +56,7 @@ 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 -uNr multiproto/linux/drivers/media/video/tveeprom.c multiproto-test-hvr400/linux/drivers/media/video/tveeprom.c --- multiproto/linux/drivers/media/video/tveeprom.c 2007-11-05 10:26:03.000000000 +0100 +++ multiproto-test-hvr400/linux/drivers/media/video/tveeprom.c 2007-11-05 12:36:14.000000000 +0100 @@ -245,7 +245,7 @@ { TUNER_ABSENT, "TCL M2523_3DBH_E"}, { TUNER_ABSENT, "TCL M2523_3DIH_E"}, { TUNER_ABSENT, "TCL MFPE05_2_U"}, - { TUNER_ABSENT, "Philips FMD1216MEX"}, + { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216MEX"}, { TUNER_ABSENT, "Philips FRH2036B"}, { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, { TUNER_ABSENT, "MaxLinear MXL5005"},
_______________________________________________ linux-dvb mailing list linux-dvb@xxxxxxxxxxx http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb