From d7ee909f5bbab277d4306e914505b71a7a54898f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 7 Sep 2008 13:38:50 +0200 Subject: dib7000m: fix powerpc build error From: Hans Verkuil The dib7000m_i2c_enumeration() function is not used by any public drivers, however some prototype board use it. So '#if 0' for now, but don't remove it. Thanks to Patrick Boettcher for clarifying this. Priority: normal Signed-off-by: Hans Verkuil CC: Patrick Boettcher --- linux/drivers/media/dvb/frontends/dib7000m.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/frontends/dib7000m.c b/linux/drivers/media/dvb/frontends/dib7000m.c index e6968317d..763407d06 100644 --- a/linux/drivers/media/dvb/frontends/dib7000m.c +++ b/linux/drivers/media/dvb/frontends/dib7000m.c @@ -1320,7 +1320,9 @@ struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum di } EXPORT_SYMBOL(dib7000m_get_i2c_master); -static int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, +#if 0 /* keep */ +/* used with some prototype boards */ +int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000m_config cfg[]) { struct dib7000m_state st = { .i2c_adap = i2c }; @@ -1366,6 +1368,7 @@ static int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, return 0; } EXPORT_SYMBOL(dib7000m_i2c_enumeration); +#endif static struct dvb_frontend_ops dib7000m_ops; struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg) -- cgit v1.2.3 From 3e2e701101286a530e336810f8ac8a2c546ed80e Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 4 Sep 2008 00:12:25 -0400 Subject: S2API: Added dvb frontend changes to support a newer tuning API From: Steven Toth This is an experimental patch to add a new tuning mechanism for dvb frontends. Rather than passing fixed structures across the user/kernel boundary, which need to be revised for each new modulation type (or feature the kernel developers want to add), this implements a simpler message based approach, allowing fe commands to be broken down into a series of small fixed size transactions, presented in an array. The goal is to avoid changing the user/kernel ABI in the future, by simply creating new frontend commands (and sequencies of commands) that help us add support for brand new demodulator, delivery system or statistics related commmands. known issues: checkpatch voilations feedback from various developers yet to be implemented, relating to namespace conventions, variable length array passing conventions, and generally some optimization. This patch should support all existing tuning mechanisms through the new API, as well as adding 8PSK, DVB-S2 NBC-QPSK and ISDB-T API support. For testing and exercise purposes, see the latest tune.c tool available from http://www.steventoth.net/linux/s2 Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 571 +++++++++++++++++++++++- linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 44 ++ 2 files changed, 609 insertions(+), 6 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 60a94dc85..e128bf221 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -766,6 +766,535 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, return 0; } +struct tv_cmds_h tv_cmds[] = { + [TV_SEQ_UNDEFINED] = { + .name = "TV_SEQ_UNDEFINED", + .cmd = TV_SEQ_UNDEFINED, + .set = 1, + }, + [TV_SEQ_START] = { + .name = "TV_SEQ_START", + .cmd = TV_SEQ_START, + .set = 1, + }, + [TV_SEQ_CONTINUE] = { + .name = "TV_SEQ_CONTINUE", + .cmd = TV_SEQ_CONTINUE, + .set = 1, + }, + [TV_SEQ_COMPLETE] = { + .name = "TV_SEQ_COMPLETE", + .cmd = TV_SEQ_COMPLETE, + .set = 1, + }, + [TV_SEQ_TERMINATE] = { + .name = "TV_SEQ_TERMINATE", + .cmd = TV_SEQ_TERMINATE, + .set = 1, + }, + + /* Set */ + [TV_SET_FREQUENCY] = { + .name = "TV_SET_FREQUENCY", + .cmd = TV_SET_FREQUENCY, + .set = 1, + }, + [TV_SET_BANDWIDTH] = { + .name = "TV_SET_BANDWIDTH", + .cmd = TV_SET_BANDWIDTH, + .set = 1, + }, + [TV_SET_MODULATION] = { + .name = "TV_SET_MODULATION", + .cmd = TV_SET_MODULATION, + .set = 1, + }, + [TV_SET_INVERSION] = { + .name = "TV_SET_INVERSION", + .cmd = TV_SET_INVERSION, + .set = 1, + }, + [TV_SET_DISEQC_MASTER] = { + .name = "TV_SET_DISEQC_MASTER", + .cmd = TV_SET_DISEQC_MASTER, + .set = 1, + .buffer = 1, + }, + [TV_SET_SYMBOLRATE] = { + .name = "TV_SET_SYMBOLRATE", + .cmd = TV_SET_SYMBOLRATE, + .set = 1, + }, + [TV_SET_INNERFEC] = { + .name = "TV_SET_INNERFEC", + .cmd = TV_SET_INNERFEC, + .set = 1, + }, + [TV_SET_VOLTAGE] = { + .name = "TV_SET_VOLTAGE", + .cmd = TV_SET_VOLTAGE, + .set = 1, + }, + [TV_SET_TONE] = { + .name = "TV_SET_TONE", + .cmd = TV_SET_TONE, + .set = 1, + }, + [TV_SET_PILOT] = { + .name = "TV_SET_PILOT", + .cmd = TV_SET_PILOT, + .set = 1, + }, + [TV_SET_ROLLOFF] = { + .name = "TV_SET_ROLLOFF", + .cmd = TV_SET_ROLLOFF, + .set = 1, + }, + [TV_SET_DELIVERY_SYSTEM] = { + .name = "TV_SET_DELIVERY_SYSTEM", + .cmd = TV_SET_DELIVERY_SYSTEM, + .set = 1, + }, + [TV_SET_ISDB_SEGMENT_NUM] = { + .name = "TV_SET_ISDB_SEGMENT_NUM", + .cmd = TV_SET_ISDB_SEGMENT_NUM, + .set = 1, + }, + [TV_SET_ISDB_SEGMENT_WIDTH] = { + .name = "TV_SET_ISDB_SEGMENT_WIDTH", + .cmd = TV_SET_ISDB_SEGMENT_WIDTH, + .set = 1, + }, + + /* Get */ + [TV_GET_FREQUENCY] = { + .name = "TV_GET_FREQUENCY", + .cmd = TV_GET_FREQUENCY, + .set = 0, + }, + [TV_GET_BANDWIDTH] = { + .name = "TV_GET_BANDWIDTH", + .cmd = TV_GET_BANDWIDTH, + .set = 0, + }, + [TV_GET_MODULATION] = { + .name = "TV_GET_MODULATION", + .cmd = TV_GET_MODULATION, + .set = 0, + }, + [TV_GET_INVERSION] = { + .name = "TV_GET_INVERSION", + .cmd = TV_GET_INVERSION, + .set = 0, + }, + [TV_GET_DISEQC_SLAVE_REPLY] = { + .name = "TV_GET_DISEQC_SLAVE_REPLY", + .cmd = TV_GET_DISEQC_SLAVE_REPLY, + .set = 0, + .buffer = 1, + }, + [TV_GET_SYMBOLRATE] = { + .name = "TV_GET_SYMBOLRATE", + .cmd = TV_GET_SYMBOLRATE, + .set = 0, + }, + [TV_GET_INNERFEC] = { + .name = "TV_GET_INNERFEC", + .cmd = TV_GET_INNERFEC, + .set = 0, + }, + [TV_GET_VOLTAGE] = { + .name = "TV_GET_VOLTAGE", + .cmd = TV_GET_VOLTAGE, + .set = 0, + }, + [TV_GET_TONE] = { + .name = "TV_GET_TONE", + .cmd = TV_GET_TONE, + .set = 0, + }, + [TV_GET_PILOT] = { + .name = "TV_GET_PILOT", + .cmd = TV_GET_PILOT, + .set = 0, + }, + [TV_GET_ROLLOFF] = { + .name = "TV_GET_ROLLOFF", + .cmd = TV_GET_ROLLOFF, + .set = 0, + }, + [TV_GET_DELIVERY_SYSTEM] = { + .name = "TV_GET_DELIVERY_SYSTEM", + .cmd = TV_GET_DELIVERY_SYSTEM, + .set = 0, + }, + [TV_GET_ISDB_SEGMENT_NUM] = { + .name = "TV_GET_ISDB_SEGMENT_NUM", + .cmd = TV_GET_ISDB_SEGMENT_NUM, + .set = 0, + }, + [TV_GET_ISDB_SEGMENT_WIDTH] = { + .name = "TV_GET_ISDB_SEGMENT_WIDTH", + .cmd = TV_GET_ISDB_SEGMENT_WIDTH, + .set = 0, + }, + [TV_GET_ISDB_LAYERA_FEC] = { + .name = "TV_GET_ISDB_LAYERA_FEC", + .cmd = TV_GET_ISDB_LAYERA_FEC, + .set = 0, + }, + [TV_GET_ISDB_LAYERA_MODULATION] = { + .name = "TV_GET_ISDB_LAYERA_MODULATION", + .cmd = TV_GET_ISDB_LAYERA_MODULATION, + .set = 0, + }, + [TV_GET_ISDB_LAYERA_SEGMENT_WIDTH] = { + .name = "TV_GET_ISDB_LAYERA_SEGMENT_WIDTH", + .cmd = TV_GET_ISDB_LAYERA_SEGMENT_WIDTH, + .set = 0, + }, + [TV_GET_ISDB_LAYERB_FEC] = { + .name = "TV_GET_ISDB_LAYERB_FEC", + .cmd = TV_GET_ISDB_LAYERB_FEC, + .set = 0, + }, + [TV_GET_ISDB_LAYERB_MODULATION] = { + .name = "TV_GET_ISDB_LAYERB_MODULATION", + .cmd = TV_GET_ISDB_LAYERB_MODULATION, + .set = 0, + }, + [TV_GET_ISDB_LAYERB_SEGMENT_WIDTH] = { + .name = "TV_GET_ISDB_LAYERB_SEGMENT_WIDTH", + .cmd = TV_GET_ISDB_LAYERB_SEGMENT_WIDTH, + .set = 0, + }, + [TV_GET_ISDB_LAYERC_FEC] = { + .name = "TV_GET_ISDB_LAYERC_FEC", + .cmd = TV_GET_ISDB_LAYERC_FEC, + .set = 0, + }, + [TV_GET_ISDB_LAYERC_MODULATION] = { + .name = "TV_GET_ISDB_LAYERC_MODULATION", + .cmd = TV_GET_ISDB_LAYERC_MODULATION, + .set = 0, + }, + [TV_GET_ISDB_LAYERC_SEGMENT_WIDTH] = { + .name = "TV_GET_ISDB_LAYERC_SEGMENT_WIDTH", + .cmd = TV_GET_ISDB_LAYERC_SEGMENT_WIDTH, + .set = 0, + }, +}; + +void tv_property_dump(tv_property_t *tvp) +{ + int i; + + printk("%s() tvp.cmd = 0x%08x (%s)\n" + ,__FUNCTION__ + ,tvp->cmd + ,tv_cmds[ tvp->cmd ].name); + + if(tv_cmds[ tvp->cmd ].buffer) { + + printk("%s() tvp.u.buffer.len = 0x%02x\n" + ,__FUNCTION__ + ,tvp->u.buffer.len); + + for(i = 0; i < tvp->u.buffer.len; i++) + printk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n" + ,__FUNCTION__ + ,i + ,tvp->u.buffer.data[i]); + + } else + printk("%s() tvp.u.data = 0x%08x\n", __FUNCTION__, tvp->u.data); +} + +int is_legacy_delivery_system(fe_delivery_system_t s) +{ + if((s == SYS_UNDEFINED) || (s == SYS_DVBC_ANNEX_AC) || + (s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS)) + return 1; + + return 0; +} + +int tv_property_cache_submit(struct dvb_frontend *fe) +{ + + /* We have to do one of two things: + * To support legacy devices using the new API we take values from + * the tv_cache and generate a legacy truning structure. + * + * Or, + * + * To support advanced tuning devices with the new API we + * notify the new advance driver type that a tuning operation is required + * and let it pull values from the cache as is, we don't need to + * pass structures. + * + * We'll use the modulation type to assess how this is handled. as the API + * progresses we'll probably want to have a flag in dvb_frontend_ops + * to allow the frontend driver to dictate how it likes to be tuned. + * + * Because of how this is attached to the ioctl handler for legacy support, + * it's important to return an appropriate result code with atleast the following + * three meanings: + * < 0 = processing error + * 0 = lecagy ioctl handler to submit a traditional set_frontend() call. + * 1 = lecagy ioctl handler should NOT submit a traditional set_frontend() call. + */ + + int r; + + struct tv_frontend_properties *c = &fe->tv_property_cache; + struct dvb_frontend_private *fepriv = fe->frontend_priv; + struct dvb_frontend_parameters p; + + printk("%s()\n", __FUNCTION__); + + /* For legacy delivery systems we don't need the delivery_system to be specified */ + if(is_legacy_delivery_system(c->delivery_system)) { + switch(c->modulation) { + case QPSK: + printk("%s() Preparing QPSK req\n", __FUNCTION__); + p.frequency = c->frequency; + p.inversion = c->inversion; + p.u.qpsk.symbol_rate = c->symbol_rate; + p.u.qpsk.fec_inner = c->fec_inner; + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + /* Call the traditional tuning mechanisms. */ + + r = 0; + break; + case QAM_16: + case QAM_32: + case QAM_64: + case QAM_128: + case QAM_256: + case QAM_AUTO: + printk("%s() Preparing QAM req\n", __FUNCTION__); + p.frequency = c->frequency; + p.inversion = c->inversion; + p.u.qam.symbol_rate = c->symbol_rate; + p.u.vsb.modulation = c->modulation; + printk("%s() frequency = %d\n", __FUNCTION__, p.frequency); + printk("%s() QAM = %d\n", __FUNCTION__, p.u.vsb.modulation); + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + /* At this point we're fully formed for backwards + * compatability and we need to return this + * via the ioctl handler as SET_FRONTEND (arg). + * We've already patched the new values into the + * frontends tuning structures so the ioctl code just + * continues as if a legacy tune structure was passed + * from userspace. + */ + + r = 0; + break; + case VSB_8: + case VSB_16: + printk("%s() Preparing VSB req\n", __FUNCTION__); + p.frequency = c->frequency; + p.u.vsb.modulation = c->modulation; + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + /* Call the traditional tuning mechanisms. */ + + r = 0; + break; + /* TODO: Add any missing modulation types */ + default: + r = -1; + } + } else { + /* For advanced delivery systems / modulation types ... + * we seed the lecacy dvb_frontend_parameters structure + * so that the sanity checking code later in the IOCTL processing + * can validate our basic frequency ranges, symbolrates, modulation + * etc. + */ + r = -1; + + switch(c->modulation) { + case _8PSK: + case _16APSK: + case NBC_QPSK: + /* Just post a notification to the demod driver and let it pull + * the specific values it wants from its tv_property_cache. + * It can decide how best to use those parameters. + * IOCTL will call set_frontend (by default) due to zigzag + * support etc. + */ + if (fe->ops.set_params) + r = fe->ops.set_params(fe); + + p.frequency = c->frequency; + p.inversion = c->inversion; + p.u.qpsk.symbol_rate = c->symbol_rate; + p.u.qpsk.fec_inner = c->fec_inner; + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + r = 0; + break; + default: + r = -1; + } + + if(c->delivery_system == SYS_ISDBT) { + /* Fake out a generic DVB-T request so we pass validation in the ioctl */ + p.frequency = c->frequency; + p.inversion = INVERSION_AUTO; + p.u.ofdm.constellation = QAM_AUTO; + p.u.ofdm.code_rate_HP = FEC_AUTO; + p.u.ofdm.code_rate_LP = FEC_AUTO; + p.u.ofdm.bandwidth = BANDWIDTH_AUTO; + p.u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + p.u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + p.u.ofdm.hierarchy_information = HIERARCHY_AUTO; + memcpy(&fepriv->parameters, &p, + sizeof (struct dvb_frontend_parameters)); + + r = 0; + } + } + return r; +} + +int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp) +{ + int r = 0; + printk("%s()\n", __FUNCTION__); + tv_property_dump(tvp); + + switch(tvp->cmd) { + case TV_SEQ_START: + case TV_SEQ_TERMINATE: + /* Reset a cache of data specific to the frontend here. This does + * not effect hardware. + */ + printk("%s() Flushing property cache\n", __FUNCTION__); + memset(&fe->tv_property_cache, 0, sizeof(struct tv_frontend_properties)); + fe->tv_property_cache.state = TV_SEQ_START; + fe->tv_property_cache.delivery_system = SYS_UNDEFINED; + break; + case TV_SEQ_COMPLETE: + /* interpret the cache of data, build either a traditional frontend + * tunerequest and submit it to a subset of the ioctl handler, + * or, call a new undefined method on the frontend to deal with + * all new tune requests. + */ + fe->tv_property_cache.state = TV_SEQ_COMPLETE; + printk("%s() Finalised property cache\n", __FUNCTION__); + r = tv_property_cache_submit(fe); + break; + case TV_SET_FREQUENCY: + fe->tv_property_cache.frequency = tvp->u.data; + break; + case TV_GET_FREQUENCY: + tvp->u.data = fe->tv_property_cache.frequency; + break; + case TV_SET_MODULATION: + fe->tv_property_cache.modulation = tvp->u.data; + break; + case TV_GET_MODULATION: + tvp->u.data = fe->tv_property_cache.modulation; + break; + case TV_SET_BANDWIDTH: + fe->tv_property_cache.bandwidth = tvp->u.data; + break; + case TV_GET_BANDWIDTH: + tvp->u.data = fe->tv_property_cache.bandwidth; + break; + case TV_SET_INVERSION: + fe->tv_property_cache.inversion = tvp->u.data; + break; + case TV_GET_INVERSION: + tvp->u.data = fe->tv_property_cache.inversion; + break; + case TV_SET_SYMBOLRATE: + fe->tv_property_cache.symbol_rate = tvp->u.data; + break; + case TV_GET_SYMBOLRATE: + tvp->u.data = fe->tv_property_cache.symbol_rate; + break; + case TV_SET_INNERFEC: + fe->tv_property_cache.fec_inner = tvp->u.data; + break; + case TV_GET_INNERFEC: + tvp->u.data = fe->tv_property_cache.fec_inner; + break; + case TV_SET_PILOT: + fe->tv_property_cache.pilot = tvp->u.data; + break; + case TV_GET_PILOT: + tvp->u.data = fe->tv_property_cache.pilot; + break; + case TV_SET_ROLLOFF: + fe->tv_property_cache.rolloff = tvp->u.data; + break; + case TV_GET_ROLLOFF: + tvp->u.data = fe->tv_property_cache.rolloff; + break; + case TV_SET_DELIVERY_SYSTEM: + fe->tv_property_cache.delivery_system = tvp->u.data; + break; + case TV_GET_DELIVERY_SYSTEM: + tvp->u.data = fe->tv_property_cache.delivery_system; + break; + + /* ISDB-T Support here */ + case TV_SET_ISDB_SEGMENT_NUM: + fe->tv_property_cache.isdb_segment_num = tvp->u.data; + break; + case TV_GET_ISDB_SEGMENT_NUM: + tvp->u.data = fe->tv_property_cache.isdb_segment_num; + break; + case TV_SET_ISDB_SEGMENT_WIDTH: + fe->tv_property_cache.isdb_segment_width = tvp->u.data; + break; + case TV_GET_ISDB_SEGMENT_WIDTH: + tvp->u.data = fe->tv_property_cache.isdb_segment_width; + break; + case TV_GET_ISDB_LAYERA_FEC: + tvp->u.data = fe->tv_property_cache.isdb_layera_fec; + break; + case TV_GET_ISDB_LAYERA_MODULATION: + tvp->u.data = fe->tv_property_cache.isdb_layera_modulation; + break; + case TV_GET_ISDB_LAYERA_SEGMENT_WIDTH: + tvp->u.data = fe->tv_property_cache.isdb_layera_segment_width; + break; + case TV_GET_ISDB_LAYERB_FEC: + tvp->u.data = fe->tv_property_cache.isdb_layerb_fec; + break; + case TV_GET_ISDB_LAYERB_MODULATION: + tvp->u.data = fe->tv_property_cache.isdb_layerb_modulation; + break; + case TV_GET_ISDB_LAYERB_SEGMENT_WIDTH: + tvp->u.data = fe->tv_property_cache.isdb_layerb_segment_width; + break; + case TV_GET_ISDB_LAYERC_FEC: + tvp->u.data = fe->tv_property_cache.isdb_layerc_fec; + break; + case TV_GET_ISDB_LAYERC_MODULATION: + tvp->u.data = fe->tv_property_cache.isdb_layerc_modulation; + break; + case TV_GET_ISDB_LAYERC_SEGMENT_WIDTH: + tvp->u.data = fe->tv_property_cache.isdb_layerc_segment_width; + break; + + } + + return 0; +} + static int dvb_frontend_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { @@ -773,6 +1302,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; int err = -EOPNOTSUPP; + tv_property_t* tvp; dprintk ("%s\n", __func__); @@ -787,6 +1317,27 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, if (down_interruptible (&fepriv->sem)) return -ERESTARTSYS; + if(cmd == FE_SET_PROPERTY) { + printk("%s() FE_SET_PROPERTY\n", __FUNCTION__); + + /* TODO: basic property validation here */ + + /* TODO: ioctl userdata out of range check here */ + tvp = parg; + while(tvp->cmd != TV_SEQ_UNDEFINED) { + tv_property_process(fe, tvp); + if( (tvp->cmd == TV_SEQ_TERMINATE) || (tvp->cmd == TV_SEQ_COMPLETE) ) + break; + tvp++; + } + + if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) { + printk("%s() Property cache is full, tuning\n", __FUNCTION__); + cmd = FE_SET_FRONTEND; + } + err = 0; + } + switch (cmd) { case FE_GET_INFO: { struct dvb_frontend_info* info = parg; @@ -953,13 +1504,20 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; - if (dvb_frontend_check_parameters(fe, parg) < 0) { - err = -EINVAL; - break; - } + if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) { + if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) { + err = -EINVAL; + break; + } + } else { + if (dvb_frontend_check_parameters(fe, parg) < 0) { + err = -EINVAL; + break; + } - memcpy (&fepriv->parameters, parg, - sizeof (struct dvb_frontend_parameters)); + memcpy (&fepriv->parameters, parg, + sizeof (struct dvb_frontend_parameters)); + } memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings)); memcpy(&fetunesettings.parameters, parg, @@ -1042,6 +1600,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, return err; } + static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait) { struct dvb_device *dvbdev = file->private_data; diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index aa4133f0b..61d53ee70 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -169,6 +169,10 @@ struct dvb_frontend_ops { struct dvb_tuner_ops tuner_ops; struct analog_demod_ops analog_ops; + + int (*set_property)(struct dvb_frontend* fe, tv_property_t* tvp); + int (*get_property)(struct dvb_frontend* fe, tv_property_t* tvp); + int (*set_params)(struct dvb_frontend* fe); }; #define MAX_EVENT 8 @@ -182,6 +186,45 @@ struct dvb_fe_events { struct mutex mtx; }; +struct tv_frontend_properties { + + /* Cache State */ + u32 state; + + u32 frequency; + fe_modulation_t modulation; + + fe_sec_voltage_t voltage; + fe_sec_tone_mode_t sectone; + fe_spectral_inversion_t inversion; + fe_code_rate_t fec_inner; + fe_transmit_mode_t transmission_mode; + fe_bandwidth_t bandwidth; + fe_guard_interval_t guard_interval; + fe_hierarchy_t hierarchy; + u32 symbol_rate; + fe_code_rate_t code_rate_HP; + fe_code_rate_t code_rate_LP; + + fe_pilot_t pilot; + fe_rolloff_t rolloff; + + fe_delivery_system_t delivery_system; + + /* ISDB-T specifics */ + u32 isdb_segment_num; + u32 isdb_segment_width; + fe_code_rate_t isdb_layera_fec; + fe_modulation_t isdb_layera_modulation; + u32 isdb_layera_segment_width; + fe_code_rate_t isdb_layerb_fec; + fe_modulation_t isdb_layerb_modulation; + u32 isdb_layerb_segment_width; + fe_code_rate_t isdb_layerc_fec; + fe_modulation_t isdb_layerc_modulation; + u32 isdb_layerc_segment_width; +}; + struct dvb_frontend { struct dvb_frontend_ops ops; struct dvb_adapter *dvb; @@ -190,6 +233,7 @@ struct dvb_frontend { void *frontend_priv; void *sec_priv; void *analog_demod_priv; + struct tv_frontend_properties tv_property_cache; }; extern int dvb_register_frontend(struct dvb_adapter *dvb, -- cgit v1.2.3 From 1b4e42f1ff264543858da24972931c7a4d995586 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 4 Sep 2008 00:14:43 -0400 Subject: cx24116: Adding DVB-S2 demodulator support From: Steven Toth Adds support for the COnexant cx24116 DVB-S2 demodulator. TODO: checkpatch cleanup. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/frontends/Kconfig | 7 + linux/drivers/media/dvb/frontends/Makefile | 1 + linux/drivers/media/dvb/frontends/cx24116.c | 950 ++++++++++++++++++++++++++++ linux/drivers/media/dvb/frontends/cx24116.h | 50 ++ 4 files changed, 1008 insertions(+) create mode 100644 linux/drivers/media/dvb/frontends/cx24116.c create mode 100644 linux/drivers/media/dvb/frontends/cx24116.h (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 774f5c2a7..5398641a9 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -92,6 +92,13 @@ config DVB_TUA6100 help A DVB-S PLL chip. +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. + comment "DVB-T (terrestrial) frontends" depends on DVB_CORE diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 262eaa429..9bcf29f86 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_DVB_AU8522) += au8522.o obj-$(CONFIG_DVB_TDA10048) += tda10048.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o +obj-$(CONFIG_DVB_CX24116) += cx24116.o diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c new file mode 100644 index 000000000..1402086a5 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -0,0 +1,950 @@ +/* + Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver + + Copyright (C) 2006-2008 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. +*/ + +/* + * 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 +#include +#include +#include +#include +#include + +#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; + fe_code_rate_t fec; + + fe_modulation_t modulation; + + /* 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", + __func__,reg, data); + + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { + printk("%s: writereg error(err == %i, reg == 0x%02x," + " value == 0x%02x)\n", __func__, 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", + __func__,reg, len); + + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) { + printk("%s: writereg error(err == %i, reg == 0x%02x\n", + __func__, 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", __func__, 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", __func__, 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; +} + +/* 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 { + fe_modulation_t modulation; + fe_code_rate_t 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 */ + { QPSK, FEC_NONE, 0xfe, 0x30 }, + { QPSK, FEC_1_2, 0x02, 0x2e }, + { QPSK, FEC_2_3, 0x04, 0x2f }, + { QPSK, FEC_3_4, 0x08, 0x30 }, + { QPSK, FEC_4_5, 0xfe, 0x30 }, + { QPSK, FEC_5_6, 0x20, 0x31 }, + { QPSK, FEC_6_7, 0xfe, 0x30 }, + { QPSK, FEC_7_8, 0x80, 0x32 }, + { QPSK, FEC_8_9, 0xfe, 0x30 }, + { QPSK, FEC_AUTO, 0xfe, 0x30 }, + /* NBC-QPSK */ + { NBC_QPSK, FEC_1_2, 0x00, 0x04 }, + { NBC_QPSK, FEC_3_5, 0x00, 0x05 }, + { NBC_QPSK, FEC_2_3, 0x00, 0x06 }, + { NBC_QPSK, FEC_3_4, 0x00, 0x07 }, + { NBC_QPSK, FEC_4_5, 0x00, 0x08 }, + { NBC_QPSK, FEC_5_6, 0x00, 0x09 }, + { NBC_QPSK, FEC_8_9, 0x00, 0x0a }, + { NBC_QPSK, FEC_9_10, 0x00, 0x0b }, + /* 8PSK */ + { _8PSK, FEC_3_5, 0x00, 0x0c }, + { _8PSK, FEC_2_3, 0x00, 0x0d }, + { _8PSK, FEC_3_4, 0x00, 0x0e }, + { _8PSK, FEC_5_6, 0x00, 0x0f }, + { _8PSK, FEC_9_10, 0x00, 0x11 }, +}; + +static int cx24116_lookup_fecmod(struct cx24116_state* state, + fe_modulation_t m, fe_code_rate_t f) +{ + int i, ret = -EOPNOTSUPP; + + for(i=0 ; i < sizeof(CX24116_MODFEC_MODES) / sizeof(struct cx24116_modfec) ; i++) + { + if( (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, fe_modulation_t mod, fe_code_rate_t fec) +{ + int ret = 0; + dprintk("%s()\n", __func__); + + ret = cx24116_lookup_fecmod(state, mod, fec); + + 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", __func__, + state->dnxt.fec_val, state->dnxt.fec_mask); + + return 0; +} + +static int cx24116_set_symbolrate(struct cx24116_state* state, u32 rate) +{ + int ret = 0; + + dprintk("%s()\n", __func__); + + state->dnxt.symbol_rate = rate; + + dprintk("%s() symbol_rate = %d\n", __func__, 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",__func__); + + 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", __func__, CX24116_DEFAULT_FIRMWARE); + ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, &state->i2c->dev); + printk("%s: Waiting for firmware upload(2)...\n", __func__); + if (ret) { + printk("%s: No firmware uploaded (timeout or file not found?)\n", __func__); + 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", __func__); + + release_firmware(fw); + + printk("%s: Firmware upload %s\n", __func__, 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", __func__); + + /* Load the firmware if required */ + if ( (ret = cx24116_firmware_ondemand(fe)) != 0) + { + printk("%s(): Unable initialise the firmware\n", __func__); + return ret; + } + + /* Write the command */ + for(i = 0; i < cmd->len ; i++) + { + dprintk("%s: 0x%02x == 0x%02x\n", __func__, 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", __func__); + 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", __func__); + 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",__func__); + + 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", __func__, 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", __func__); + *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", __func__); + + 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", + __func__,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", __func__); + *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", __func__); + *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)); +} + +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", __func__, tone); + if ( (tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF) ) { + printk("%s: Invalid, tone=%d\n", __func__, 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", __func__); + cmd.args[0x03] = 0x01; + break; + case SEC_TONE_OFF: + dprintk("%s: setting tone off\n",__func__); + 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(", __func__); + 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",__func__,(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",__func__); + 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",__func__); + + /* 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 int cx24116_get_params(struct dvb_frontend* fe) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct tv_frontend_properties *cache = &fe->tv_property_cache; + + dprintk("%s()\n",__func__); + + cache->frequency = state->dcur.frequency; + cache->inversion = state->dcur.inversion; + cache->modulation = state->dcur.modulation; + cache->fec_inner = state->dcur.fec; + cache->symbol_rate = state->dcur.symbol_rate; + + return 0; +} + +static int cx24116_initfe(struct dvb_frontend* fe) +{ + dprintk("%s()\n",__func__); + + return cx24116_diseqc_init(fe); +} + +static int cx24116_set_property(struct dvb_frontend *fe, tv_property_t* tvp) +{ + dprintk("%s(..)\n", __func__); + return 0; +} + +static int cx24116_set_params(struct dvb_frontend *fe) +{ + dprintk("%s(..) We were notified that a tune request may occur\n", __func__); + return 0; +} + +/* dvb-core told us to tune, the tv property cache will be complete, + * it's safe for is to pull values and use them for tuning purposes. + */ +static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct cx24116_state *state = fe->demodulator_priv; + struct tv_frontend_properties *c = &fe->tv_property_cache; + struct cx24116_cmd cmd; + fe_status_t tunerstat; + int ret; + u8 retune=4; + + dprintk("%s()\n",__func__); + + state->dnxt.modulation = c->modulation; + state->dnxt.frequency = c->frequency; + + if ((ret = cx24116_set_inversion(state, c->inversion)) != 0) + return ret; + + if ((ret = cx24116_set_fec(state, c->modulation, c->fec_inner)) != 0) + return ret; + + if ((ret = cx24116_set_symbolrate(state, c->symbol_rate)) != 0) + return ret; + + /* discard the 'current' tuning parameters and prepare to tune */ + cx24116_clone_params(fe); + + dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); + dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); + dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__, + state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); + dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__, + 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 & Pilot Off */ + cmd.args[0x07] = state->dcur.fec_val; + + if (c->pilot == PILOT_ON) + cmd.args[0x07] |= 0x40; + + 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; + + /* We need to support pilot and non-pilot tuning in the + * driver automatically. This is a workaround for because + * the demod does not support autodetect. + */ + 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(c->pilot == PILOT_AUTO) + /* Toggle pilot bit */ + cmd.args[0x07] ^= 0x40; + } + } + while(--retune); + + return ret; +} + +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, + .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_property = cx24116_set_property, + .set_params = cx24116_set_params, + .set_frontend = cx24116_set_frontend, +}; + +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 --git a/linux/drivers/media/dvb/frontends/cx24116.h b/linux/drivers/media/dvb/frontends/cx24116.h new file mode 100644 index 000000000..278967252 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/cx24116.h @@ -0,0 +1,50 @@ +/* + 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); +}; + +#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 */ -- cgit v1.2.3 From 095219cbe03bb0401bd154ab1bd6dfb831f17a5a Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 4 Sep 2008 20:19:43 -0400 Subject: S2API: Allow the properties to call legacy ioctls From: Steven Toth SET_TONE and SET_VOLTAGE were not previously implemented. Two options existed. Either cut/paste from the previous ioctl handler into the process_properties function, which is code duplication. Or, split the current ioctl handler into it's two major pieces. Piece 1, responsible for input validation and semaphore acquiring Piece 2 the processing of the previous ioctls and finally, a new third pieces where the array of properties is processed, and can freely call the legacy ioctl handler without having to re-acquire the fepriv->sem semaphore. This is a clean approach and ensures the existing legacy ioctls are processed as they were previously (but with an extra function call) and allows the new API to share code without duplication. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 67 ++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 8 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index e128bf221..e17dd050c 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1167,9 +1167,16 @@ int tv_property_cache_submit(struct dvb_frontend *fe) return r; } -int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp) +static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, + unsigned int cmd, void *parg); +static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, + unsigned int cmd, void *parg); + +int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp, + struct inode *inode, struct file *file) { int r = 0; + struct dvb_frontend_private *fepriv = fe->frontend_priv; printk("%s()\n", __FUNCTION__); tv_property_dump(tvp); @@ -1192,7 +1199,9 @@ int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp) */ fe->tv_property_cache.state = TV_SEQ_COMPLETE; printk("%s() Finalised property cache\n", __FUNCTION__); - r = tv_property_cache_submit(fe); + r |= tv_property_cache_submit(fe); + r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, + &fepriv->parameters); break; case TV_SET_FREQUENCY: fe->tv_property_cache.frequency = tvp->u.data; @@ -1289,10 +1298,25 @@ int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp) case TV_GET_ISDB_LAYERC_SEGMENT_WIDTH: tvp->u.data = fe->tv_property_cache.isdb_layerc_segment_width; break; - + case TV_SET_VOLTAGE: + fe->tv_property_cache.voltage = tvp->u.data; + r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE, + &fe->tv_property_cache.voltage); + break; + case TV_GET_VOLTAGE: + tvp->u.data = fe->tv_property_cache.voltage; + break; + case TV_SET_TONE: + fe->tv_property_cache.sectone = tvp->u.data; + r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE, + (void *)fe->tv_property_cache.sectone); + break; + case TV_GET_TONE: + tvp->u.data = fe->tv_property_cache.sectone; + break; } - return 0; + return r; } static int dvb_frontend_ioctl(struct inode *inode, struct file *file, @@ -1302,7 +1326,6 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; int err = -EOPNOTSUPP; - tv_property_t* tvp; dprintk ("%s\n", __func__); @@ -1317,6 +1340,25 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, if (down_interruptible (&fepriv->sem)) return -ERESTARTSYS; + if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) + err = dvb_frontend_ioctl_properties(inode, file, cmd, parg); + else + err = dvb_frontend_ioctl_legacy(inode, file, cmd, parg); + + up(&fepriv->sem); + return err; +} + +static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_frontend *fe = dvbdev->priv; + int err = -EOPNOTSUPP; + tv_property_t *tvp; + + dprintk("%s\n", __func__); + if(cmd == FE_SET_PROPERTY) { printk("%s() FE_SET_PROPERTY\n", __FUNCTION__); @@ -1325,7 +1367,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, /* TODO: ioctl userdata out of range check here */ tvp = parg; while(tvp->cmd != TV_SEQ_UNDEFINED) { - tv_property_process(fe, tvp); + tv_property_process(fe, tvp, inode, file); if( (tvp->cmd == TV_SEQ_TERMINATE) || (tvp->cmd == TV_SEQ_COMPLETE) ) break; tvp++; @@ -1333,11 +1375,21 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); - cmd = FE_SET_FRONTEND; } err = 0; } + return err; +} + +static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_frontend *fe = dvbdev->priv; + struct dvb_frontend_private *fepriv = fe->frontend_priv; + int err = -EOPNOTSUPP; + switch (cmd) { case FE_GET_INFO: { struct dvb_frontend_info* info = parg; @@ -1596,7 +1648,6 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, break; }; - up (&fepriv->sem); return err; } -- cgit v1.2.3 From 6d9b01f13a3c2d24b45b7ca718606854e8cdf17a Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Mon, 8 Sep 2008 11:07:06 -0400 Subject: S2API: DVB-S/S2 voltage selection bug fix From: Steven Toth Thanks to Hans Werner for finding the problem. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index e17dd050c..fbe2b8c6d 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1301,7 +1301,7 @@ int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp, case TV_SET_VOLTAGE: fe->tv_property_cache.voltage = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE, - &fe->tv_property_cache.voltage); + (void *)fe->tv_property_cache.voltage); break; case TV_GET_VOLTAGE: tvp->u.data = fe->tv_property_cache.voltage; -- cgit v1.2.3 From a3d68eb5a8273cdc2468591d673aa60cfa872007 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Mon, 8 Sep 2008 23:16:40 +0300 Subject: Added support for DVBWorld 2104 and TeVii S650 USB DVB-S2 cards From: Igor M. Liplianin Added support for DVBWorld 2104 and TeVii S650 USB DVB-S2 cards This cards based on cx24116 demodulator. Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dvb-usb/dw2102.c | 305 +++++++++++++++++++++++++------ linux/drivers/media/dvb/dvb-usb/dw2102.h | 1 - 2 files changed, 248 insertions(+), 58 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c index a4d898b44..57015f3ca 100644 --- a/linux/drivers/media/dvb/dvb-usb/dw2102.c +++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c @@ -1,4 +1,5 @@ -/* DVB USB framework compliant Linux driver for the DVBWorld DVB-S 2102 Card +/* DVB USB framework compliant Linux driver for the +* DVBWorld DVB-S 2101, 2102, DVB-S2 2104 Card * * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) * @@ -12,18 +13,23 @@ #include "dw2102.h" #include "stv0299.h" #include "z0194a.h" +#include "cx24116.h" #ifndef USB_PID_DW2102 #define USB_PID_DW2102 0x2102 #endif +#ifndef USB_PID_DW2104 +#define USB_PID_DW2104 0x2104 +#endif + #define DW2102_READ_MSG 0 #define DW2102_WRITE_MSG 1 #define REG_1F_SYMBOLRATE_BYTE0 0x1f #define REG_20_SYMBOLRATE_BYTE1 0x20 #define REG_21_SYMBOLRATE_BYTE2 0x21 - +/* on my own*/ #define DW2102_VOLTAGE_CTRL (0x1800) #define DW2102_RC_QUERY (0x1a00) @@ -35,22 +41,27 @@ struct dw2102_rc_keys { u32 event; }; +/* debug */ +static int dvb_usb_dw2102_debug; +module_param_named(debug, dvb_usb_dw2102_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer (or-able))." DVB_USB_DEBUG_STATUS); + DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static int dw2102_op_rw(struct usb_device *dev, u8 request, u16 value, - u8 *data, u16 len, int flags) + u16 index, u8 * data, u16 len, int flags) { int ret; u8 u8buf[len]; unsigned int pipe = (flags == DW2102_READ_MSG) ? - usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0); + usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0); u8 request_type = (flags == DW2102_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; if (flags == DW2102_WRITE_MSG) memcpy(u8buf, data, len); - ret = usb_control_msg(dev, pipe, request, - request_type | USB_TYPE_VENDOR, value, 0 , u8buf, len, 2000); + ret = usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR, + value, index , u8buf, len, 2000); if (flags == DW2102_READ_MSG) memcpy(data, u8buf, len); @@ -65,7 +76,6 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], struct dvb_usb_device *d = i2c_get_adapdata(adap); int i = 0, ret = 0; u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0}; - u8 request; u16 value; if (!d) @@ -76,12 +86,11 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap); switch (num) { case 2: /* read stv0299 register */ - request = 0xb5; value = msg[0].buf[0];/* register */ for (i = 0; i < msg[1].len; i++) { value = value + i; - ret = dw2102_op_rw(d->udev, 0xb5, - value, buf6, 2, DW2102_READ_MSG); + ret = dw2102_op_rw(d->udev, 0xb5, value, 0, + buf6, 2, DW2102_READ_MSG); msg[1].buf[i] = buf6[0]; } @@ -93,8 +102,8 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap); buf6[0] = 0x2a; buf6[1] = msg[0].buf[0]; buf6[2] = msg[0].buf[1]; - ret = dw2102_op_rw(d->udev, 0xb2, - 0, buf6, 3, DW2102_WRITE_MSG); + ret = dw2102_op_rw(d->udev, 0xb2, 0, 0, + buf6, 3, DW2102_WRITE_MSG); break; case 0x60: if (msg[0].flags == 0) { @@ -106,26 +115,26 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap); buf6[4] = msg[0].buf[1]; buf6[5] = msg[0].buf[2]; buf6[6] = msg[0].buf[3]; - ret = dw2102_op_rw(d->udev, 0xb2, - 0, buf6, 7, DW2102_WRITE_MSG); + ret = dw2102_op_rw(d->udev, 0xb2, 0, 0, + buf6, 7, DW2102_WRITE_MSG); } else { - /* write to tuner pll */ - ret = dw2102_op_rw(d->udev, 0xb5, - 0, buf6, 1, DW2102_READ_MSG); + /* read from tuner */ + ret = dw2102_op_rw(d->udev, 0xb5, 0,0, + buf6, 1, DW2102_READ_MSG); msg[0].buf[0] = buf6[0]; } break; case (DW2102_RC_QUERY): - ret = dw2102_op_rw(d->udev, 0xb8, - 0, buf6, 2, DW2102_READ_MSG); + ret = dw2102_op_rw(d->udev, 0xb8, 0, 0, + buf6, 2, DW2102_READ_MSG); msg[0].buf[0] = buf6[0]; msg[0].buf[1] = buf6[1]; break; case (DW2102_VOLTAGE_CTRL): buf6[0] = 0x30; buf6[1] = msg[0].buf[0]; - ret = dw2102_op_rw(d->udev, 0xb2, - 0, buf6, 2, DW2102_WRITE_MSG); + ret = dw2102_op_rw(d->udev, 0xb2, 0, 0, + buf6, 2, DW2102_WRITE_MSG); break; } @@ -136,6 +145,88 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap); return num; } +static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret = 0; + int len, i; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 2: { + /* read */ + /* first write first register number */ + u8 ibuf [msg[1].len + 2], obuf[3]; + obuf[0] = 0xaa; + obuf[1] = msg[0].len; + obuf[2] = msg[0].buf[0]; + ret = dw2102_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[0].len + 2, DW2102_WRITE_MSG); + /* second read registers */ + ret = dw2102_op_rw(d->udev, 0xc3, 0xab , 0, + ibuf, msg[1].len + 2, DW2102_READ_MSG); + memcpy(msg[1].buf, ibuf + 2, msg[1].len); + + break; + } + case 1: + switch (msg[0].addr) { + case 0x55: { + if (msg[0].buf[0] == 0xf7) { + /* firmware */ + /* Write in small blocks */ + u8 obuf[19]; + obuf[0] = 0xaa; + obuf[1] = 0x11; + obuf[2] = 0xf7; + len = msg[0].len - 1; + i = 1; + do { + memcpy(obuf + 3, msg[0].buf + i, (len > 16 ? 16 : len)); + ret = dw2102_op_rw(d->udev, 0xc2, 0, 0, + obuf, (len > 16 ? 16 : len) + 3, DW2102_WRITE_MSG); + i += 16; + len -= 16; + } while (len > 0); + } else { + /* write to register */ + u8 obuf[msg[0].len + 2]; + obuf[0] = 0xaa; + obuf[1] = msg[0].len; + memcpy(obuf + 2, msg[0].buf, msg[0].len); + ret = dw2102_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[0].len + 2, DW2102_WRITE_MSG); + } + break; + } + case(DW2102_RC_QUERY): { + u8 ibuf[2]; + ret = dw2102_op_rw(d->udev, 0xb8, 0, 0, + ibuf, 2, DW2102_READ_MSG); + memcpy(msg[0].buf, ibuf , 2); + break; + } + case(DW2102_VOLTAGE_CTRL): { + u8 obuf[2]; + obuf[0] = 0x30; + obuf[1] = msg[0].buf[0]; + ret = dw2102_op_rw(d->udev, 0xb2, 0, 0, + obuf, 2, DW2102_WRITE_MSG); + break; + } + } + + break; + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + static u32 dw2102_i2c_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C; @@ -146,6 +237,34 @@ static struct i2c_algorithm dw2102_i2c_algo = { .functionality = dw2102_i2c_func, }; +static struct i2c_algorithm dw2104_i2c_algo = { + .master_xfer = dw2104_i2c_transfer, + .functionality = dw2102_i2c_func, +}; + +static int dw2102_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + int i; + u8 ibuf[] = {0, 0}; + u8 eeprom[256], eepromline[16]; + + for (i = 0; i < 256; i++) { + if (dw2102_op_rw(d->udev, 0xb6, 0xa0 , i, ibuf, 2, DW2102_READ_MSG) < 0) { + err("read eeprom failed."); + return -1; + } else { + eepromline[i%16] = ibuf[0]; + eeprom[i] = ibuf[0]; + } + if ((i % 16) == 15) { + deb_xfer("%02x: ", i - 15); + debug_dump(eepromline, 16, deb_xfer); + } + } + memcpy(mac, eeprom + 8, 6); + return 0; +}; + static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { static u8 command_13v[1] = {0x00}; @@ -163,6 +282,22 @@ static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } +static struct cx24116_config dw2104_config = { + .demod_address = 0x55, + /*.mpg_clk_pos_pol = 0x01,*/ +}; + +static int dw2104_frontend_attach(struct dvb_usb_adapter *d) +{ + if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config, + &d->dev->i2c_adap)) != NULL) { + d->fe->ops.set_voltage = dw2102_set_voltage; + info("Attached cx24116!\n"); + return 0; + } + return -EIO; +} + static int dw2102_frontend_attach(struct dvb_usb_adapter *d) { d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config, @@ -249,6 +384,8 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) static struct usb_device_id dw2102_table[] = { {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2102)}, {USB_DEVICE(USB_VID_CYPRESS, 0x2101)}, + {USB_DEVICE(USB_VID_CYPRESS, 0x2104)}, + {USB_DEVICE(0x9022, 0xd650)}, { } }; @@ -273,25 +410,23 @@ static int dw2102_load_firmware(struct usb_device *dev, return ret; } break; - case USB_PID_DW2102: + default: fw = frmwr; break; } - info("start downloading DW2102 firmware"); + info("start downloading DW210X firmware"); p = kmalloc(fw->size, GFP_KERNEL); reset = 1; /*stop the CPU*/ - dw2102_op_rw(dev, 0xa0, 0x7f92, &reset, 1, DW2102_WRITE_MSG); - dw2102_op_rw(dev, 0xa0, 0xe600, &reset, 1, DW2102_WRITE_MSG); + dw2102_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, DW2102_WRITE_MSG); + dw2102_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, DW2102_WRITE_MSG); if (p != NULL) { memcpy(p, fw->data, fw->size); for (i = 0; i < fw->size; i += 0x40) { b = (u8 *) p + i; - if (dw2102_op_rw - (dev, 0xa0, i, b , 0x40, - DW2102_WRITE_MSG) != 0x40 - ) { + if (dw2102_op_rw(dev, 0xa0, i, 0, b , 0x40, + DW2102_WRITE_MSG) != 0x40) { err("error while transferring firmware"); ret = -EINVAL; break; @@ -299,43 +434,45 @@ static int dw2102_load_firmware(struct usb_device *dev, } /* restart the CPU */ reset = 0; - if (ret || dw2102_op_rw - (dev, 0xa0, 0x7f92, &reset, 1, - DW2102_WRITE_MSG) != 1) { + if (ret || dw2102_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, + DW2102_WRITE_MSG) != 1) { err("could not restart the USB controller CPU."); ret = -EINVAL; } - if (ret || dw2102_op_rw - (dev, 0xa0, 0xe600, &reset, 1, - DW2102_WRITE_MSG) != 1) { + if (ret || dw2102_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, + DW2102_WRITE_MSG) != 1) { err("could not restart the USB controller CPU."); ret = -EINVAL; } /* init registers */ switch (dev->descriptor.idProduct) { + case USB_PID_DW2104: + case 0xd650: + reset = 1; + dw2102_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1, + DW2102_WRITE_MSG); + reset = 0; + dw2102_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, + DW2102_WRITE_MSG); + break; case USB_PID_DW2102: - dw2102_op_rw - (dev, 0xbf, 0x0040, &reset, 0, - DW2102_WRITE_MSG); - dw2102_op_rw - (dev, 0xb9, 0x0000, &reset16[0], 2, - DW2102_READ_MSG); + dw2102_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, + DW2102_WRITE_MSG); + dw2102_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, + DW2102_READ_MSG); break; case 0x2101: - dw2102_op_rw - (dev, 0xbc, 0x0030, &reset16[0], 2, - DW2102_READ_MSG); - dw2102_op_rw - (dev, 0xba, 0x0000, &reset16[0], 7, - DW2102_READ_MSG); - dw2102_op_rw - (dev, 0xba, 0x0000, &reset16[0], 7, - DW2102_READ_MSG); - dw2102_op_rw - (dev, 0xb9, 0x0000, &reset16[0], 2, - DW2102_READ_MSG); + dw2102_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2, + DW2102_READ_MSG); + dw2102_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, + DW2102_READ_MSG); + dw2102_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, + DW2102_READ_MSG); + dw2102_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, + DW2102_READ_MSG); break; } + msleep(100); kfree(p); } return ret; @@ -358,7 +495,8 @@ static struct dvb_usb_device_properties dw2102_properties = { /* parameter for the MPEG2-data transfer */ .num_adapters = 1, .download_firmware = dw2102_load_firmware, - .adapter = { + .read_mac_address = dw2102_read_mac_address, + .adapter = { { .frontend_attach = dw2102_frontend_attach, .streaming_ctrl = NULL, @@ -388,11 +526,64 @@ static struct dvb_usb_device_properties dw2102_properties = { } }; +static struct dvb_usb_device_properties dw2104_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-dw2104.fw", + .size_of_priv = sizeof(struct dw2102_state), + .no_reconnect = 1, + + .i2c_algo = &dw2104_i2c_algo, + .rc_key_map = dw2102_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dw2102_rc_keys), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + + .generic_bulk_ctrl_endpoint = 0x81, + /* parameter for the MPEG2-data transfer */ + .num_adapters = 1, + .download_firmware = dw2102_load_firmware, + .read_mac_address = dw2102_read_mac_address, + .adapter = { + { + .frontend_attach = dw2104_frontend_attach, + .streaming_ctrl = NULL, + /*.tuner_attach = dw2104_tuner_attach,*/ + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } + }, + .num_device_descs = 2, + .devices = { + { "DVBWorld DW2104 USB2.0", + {&dw2102_table[2], NULL}, + {NULL}, + }, + { "TeVii S650 USB2.0", + {&dw2102_table[3], NULL}, + {NULL}, + }, + } +}; + static int dw2102_probe(struct usb_interface *intf, const struct usb_device_id *id) { - return dvb_usb_device_init(intf, &dw2102_properties, - THIS_MODULE, NULL, adapter_nr); + if (0 == dvb_usb_device_init(intf, &dw2102_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &dw2104_properties, + THIS_MODULE, NULL, adapter_nr)) { + return 0; + } + return -ENODEV; } static struct usb_driver dw2102_driver = { @@ -420,6 +611,6 @@ module_init(dw2102_module_init); module_exit(dw2102_module_exit); MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); -MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101 2102 USB2.0 device"); +MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104 USB2.0 device"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.h b/linux/drivers/media/dvb/dvb-usb/dw2102.h index 7a310f906..e3370734e 100644 --- a/linux/drivers/media/dvb/dvb-usb/dw2102.h +++ b/linux/drivers/media/dvb/dvb-usb/dw2102.h @@ -4,6 +4,5 @@ #define DVB_USB_LOG_PREFIX "dw2102" #include "dvb-usb.h" -extern int dvb_usb_dw2102_debug; #define deb_xfer(args...) dprintk(dvb_usb_dw2102_debug, 0x02, args) #endif -- cgit v1.2.3 From 16bf7b03be158d81893389f5c81273d1aa8bd1cb Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 9 Sep 2008 17:29:43 +0300 Subject: Kconfig corrections for DVBWorld 2104 and TeVii S650 USB DVB-S2 cards From: Igor M. Liplianin Change menu item in Kconfig for DVBWorld 2104 and TeVii S650 USB DVB-S2 cards Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dvb-usb/Kconfig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig index 1d9e98cee..a28154448 100644 --- a/linux/drivers/media/dvb/dvb-usb/Kconfig +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -247,12 +247,14 @@ config DVB_USB_AF9005_REMOTE Afatech AF9005 based receiver. config DVB_USB_DW2102 - tristate "DvbWorld 2102 DVB-S USB2.0 receiver" + tristate "DvbWorld DVB-S/S2 USB2.0 support" depends on DVB_USB - select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_CX24116 if !DVB_FE_CUSTOMISE help - Say Y here to support the DvbWorld 2102 DVB-S USB2.0 receiver. + Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers + and the TeVii S650. config DVB_USB_ANYSEE tristate "Anysee DVB-T/C USB2.0 support" -- cgit v1.2.3 From 2bbc242362b05e9c2f826038c265db72209ae7a2 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 9 Sep 2008 19:22:29 +0300 Subject: cx24116: Fix lock for high (above 30000 kSyms) symbol rates From: Igor M. Liplianin cx24116: Fix lock for high (above 30000 kSyms) symbol rates Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/frontends/cx24116.c | 37 ++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index 1402086a5..cc8b0358c 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -30,6 +30,11 @@ * Sync with legacy version. * Some clean ups. */ +/* Updates by Igor Liplianin + * + * September, 9th 2008 + * Fixed locking on high symbol rates (>30000). + */ #include #include @@ -809,7 +814,7 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par struct tv_frontend_properties *c = &fe->tv_property_cache; struct cx24116_cmd cmd; fe_status_t tunerstat; - int ret; + int ret, above30msps; u8 retune=4; dprintk("%s()\n",__func__); @@ -839,6 +844,16 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); + above30msps = (state->dcur.symbol_rate > 30000000); + + if (above30msps){ + cx24116_writereg(state, 0xF9, 0x01); + cx24116_writereg(state, 0xF3, 0x44); + } else { + cx24116_writereg(state, 0xF9, 0x00); + cx24116_writereg(state, 0xF3, 0x46); + } + /* Prepare a tune request */ cmd.args[0x00] = CMD_TUNEREQUEST; @@ -866,11 +881,21 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par 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; + + if (above30msps){ + cmd.args[0x0e] = 0x04; + cmd.args[0x0f] = 0x00; + cmd.args[0x10] = 0x01; + cmd.args[0x11] = 0x77; + cmd.args[0x12] = 0x36; + } else { + cmd.args[0x0e] = 0x06; + cmd.args[0x0f] = 0x00; + cmd.args[0x10] = 0x00; + cmd.args[0x11] = 0xFA; + cmd.args[0x12] = 0x24; + } + cmd.len= 0x13; /* We need to support pilot and non-pilot tuning in the -- cgit v1.2.3 From 50d5ab75a9850d1005a8081553333b23556d1e99 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 9 Sep 2008 19:57:47 +0300 Subject: Adjust MPEG initialization in cx24116 From: Igor M. Liplianin Adjust MPEG initialization in cx24116 in order to accomodate different MPEG CLK position and polarity in different cards. Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dvb-usb/dw2102.c | 2 +- linux/drivers/media/dvb/frontends/cx24116.c | 5 ++++- linux/drivers/media/dvb/frontends/cx24116.h | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c index 57015f3ca..169951fc2 100644 --- a/linux/drivers/media/dvb/dvb-usb/dw2102.c +++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c @@ -284,7 +284,7 @@ static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) static struct cx24116_config dw2104_config = { .demod_address = 0x55, - /*.mpg_clk_pos_pol = 0x01,*/ + .mpg_clk_pos_pol = 0x01, }; static int dw2104_frontend_attach(struct dvb_usb_adapter *d) diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index cc8b0358c..fbb459245 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -478,7 +478,10 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware cmd.args[0x01] = 0x01; cmd.args[0x02] = 0x75; cmd.args[0x03] = 0x00; - cmd.args[0x04] = 0x02; + if (state->config->mpg_clk_pos_pol) + cmd.args[0x04] = state->config->mpg_clk_pos_pol; + else + cmd.args[0x04] = 0x02; cmd.args[0x05] = 0x00; cmd.len= 0x06; ret = cx24116_cmd_execute(fe, &cmd); diff --git a/linux/drivers/media/dvb/frontends/cx24116.h b/linux/drivers/media/dvb/frontends/cx24116.h index 278967252..8dbcec268 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.h +++ b/linux/drivers/media/dvb/frontends/cx24116.h @@ -33,6 +33,9 @@ struct cx24116_config /* Need to reset device during firmware loading */ int (*reset_device)(struct dvb_frontend* fe); + + /* Need to set MPEG parameters */ + u8 mpg_clk_pos_pol:0x02; }; #if defined(CONFIG_DVB_CX24116) || defined(CONFIG_DVB_CX24116_MODULE) -- cgit v1.2.3 From 68901d77882c18dcb4f6e1fa0309e93893f1a7a0 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 09:19:27 -0400 Subject: S2API: tv_ / TV_ to dtv_ / DTV_ namespace changes From: Steven Toth The group preferred dtv_ over tv_, this implements it. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 442 ++++++++++++------------ linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 8 +- linux/drivers/media/dvb/frontends/cx24116.c | 8 +- 3 files changed, 229 insertions(+), 229 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index fbe2b8c6d..7a94aaa09 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -766,235 +766,235 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, return 0; } -struct tv_cmds_h tv_cmds[] = { - [TV_SEQ_UNDEFINED] = { - .name = "TV_SEQ_UNDEFINED", - .cmd = TV_SEQ_UNDEFINED, +struct dtv_cmds_h dtv_cmds[] = { + [DTV_SEQ_UNDEFINED] = { + .name = "DTV_SEQ_UNDEFINED", + .cmd = DTV_SEQ_UNDEFINED, .set = 1, }, - [TV_SEQ_START] = { - .name = "TV_SEQ_START", - .cmd = TV_SEQ_START, + [DTV_SEQ_START] = { + .name = "DTV_SEQ_START", + .cmd = DTV_SEQ_START, .set = 1, }, - [TV_SEQ_CONTINUE] = { - .name = "TV_SEQ_CONTINUE", - .cmd = TV_SEQ_CONTINUE, + [DTV_SEQ_CONTINUE] = { + .name = "DTV_SEQ_CONTINUE", + .cmd = DTV_SEQ_CONTINUE, .set = 1, }, - [TV_SEQ_COMPLETE] = { - .name = "TV_SEQ_COMPLETE", - .cmd = TV_SEQ_COMPLETE, + [DTV_SEQ_COMPLETE] = { + .name = "DTV_SEQ_COMPLETE", + .cmd = DTV_SEQ_COMPLETE, .set = 1, }, - [TV_SEQ_TERMINATE] = { - .name = "TV_SEQ_TERMINATE", - .cmd = TV_SEQ_TERMINATE, + [DTV_SEQ_TERMINATE] = { + .name = "DTV_SEQ_TERMINATE", + .cmd = DTV_SEQ_TERMINATE, .set = 1, }, /* Set */ - [TV_SET_FREQUENCY] = { - .name = "TV_SET_FREQUENCY", - .cmd = TV_SET_FREQUENCY, + [DTV_SET_FREQUENCY] = { + .name = "DTV_SET_FREQUENCY", + .cmd = DTV_SET_FREQUENCY, .set = 1, }, - [TV_SET_BANDWIDTH] = { - .name = "TV_SET_BANDWIDTH", - .cmd = TV_SET_BANDWIDTH, + [DTV_SET_BANDWIDTH] = { + .name = "DTV_SET_BANDWIDTH", + .cmd = DTV_SET_BANDWIDTH, .set = 1, }, - [TV_SET_MODULATION] = { - .name = "TV_SET_MODULATION", - .cmd = TV_SET_MODULATION, + [DTV_SET_MODULATION] = { + .name = "DTV_SET_MODULATION", + .cmd = DTV_SET_MODULATION, .set = 1, }, - [TV_SET_INVERSION] = { - .name = "TV_SET_INVERSION", - .cmd = TV_SET_INVERSION, + [DTV_SET_INVERSION] = { + .name = "DTV_SET_INVERSION", + .cmd = DTV_SET_INVERSION, .set = 1, }, - [TV_SET_DISEQC_MASTER] = { - .name = "TV_SET_DISEQC_MASTER", - .cmd = TV_SET_DISEQC_MASTER, + [DTV_SET_DISEQC_MASTER] = { + .name = "DTV_SET_DISEQC_MASTER", + .cmd = DTV_SET_DISEQC_MASTER, .set = 1, .buffer = 1, }, - [TV_SET_SYMBOLRATE] = { - .name = "TV_SET_SYMBOLRATE", - .cmd = TV_SET_SYMBOLRATE, + [DTV_SET_SYMBOLRATE] = { + .name = "DTV_SET_SYMBOLRATE", + .cmd = DTV_SET_SYMBOLRATE, .set = 1, }, - [TV_SET_INNERFEC] = { - .name = "TV_SET_INNERFEC", - .cmd = TV_SET_INNERFEC, + [DTV_SET_INNERFEC] = { + .name = "DTV_SET_INNERFEC", + .cmd = DTV_SET_INNERFEC, .set = 1, }, - [TV_SET_VOLTAGE] = { - .name = "TV_SET_VOLTAGE", - .cmd = TV_SET_VOLTAGE, + [DTV_SET_VOLTAGE] = { + .name = "DTV_SET_VOLTAGE", + .cmd = DTV_SET_VOLTAGE, .set = 1, }, - [TV_SET_TONE] = { - .name = "TV_SET_TONE", - .cmd = TV_SET_TONE, + [DTV_SET_TONE] = { + .name = "DTV_SET_TONE", + .cmd = DTV_SET_TONE, .set = 1, }, - [TV_SET_PILOT] = { - .name = "TV_SET_PILOT", - .cmd = TV_SET_PILOT, + [DTV_SET_PILOT] = { + .name = "DTV_SET_PILOT", + .cmd = DTV_SET_PILOT, .set = 1, }, - [TV_SET_ROLLOFF] = { - .name = "TV_SET_ROLLOFF", - .cmd = TV_SET_ROLLOFF, + [DTV_SET_ROLLOFF] = { + .name = "DTV_SET_ROLLOFF", + .cmd = DTV_SET_ROLLOFF, .set = 1, }, - [TV_SET_DELIVERY_SYSTEM] = { - .name = "TV_SET_DELIVERY_SYSTEM", - .cmd = TV_SET_DELIVERY_SYSTEM, + [DTV_SET_DELIVERY_SYSTEM] = { + .name = "DTV_SET_DELIVERY_SYSTEM", + .cmd = DTV_SET_DELIVERY_SYSTEM, .set = 1, }, - [TV_SET_ISDB_SEGMENT_NUM] = { - .name = "TV_SET_ISDB_SEGMENT_NUM", - .cmd = TV_SET_ISDB_SEGMENT_NUM, + [DTV_SET_ISDB_SEGMENT_NUM] = { + .name = "DTV_SET_ISDB_SEGMENT_NUM", + .cmd = DTV_SET_ISDB_SEGMENT_NUM, .set = 1, }, - [TV_SET_ISDB_SEGMENT_WIDTH] = { - .name = "TV_SET_ISDB_SEGMENT_WIDTH", - .cmd = TV_SET_ISDB_SEGMENT_WIDTH, + [DTV_SET_ISDB_SEGMENT_WIDTH] = { + .name = "DTV_SET_ISDB_SEGMENT_WIDTH", + .cmd = DTV_SET_ISDB_SEGMENT_WIDTH, .set = 1, }, /* Get */ - [TV_GET_FREQUENCY] = { - .name = "TV_GET_FREQUENCY", - .cmd = TV_GET_FREQUENCY, + [DTV_GET_FREQUENCY] = { + .name = "DTV_GET_FREQUENCY", + .cmd = DTV_GET_FREQUENCY, .set = 0, }, - [TV_GET_BANDWIDTH] = { - .name = "TV_GET_BANDWIDTH", - .cmd = TV_GET_BANDWIDTH, + [DTV_GET_BANDWIDTH] = { + .name = "DTV_GET_BANDWIDTH", + .cmd = DTV_GET_BANDWIDTH, .set = 0, }, - [TV_GET_MODULATION] = { - .name = "TV_GET_MODULATION", - .cmd = TV_GET_MODULATION, + [DTV_GET_MODULATION] = { + .name = "DTV_GET_MODULATION", + .cmd = DTV_GET_MODULATION, .set = 0, }, - [TV_GET_INVERSION] = { - .name = "TV_GET_INVERSION", - .cmd = TV_GET_INVERSION, + [DTV_GET_INVERSION] = { + .name = "DTV_GET_INVERSION", + .cmd = DTV_GET_INVERSION, .set = 0, }, - [TV_GET_DISEQC_SLAVE_REPLY] = { - .name = "TV_GET_DISEQC_SLAVE_REPLY", - .cmd = TV_GET_DISEQC_SLAVE_REPLY, + [DTV_GET_DISEQC_SLAVE_REPLY] = { + .name = "DTV_GET_DISEQC_SLAVE_REPLY", + .cmd = DTV_GET_DISEQC_SLAVE_REPLY, .set = 0, .buffer = 1, }, - [TV_GET_SYMBOLRATE] = { - .name = "TV_GET_SYMBOLRATE", - .cmd = TV_GET_SYMBOLRATE, + [DTV_GET_SYMBOLRATE] = { + .name = "DTV_GET_SYMBOLRATE", + .cmd = DTV_GET_SYMBOLRATE, .set = 0, }, - [TV_GET_INNERFEC] = { - .name = "TV_GET_INNERFEC", - .cmd = TV_GET_INNERFEC, + [DTV_GET_INNERFEC] = { + .name = "DTV_GET_INNERFEC", + .cmd = DTV_GET_INNERFEC, .set = 0, }, - [TV_GET_VOLTAGE] = { - .name = "TV_GET_VOLTAGE", - .cmd = TV_GET_VOLTAGE, + [DTV_GET_VOLTAGE] = { + .name = "DTV_GET_VOLTAGE", + .cmd = DTV_GET_VOLTAGE, .set = 0, }, - [TV_GET_TONE] = { - .name = "TV_GET_TONE", - .cmd = TV_GET_TONE, + [DTV_GET_TONE] = { + .name = "DTV_GET_TONE", + .cmd = DTV_GET_TONE, .set = 0, }, - [TV_GET_PILOT] = { - .name = "TV_GET_PILOT", - .cmd = TV_GET_PILOT, + [DTV_GET_PILOT] = { + .name = "DTV_GET_PILOT", + .cmd = DTV_GET_PILOT, .set = 0, }, - [TV_GET_ROLLOFF] = { - .name = "TV_GET_ROLLOFF", - .cmd = TV_GET_ROLLOFF, + [DTV_GET_ROLLOFF] = { + .name = "DTV_GET_ROLLOFF", + .cmd = DTV_GET_ROLLOFF, .set = 0, }, - [TV_GET_DELIVERY_SYSTEM] = { - .name = "TV_GET_DELIVERY_SYSTEM", - .cmd = TV_GET_DELIVERY_SYSTEM, + [DTV_GET_DELIVERY_SYSTEM] = { + .name = "DTV_GET_DELIVERY_SYSTEM", + .cmd = DTV_GET_DELIVERY_SYSTEM, .set = 0, }, - [TV_GET_ISDB_SEGMENT_NUM] = { - .name = "TV_GET_ISDB_SEGMENT_NUM", - .cmd = TV_GET_ISDB_SEGMENT_NUM, + [DTV_GET_ISDB_SEGMENT_NUM] = { + .name = "DTV_GET_ISDB_SEGMENT_NUM", + .cmd = DTV_GET_ISDB_SEGMENT_NUM, .set = 0, }, - [TV_GET_ISDB_SEGMENT_WIDTH] = { - .name = "TV_GET_ISDB_SEGMENT_WIDTH", - .cmd = TV_GET_ISDB_SEGMENT_WIDTH, + [DTV_GET_ISDB_SEGMENT_WIDTH] = { + .name = "DTV_GET_ISDB_SEGMENT_WIDTH", + .cmd = DTV_GET_ISDB_SEGMENT_WIDTH, .set = 0, }, - [TV_GET_ISDB_LAYERA_FEC] = { - .name = "TV_GET_ISDB_LAYERA_FEC", - .cmd = TV_GET_ISDB_LAYERA_FEC, + [DTV_GET_ISDB_LAYERA_FEC] = { + .name = "DTV_GET_ISDB_LAYERA_FEC", + .cmd = DTV_GET_ISDB_LAYERA_FEC, .set = 0, }, - [TV_GET_ISDB_LAYERA_MODULATION] = { - .name = "TV_GET_ISDB_LAYERA_MODULATION", - .cmd = TV_GET_ISDB_LAYERA_MODULATION, + [DTV_GET_ISDB_LAYERA_MODULATION] = { + .name = "DTV_GET_ISDB_LAYERA_MODULATION", + .cmd = DTV_GET_ISDB_LAYERA_MODULATION, .set = 0, }, - [TV_GET_ISDB_LAYERA_SEGMENT_WIDTH] = { - .name = "TV_GET_ISDB_LAYERA_SEGMENT_WIDTH", - .cmd = TV_GET_ISDB_LAYERA_SEGMENT_WIDTH, + [DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH] = { + .name = "DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH", + .cmd = DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH, .set = 0, }, - [TV_GET_ISDB_LAYERB_FEC] = { - .name = "TV_GET_ISDB_LAYERB_FEC", - .cmd = TV_GET_ISDB_LAYERB_FEC, + [DTV_GET_ISDB_LAYERB_FEC] = { + .name = "DTV_GET_ISDB_LAYERB_FEC", + .cmd = DTV_GET_ISDB_LAYERB_FEC, .set = 0, }, - [TV_GET_ISDB_LAYERB_MODULATION] = { - .name = "TV_GET_ISDB_LAYERB_MODULATION", - .cmd = TV_GET_ISDB_LAYERB_MODULATION, + [DTV_GET_ISDB_LAYERB_MODULATION] = { + .name = "DTV_GET_ISDB_LAYERB_MODULATION", + .cmd = DTV_GET_ISDB_LAYERB_MODULATION, .set = 0, }, - [TV_GET_ISDB_LAYERB_SEGMENT_WIDTH] = { - .name = "TV_GET_ISDB_LAYERB_SEGMENT_WIDTH", - .cmd = TV_GET_ISDB_LAYERB_SEGMENT_WIDTH, + [DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH] = { + .name = "DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH", + .cmd = DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH, .set = 0, }, - [TV_GET_ISDB_LAYERC_FEC] = { - .name = "TV_GET_ISDB_LAYERC_FEC", - .cmd = TV_GET_ISDB_LAYERC_FEC, + [DTV_GET_ISDB_LAYERC_FEC] = { + .name = "DTV_GET_ISDB_LAYERC_FEC", + .cmd = DTV_GET_ISDB_LAYERC_FEC, .set = 0, }, - [TV_GET_ISDB_LAYERC_MODULATION] = { - .name = "TV_GET_ISDB_LAYERC_MODULATION", - .cmd = TV_GET_ISDB_LAYERC_MODULATION, + [DTV_GET_ISDB_LAYERC_MODULATION] = { + .name = "DTV_GET_ISDB_LAYERC_MODULATION", + .cmd = DTV_GET_ISDB_LAYERC_MODULATION, .set = 0, }, - [TV_GET_ISDB_LAYERC_SEGMENT_WIDTH] = { - .name = "TV_GET_ISDB_LAYERC_SEGMENT_WIDTH", - .cmd = TV_GET_ISDB_LAYERC_SEGMENT_WIDTH, + [DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH] = { + .name = "DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH", + .cmd = DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH, .set = 0, }, }; -void tv_property_dump(tv_property_t *tvp) +void dtv_property_dump(dtv_property_t *tvp) { int i; printk("%s() tvp.cmd = 0x%08x (%s)\n" ,__FUNCTION__ ,tvp->cmd - ,tv_cmds[ tvp->cmd ].name); + ,dtv_cmds[ tvp->cmd ].name); - if(tv_cmds[ tvp->cmd ].buffer) { + if(dtv_cmds[ tvp->cmd ].buffer) { printk("%s() tvp.u.buffer.len = 0x%02x\n" ,__FUNCTION__ @@ -1019,7 +1019,7 @@ int is_legacy_delivery_system(fe_delivery_system_t s) return 0; } -int tv_property_cache_submit(struct dvb_frontend *fe) +int dtv_property_cache_submit(struct dvb_frontend *fe) { /* We have to do one of two things: @@ -1047,7 +1047,7 @@ int tv_property_cache_submit(struct dvb_frontend *fe) int r; - struct tv_frontend_properties *c = &fe->tv_property_cache; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct dvb_frontend_private *fepriv = fe->frontend_priv; struct dvb_frontend_parameters p; @@ -1126,7 +1126,7 @@ int tv_property_cache_submit(struct dvb_frontend *fe) case _16APSK: case NBC_QPSK: /* Just post a notification to the demod driver and let it pull - * the specific values it wants from its tv_property_cache. + * the specific values it wants from its dtv_property_cache. * It can decide how best to use those parameters. * IOCTL will call set_frontend (by default) due to zigzag * support etc. @@ -1172,147 +1172,147 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, unsigned int cmd, void *parg); -int tv_property_process(struct dvb_frontend *fe, tv_property_t *tvp, +int dtv_property_process(struct dvb_frontend *fe, dtv_property_t *tvp, struct inode *inode, struct file *file) { int r = 0; struct dvb_frontend_private *fepriv = fe->frontend_priv; printk("%s()\n", __FUNCTION__); - tv_property_dump(tvp); + dtv_property_dump(tvp); switch(tvp->cmd) { - case TV_SEQ_START: - case TV_SEQ_TERMINATE: + case DTV_SEQ_START: + case DTV_SEQ_TERMINATE: /* Reset a cache of data specific to the frontend here. This does * not effect hardware. */ printk("%s() Flushing property cache\n", __FUNCTION__); - memset(&fe->tv_property_cache, 0, sizeof(struct tv_frontend_properties)); - fe->tv_property_cache.state = TV_SEQ_START; - fe->tv_property_cache.delivery_system = SYS_UNDEFINED; + memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties)); + fe->dtv_property_cache.state = DTV_SEQ_START; + fe->dtv_property_cache.delivery_system = SYS_UNDEFINED; break; - case TV_SEQ_COMPLETE: + case DTV_SEQ_COMPLETE: /* interpret the cache of data, build either a traditional frontend * tunerequest and submit it to a subset of the ioctl handler, * or, call a new undefined method on the frontend to deal with * all new tune requests. */ - fe->tv_property_cache.state = TV_SEQ_COMPLETE; + fe->dtv_property_cache.state = DTV_SEQ_COMPLETE; printk("%s() Finalised property cache\n", __FUNCTION__); - r |= tv_property_cache_submit(fe); + r |= dtv_property_cache_submit(fe); r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, &fepriv->parameters); break; - case TV_SET_FREQUENCY: - fe->tv_property_cache.frequency = tvp->u.data; + case DTV_SET_FREQUENCY: + fe->dtv_property_cache.frequency = tvp->u.data; break; - case TV_GET_FREQUENCY: - tvp->u.data = fe->tv_property_cache.frequency; + case DTV_GET_FREQUENCY: + tvp->u.data = fe->dtv_property_cache.frequency; break; - case TV_SET_MODULATION: - fe->tv_property_cache.modulation = tvp->u.data; + case DTV_SET_MODULATION: + fe->dtv_property_cache.modulation = tvp->u.data; break; - case TV_GET_MODULATION: - tvp->u.data = fe->tv_property_cache.modulation; + case DTV_GET_MODULATION: + tvp->u.data = fe->dtv_property_cache.modulation; break; - case TV_SET_BANDWIDTH: - fe->tv_property_cache.bandwidth = tvp->u.data; + case DTV_SET_BANDWIDTH: + fe->dtv_property_cache.bandwidth = tvp->u.data; break; - case TV_GET_BANDWIDTH: - tvp->u.data = fe->tv_property_cache.bandwidth; + case DTV_GET_BANDWIDTH: + tvp->u.data = fe->dtv_property_cache.bandwidth; break; - case TV_SET_INVERSION: - fe->tv_property_cache.inversion = tvp->u.data; + case DTV_SET_INVERSION: + fe->dtv_property_cache.inversion = tvp->u.data; break; - case TV_GET_INVERSION: - tvp->u.data = fe->tv_property_cache.inversion; + case DTV_GET_INVERSION: + tvp->u.data = fe->dtv_property_cache.inversion; break; - case TV_SET_SYMBOLRATE: - fe->tv_property_cache.symbol_rate = tvp->u.data; + case DTV_SET_SYMBOLRATE: + fe->dtv_property_cache.symbol_rate = tvp->u.data; break; - case TV_GET_SYMBOLRATE: - tvp->u.data = fe->tv_property_cache.symbol_rate; + case DTV_GET_SYMBOLRATE: + tvp->u.data = fe->dtv_property_cache.symbol_rate; break; - case TV_SET_INNERFEC: - fe->tv_property_cache.fec_inner = tvp->u.data; + case DTV_SET_INNERFEC: + fe->dtv_property_cache.fec_inner = tvp->u.data; break; - case TV_GET_INNERFEC: - tvp->u.data = fe->tv_property_cache.fec_inner; + case DTV_GET_INNERFEC: + tvp->u.data = fe->dtv_property_cache.fec_inner; break; - case TV_SET_PILOT: - fe->tv_property_cache.pilot = tvp->u.data; + case DTV_SET_PILOT: + fe->dtv_property_cache.pilot = tvp->u.data; break; - case TV_GET_PILOT: - tvp->u.data = fe->tv_property_cache.pilot; + case DTV_GET_PILOT: + tvp->u.data = fe->dtv_property_cache.pilot; break; - case TV_SET_ROLLOFF: - fe->tv_property_cache.rolloff = tvp->u.data; + case DTV_SET_ROLLOFF: + fe->dtv_property_cache.rolloff = tvp->u.data; break; - case TV_GET_ROLLOFF: - tvp->u.data = fe->tv_property_cache.rolloff; + case DTV_GET_ROLLOFF: + tvp->u.data = fe->dtv_property_cache.rolloff; break; - case TV_SET_DELIVERY_SYSTEM: - fe->tv_property_cache.delivery_system = tvp->u.data; + case DTV_SET_DELIVERY_SYSTEM: + fe->dtv_property_cache.delivery_system = tvp->u.data; break; - case TV_GET_DELIVERY_SYSTEM: - tvp->u.data = fe->tv_property_cache.delivery_system; + case DTV_GET_DELIVERY_SYSTEM: + tvp->u.data = fe->dtv_property_cache.delivery_system; break; /* ISDB-T Support here */ - case TV_SET_ISDB_SEGMENT_NUM: - fe->tv_property_cache.isdb_segment_num = tvp->u.data; + case DTV_SET_ISDB_SEGMENT_NUM: + fe->dtv_property_cache.isdb_segment_num = tvp->u.data; break; - case TV_GET_ISDB_SEGMENT_NUM: - tvp->u.data = fe->tv_property_cache.isdb_segment_num; + case DTV_GET_ISDB_SEGMENT_NUM: + tvp->u.data = fe->dtv_property_cache.isdb_segment_num; break; - case TV_SET_ISDB_SEGMENT_WIDTH: - fe->tv_property_cache.isdb_segment_width = tvp->u.data; + case DTV_SET_ISDB_SEGMENT_WIDTH: + fe->dtv_property_cache.isdb_segment_width = tvp->u.data; break; - case TV_GET_ISDB_SEGMENT_WIDTH: - tvp->u.data = fe->tv_property_cache.isdb_segment_width; + case DTV_GET_ISDB_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_segment_width; break; - case TV_GET_ISDB_LAYERA_FEC: - tvp->u.data = fe->tv_property_cache.isdb_layera_fec; + case DTV_GET_ISDB_LAYERA_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layera_fec; break; - case TV_GET_ISDB_LAYERA_MODULATION: - tvp->u.data = fe->tv_property_cache.isdb_layera_modulation; + case DTV_GET_ISDB_LAYERA_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layera_modulation; break; - case TV_GET_ISDB_LAYERA_SEGMENT_WIDTH: - tvp->u.data = fe->tv_property_cache.isdb_layera_segment_width; + case DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layera_segment_width; break; - case TV_GET_ISDB_LAYERB_FEC: - tvp->u.data = fe->tv_property_cache.isdb_layerb_fec; + case DTV_GET_ISDB_LAYERB_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_fec; break; - case TV_GET_ISDB_LAYERB_MODULATION: - tvp->u.data = fe->tv_property_cache.isdb_layerb_modulation; + case DTV_GET_ISDB_LAYERB_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_modulation; break; - case TV_GET_ISDB_LAYERB_SEGMENT_WIDTH: - tvp->u.data = fe->tv_property_cache.isdb_layerb_segment_width; + case DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_segment_width; break; - case TV_GET_ISDB_LAYERC_FEC: - tvp->u.data = fe->tv_property_cache.isdb_layerc_fec; + case DTV_GET_ISDB_LAYERC_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_fec; break; - case TV_GET_ISDB_LAYERC_MODULATION: - tvp->u.data = fe->tv_property_cache.isdb_layerc_modulation; + case DTV_GET_ISDB_LAYERC_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_modulation; break; - case TV_GET_ISDB_LAYERC_SEGMENT_WIDTH: - tvp->u.data = fe->tv_property_cache.isdb_layerc_segment_width; + case DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width; break; - case TV_SET_VOLTAGE: - fe->tv_property_cache.voltage = tvp->u.data; + case DTV_SET_VOLTAGE: + fe->dtv_property_cache.voltage = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE, - (void *)fe->tv_property_cache.voltage); + (void *)fe->dtv_property_cache.voltage); break; - case TV_GET_VOLTAGE: - tvp->u.data = fe->tv_property_cache.voltage; + case DTV_GET_VOLTAGE: + tvp->u.data = fe->dtv_property_cache.voltage; break; - case TV_SET_TONE: - fe->tv_property_cache.sectone = tvp->u.data; + case DTV_SET_TONE: + fe->dtv_property_cache.sectone = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE, - (void *)fe->tv_property_cache.sectone); + (void *)fe->dtv_property_cache.sectone); break; - case TV_GET_TONE: - tvp->u.data = fe->tv_property_cache.sectone; + case DTV_GET_TONE: + tvp->u.data = fe->dtv_property_cache.sectone; break; } @@ -1355,7 +1355,7 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; int err = -EOPNOTSUPP; - tv_property_t *tvp; + dtv_property_t *tvp; dprintk("%s\n", __func__); @@ -1366,14 +1366,14 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, /* TODO: ioctl userdata out of range check here */ tvp = parg; - while(tvp->cmd != TV_SEQ_UNDEFINED) { - tv_property_process(fe, tvp, inode, file); - if( (tvp->cmd == TV_SEQ_TERMINATE) || (tvp->cmd == TV_SEQ_COMPLETE) ) + while(tvp->cmd != DTV_SEQ_UNDEFINED) { + dtv_property_process(fe, tvp, inode, file); + if( (tvp->cmd == DTV_SEQ_TERMINATE) || (tvp->cmd == DTV_SEQ_COMPLETE) ) break; tvp++; } - if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) { + if(fe->dtv_property_cache.state == DTV_SEQ_COMPLETE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); } err = 0; @@ -1556,7 +1556,7 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; - if(fe->tv_property_cache.state == TV_SEQ_COMPLETE) { + if(fe->dtv_property_cache.state == DTV_SEQ_COMPLETE) { if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) { err = -EINVAL; break; diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 61d53ee70..f376f281c 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -170,8 +170,8 @@ struct dvb_frontend_ops { struct dvb_tuner_ops tuner_ops; struct analog_demod_ops analog_ops; - int (*set_property)(struct dvb_frontend* fe, tv_property_t* tvp); - int (*get_property)(struct dvb_frontend* fe, tv_property_t* tvp); + int (*set_property)(struct dvb_frontend* fe, dtv_property_t* tvp); + int (*get_property)(struct dvb_frontend* fe, dtv_property_t* tvp); int (*set_params)(struct dvb_frontend* fe); }; @@ -186,7 +186,7 @@ struct dvb_fe_events { struct mutex mtx; }; -struct tv_frontend_properties { +struct dtv_frontend_properties { /* Cache State */ u32 state; @@ -233,7 +233,7 @@ struct dvb_frontend { void *frontend_priv; void *sec_priv; void *analog_demod_priv; - struct tv_frontend_properties tv_property_cache; + struct dtv_frontend_properties dtv_property_cache; }; extern int dvb_register_frontend(struct dvb_adapter *dvb, diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index fbb459245..f150fa24f 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -776,7 +776,7 @@ error: static int cx24116_get_params(struct dvb_frontend* fe) { struct cx24116_state *state = fe->demodulator_priv; - struct tv_frontend_properties *cache = &fe->tv_property_cache; + struct dtv_frontend_properties *cache = &fe->dtv_property_cache; dprintk("%s()\n",__func__); @@ -796,7 +796,7 @@ static int cx24116_initfe(struct dvb_frontend* fe) return cx24116_diseqc_init(fe); } -static int cx24116_set_property(struct dvb_frontend *fe, tv_property_t* tvp) +static int cx24116_set_property(struct dvb_frontend *fe, dtv_property_t* tvp) { dprintk("%s(..)\n", __func__); return 0; @@ -814,7 +814,7 @@ static int cx24116_set_params(struct dvb_frontend *fe) static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { struct cx24116_state *state = fe->demodulator_priv; - struct tv_frontend_properties *c = &fe->tv_property_cache; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct cx24116_cmd cmd; fe_status_t tunerstat; int ret, above30msps; @@ -852,7 +852,7 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par if (above30msps){ cx24116_writereg(state, 0xF9, 0x01); cx24116_writereg(state, 0xF3, 0x44); - } else { + } else { cx24116_writereg(state, 0xF9, 0x00); cx24116_writereg(state, 0xF3, 0x46); } -- cgit v1.2.3 From 991e6d40e8d9894e3dba60323fad2c7b79948788 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 09:23:01 -0400 Subject: S2API: typedefs replaced, _SEQ_'s removed, fixed 16 command arrays replaced From: Steven Toth After discussion the following changes were made: 1. Removed the typedefs in frontend.h, use structures. 2. In the frontend.h, remove the 16 command limit on the API and switch to a flexible variable length API. For practical reasons a #define limits this to 64, this should be discussed. 3. Changed dvb-core ioctl handing to deal with variable sequences of commands. tune-v0.0.3.c is required to use this API, it contains the interface changes. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 83 +++++++++++++------------ linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 4 +- linux/drivers/media/dvb/frontends/cx24116.c | 2 +- 3 files changed, 46 insertions(+), 43 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 7a94aaa09..98819daf8 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -767,29 +767,14 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, } struct dtv_cmds_h dtv_cmds[] = { - [DTV_SEQ_UNDEFINED] = { - .name = "DTV_SEQ_UNDEFINED", - .cmd = DTV_SEQ_UNDEFINED, + [DTV_TUNE] = { + .name = "DTV_TUNE", + .cmd = DTV_TUNE, .set = 1, }, - [DTV_SEQ_START] = { - .name = "DTV_SEQ_START", - .cmd = DTV_SEQ_START, - .set = 1, - }, - [DTV_SEQ_CONTINUE] = { - .name = "DTV_SEQ_CONTINUE", - .cmd = DTV_SEQ_CONTINUE, - .set = 1, - }, - [DTV_SEQ_COMPLETE] = { - .name = "DTV_SEQ_COMPLETE", - .cmd = DTV_SEQ_COMPLETE, - .set = 1, - }, - [DTV_SEQ_TERMINATE] = { - .name = "DTV_SEQ_TERMINATE", - .cmd = DTV_SEQ_TERMINATE, + [DTV_CLEAR] = { + .name = "DTV_CLEAR", + .cmd = DTV_CLEAR, .set = 1, }, @@ -985,7 +970,7 @@ struct dtv_cmds_h dtv_cmds[] = { }, }; -void dtv_property_dump(dtv_property_t *tvp) +void dtv_property_dump(struct dtv_property *tvp) { int i; @@ -1055,6 +1040,7 @@ int dtv_property_cache_submit(struct dvb_frontend *fe) /* For legacy delivery systems we don't need the delivery_system to be specified */ if(is_legacy_delivery_system(c->delivery_system)) { + printk("%s() legacy, modulation = %d\n", __FUNCTION__, c->modulation); switch(c->modulation) { case QPSK: printk("%s() Preparing QPSK req\n", __FUNCTION__); @@ -1172,7 +1158,7 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, unsigned int cmd, void *parg); -int dtv_property_process(struct dvb_frontend *fe, dtv_property_t *tvp, +int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, struct inode *inode, struct file *file) { int r = 0; @@ -1181,23 +1167,22 @@ int dtv_property_process(struct dvb_frontend *fe, dtv_property_t *tvp, dtv_property_dump(tvp); switch(tvp->cmd) { - case DTV_SEQ_START: - case DTV_SEQ_TERMINATE: + case DTV_CLEAR: /* Reset a cache of data specific to the frontend here. This does * not effect hardware. */ printk("%s() Flushing property cache\n", __FUNCTION__); memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties)); - fe->dtv_property_cache.state = DTV_SEQ_START; + fe->dtv_property_cache.state = tvp->cmd; fe->dtv_property_cache.delivery_system = SYS_UNDEFINED; break; - case DTV_SEQ_COMPLETE: + case DTV_TUNE: /* interpret the cache of data, build either a traditional frontend * tunerequest and submit it to a subset of the ioctl handler, * or, call a new undefined method on the frontend to deal with * all new tune requests. */ - fe->dtv_property_cache.state = DTV_SEQ_COMPLETE; + fe->dtv_property_cache.state = tvp->cmd; printk("%s() Finalised property cache\n", __FUNCTION__); r |= dtv_property_cache_submit(fe); r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, @@ -1355,30 +1340,48 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; int err = -EOPNOTSUPP; - dtv_property_t *tvp; + + struct dtv_properties *tvps = NULL; + struct dtv_property *tvp = NULL; + int i; dprintk("%s\n", __func__); if(cmd == FE_SET_PROPERTY) { printk("%s() FE_SET_PROPERTY\n", __FUNCTION__); - /* TODO: basic property validation here */ + tvps = (struct dtv_properties __user *)parg; - /* TODO: ioctl userdata out of range check here */ - tvp = parg; - while(tvp->cmd != DTV_SEQ_UNDEFINED) { - dtv_property_process(fe, tvp, inode, file); - if( (tvp->cmd == DTV_SEQ_TERMINATE) || (tvp->cmd == DTV_SEQ_COMPLETE) ) - break; - tvp++; + printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num); + printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props); + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if (tvps->num > DTV_IOCTL_MAX_MSGS) + return -EINVAL; + + tvp = (struct dtv_property *) kmalloc(tvps->num * + sizeof(struct dtv_property), GFP_KERNEL); + if (!tvp) { + err = -ENOMEM; + goto out; } - if(fe->dtv_property_cache.state == DTV_SEQ_COMPLETE) { + if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { + err = -EFAULT; + goto out; + } + + for (i = 0; i < tvps->num; i++) + dtv_property_process(fe, tvp + i, inode, file); + + if(fe->dtv_property_cache.state == DTV_TUNE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); } err = 0; } - +out: + kfree(tvp); return err; } @@ -1556,7 +1559,7 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; - if(fe->dtv_property_cache.state == DTV_SEQ_COMPLETE) { + if(fe->dtv_property_cache.state == DTV_TUNE) { if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) { err = -EINVAL; break; diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index f376f281c..85d30201a 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -170,8 +170,8 @@ struct dvb_frontend_ops { struct dvb_tuner_ops tuner_ops; struct analog_demod_ops analog_ops; - int (*set_property)(struct dvb_frontend* fe, dtv_property_t* tvp); - int (*get_property)(struct dvb_frontend* fe, dtv_property_t* tvp); + int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp); + int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp); int (*set_params)(struct dvb_frontend* fe); }; diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index f150fa24f..9f93930a2 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -796,7 +796,7 @@ static int cx24116_initfe(struct dvb_frontend* fe) return cx24116_diseqc_init(fe); } -static int cx24116_set_property(struct dvb_frontend *fe, dtv_property_t* tvp) +static int cx24116_set_property(struct dvb_frontend *fe, struct dtv_property* tvp) { dprintk("%s(..)\n", __func__); return 0; -- cgit v1.2.3 From ce1c579ddcf7b3f27370a985b9563af2e4907a9b Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 09:34:19 -0400 Subject: S2API: Cleanup SYMBOLRATE, INNERFEC -> SYMBOL_RATE, INNER_FEC From: Steven Toth This is now consistent with the existing API. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 98819daf8..9f9a92457 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -805,14 +805,14 @@ struct dtv_cmds_h dtv_cmds[] = { .set = 1, .buffer = 1, }, - [DTV_SET_SYMBOLRATE] = { - .name = "DTV_SET_SYMBOLRATE", - .cmd = DTV_SET_SYMBOLRATE, + [DTV_SET_SYMBOL_RATE] = { + .name = "DTV_SET_SYMBOL_RATE", + .cmd = DTV_SET_SYMBOL_RATE, .set = 1, }, - [DTV_SET_INNERFEC] = { - .name = "DTV_SET_INNERFEC", - .cmd = DTV_SET_INNERFEC, + [DTV_SET_INNER_FEC] = { + .name = "DTV_SET_INNER_FEC", + .cmd = DTV_SET_INNER_FEC, .set = 1, }, [DTV_SET_VOLTAGE] = { @@ -878,14 +878,14 @@ struct dtv_cmds_h dtv_cmds[] = { .set = 0, .buffer = 1, }, - [DTV_GET_SYMBOLRATE] = { - .name = "DTV_GET_SYMBOLRATE", - .cmd = DTV_GET_SYMBOLRATE, + [DTV_GET_SYMBOL_RATE] = { + .name = "DTV_GET_SYMBOL_RATE", + .cmd = DTV_GET_SYMBOL_RATE, .set = 0, }, - [DTV_GET_INNERFEC] = { - .name = "DTV_GET_INNERFEC", - .cmd = DTV_GET_INNERFEC, + [DTV_GET_INNER_FEC] = { + .name = "DTV_GET_INNER_FEC", + .cmd = DTV_GET_INNER_FEC, .set = 0, }, [DTV_GET_VOLTAGE] = { @@ -1212,16 +1212,16 @@ int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_GET_INVERSION: tvp->u.data = fe->dtv_property_cache.inversion; break; - case DTV_SET_SYMBOLRATE: + case DTV_SET_SYMBOL_RATE: fe->dtv_property_cache.symbol_rate = tvp->u.data; break; - case DTV_GET_SYMBOLRATE: + case DTV_GET_SYMBOL_RATE: tvp->u.data = fe->dtv_property_cache.symbol_rate; break; - case DTV_SET_INNERFEC: + case DTV_SET_INNER_FEC: fe->dtv_property_cache.fec_inner = tvp->u.data; break; - case DTV_GET_INNERFEC: + case DTV_GET_INNER_FEC: tvp->u.data = fe->dtv_property_cache.fec_inner; break; case DTV_SET_PILOT: -- cgit v1.2.3 From a8eaf0e515950bc3f8f591d838ab901ff970947a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 11 Sep 2008 22:02:12 -0400 Subject: sms1xxx: fix product name for Hauppauge WinTV MiniStick From: Michael Krufky Priority: high Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/sms-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index cc5efb643..06297665c 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -87,7 +87,7 @@ static struct sms_board sms_boards[] = { .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", }, [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { - .name = "Hauppauge WinTV-Nova-T-MiniStick", + .name = "Hauppauge WinTV MiniStick", .type = SMS_NOVA_B0, .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-01.fw", }, -- cgit v1.2.3 From 60bc4d4c87081996e0d9a995671f4b3f52441a41 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 22:52:32 -0400 Subject: S2API: Reduce demod driver complexity by using a cache sync From: Steven Toth For demod drivers, we want a single tuning function (set_frontend) to have access to the properly constructed dvb_frontend_parameters structure, or the cache values - regardless of whether the legacy or new API is being used. This cuts down on redundant code in the demod drivers and ensures the tuning parameters are consistent regardless of the API entry type. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 9f9a92457..e618aa131 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1004,6 +1004,42 @@ int is_legacy_delivery_system(fe_delivery_system_t s) return 0; } +/* Synchronise the legacy tuning parameters into the cache, so that demodulator + * drivers can use a single set_frontend tuning function, regardless of whether + * it's being used for the legacy or new API, reducing code and complexity. + */ +void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + c->frequency = p->frequency; + c->inversion = p->inversion; + + switch (fe->ops.info.type) { + case FE_QPSK: + c->symbol_rate = p->u.qpsk.symbol_rate; + c->fec_inner = p->u.qpsk.fec_inner; + break; + case FE_QAM: + c->symbol_rate = p->u.qam.symbol_rate; + c->fec_inner = p->u.qam.fec_inner; + c->modulation = p->u.qam.modulation; + break; + case FE_OFDM: + c->bandwidth = p->u.ofdm.bandwidth; + c->code_rate_HP = p->u.ofdm.code_rate_HP; + c->code_rate_LP = p->u.ofdm.code_rate_LP; + c->modulation = p->u.ofdm.constellation; + c->transmission_mode = p->u.ofdm.transmission_mode; + c->guard_interval = p->u.ofdm.guard_interval; + c->hierarchy = p->u.ofdm.hierarchy_information; + break; + case FE_ATSC: + c->modulation = p->u.vsb.modulation; + break; + } +} + int dtv_property_cache_submit(struct dvb_frontend *fe) { @@ -1559,6 +1595,8 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; + dtv_property_cache_sync(fe, &fepriv->parameters); + if(fe->dtv_property_cache.state == DTV_TUNE) { if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) { err = -EINVAL; -- cgit v1.2.3 From 31b33763bbe39a4cf92fd54b60312687aace97a1 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 23:27:13 -0400 Subject: S2API: Cleanup code that prepares tuning structures. From: Steven Toth Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 219 ++++++++++-------------- 1 file changed, 91 insertions(+), 128 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index e618aa131..5a97f749d 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1012,6 +1012,8 @@ void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parame { struct dtv_frontend_properties *c = &fe->dtv_property_cache; + printk("%s()\n", __FUNCTION__); + c->frequency = p->frequency; c->inversion = p->inversion; @@ -1040,153 +1042,114 @@ void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parame } } -int dtv_property_cache_submit(struct dvb_frontend *fe) +/* Ensure the cached values are set correctly in the frontend + * legacy tuning structures, for the advanced tuning API. + */ +void dtv_property_legacy_params_sync(struct dvb_frontend *fe) { + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct dvb_frontend_private *fepriv = fe->frontend_priv; + struct dvb_frontend_parameters *p = &fepriv->parameters; - /* We have to do one of two things: - * To support legacy devices using the new API we take values from - * the tv_cache and generate a legacy truning structure. - * - * Or, - * - * To support advanced tuning devices with the new API we - * notify the new advance driver type that a tuning operation is required - * and let it pull values from the cache as is, we don't need to - * pass structures. - * - * We'll use the modulation type to assess how this is handled. as the API - * progresses we'll probably want to have a flag in dvb_frontend_ops - * to allow the frontend driver to dictate how it likes to be tuned. - * - * Because of how this is attached to the ioctl handler for legacy support, - * it's important to return an appropriate result code with atleast the following - * three meanings: - * < 0 = processing error - * 0 = lecagy ioctl handler to submit a traditional set_frontend() call. - * 1 = lecagy ioctl handler should NOT submit a traditional set_frontend() call. - */ + printk("%s()\n", __FUNCTION__); - int r; + p->frequency = c->frequency; + p->inversion = c->inversion; + switch (fe->ops.info.type) { + case FE_QPSK: + printk("%s() Preparing QPSK req\n", __FUNCTION__); + p->u.qpsk.symbol_rate = c->symbol_rate; + p->u.qpsk.fec_inner = c->fec_inner; + break; + case FE_QAM: + printk("%s() Preparing QAM req\n", __FUNCTION__); + p->u.qam.symbol_rate = c->symbol_rate; + p->u.qam.fec_inner = c->fec_inner; + p->u.qam.modulation = c->modulation; + break; + case FE_OFDM: + printk("%s() Preparing OFDM req\n", __FUNCTION__); + p->u.ofdm.bandwidth = c->bandwidth; + p->u.ofdm.code_rate_HP = c->code_rate_HP; + p->u.ofdm.code_rate_LP = c->code_rate_LP; + p->u.ofdm.constellation = c->modulation; + p->u.ofdm.transmission_mode = c->transmission_mode; + p->u.ofdm.guard_interval = c->guard_interval; + p->u.ofdm.hierarchy_information = c->hierarchy; + break; + case FE_ATSC: + printk("%s() Preparing VSB req\n", __FUNCTION__); + p->u.vsb.modulation = c->modulation; + break; + } +} + +/* Ensure the cached values are set correctly in the frontend + * legacy tuning structures, for the legacy tuning API. + */ +void dtv_property_adv_params_sync(struct dvb_frontend *fe) +{ struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct dvb_frontend_private *fepriv = fe->frontend_priv; - struct dvb_frontend_parameters p; + struct dvb_frontend_parameters *p = &fepriv->parameters; printk("%s()\n", __FUNCTION__); - /* For legacy delivery systems we don't need the delivery_system to be specified */ - if(is_legacy_delivery_system(c->delivery_system)) { - printk("%s() legacy, modulation = %d\n", __FUNCTION__, c->modulation); - switch(c->modulation) { - case QPSK: - printk("%s() Preparing QPSK req\n", __FUNCTION__); - p.frequency = c->frequency; - p.inversion = c->inversion; - p.u.qpsk.symbol_rate = c->symbol_rate; - p.u.qpsk.fec_inner = c->fec_inner; - memcpy(&fepriv->parameters, &p, - sizeof (struct dvb_frontend_parameters)); + p->frequency = c->frequency; + p->inversion = c->inversion; - /* Call the traditional tuning mechanisms. */ + switch(c->modulation) { + case _8PSK: + case _16APSK: + case NBC_QPSK: + p->u.qpsk.symbol_rate = c->symbol_rate; + p->u.qpsk.fec_inner = c->fec_inner; + break; + default: + break; + } - r = 0; - break; - case QAM_16: - case QAM_32: - case QAM_64: - case QAM_128: - case QAM_256: - case QAM_AUTO: - printk("%s() Preparing QAM req\n", __FUNCTION__); - p.frequency = c->frequency; - p.inversion = c->inversion; - p.u.qam.symbol_rate = c->symbol_rate; - p.u.vsb.modulation = c->modulation; - printk("%s() frequency = %d\n", __FUNCTION__, p.frequency); - printk("%s() QAM = %d\n", __FUNCTION__, p.u.vsb.modulation); - memcpy(&fepriv->parameters, &p, - sizeof (struct dvb_frontend_parameters)); + if(c->delivery_system == SYS_ISDBT) { + /* Fake out a generic DVB-T request so we pass validation in the ioctl */ + p->frequency = c->frequency; + p->inversion = INVERSION_AUTO; + p->u.ofdm.constellation = QAM_AUTO; + p->u.ofdm.code_rate_HP = FEC_AUTO; + p->u.ofdm.code_rate_LP = FEC_AUTO; + p->u.ofdm.bandwidth = BANDWIDTH_AUTO; + p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + p->u.ofdm.hierarchy_information = HIERARCHY_AUTO; + } +} - /* At this point we're fully formed for backwards - * compatability and we need to return this - * via the ioctl handler as SET_FRONTEND (arg). - * We've already patched the new values into the - * frontends tuning structures so the ioctl code just - * continues as if a legacy tune structure was passed - * from userspace. - */ +void dtv_property_cache_submit(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - r = 0; - break; - case VSB_8: - case VSB_16: - printk("%s() Preparing VSB req\n", __FUNCTION__); - p.frequency = c->frequency; - p.u.vsb.modulation = c->modulation; - memcpy(&fepriv->parameters, &p, - sizeof (struct dvb_frontend_parameters)); + printk("%s()\n", __FUNCTION__); - /* Call the traditional tuning mechanisms. */ + /* For legacy delivery systems we don't need the delivery_system to + * be specified, but we populate the older structures from the cache + * so we can call set_frontend on older drivers. + */ + if(is_legacy_delivery_system(c->delivery_system)) { + + printk("%s() legacy, modulation = %d\n", __FUNCTION__, c->modulation); + dtv_property_legacy_params_sync(fe); - r = 0; - break; - /* TODO: Add any missing modulation types */ - default: - r = -1; - } } else { + printk("%s() adv, modulation = %d\n", __FUNCTION__, c->modulation); + /* For advanced delivery systems / modulation types ... * we seed the lecacy dvb_frontend_parameters structure * so that the sanity checking code later in the IOCTL processing * can validate our basic frequency ranges, symbolrates, modulation * etc. */ - r = -1; - - switch(c->modulation) { - case _8PSK: - case _16APSK: - case NBC_QPSK: - /* Just post a notification to the demod driver and let it pull - * the specific values it wants from its dtv_property_cache. - * It can decide how best to use those parameters. - * IOCTL will call set_frontend (by default) due to zigzag - * support etc. - */ - if (fe->ops.set_params) - r = fe->ops.set_params(fe); - - p.frequency = c->frequency; - p.inversion = c->inversion; - p.u.qpsk.symbol_rate = c->symbol_rate; - p.u.qpsk.fec_inner = c->fec_inner; - memcpy(&fepriv->parameters, &p, - sizeof (struct dvb_frontend_parameters)); - - r = 0; - break; - default: - r = -1; - } - - if(c->delivery_system == SYS_ISDBT) { - /* Fake out a generic DVB-T request so we pass validation in the ioctl */ - p.frequency = c->frequency; - p.inversion = INVERSION_AUTO; - p.u.ofdm.constellation = QAM_AUTO; - p.u.ofdm.code_rate_HP = FEC_AUTO; - p.u.ofdm.code_rate_LP = FEC_AUTO; - p.u.ofdm.bandwidth = BANDWIDTH_AUTO; - p.u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; - p.u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; - p.u.ofdm.hierarchy_information = HIERARCHY_AUTO; - memcpy(&fepriv->parameters, &p, - sizeof (struct dvb_frontend_parameters)); - - r = 0; - } + dtv_property_adv_params_sync(fe); } - return r; } static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, @@ -1214,13 +1177,13 @@ int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, break; case DTV_TUNE: /* interpret the cache of data, build either a traditional frontend - * tunerequest and submit it to a subset of the ioctl handler, - * or, call a new undefined method on the frontend to deal with - * all new tune requests. + * tunerequest so we can pass validation in the FE_SET_FRONTEND + * ioctl. */ fe->dtv_property_cache.state = tvp->cmd; printk("%s() Finalised property cache\n", __FUNCTION__); - r |= dtv_property_cache_submit(fe); + dtv_property_cache_submit(fe); + r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, &fepriv->parameters); break; -- cgit v1.2.3 From 68c60334487dbea91c1e89b8d5034623b1b4dbc8 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 23:33:04 -0400 Subject: S2API: ISDBT_SEGMENT_NUM -> ISDBT_SEGMENT_IDX From: Steven Toth Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 20 ++++++++++---------- linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 5a97f749d..405fc7eab 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -840,9 +840,9 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_SET_DELIVERY_SYSTEM, .set = 1, }, - [DTV_SET_ISDB_SEGMENT_NUM] = { - .name = "DTV_SET_ISDB_SEGMENT_NUM", - .cmd = DTV_SET_ISDB_SEGMENT_NUM, + [DTV_SET_ISDB_SEGMENT_IDX] = { + .name = "DTV_SET_ISDB_SEGMENT_IDX", + .cmd = DTV_SET_ISDB_SEGMENT_IDX, .set = 1, }, [DTV_SET_ISDB_SEGMENT_WIDTH] = { @@ -913,9 +913,9 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_GET_DELIVERY_SYSTEM, .set = 0, }, - [DTV_GET_ISDB_SEGMENT_NUM] = { - .name = "DTV_GET_ISDB_SEGMENT_NUM", - .cmd = DTV_GET_ISDB_SEGMENT_NUM, + [DTV_GET_ISDB_SEGMENT_IDX] = { + .name = "DTV_GET_ISDB_SEGMENT_IDX", + .cmd = DTV_GET_ISDB_SEGMENT_IDX, .set = 0, }, [DTV_GET_ISDB_SEGMENT_WIDTH] = { @@ -1243,11 +1243,11 @@ int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, break; /* ISDB-T Support here */ - case DTV_SET_ISDB_SEGMENT_NUM: - fe->dtv_property_cache.isdb_segment_num = tvp->u.data; + case DTV_SET_ISDB_SEGMENT_IDX: + fe->dtv_property_cache.isdb_segment_idx = tvp->u.data; break; - case DTV_GET_ISDB_SEGMENT_NUM: - tvp->u.data = fe->dtv_property_cache.isdb_segment_num; + case DTV_GET_ISDB_SEGMENT_IDX: + tvp->u.data = fe->dtv_property_cache.isdb_segment_idx; break; case DTV_SET_ISDB_SEGMENT_WIDTH: fe->dtv_property_cache.isdb_segment_width = tvp->u.data; diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 85d30201a..1c2090966 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -212,7 +212,7 @@ struct dtv_frontend_properties { fe_delivery_system_t delivery_system; /* ISDB-T specifics */ - u32 isdb_segment_num; + u32 isdb_segment_idx; u32 isdb_segment_width; fe_code_rate_t isdb_layera_fec; fe_modulation_t isdb_layera_modulation; -- cgit v1.2.3 From 78ac88986b11f05c64e919c58df4d5033ed70e19 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 11 Sep 2008 23:53:50 -0400 Subject: S2API: Ensure cache->delivery_system is set at all times. From: Steven Toth This helps complex demods which support different modulation types be better informed. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 405fc7eab..35351d6ed 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1021,11 +1021,13 @@ void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parame case FE_QPSK: c->symbol_rate = p->u.qpsk.symbol_rate; c->fec_inner = p->u.qpsk.fec_inner; + c->delivery_system = SYS_DVBS; break; case FE_QAM: c->symbol_rate = p->u.qam.symbol_rate; c->fec_inner = p->u.qam.fec_inner; c->modulation = p->u.qam.modulation; + c->delivery_system = SYS_DVBC_ANNEX_AC; break; case FE_OFDM: c->bandwidth = p->u.ofdm.bandwidth; @@ -1035,9 +1037,14 @@ void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parame c->transmission_mode = p->u.ofdm.transmission_mode; c->guard_interval = p->u.ofdm.guard_interval; c->hierarchy = p->u.ofdm.hierarchy_information; + c->delivery_system = SYS_DVBT; break; case FE_ATSC: c->modulation = p->u.vsb.modulation; + if ((c->modulation == VSB_8) || (c->modulation == VSB_16)) + c->delivery_system = SYS_ATSC; + else + c->delivery_system = SYS_DVBC_ANNEX_B; break; } } @@ -1061,12 +1068,14 @@ void dtv_property_legacy_params_sync(struct dvb_frontend *fe) printk("%s() Preparing QPSK req\n", __FUNCTION__); p->u.qpsk.symbol_rate = c->symbol_rate; p->u.qpsk.fec_inner = c->fec_inner; + c->delivery_system = SYS_DVBS; break; case FE_QAM: printk("%s() Preparing QAM req\n", __FUNCTION__); p->u.qam.symbol_rate = c->symbol_rate; p->u.qam.fec_inner = c->fec_inner; p->u.qam.modulation = c->modulation; + c->delivery_system = SYS_DVBC_ANNEX_AC; break; case FE_OFDM: printk("%s() Preparing OFDM req\n", __FUNCTION__); @@ -1077,10 +1086,15 @@ void dtv_property_legacy_params_sync(struct dvb_frontend *fe) p->u.ofdm.transmission_mode = c->transmission_mode; p->u.ofdm.guard_interval = c->guard_interval; p->u.ofdm.hierarchy_information = c->hierarchy; + c->delivery_system = SYS_DVBT; break; case FE_ATSC: printk("%s() Preparing VSB req\n", __FUNCTION__); p->u.vsb.modulation = c->modulation; + if ((c->modulation == VSB_8) || (c->modulation == VSB_16)) + c->delivery_system = SYS_ATSC; + else + c->delivery_system = SYS_DVBC_ANNEX_B; break; } } -- cgit v1.2.3 From 72a780c4333fa7b78c93bdc12cf740ca51389daa Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Fri, 12 Sep 2008 00:34:28 -0400 Subject: S2API: Remove the DTV_SET_ and DTV_GET_ prefixes From: Steven Toth The API now assumes that ioctl calls for FE_SET_PROPERTY and all set commands, and FE_GET_PROPERTY are get commands. Simplification. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 431 ++++++++++++------------ 1 file changed, 212 insertions(+), 219 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 35351d6ed..3df500179 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -779,193 +779,128 @@ struct dtv_cmds_h dtv_cmds[] = { }, /* Set */ - [DTV_SET_FREQUENCY] = { - .name = "DTV_SET_FREQUENCY", - .cmd = DTV_SET_FREQUENCY, + [DTV_FREQUENCY] = { + .name = "DTV_FREQUENCY", + .cmd = DTV_FREQUENCY, .set = 1, }, - [DTV_SET_BANDWIDTH] = { - .name = "DTV_SET_BANDWIDTH", - .cmd = DTV_SET_BANDWIDTH, + [DTV_BANDWIDTH] = { + .name = "DTV_BANDWIDTH", + .cmd = DTV_BANDWIDTH, .set = 1, }, - [DTV_SET_MODULATION] = { - .name = "DTV_SET_MODULATION", - .cmd = DTV_SET_MODULATION, + [DTV_MODULATION] = { + .name = "DTV_MODULATION", + .cmd = DTV_MODULATION, .set = 1, }, - [DTV_SET_INVERSION] = { - .name = "DTV_SET_INVERSION", - .cmd = DTV_SET_INVERSION, + [DTV_INVERSION] = { + .name = "DTV_INVERSION", + .cmd = DTV_INVERSION, .set = 1, }, - [DTV_SET_DISEQC_MASTER] = { - .name = "DTV_SET_DISEQC_MASTER", - .cmd = DTV_SET_DISEQC_MASTER, + [DTV_DISEQC_MASTER] = { + .name = "DTV_DISEQC_MASTER", + .cmd = DTV_DISEQC_MASTER, .set = 1, .buffer = 1, }, - [DTV_SET_SYMBOL_RATE] = { - .name = "DTV_SET_SYMBOL_RATE", - .cmd = DTV_SET_SYMBOL_RATE, + [DTV_SYMBOL_RATE] = { + .name = "DTV_SYMBOL_RATE", + .cmd = DTV_SYMBOL_RATE, .set = 1, }, - [DTV_SET_INNER_FEC] = { - .name = "DTV_SET_INNER_FEC", - .cmd = DTV_SET_INNER_FEC, + [DTV_INNER_FEC] = { + .name = "DTV_INNER_FEC", + .cmd = DTV_INNER_FEC, .set = 1, }, - [DTV_SET_VOLTAGE] = { - .name = "DTV_SET_VOLTAGE", - .cmd = DTV_SET_VOLTAGE, + [DTV_VOLTAGE] = { + .name = "DTV_VOLTAGE", + .cmd = DTV_VOLTAGE, .set = 1, }, - [DTV_SET_TONE] = { - .name = "DTV_SET_TONE", - .cmd = DTV_SET_TONE, + [DTV_TONE] = { + .name = "DTV_TONE", + .cmd = DTV_TONE, .set = 1, }, - [DTV_SET_PILOT] = { - .name = "DTV_SET_PILOT", - .cmd = DTV_SET_PILOT, + [DTV_PILOT] = { + .name = "DTV_PILOT", + .cmd = DTV_PILOT, .set = 1, }, - [DTV_SET_ROLLOFF] = { - .name = "DTV_SET_ROLLOFF", - .cmd = DTV_SET_ROLLOFF, + [DTV_ROLLOFF] = { + .name = "DTV_ROLLOFF", + .cmd = DTV_ROLLOFF, .set = 1, }, - [DTV_SET_DELIVERY_SYSTEM] = { - .name = "DTV_SET_DELIVERY_SYSTEM", - .cmd = DTV_SET_DELIVERY_SYSTEM, + [DTV_DELIVERY_SYSTEM] = { + .name = "DTV_DELIVERY_SYSTEM", + .cmd = DTV_DELIVERY_SYSTEM, .set = 1, }, - [DTV_SET_ISDB_SEGMENT_IDX] = { - .name = "DTV_SET_ISDB_SEGMENT_IDX", - .cmd = DTV_SET_ISDB_SEGMENT_IDX, + [DTV_ISDB_SEGMENT_IDX] = { + .name = "DTV_ISDB_SEGMENT_IDX", + .cmd = DTV_ISDB_SEGMENT_IDX, .set = 1, }, - [DTV_SET_ISDB_SEGMENT_WIDTH] = { - .name = "DTV_SET_ISDB_SEGMENT_WIDTH", - .cmd = DTV_SET_ISDB_SEGMENT_WIDTH, + [DTV_ISDB_SEGMENT_WIDTH] = { + .name = "DTV_ISDB_SEGMENT_WIDTH", + .cmd = DTV_ISDB_SEGMENT_WIDTH, .set = 1, }, /* Get */ - [DTV_GET_FREQUENCY] = { - .name = "DTV_GET_FREQUENCY", - .cmd = DTV_GET_FREQUENCY, - .set = 0, - }, - [DTV_GET_BANDWIDTH] = { - .name = "DTV_GET_BANDWIDTH", - .cmd = DTV_GET_BANDWIDTH, - .set = 0, - }, - [DTV_GET_MODULATION] = { - .name = "DTV_GET_MODULATION", - .cmd = DTV_GET_MODULATION, - .set = 0, - }, - [DTV_GET_INVERSION] = { - .name = "DTV_GET_INVERSION", - .cmd = DTV_GET_INVERSION, - .set = 0, - }, - [DTV_GET_DISEQC_SLAVE_REPLY] = { - .name = "DTV_GET_DISEQC_SLAVE_REPLY", - .cmd = DTV_GET_DISEQC_SLAVE_REPLY, + [DTV_DISEQC_SLAVE_REPLY] = { + .name = "DTV_DISEQC_SLAVE_REPLY", + .cmd = DTV_DISEQC_SLAVE_REPLY, .set = 0, .buffer = 1, }, - [DTV_GET_SYMBOL_RATE] = { - .name = "DTV_GET_SYMBOL_RATE", - .cmd = DTV_GET_SYMBOL_RATE, - .set = 0, - }, - [DTV_GET_INNER_FEC] = { - .name = "DTV_GET_INNER_FEC", - .cmd = DTV_GET_INNER_FEC, - .set = 0, - }, - [DTV_GET_VOLTAGE] = { - .name = "DTV_GET_VOLTAGE", - .cmd = DTV_GET_VOLTAGE, - .set = 0, - }, - [DTV_GET_TONE] = { - .name = "DTV_GET_TONE", - .cmd = DTV_GET_TONE, - .set = 0, - }, - [DTV_GET_PILOT] = { - .name = "DTV_GET_PILOT", - .cmd = DTV_GET_PILOT, - .set = 0, - }, - [DTV_GET_ROLLOFF] = { - .name = "DTV_GET_ROLLOFF", - .cmd = DTV_GET_ROLLOFF, - .set = 0, - }, - [DTV_GET_DELIVERY_SYSTEM] = { - .name = "DTV_GET_DELIVERY_SYSTEM", - .cmd = DTV_GET_DELIVERY_SYSTEM, - .set = 0, - }, - [DTV_GET_ISDB_SEGMENT_IDX] = { - .name = "DTV_GET_ISDB_SEGMENT_IDX", - .cmd = DTV_GET_ISDB_SEGMENT_IDX, - .set = 0, - }, - [DTV_GET_ISDB_SEGMENT_WIDTH] = { - .name = "DTV_GET_ISDB_SEGMENT_WIDTH", - .cmd = DTV_GET_ISDB_SEGMENT_WIDTH, + [DTV_ISDB_LAYERA_FEC] = { + .name = "DTV_ISDB_LAYERA_FEC", + .cmd = DTV_ISDB_LAYERA_FEC, .set = 0, }, - [DTV_GET_ISDB_LAYERA_FEC] = { - .name = "DTV_GET_ISDB_LAYERA_FEC", - .cmd = DTV_GET_ISDB_LAYERA_FEC, + [DTV_ISDB_LAYERA_MODULATION] = { + .name = "DTV_ISDB_LAYERA_MODULATION", + .cmd = DTV_ISDB_LAYERA_MODULATION, .set = 0, }, - [DTV_GET_ISDB_LAYERA_MODULATION] = { - .name = "DTV_GET_ISDB_LAYERA_MODULATION", - .cmd = DTV_GET_ISDB_LAYERA_MODULATION, + [DTV_ISDB_LAYERA_SEGMENT_WIDTH] = { + .name = "DTV_ISDB_LAYERA_SEGMENT_WIDTH", + .cmd = DTV_ISDB_LAYERA_SEGMENT_WIDTH, .set = 0, }, - [DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH] = { - .name = "DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH", - .cmd = DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH, + [DTV_ISDB_LAYERB_FEC] = { + .name = "DTV_ISDB_LAYERB_FEC", + .cmd = DTV_ISDB_LAYERB_FEC, .set = 0, }, - [DTV_GET_ISDB_LAYERB_FEC] = { - .name = "DTV_GET_ISDB_LAYERB_FEC", - .cmd = DTV_GET_ISDB_LAYERB_FEC, + [DTV_ISDB_LAYERB_MODULATION] = { + .name = "DTV_ISDB_LAYERB_MODULATION", + .cmd = DTV_ISDB_LAYERB_MODULATION, .set = 0, }, - [DTV_GET_ISDB_LAYERB_MODULATION] = { - .name = "DTV_GET_ISDB_LAYERB_MODULATION", - .cmd = DTV_GET_ISDB_LAYERB_MODULATION, + [DTV_ISDB_LAYERB_SEGMENT_WIDTH] = { + .name = "DTV_ISDB_LAYERB_SEGMENT_WIDTH", + .cmd = DTV_ISDB_LAYERB_SEGMENT_WIDTH, .set = 0, }, - [DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH] = { - .name = "DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH", - .cmd = DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH, + [DTV_ISDB_LAYERC_FEC] = { + .name = "DTV_ISDB_LAYERC_FEC", + .cmd = DTV_ISDB_LAYERC_FEC, .set = 0, }, - [DTV_GET_ISDB_LAYERC_FEC] = { - .name = "DTV_GET_ISDB_LAYERC_FEC", - .cmd = DTV_GET_ISDB_LAYERC_FEC, + [DTV_ISDB_LAYERC_MODULATION] = { + .name = "DTV_ISDB_LAYERC_MODULATION", + .cmd = DTV_ISDB_LAYERC_MODULATION, .set = 0, }, - [DTV_GET_ISDB_LAYERC_MODULATION] = { - .name = "DTV_GET_ISDB_LAYERC_MODULATION", - .cmd = DTV_GET_ISDB_LAYERC_MODULATION, - .set = 0, - }, - [DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH] = { - .name = "DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH", - .cmd = DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH, + [DTV_ISDB_LAYERC_SEGMENT_WIDTH] = { + .name = "DTV_ISDB_LAYERC_SEGMENT_WIDTH", + .cmd = DTV_ISDB_LAYERC_SEGMENT_WIDTH, .set = 0, }, }; @@ -1171,7 +1106,92 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, unsigned int cmd, void *parg); -int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, +int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, + struct inode *inode, struct file *file) +{ + int r = 0; + + printk("%s()\n", __FUNCTION__); + + dtv_property_dump(tvp); + + switch(tvp->cmd) { + case DTV_FREQUENCY: + tvp->u.data = fe->dtv_property_cache.frequency; + break; + case DTV_MODULATION: + tvp->u.data = fe->dtv_property_cache.modulation; + break; + case DTV_BANDWIDTH: + tvp->u.data = fe->dtv_property_cache.bandwidth; + break; + case DTV_INVERSION: + tvp->u.data = fe->dtv_property_cache.inversion; + break; + case DTV_SYMBOL_RATE: + tvp->u.data = fe->dtv_property_cache.symbol_rate; + break; + case DTV_INNER_FEC: + tvp->u.data = fe->dtv_property_cache.fec_inner; + break; + case DTV_PILOT: + tvp->u.data = fe->dtv_property_cache.pilot; + break; + case DTV_ROLLOFF: + tvp->u.data = fe->dtv_property_cache.rolloff; + break; + case DTV_DELIVERY_SYSTEM: + tvp->u.data = fe->dtv_property_cache.delivery_system; + break; + + /* ISDB-T Support here */ + case DTV_ISDB_SEGMENT_IDX: + tvp->u.data = fe->dtv_property_cache.isdb_segment_idx; + break; + case DTV_ISDB_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_segment_width; + break; + case DTV_ISDB_LAYERA_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layera_fec; + break; + case DTV_ISDB_LAYERA_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layera_modulation; + break; + case DTV_ISDB_LAYERA_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layera_segment_width; + break; + case DTV_ISDB_LAYERB_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_fec; + break; + case DTV_ISDB_LAYERB_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_modulation; + break; + case DTV_ISDB_LAYERB_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layerb_segment_width; + break; + case DTV_ISDB_LAYERC_FEC: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_fec; + break; + case DTV_ISDB_LAYERC_MODULATION: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_modulation; + break; + case DTV_ISDB_LAYERC_SEGMENT_WIDTH: + tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width; + break; + case DTV_VOLTAGE: + tvp->u.data = fe->dtv_property_cache.voltage; + break; + case DTV_TONE: + tvp->u.data = fe->dtv_property_cache.sectone; + break; + default: + r = -1; + } + + return r; +} + +int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, struct inode *inode, struct file *file) { int r = 0; @@ -1201,117 +1221,53 @@ int dtv_property_process(struct dvb_frontend *fe, struct dtv_property *tvp, r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, &fepriv->parameters); break; - case DTV_SET_FREQUENCY: + case DTV_FREQUENCY: fe->dtv_property_cache.frequency = tvp->u.data; break; - case DTV_GET_FREQUENCY: - tvp->u.data = fe->dtv_property_cache.frequency; - break; - case DTV_SET_MODULATION: + case DTV_MODULATION: fe->dtv_property_cache.modulation = tvp->u.data; break; - case DTV_GET_MODULATION: - tvp->u.data = fe->dtv_property_cache.modulation; - break; - case DTV_SET_BANDWIDTH: + case DTV_BANDWIDTH: fe->dtv_property_cache.bandwidth = tvp->u.data; break; - case DTV_GET_BANDWIDTH: - tvp->u.data = fe->dtv_property_cache.bandwidth; - break; - case DTV_SET_INVERSION: + case DTV_INVERSION: fe->dtv_property_cache.inversion = tvp->u.data; break; - case DTV_GET_INVERSION: - tvp->u.data = fe->dtv_property_cache.inversion; - break; - case DTV_SET_SYMBOL_RATE: + case DTV_SYMBOL_RATE: fe->dtv_property_cache.symbol_rate = tvp->u.data; break; - case DTV_GET_SYMBOL_RATE: - tvp->u.data = fe->dtv_property_cache.symbol_rate; - break; - case DTV_SET_INNER_FEC: + case DTV_INNER_FEC: fe->dtv_property_cache.fec_inner = tvp->u.data; break; - case DTV_GET_INNER_FEC: - tvp->u.data = fe->dtv_property_cache.fec_inner; - break; - case DTV_SET_PILOT: + case DTV_PILOT: fe->dtv_property_cache.pilot = tvp->u.data; break; - case DTV_GET_PILOT: - tvp->u.data = fe->dtv_property_cache.pilot; - break; - case DTV_SET_ROLLOFF: + case DTV_ROLLOFF: fe->dtv_property_cache.rolloff = tvp->u.data; break; - case DTV_GET_ROLLOFF: - tvp->u.data = fe->dtv_property_cache.rolloff; - break; - case DTV_SET_DELIVERY_SYSTEM: + case DTV_DELIVERY_SYSTEM: fe->dtv_property_cache.delivery_system = tvp->u.data; break; - case DTV_GET_DELIVERY_SYSTEM: - tvp->u.data = fe->dtv_property_cache.delivery_system; - break; /* ISDB-T Support here */ - case DTV_SET_ISDB_SEGMENT_IDX: + case DTV_ISDB_SEGMENT_IDX: fe->dtv_property_cache.isdb_segment_idx = tvp->u.data; break; - case DTV_GET_ISDB_SEGMENT_IDX: - tvp->u.data = fe->dtv_property_cache.isdb_segment_idx; - break; - case DTV_SET_ISDB_SEGMENT_WIDTH: + case DTV_ISDB_SEGMENT_WIDTH: fe->dtv_property_cache.isdb_segment_width = tvp->u.data; break; - case DTV_GET_ISDB_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_segment_width; - break; - case DTV_GET_ISDB_LAYERA_FEC: - tvp->u.data = fe->dtv_property_cache.isdb_layera_fec; - break; - case DTV_GET_ISDB_LAYERA_MODULATION: - tvp->u.data = fe->dtv_property_cache.isdb_layera_modulation; - break; - case DTV_GET_ISDB_LAYERA_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_layera_segment_width; - break; - case DTV_GET_ISDB_LAYERB_FEC: - tvp->u.data = fe->dtv_property_cache.isdb_layerb_fec; - break; - case DTV_GET_ISDB_LAYERB_MODULATION: - tvp->u.data = fe->dtv_property_cache.isdb_layerb_modulation; - break; - case DTV_GET_ISDB_LAYERB_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_layerb_segment_width; - break; - case DTV_GET_ISDB_LAYERC_FEC: - tvp->u.data = fe->dtv_property_cache.isdb_layerc_fec; - break; - case DTV_GET_ISDB_LAYERC_MODULATION: - tvp->u.data = fe->dtv_property_cache.isdb_layerc_modulation; - break; - case DTV_GET_ISDB_LAYERC_SEGMENT_WIDTH: - tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width; - break; - case DTV_SET_VOLTAGE: + case DTV_VOLTAGE: fe->dtv_property_cache.voltage = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE, (void *)fe->dtv_property_cache.voltage); break; - case DTV_GET_VOLTAGE: - tvp->u.data = fe->dtv_property_cache.voltage; - break; - case DTV_SET_TONE: + case DTV_TONE: fe->dtv_property_cache.sectone = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE, (void *)fe->dtv_property_cache.sectone); break; - case DTV_GET_TONE: - tvp->u.data = fe->dtv_property_cache.sectone; - break; + default: + r = -1; } return r; @@ -1386,13 +1342,50 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, } for (i = 0; i < tvps->num; i++) - dtv_property_process(fe, tvp + i, inode, file); + dtv_property_process_set(fe, tvp + i, inode, file); if(fe->dtv_property_cache.state == DTV_TUNE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); } err = 0; - } + } else + if(cmd == FE_GET_PROPERTY) { + printk("%s() FE_GET_PROPERTY\n", __FUNCTION__); + + tvps = (struct dtv_properties __user *)parg; + + printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num); + printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props); + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if (tvps->num > DTV_IOCTL_MAX_MSGS) + return -EINVAL; + + tvp = (struct dtv_property *) kmalloc(tvps->num * + sizeof(struct dtv_property), GFP_KERNEL); + if (!tvp) { + err = -ENOMEM; + goto out; + } + + if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { + err = -EFAULT; + goto out; + } + + for (i = 0; i < tvps->num; i++) + dtv_property_process_get(fe, tvp + i, inode, file); + + if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) { + err = -EFAULT; + goto out; + } + + err = 0; + } else + err = -EOPNOTSUPP; + out: kfree(tvp); return err; -- cgit v1.2.3 From 099550d3d61d1b5857297d96bd736b6457011396 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Fri, 12 Sep 2008 00:37:37 -0400 Subject: S2API: Implement GET/SET handing to the demods From: Steven Toth The frontends will be notified (if they chose) of all _get and _set commands so they can help determine result or action. Results are now returned to userspace correctly. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 21 +++++++++++++++++---- linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 1 - linux/drivers/media/dvb/frontends/cx24116.c | 6 +++--- 3 files changed, 20 insertions(+), 8 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 3df500179..b63e193ce 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1115,6 +1115,13 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, dtv_property_dump(tvp); + /* Allow the frontend to validate incoming properties */ + if (fe->ops.get_property) + r = fe->ops.get_property(fe, tvp); + + if (r < 0) + return r; + switch(tvp->cmd) { case DTV_FREQUENCY: tvp->u.data = fe->dtv_property_cache.frequency; @@ -1199,6 +1206,13 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, printk("%s()\n", __FUNCTION__); dtv_property_dump(tvp); + /* Allow the frontend to validate incoming properties */ + if (fe->ops.set_property) + r = fe->ops.set_property(fe, tvp); + + if (r < 0) + return r; + switch(tvp->cmd) { case DTV_CLEAR: /* Reset a cache of data specific to the frontend here. This does @@ -1342,12 +1356,12 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, } for (i = 0; i < tvps->num; i++) - dtv_property_process_set(fe, tvp + i, inode, file); + err |= dtv_property_process_set(fe, tvp + i, inode, file); if(fe->dtv_property_cache.state == DTV_TUNE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); } - err = 0; + } else if(cmd == FE_GET_PROPERTY) { printk("%s() FE_GET_PROPERTY\n", __FUNCTION__); @@ -1375,14 +1389,13 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, } for (i = 0; i < tvps->num; i++) - dtv_property_process_get(fe, tvp + i, inode, file); + err |= dtv_property_process_get(fe, tvp + i, inode, file); if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) { err = -EFAULT; goto out; } - err = 0; } else err = -EOPNOTSUPP; diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 1c2090966..784e8fe1d 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -172,7 +172,6 @@ struct dvb_frontend_ops { int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp); int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp); - int (*set_params)(struct dvb_frontend* fe); }; #define MAX_EVENT 8 diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index 9f93930a2..0ac2c5493 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -802,9 +802,9 @@ static int cx24116_set_property(struct dvb_frontend *fe, struct dtv_property* tv return 0; } -static int cx24116_set_params(struct dvb_frontend *fe) +static int cx24116_get_property(struct dvb_frontend *fe, struct dtv_property* tvp) { - dprintk("%s(..) We were notified that a tune request may occur\n", __func__); + dprintk("%s(..)\n", __func__); return 0; } @@ -964,7 +964,7 @@ static struct dvb_frontend_ops cx24116_ops = { .diseqc_send_burst = cx24116_diseqc_send_burst, .set_property = cx24116_set_property, - .set_params = cx24116_set_params, + .get_property = cx24116_get_property, .set_frontend = cx24116_set_frontend, }; -- cgit v1.2.3 From d5bd2a43985d53bd37e31d640177c9b5306716d7 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 12 Sep 2008 20:49:06 +0300 Subject: Bug fix: ioctl FE_SET_PROPERTY/FE_GET_PROPERTY always return error From: Igor M. Liplianin Bug fix: ioctl FE_SET_PROPERTY/FE_GET_PROPERTY always return error Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index b63e193ce..ab821d5f8 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1322,7 +1322,7 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, { struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; - int err = -EOPNOTSUPP; + int err = 0; struct dtv_properties *tvps = NULL; struct dtv_property *tvp = NULL; -- cgit v1.2.3 From 9dcfc9e3a50997d1024e86a0ad8686c7d027f6de Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 13 Sep 2008 14:09:07 -0400 Subject: S2API: Allow reliable use of old and new api on the same frontend, regardless. From: Steven Toth Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index ab821d5f8..6a8b45d58 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1310,8 +1310,10 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) err = dvb_frontend_ioctl_properties(inode, file, cmd, parg); - else + else { + fe->dtv_property_cache.state = DTV_UNDEFINED; err = dvb_frontend_ioctl_legacy(inode, file, cmd, parg); + } up(&fepriv->sem); return err; @@ -1578,8 +1580,6 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; - dtv_property_cache_sync(fe, &fepriv->parameters); - if(fe->dtv_property_cache.state == DTV_TUNE) { if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) { err = -EINVAL; @@ -1591,6 +1591,7 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, break; } + dtv_property_cache_sync(fe, &fepriv->parameters); memcpy (&fepriv->parameters, parg, sizeof (struct dvb_frontend_parameters)); } -- cgit v1.2.3 From 997940c5b22eab62f570dcd410179b8a60f1ad84 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 13 Sep 2008 15:56:34 -0400 Subject: S2API: Changed bandwidth to be expressed in HZ From: Steven Toth Also added some compat code for the older API. Added more ISDB message/command suggestions, current not connected in dvb-core. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 33 ++++++++++++++++++------- linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 2 +- 2 files changed, 25 insertions(+), 10 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 6a8b45d58..c8865d639 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -784,9 +784,9 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_FREQUENCY, .set = 1, }, - [DTV_BANDWIDTH] = { - .name = "DTV_BANDWIDTH", - .cmd = DTV_BANDWIDTH, + [DTV_BANDWIDTH_HZ] = { + .name = "DTV_BANDWIDTH_HZ", + .cmd = DTV_BANDWIDTH_HZ, .set = 1, }, [DTV_MODULATION] = { @@ -965,7 +965,15 @@ void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parame c->delivery_system = SYS_DVBC_ANNEX_AC; break; case FE_OFDM: - c->bandwidth = p->u.ofdm.bandwidth; + if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) + c->bandwidth_hz = 6000000; + else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ) + c->bandwidth_hz = 7000000; + else if (p->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) + c->bandwidth_hz = 8000000; + else + /* Including BANDWIDTH_AUTO */ + c->bandwidth_hz = 0; c->code_rate_HP = p->u.ofdm.code_rate_HP; c->code_rate_LP = p->u.ofdm.code_rate_LP; c->modulation = p->u.ofdm.constellation; @@ -1014,7 +1022,14 @@ void dtv_property_legacy_params_sync(struct dvb_frontend *fe) break; case FE_OFDM: printk("%s() Preparing OFDM req\n", __FUNCTION__); - p->u.ofdm.bandwidth = c->bandwidth; + if (c->bandwidth_hz == 6000000) + p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + else if (c->bandwidth_hz == 7000000) + p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; + else if (c->bandwidth_hz == 8000000) + p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + else + p->u.ofdm.bandwidth = BANDWIDTH_AUTO; p->u.ofdm.code_rate_HP = c->code_rate_HP; p->u.ofdm.code_rate_LP = c->code_rate_LP; p->u.ofdm.constellation = c->modulation; @@ -1129,8 +1144,8 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_MODULATION: tvp->u.data = fe->dtv_property_cache.modulation; break; - case DTV_BANDWIDTH: - tvp->u.data = fe->dtv_property_cache.bandwidth; + case DTV_BANDWIDTH_HZ: + tvp->u.data = fe->dtv_property_cache.bandwidth_hz; break; case DTV_INVERSION: tvp->u.data = fe->dtv_property_cache.inversion; @@ -1241,8 +1256,8 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_MODULATION: fe->dtv_property_cache.modulation = tvp->u.data; break; - case DTV_BANDWIDTH: - fe->dtv_property_cache.bandwidth = tvp->u.data; + case DTV_BANDWIDTH_HZ: + fe->dtv_property_cache.bandwidth_hz = tvp->u.data; break; case DTV_INVERSION: fe->dtv_property_cache.inversion = tvp->u.data; diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 784e8fe1d..2fa37f5a0 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -198,7 +198,7 @@ struct dtv_frontend_properties { fe_spectral_inversion_t inversion; fe_code_rate_t fec_inner; fe_transmit_mode_t transmission_mode; - fe_bandwidth_t bandwidth; + u32 bandwidth_hz; /* 0 = AUTO */ fe_guard_interval_t guard_interval; fe_hierarchy_t hierarchy; u32 symbol_rate; -- cgit v1.2.3 From 7d66b3c4dc460d812e2f899802be74dc0c7be39b Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 13 Sep 2008 17:15:17 -0400 Subject: S2API: Bugfix related to syncing the cache when used with the old API. From: Steven Toth Many thanks to Darron Broad for pointing out the obvious. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index c8865d639..98aaa1a65 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1606,9 +1606,9 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, break; } - dtv_property_cache_sync(fe, &fepriv->parameters); memcpy (&fepriv->parameters, parg, sizeof (struct dvb_frontend_parameters)); + dtv_property_cache_sync(fe, &fepriv->parameters); } memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings)); -- cgit v1.2.3 From d9ee7f39c3a6582c9e1527a550c3eaee5ea7e0a7 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sat, 13 Sep 2008 16:10:53 +0300 Subject: Add support for SDMC DM1105 PCI chip From: Igor M. Liplianin Add support for SDMC DM1105 PCI chip. There is a lot of cards based on it, like DvbWorld 2002 DVB-S , 2004 DVB-S2 Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/Kconfig | 4 + linux/drivers/media/dvb/Makefile | 2 +- linux/drivers/media/dvb/dm1105/Kconfig | 14 + linux/drivers/media/dvb/dm1105/Makefile | 3 + linux/drivers/media/dvb/dm1105/dm1105.c | 925 ++++++++++++++++++++++++++++++++ 5 files changed, 947 insertions(+), 1 deletion(-) create mode 100644 linux/drivers/media/dvb/dm1105/Kconfig create mode 100644 linux/drivers/media/dvb/dm1105/Makefile create mode 100644 linux/drivers/media/dvb/dm1105/dm1105.c (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/Kconfig b/linux/drivers/media/dvb/Kconfig index 8bc1445bd..5858744e7 100644 --- a/linux/drivers/media/dvb/Kconfig +++ b/linux/drivers/media/dvb/Kconfig @@ -35,6 +35,10 @@ comment "Supported Pluto2 Adapters" depends on DVB_CORE && PCI && I2C source "drivers/media/dvb/pluto2/Kconfig" +comment "Supported SDMC DM1105 Adapters" + depends on DVB_CORE && PCI && I2C +source "drivers/media/dvb/dm1105/Kconfig" + comment "Supported DVB Frontends" depends on DVB_CORE source "drivers/media/dvb/frontends/Kconfig" diff --git a/linux/drivers/media/dvb/Makefile b/linux/drivers/media/dvb/Makefile index d6ba4d195..ea953a03e 100644 --- a/linux/drivers/media/dvb/Makefile +++ b/linux/drivers/media/dvb/Makefile @@ -2,4 +2,4 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/ +obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/ dm1105/ diff --git a/linux/drivers/media/dvb/dm1105/Kconfig b/linux/drivers/media/dvb/dm1105/Kconfig new file mode 100644 index 000000000..fc2ca7dca --- /dev/null +++ b/linux/drivers/media/dvb/dm1105/Kconfig @@ -0,0 +1,14 @@ +config DVB_DM1105 + tristate "SDMC DM1105 based PCI cards" + depends on DVB_CORE && PCI && I2C + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + help + Support for cards based on the SDMC DM1105 PCI chip like + DvbWorld 2002 + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the PCI bus, so you need + an external software decoder to watch TV on your computer. + + Say Y or M if you own such a device and want to use it. diff --git a/linux/drivers/media/dvb/dm1105/Makefile b/linux/drivers/media/dvb/dm1105/Makefile new file mode 100644 index 000000000..8ac28b054 --- /dev/null +++ b/linux/drivers/media/dvb/dm1105/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_DVB_DM1105) += dm1105.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends diff --git a/linux/drivers/media/dvb/dm1105/dm1105.c b/linux/drivers/media/dvb/dm1105/dm1105.c new file mode 100644 index 000000000..2dd31ee00 --- /dev/null +++ b/linux/drivers/media/dvb/dm1105/dm1105.c @@ -0,0 +1,925 @@ +/* + * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip + * + * Copyright (C) 2008 Igor M. Liplianin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "demux.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" +#include "dvbdev.h" +#include "dvb-pll.h" + +#include "stv0299.h" +/*#include "stv0288.h" + *#include "si21xx.h" + *#include "stb6000.h" + *#include "cx24116.h"*/ +#include "z0194a.h" + +/* ----------------------------------------------- */ +/* + * PCI ID's + */ +#ifndef PCI_VENDOR_ID_TRIGEM +#define PCI_VENDOR_ID_TRIGEM 0x109f +#endif +#ifndef PCI_DEVICE_ID_DM1105 +#define PCI_DEVICE_ID_DM1105 0x036f +#endif +#ifndef PCI_DEVICE_ID_DW2002 +#define PCI_DEVICE_ID_DW2002 0x2002 +#endif +#ifndef PCI_DEVICE_ID_DW2004 +#define PCI_DEVICE_ID_DW2004 0x2004 +#endif +/* ----------------------------------------------- */ +/* sdmc dm1105 registers */ + +/* TS Control */ +#define DM1105_TSCTR 0x00 +#define DM1105_DTALENTH 0x04 + +/* GPIO Interface */ +#define DM1105_GPIOVAL 0x08 +#define DM1105_GPIOCTR 0x0c + +/* PID serial number */ +#define DM1105_PIDN 0x10 + +/* Odd-even secret key select */ +#define DM1105_CWSEL 0x14 + +/* Host Command Interface */ +#define DM1105_HOST_CTR 0x18 +#define DM1105_HOST_AD 0x1c + +/* PCI Interface */ +#define DM1105_CR 0x30 +#define DM1105_RST 0x34 +#define DM1105_STADR 0x38 +#define DM1105_RLEN 0x3c +#define DM1105_WRP 0x40 +#define DM1105_INTCNT 0x44 +#define DM1105_INTMAK 0x48 +#define DM1105_INTSTS 0x4c + +/* CW Value */ +#define DM1105_ODD 0x50 +#define DM1105_EVEN 0x58 + +/* PID Value */ +#define DM1105_PID 0x60 + +/* IR Control */ +#define DM1105_IRCTR 0x64 +#define DM1105_IRMODE 0x68 +#define DM1105_SYSTEMCODE 0x6c +#define DM1105_IRCODE 0x70 + +/* Unknown Values */ +#define DM1105_ENCRYPT 0x74 +#define DM1105_VER 0x7c + +/* I2C Interface */ +#define DM1105_I2CCTR 0x80 +#define DM1105_I2CSTS 0x81 +#define DM1105_I2CDAT 0x82 +#define DM1105_I2C_RA 0x83 +/* ----------------------------------------------- */ +/* Interrupt Mask Bits */ + +#define INTMAK_TSIRQM 0x01 +#define INTMAK_HIRQM 0x04 +#define INTMAK_IRM 0x08 +#define INTMAK_ALLMASK (INTMAK_TSIRQM | \ + INTMAK_HIRQM | \ + INTMAK_IRM) +#define INTMAK_NONEMASK 0x00 + +/* Interrupt Status Bits */ +#define INTSTS_TSIRQ 0x01 +#define INTSTS_HIRQ 0x04 +#define INTSTS_IR 0x08 + +/* IR Control Bits */ +#define DM1105_IR_EN 0x01 +#define DM1105_SYS_CHK 0x02 +#define DM1105_REP_FLG 0x08 + +/* EEPROM addr */ +#define IIC_24C01_addr 0xa0 +/* Max board count */ +#define DM1105_MAX 0x04 + +#define DRIVER_NAME "dm1105" + +#define DM1105_DMA_PACKETS 47 +#define DM1105_DMA_PACKET_LENGTH (128*4) +#define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS) + +/* GPIO's for LNB power control */ +#define DM1105_LNB_MASK 0x00000000 +#define DM1105_LNB_13V 0x00010100 +#define DM1105_LNB_18V 0x00000100 + +static int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static u16 ir_codes_dm1105_nec[128] = { + [0x0a] = KEY_Q, /*power*/ + [0x0c] = KEY_M, /*mute*/ + [0x11] = KEY_1, + [0x12] = KEY_2, + [0x13] = KEY_3, + [0x14] = KEY_4, + [0x15] = KEY_5, + [0x16] = KEY_6, + [0x17] = KEY_7, + [0x18] = KEY_8, + [0x19] = KEY_9, + [0x10] = KEY_0, + [0x1c] = KEY_PAGEUP, /*ch+*/ + [0x0f] = KEY_PAGEDOWN, /*ch-*/ + [0x1a] = KEY_O, /*vol+*/ + [0x0e] = KEY_Z, /*vol-*/ + [0x04] = KEY_R, /*rec*/ + [0x09] = KEY_D, /*fav*/ + [0x08] = KEY_BACKSPACE, /*rewind*/ + [0x07] = KEY_A, /*fast*/ + [0x0b] = KEY_P, /*pause*/ + [0x02] = KEY_ESC, /*cancel*/ + [0x03] = KEY_G, /*tab*/ + [0x00] = KEY_UP, /*up*/ + [0x1f] = KEY_ENTER, /*ok*/ + [0x01] = KEY_DOWN, /*down*/ + [0x05] = KEY_C, /*cap*/ + [0x06] = KEY_S, /*stop*/ + [0x40] = KEY_F, /*full*/ + [0x1e] = KEY_W, /*tvmode*/ + [0x1b] = KEY_B, /*recall*/ +}; + +/* infrared remote control */ +struct infrared { + u16 key_map[128]; + struct input_dev *input_dev; + char input_phys[32]; + struct tasklet_struct ir_tasklet; + u32 ir_command; +}; + +struct dm1105dvb { + /* pci */ + struct pci_dev *pdev; + u8 __iomem *io_mem; + + /* ir */ + struct infrared ir; + + /* dvb */ + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + struct dmxdev dmxdev; + struct dvb_adapter dvb_adapter; + struct dvb_demux demux; + struct dvb_frontend *fe; + struct dvb_net dvbnet; + unsigned int full_ts_users; + + /* i2c */ + struct i2c_adapter i2c_adap; + + /* dma */ + dma_addr_t dma_addr; + unsigned char *ts_buf; + u32 wrp; + u32 buffer_size; + unsigned int PacketErrorCount; + unsigned int dmarst; + spinlock_t lock; + +}; + +#define dm_io_mem(reg) ((unsigned long)(&dm1105dvb->io_mem[reg])) + +static struct dm1105dvb *dm1105dvb_local; + +static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + struct dm1105dvb *dm1105dvb ; + + int addr, rc, i, j, k, len, byte, data; + u8 status; + + dm1105dvb = i2c_adap->algo_data; + for (i = 0; i < num; i++) { + outb(0x00, dm_io_mem(DM1105_I2CCTR)); + if (msgs[i].flags & I2C_M_RD) { + /* read bytes */ + addr = msgs[i].addr << 1; + addr |= 1; + outb(addr, dm_io_mem(DM1105_I2CDAT)); + for (byte = 0; byte < msgs[i].len; byte++) + outb(0, dm_io_mem(DM1105_I2CDAT + byte + 1)); + + outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR)); + for (j = 0; j < 55; j++) { + mdelay(10); + status = inb(dm_io_mem(DM1105_I2CSTS)); + if ((status & 0xc0) == 0x40) + break; + } + if (j >= 55) + return -1; + + for (byte = 0; byte < msgs[i].len; byte++) { + rc = inb(dm_io_mem(DM1105_I2CDAT + byte + 1)); + if (rc < 0) + goto err; + msgs[i].buf[byte] = rc; + } + } else { + if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) { + /* prepaired for cx24116 firmware */ + /* Write in small blocks */ + len = msgs[i].len - 1; + k = 1; + do { + outb(msgs[i].addr << 1, dm_io_mem(DM1105_I2CDAT)); + outb(0xf7, dm_io_mem(DM1105_I2CDAT + 1)); + for (byte = 0; byte < (len > 48 ? 48 : len); byte++) { + data = msgs[i].buf[k+byte]; + outb(data, dm_io_mem(DM1105_I2CDAT + byte + 2)); + } + outb(0x82 + (len > 48 ? 48 : len), dm_io_mem(DM1105_I2CCTR)); + for (j = 0; j < 25; j++) { + mdelay(10); + status = inb(dm_io_mem(DM1105_I2CSTS)); + if ((status & 0xc0) == 0x40) + break; + } + + if (j >= 25) + return -1; + + k += 48; + len -= 48; + } while (len > 0); + } else { + /* write bytes */ + outb(msgs[i].addr<<1, dm_io_mem(DM1105_I2CDAT)); + for (byte = 0; byte < msgs[i].len; byte++) { + data = msgs[i].buf[byte]; + outb(data, dm_io_mem(DM1105_I2CDAT + byte + 1)); + } + outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR)); + for (j = 0; j < 25; j++) { + mdelay(10); + status = inb(dm_io_mem(DM1105_I2CSTS)); + if ((status & 0xc0) == 0x40) + break; + } + + if (j >= 25) + return -1; + } + } + } + return num; + err: + return rc; +} + +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm dm1105_algo = { + .master_xfer = dm1105_i2c_xfer, + .functionality = functionality, +}; + +static inline struct dm1105dvb *feed_to_dm1105dvb(struct dvb_demux_feed *feed) +{ + return container_of(feed->demux, struct dm1105dvb, demux); +} + +static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe) +{ + return container_of(fe->dvb, struct dm1105dvb, dvb_adapter); +} + +static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe); + + if (voltage == SEC_VOLTAGE_18) { + outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR)); + outl(DM1105_LNB_18V, dm_io_mem(DM1105_GPIOVAL)); + } else { + /*LNB ON-13V by default!*/ + outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR)); + outl(DM1105_LNB_13V, dm_io_mem(DM1105_GPIOVAL)); + } + + return 0; +} + +static void dm1105dvb_set_dma_addr(struct dm1105dvb *dm1105dvb) +{ + outl(cpu_to_le32(dm1105dvb->dma_addr), dm_io_mem(DM1105_STADR)); +} + +static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb) +{ + dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) + return pci_dma_mapping_error(dm1105dvb->dma_addr); +#else + return pci_dma_mapping_error(dm1105dvb->pdev, dm1105dvb->dma_addr); +#endif +} + +static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb) +{ + pci_free_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, dm1105dvb->ts_buf, dm1105dvb->dma_addr); +} + +static void __devinit dm1105dvb_enable_irqs(struct dm1105dvb *dm1105dvb) +{ + outb(INTMAK_ALLMASK, dm_io_mem(DM1105_INTMAK)); + outb(1, dm_io_mem(DM1105_CR)); +} + +static void dm1105dvb_disable_irqs(struct dm1105dvb *dm1105dvb) +{ + outb(INTMAK_IRM, dm_io_mem(DM1105_INTMAK)); + outb(0, dm_io_mem(DM1105_CR)); +} + +static int dm1105dvb_start_feed(struct dvb_demux_feed *f) +{ + struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f); + + if (dm1105dvb->full_ts_users++ == 0) + dm1105dvb_enable_irqs(dm1105dvb); + + return 0; +} + +static int dm1105dvb_stop_feed(struct dvb_demux_feed *f) +{ + struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f); + + if (--dm1105dvb->full_ts_users == 0) + dm1105dvb_disable_irqs(dm1105dvb); + + return 0; +} + +/* ir tasklet */ +static void dm1105_emit_key(unsigned long parm) +{ + struct infrared *ir = (struct infrared *) parm; + u32 ircom = ir->ir_command; + u8 data; + u16 keycode; + + data = (ircom >> 8) & 0x7f; + + input_event(ir->input_dev, EV_MSC, MSC_RAW, (0x0000f8 << 16) | data); + input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); + keycode = ir->key_map[data]; + + if (!keycode) + return; + + input_event(ir->input_dev, EV_KEY, keycode, 1); + input_sync(ir->input_dev); + input_event(ir->input_dev, EV_KEY, keycode, 0); + input_sync(ir->input_dev); + +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static irqreturn_t dm1105dvb_irq(int irq, void *dev_id, struct pt_regs *regs) +#else +static irqreturn_t dm1105dvb_irq(int irq, void *dev_id) +#endif +{ + struct dm1105dvb *dm1105dvb = dev_id; + unsigned int piece; + unsigned int nbpackets; + u32 command; + u32 nextwrp; + u32 oldwrp; + + /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */ + unsigned int intsts = inb(dm_io_mem(DM1105_INTSTS)); + outb(intsts, dm_io_mem(DM1105_INTSTS)); + + switch (intsts) { + case INTSTS_TSIRQ: + case (INTSTS_TSIRQ | INTSTS_IR): + nextwrp = inl(dm_io_mem(DM1105_WRP)) - + inl(dm_io_mem(DM1105_STADR)) ; + oldwrp = dm1105dvb->wrp; + spin_lock(&dm1105dvb->lock); + if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) && + (dm1105dvb->ts_buf[oldwrp + 188] == 0x47) && + (dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) { + dm1105dvb->PacketErrorCount++; + /* bad packet found */ + if ((dm1105dvb->PacketErrorCount >= 2) && + (dm1105dvb->dmarst == 0)) { + outb(1, dm_io_mem(DM1105_RST)); + dm1105dvb->wrp = 0; + dm1105dvb->PacketErrorCount = 0; + dm1105dvb->dmarst = 0; + spin_unlock(&dm1105dvb->lock); + return IRQ_HANDLED; + } + } + if (nextwrp < oldwrp) { + piece = dm1105dvb->buffer_size - oldwrp; + memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size, dm1105dvb->ts_buf, nextwrp); + nbpackets = (piece + nextwrp)/188; + } else { + nbpackets = (nextwrp - oldwrp)/188; + } + dvb_dmx_swfilter_packets(&dm1105dvb->demux, &dm1105dvb->ts_buf[oldwrp], nbpackets); + dm1105dvb->wrp = nextwrp; + spin_unlock(&dm1105dvb->lock); + break; + case INTSTS_IR: + command = inl(dm_io_mem(DM1105_IRCODE)); + if (ir_debug) + printk("dm1105: received byte 0x%04x\n", command); + + dm1105dvb->ir.ir_command = command; + tasklet_schedule(&dm1105dvb->ir.ir_tasklet); + break; + } + return IRQ_HANDLED; + + +} + +/* register with input layer */ +static void input_register_keys(struct infrared *ir) +{ + int i; + + memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); + + for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) + set_bit(ir->key_map[i], ir->input_dev->keybit); + + ir->input_dev->keycode = ir->key_map; + ir->input_dev->keycodesize = sizeof(ir->key_map[0]); + ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); +} + +int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) +{ + struct input_dev *input_dev; + int err; + + dm1105dvb_local = dm1105; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + dm1105->ir.input_dev = input_dev; + snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys), + "pci-%s/ir0", pci_name(dm1105->pdev)); + + input_dev->evbit[0] = BIT(EV_KEY); + input_dev->name = "DVB on-card IR receiver"; + + input_dev->phys = dm1105->ir.input_phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.version = 2; + if (dm1105->pdev->subsystem_vendor) { + input_dev->id.vendor = dm1105->pdev->subsystem_vendor; + input_dev->id.product = dm1105->pdev->subsystem_device; + } else { + input_dev->id.vendor = dm1105->pdev->vendor; + input_dev->id.product = dm1105->pdev->device; + } + input_dev->dev.parent = &dm1105->pdev->dev; + /* initial keymap */ + memcpy(dm1105->ir.key_map, ir_codes_dm1105_nec, sizeof dm1105->ir.key_map); + input_register_keys(&dm1105->ir); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + tasklet_init(&dm1105->ir.ir_tasklet, dm1105_emit_key, (unsigned long) &dm1105->ir); + + return 0; +} + + +void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105) +{ + tasklet_kill(&dm1105->ir.ir_tasklet); + input_unregister_device(dm1105->ir.input_dev); + +} + +static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb) +{ + dm1105dvb_disable_irqs(dm1105dvb); + + outb(0, dm_io_mem(DM1105_HOST_CTR)); + + /*DATALEN 188,*/ + outb(188, dm_io_mem(DM1105_DTALENTH)); + /*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/ + outw(0xc10a, dm_io_mem(DM1105_TSCTR)); + + /* map DMA and set address */ + dm1105dvb_dma_map(dm1105dvb); + dm1105dvb_set_dma_addr(dm1105dvb); + /* big buffer */ + outl(5*DM1105_DMA_BYTES, dm_io_mem(DM1105_RLEN)); + outb(47, dm_io_mem(DM1105_INTCNT)); + + /* IR NEC mode enable */ + outb((DM1105_IR_EN | DM1105_SYS_CHK), dm_io_mem(DM1105_IRCTR)); + outb(0, dm_io_mem(DM1105_IRMODE)); + outw(0, dm_io_mem(DM1105_SYSTEMCODE)); + + return 0; +} + +static void dm1105dvb_hw_exit(struct dm1105dvb *dm1105dvb) +{ + dm1105dvb_disable_irqs(dm1105dvb); + + /* IR disable */ + outb(0, dm_io_mem(DM1105_IRCTR)); + outb(INTMAK_NONEMASK, dm_io_mem(DM1105_INTMAK)); + + dm1105dvb_dma_unmap(dm1105dvb); +} +#if 0 /* keep */ +static struct stv0288_config earda_config = { + .demod_address = 0x68, + .min_delay_ms = 100, +}; + +static struct si21xx_config serit_config = { + .demod_address = 0x68, + .min_delay_ms = 100, + +}; + +static struct cx24116_config serit_sp2633_config = { + .demod_address = 0x55, +}; +#endif /* keep */ + +static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) +{ + int ret; + + switch (dm1105dvb->pdev->subsystem_device) { + case PCI_DEVICE_ID_DW2002: + dm1105dvb->fe = dvb_attach( + stv0299_attach, &sharp_z0194a_config, + &dm1105dvb->i2c_adap); + + if (dm1105dvb->fe) { + dm1105dvb->fe->ops.set_voltage = + dm1105dvb_set_voltage; + dvb_attach(dvb_pll_attach, dm1105dvb->fe, 0x60, + &dm1105dvb->i2c_adap, DVB_PLL_OPERA1); + } +#if 0 /* keep */ + if (!dm1105dvb->fe) { + dm1105dvb->fe = dvb_attach( + stv0288_attach, &earda_config, + &dm1105dvb->i2c_adap); + if (dm1105dvb->fe) { + dm1105dvb->fe->ops.set_voltage = + dm1105dvb_set_voltage; + dvb_attach(stb6000_attach, dm1105dvb->fe, 0x61, + &dm1105dvb->i2c_adap); + } + } + + if (!dm1105dvb->fe) { + dm1105dvb->fe = dvb_attach( + si21xx_attach, &serit_config, + &dm1105dvb->i2c_adap); + if (dm1105dvb->fe) + dm1105dvb->fe->ops.set_voltage = + dm1105dvb_set_voltage; + } +#endif /* keep */ + break; + case PCI_DEVICE_ID_DW2004: +#if 0 /* keep */ + dm1105dvb->fe = dvb_attach( + cx24116_attach, &serit_sp2633_config, + &dm1105dvb->i2c_adap); + if (dm1105dvb->fe) + dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage; +#else /* keep */ + dev_err(&dm1105dvb->pdev->dev, "needs cx24116 module\n"); +#endif /* keep */ + break; + } + + if (!dm1105dvb->fe) { + dev_err(&dm1105dvb->pdev->dev, "could not attach frontend\n"); + return -ENODEV; + } + + ret = dvb_register_frontend(&dm1105dvb->dvb_adapter, dm1105dvb->fe); + if (ret < 0) { + if (dm1105dvb->fe->ops.release) + dm1105dvb->fe->ops.release(dm1105dvb->fe); + dm1105dvb->fe = NULL; + return ret; + } + + return 0; +} + +static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac) +{ + static u8 command[1] = { 0x28 }; + + struct i2c_msg msg[] = { + { .addr = IIC_24C01_addr >> 1, .flags = 0, + .buf = command, .len = 1 }, + { .addr = IIC_24C01_addr >> 1, .flags = I2C_M_RD, + .buf = mac, .len = 6 }, + }; + + dm1105_i2c_xfer(&dm1105dvb->i2c_adap, msg , 2); + dev_info(&dm1105dvb->pdev->dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + +static int __devinit dm1105_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct dm1105dvb *dm1105dvb; + struct dvb_adapter *dvb_adapter; + struct dvb_demux *dvbdemux; + struct dmx_demux *dmx; + int ret = -ENOMEM; + + dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL); + if (!dm1105dvb) + goto out; + + dm1105dvb->pdev = pdev; + dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES; + dm1105dvb->PacketErrorCount = 0; + dm1105dvb->dmarst = 0; + + ret = pci_enable_device(pdev); + if (ret < 0) + goto err_kfree; + + ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (ret < 0) + goto err_pci_disable_device; + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, DRIVER_NAME); + if (ret < 0) + goto err_pci_disable_device; + + dm1105dvb->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!dm1105dvb->io_mem) { + ret = -EIO; + goto err_pci_release_regions; + } + + spin_lock_init(&dm1105dvb->lock); + pci_set_drvdata(pdev, dm1105dvb); + + ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED, DRIVER_NAME, dm1105dvb); + if (ret < 0) + goto err_pci_iounmap; + + ret = dm1105dvb_hw_init(dm1105dvb); + if (ret < 0) + goto err_free_irq; + + /* i2c */ + i2c_set_adapdata(&dm1105dvb->i2c_adap, dm1105dvb); + strcpy(dm1105dvb->i2c_adap.name, DRIVER_NAME); + dm1105dvb->i2c_adap.owner = THIS_MODULE; + dm1105dvb->i2c_adap.class = I2C_CLASS_TV_DIGITAL; + dm1105dvb->i2c_adap.dev.parent = &pdev->dev; + dm1105dvb->i2c_adap.algo = &dm1105_algo; + dm1105dvb->i2c_adap.algo_data = dm1105dvb; + ret = i2c_add_adapter(&dm1105dvb->i2c_adap); + + if (ret < 0) + goto err_dm1105dvb_hw_exit; + + /* dvb */ + ret = dvb_register_adapter(&dm1105dvb->dvb_adapter, DRIVER_NAME, + THIS_MODULE, &pdev->dev, adapter_nr); + if (ret < 0) + goto err_i2c_del_adapter; + + dvb_adapter = &dm1105dvb->dvb_adapter; + + dm1105dvb_read_mac(dm1105dvb, dvb_adapter->proposed_mac); + + dvbdemux = &dm1105dvb->demux; + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = dm1105dvb_start_feed; + dvbdemux->stop_feed = dm1105dvb_stop_feed; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); + ret = dvb_dmx_init(dvbdemux); + if (ret < 0) + goto err_dvb_unregister_adapter; + + dmx = &dvbdemux->dmx; + dm1105dvb->dmxdev.filternum = 256; + dm1105dvb->dmxdev.demux = dmx; + dm1105dvb->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&dm1105dvb->dmxdev, dvb_adapter); + if (ret < 0) + goto err_dvb_dmx_release; + + dm1105dvb->hw_frontend.source = DMX_FRONTEND_0; + + ret = dmx->add_frontend(dmx, &dm1105dvb->hw_frontend); + if (ret < 0) + goto err_dvb_dmxdev_release; + + dm1105dvb->mem_frontend.source = DMX_MEMORY_FE; + + ret = dmx->add_frontend(dmx, &dm1105dvb->mem_frontend); + if (ret < 0) + goto err_remove_hw_frontend; + + ret = dmx->connect_frontend(dmx, &dm1105dvb->hw_frontend); + if (ret < 0) + goto err_remove_mem_frontend; + + ret = frontend_init(dm1105dvb); + if (ret < 0) + goto err_disconnect_frontend; + + dvb_net_init(dvb_adapter, &dm1105dvb->dvbnet, dmx); + dm1105_ir_init(dm1105dvb); +out: + return ret; + +err_disconnect_frontend: + dmx->disconnect_frontend(dmx); +err_remove_mem_frontend: + dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend); +err_remove_hw_frontend: + dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend); +err_dvb_dmxdev_release: + dvb_dmxdev_release(&dm1105dvb->dmxdev); +err_dvb_dmx_release: + dvb_dmx_release(dvbdemux); +err_dvb_unregister_adapter: + dvb_unregister_adapter(dvb_adapter); +err_i2c_del_adapter: + i2c_del_adapter(&dm1105dvb->i2c_adap); +err_dm1105dvb_hw_exit: + dm1105dvb_hw_exit(dm1105dvb); +err_free_irq: + free_irq(pdev->irq, dm1105dvb); +err_pci_iounmap: + pci_iounmap(pdev, dm1105dvb->io_mem); +err_pci_release_regions: + pci_release_regions(pdev); +err_pci_disable_device: + pci_disable_device(pdev); +err_kfree: + pci_set_drvdata(pdev, NULL); + kfree(dm1105dvb); + goto out; +} + +static void __devexit dm1105_remove(struct pci_dev *pdev) +{ + struct dm1105dvb *dm1105dvb = pci_get_drvdata(pdev); + struct dvb_adapter *dvb_adapter = &dm1105dvb->dvb_adapter; + struct dvb_demux *dvbdemux = &dm1105dvb->demux; + struct dmx_demux *dmx = &dvbdemux->dmx; + + dm1105_ir_exit(dm1105dvb); + dmx->close(dmx); + dvb_net_release(&dm1105dvb->dvbnet); + if (dm1105dvb->fe) + dvb_unregister_frontend(dm1105dvb->fe); + + dmx->disconnect_frontend(dmx); + dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend); + dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend); + dvb_dmxdev_release(&dm1105dvb->dmxdev); + dvb_dmx_release(dvbdemux); + dvb_unregister_adapter(dvb_adapter); + if (&dm1105dvb->i2c_adap) + i2c_del_adapter(&dm1105dvb->i2c_adap); + + dm1105dvb_hw_exit(dm1105dvb); + synchronize_irq(pdev->irq); + free_irq(pdev->irq, dm1105dvb); + pci_iounmap(pdev, dm1105dvb->io_mem); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + kfree(dm1105dvb); +} + +static struct pci_device_id dm1105_id_table[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_TRIGEM, + .device = PCI_DEVICE_ID_DM1105, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_DEVICE_ID_DW2002, + }, { + .vendor = PCI_VENDOR_ID_TRIGEM, + .device = PCI_DEVICE_ID_DM1105, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_DEVICE_ID_DW2004, + }, { + /* empty */ + }, +}; + +MODULE_DEVICE_TABLE(pci, dm1105_id_table); + +static struct pci_driver dm1105_driver = { + .name = DRIVER_NAME, + .id_table = dm1105_id_table, + .probe = dm1105_probe, + .remove = __devexit_p(dm1105_remove), +}; + +static int __init dm1105_init(void) +{ + return pci_register_driver(&dm1105_driver); +} + +static void __exit dm1105_exit(void) +{ + pci_unregister_driver(&dm1105_driver); +} + +module_init(dm1105_init); +module_exit(dm1105_exit); + +MODULE_AUTHOR("Igor M. Liplianin "); +MODULE_DESCRIPTION("SDMC DM1105 DVB driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 03f126b4400180196e62b69757c708b8ac2b40ae Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 13 Sep 2008 18:42:16 -0400 Subject: S2API: A number of cleanusp from the last 24 months. From: Darron Broad I was given these changes by Darron Broad and Igor Liplianin and represent a series of patches they've been making to the cx24116 driver over the last two years. Changes for handling symbolrates >30Ksps Tone handling changes. Diseqc support. Sleep support, shutting down the clocks correctly. Cleanup on ROLL_OFF and PILOT support. *** ST - We need to cleanup the sysclt control, this is abnormal in a demod driver. We should work towards understanding the missing API's and ensure we have them in S2API. *** Priority: normal Signed-off-by: Steven Toth Signed-off-by: Darron Broad --- linux/drivers/media/dvb/frontends/cx24116.c | 805 +++++++++++++++++++++------- 1 file changed, 624 insertions(+), 181 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index 0ac2c5493..8bc622d95 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -2,6 +2,18 @@ Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver Copyright (C) 2006-2008 Steven Toth + Copyright (C) 2006-2007 Georg Acher + Copyright (C) 2007-2008 Darron Broad + March 2007 + Fixed some bugs. + Added diseqc support. + Added corrected signal strength support. + August 2007 + Sync with legacy version. + Some clean ups. + Copyright (C) 2008 Igor Liplianin + September, 9th 2008 + Fixed locking on high symbol rates (>30000). 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 @@ -18,51 +30,69 @@ 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. - */ -/* Updates by Igor Liplianin - * - * September, 9th 2008 - * Fixed locking on high symbol rates (>30000). - */ - #include #include #include #include #include #include +#include #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 - */ +static int debug = 0; +#define dprintk(args...) \ + do { \ + if (debug) printk ("cx24116: " args); \ + } while (0) + #define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw" #define CX24116_SEARCH_RANGE_KHZ 5000 -/* registers (TO BE COMPLETED) */ -#define CX24116_REG_SIGNAL (0xd5) +/* 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_REG_CLKDIV (0xf3) +#define CX24116_REG_RATEDIV (0xf9) /* arg buffer size */ #define CX24116_ARGLEN (0x1e) +/* rolloff */ +#define CX24116_ROLLOFF_020 (0x00) +#define CX24116_ROLLOFF_025 (0x01) +#define CX24116_ROLLOFF_035 (0x02) + +/* pilot bit */ +#define CX24116_PILOT (0x40) + +/* 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) + +#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 */ @@ -75,21 +105,88 @@ #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) +/* DiSEqC tone burst */ +static int toneburst = 1; + +/* debug & toneburst sysctl */ +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 = "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}, +}; enum cmds { - CMD_INIT_CMD10 = 0x10, + CMD_SET_VCO = 0x10, CMD_TUNEREQUEST = 0x11, - CMD_INIT_CMD13 = 0x13, - CMD_INIT_CMD14 = 0x14, - CMD_SEND_DISEQC = 0x21, + 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 */ @@ -101,11 +198,14 @@ struct cx24116_tuning fe_code_rate_t fec; fe_modulation_t modulation; + fe_pilot_t pilot; + fe_rolloff_t rolloff; /* Demod values */ u8 fec_val; u8 fec_mask; u8 inversion_val; + u8 rolloff_val; }; /* Basic commands that are sent to the firmware */ @@ -127,6 +227,7 @@ struct cx24116_state u8 skip_fw_load; u8 burst; + struct cx24116_cmd dsec_cmd; }; static int cx24116_writereg(struct cx24116_state* state, int reg, int data) @@ -233,6 +334,66 @@ static int cx24116_set_inversion(struct cx24116_state* state, fe_spectral_invers return 0; } +/* + * modfec (modulation and FEC) + * =========================== + * + * MOD FEC mask/val standard + * ---- -------- ----------- -------- + * QPSK FEC_1_2 0x02 0x02+X DVB-S + * QPSK FEC_2_3 0x04 0x02+X DVB-S + * QPSK FEC_3_4 0x08 0x02+X DVB-S + * QPSK FEC_4_5 0x10 0x02+X DVB-S (?) + * QPSK FEC_5_6 0x20 0x02+X DVB-S + * QPSK FEC_6_7 0x40 0x02+X DVB-S + * QPSK FEC_7_8 0x80 0x02+X DVB-S + * QPSK FEC_8_9 0x01 0x02+X DVB-S (?) (NOT SUPPORTED?) + * QPSK AUTO 0xff 0x02+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" + * + * mask/val = 0x04, 0x20 + * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 00000000 | FE_HAS_LOCK + * + * mask/val = 0x04, 0x30 + * 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=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only) + * + * NBC-QPSK FEC_1_2 0x00, 0x04 DVB-S2 + * NBC-QPSK FEC_3_5 0x00, 0x05 DVB-S2 + * NBC-QPSK FEC_2_3 0x00, 0x06 DVB-S2 + * NBC-QPSK FEC_3_4 0x00, 0x07 DVB-S2 + * NBC-QPSK FEC_4_5 0x00, 0x08 DVB-S2 + * NBC-QPSK FEC_5_6 0x00, 0x09 DVB-S2 + * NBC-QPSK FEC_8_9 0x00, 0x0a DVB-S2 + * NBC-QPSK FEC_9_10 0x00, 0x0b DVB-S2 + * + * NBC-8PSK FEC_3_5 0x00, 0x0c DVB-S2 + * NBC-8PSK FEC_2_3 0x00, 0x0d DVB-S2 + * NBC-8PSK FEC_3_4 0x00, 0x0e DVB-S2 + * NBC-8PSK FEC_5_6 0x00, 0x0f DVB-S2 + * NBC-8PSK FEC_8_9 0x00, 0x10 DVB-S2 + * NBC-8PSK FEC_9_10 0x00, 0x11 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. + */ + /* 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. @@ -244,15 +405,17 @@ struct cx24116_modfec { 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 */ + + /*mod fec mask val */ { QPSK, FEC_NONE, 0xfe, 0x30 }, - { QPSK, FEC_1_2, 0x02, 0x2e }, - { QPSK, FEC_2_3, 0x04, 0x2f }, - { QPSK, FEC_3_4, 0x08, 0x30 }, - { QPSK, FEC_4_5, 0xfe, 0x30 }, - { QPSK, FEC_5_6, 0x20, 0x31 }, - { QPSK, FEC_6_7, 0xfe, 0x30 }, - { QPSK, FEC_7_8, 0x80, 0x32 }, - { QPSK, FEC_8_9, 0xfe, 0x30 }, + { QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */ + { QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */ + { QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */ + { QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */ + { QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */ + { QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */ + { QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */ + { QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */ { QPSK, FEC_AUTO, 0xfe, 0x30 }, /* NBC-QPSK */ { NBC_QPSK, FEC_1_2, 0x00, 0x04 }, @@ -268,7 +431,12 @@ struct cx24116_modfec { { _8PSK, FEC_2_3, 0x00, 0x0d }, { _8PSK, FEC_3_4, 0x00, 0x0e }, { _8PSK, FEC_5_6, 0x00, 0x0f }, + { _8PSK, FEC_8_9, 0x00, 0x10 }, { _8PSK, FEC_9_10, 0x00, 0x11 }, + /* + * `val' 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_fecmod(struct cx24116_state* state, @@ -276,6 +444,8 @@ static int cx24116_lookup_fecmod(struct cx24116_state* state, { int i, ret = -EOPNOTSUPP; + dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f); + for(i=0 ; i < sizeof(CX24116_MODFEC_MODES) / sizeof(struct cx24116_modfec) ; i++) { if( (m == CX24116_MODFEC_MODES[i].modulation) && @@ -292,37 +462,38 @@ static int cx24116_lookup_fecmod(struct cx24116_state* state, static int cx24116_set_fec(struct cx24116_state* state, fe_modulation_t mod, fe_code_rate_t fec) { int ret = 0; - dprintk("%s()\n", __func__); + + dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec); ret = cx24116_lookup_fecmod(state, mod, fec); if(ret < 0) return ret; + state->dnxt.fec = fec; 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", __func__, - state->dnxt.fec_val, state->dnxt.fec_mask); + dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__, + state->dnxt.fec_mask, state->dnxt.fec_val); return 0; } static int cx24116_set_symbolrate(struct cx24116_state* state, u32 rate) { - int ret = 0; + dprintk("%s(%d)\n", __func__, rate); - dprintk("%s()\n", __func__); + /* check if symbol rate is within limits */ + if ((rate > state->frontend.ops.info.symbol_rate_max) || + (rate < state->frontend.ops.info.symbol_rate_min)) { + dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate); + return -EOPNOTSUPP; + } state->dnxt.symbol_rate = rate; + dprintk("%s() symbol_rate = %d\n", __func__, rate); - dprintk("%s() symbol_rate = %d\n", __func__, 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; + return 0; } static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw); @@ -392,8 +563,8 @@ static int cx24116_cmd_execute(struct dvb_frontend* fe, struct cx24116_cmd *cmd) } /* Start execution and wait for cmd to terminate */ - cx24116_writereg(state, 0x1f, 0x01); - while( cx24116_readreg(state, 0x1f) ) + cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01); + while( cx24116_readreg(state, CX24116_REG_EXECUTE) ) { msleep(10); if(i++ > 64) @@ -410,7 +581,8 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware { struct cx24116_state* state = fe->demodulator_priv; struct cx24116_cmd cmd; - int ret; + int i, ret; + unsigned char vers[4]; dprintk("%s\n", __func__); dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n" @@ -427,11 +599,21 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware /* 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, cx24116_readreg(state, 0xF2) | 0x03); - cx24116_writereg(state, 0xF3, 0x46); - cx24116_writereg(state, 0xF9, 0x00); + cx24116_writereg(state, 0xF2, 0x13); + + /* Start PLL */ + cx24116_writereg(state, 0xe0, 0x03); + cx24116_writereg(state, 0xe0, 0x00); + /* Unknown */ + cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); + cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); + + /* Unknown */ cx24116_writereg(state, 0xF0, 0x03); cx24116_writereg(state, 0xF4, 0x81); cx24116_writereg(state, 0xF5, 0x00); @@ -444,8 +626,8 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware cx24116_writereg(state, 0xF0, 0x00); cx24116_writereg(state, 0xF8, 0x06); - /* Firmware CMD 10: Chip config? */ - cmd.args[0x00] = CMD_INIT_CMD10; + /* Firmware CMD 10: VCO config */ + cmd.args[0x00] = CMD_SET_VCO; cmd.args[0x01] = 0x05; cmd.args[0x02] = 0xdc; cmd.args[0x03] = 0xda; @@ -460,10 +642,10 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware if (ret != 0) return ret; - cx24116_writereg(state, 0x9d, 0x00); + cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00); - /* Firmware CMD 14: Unknown */ - cmd.args[0x00] = CMD_INIT_CMD14; + /* Firmware CMD 14: Tuner config */ + cmd.args[0x00] = CMD_TUNERINIT; cmd.args[0x01] = 0x00; cmd.args[0x02] = 0x00; cmd.len= 0x03; @@ -473,8 +655,8 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware cx24116_writereg(state, 0xe5, 0x00); - /* Firmware CMD 13: Unknown - Firmware config? */ - cmd.args[0x00] = CMD_INIT_CMD13; + /* Firmware CMD 13: MPEG config */ + cmd.args[0x00] = CMD_MPEGCONFIG; cmd.args[0x01] = 0x01; cmd.args[0x02] = 0x75; cmd.args[0x03] = 0x00; @@ -488,6 +670,19 @@ static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware 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", __func__, + vers[0], vers[1], vers[2], vers[3]); + return 0; } @@ -503,75 +698,98 @@ 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); + int lock = cx24116_readreg(state, CX24116_REG_SSTATUS); dprintk("%s: status = 0x%02x\n", __func__, lock); *status = 0; - if (lock & 0x01) + if (lock & CX24116_HAS_SIGNAL) *status |= FE_HAS_SIGNAL; - if (lock & 0x02) + if (lock & CX24116_HAS_CARRIER) *status |= FE_HAS_CARRIER; - if (lock & 0x04) + if (lock & CX24116_HAS_VITERBI) *status |= FE_HAS_VITERBI; - if (lock & 0x08) + if (lock & CX24116_HAS_SYNCLOCK) *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; + struct cx24116_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); - *ber = 0; + + *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; } -/* Signal strength (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */ +/* 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; - 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 }; + struct cx24116_cmd cmd; + int ret; + u16 sig_reading; dprintk("%s()\n", __func__); - strength_reg = cx24116_readreg(state, CX24116_REG_SIGNAL); + /* Firmware CMD 19: Get AGC */ + cmd.args[0x00] = CMD_GETAGC; + cmd.len= 0x01; + ret = cx24116_cmd_execute(fe, &cmd); + if (ret != 0) + return ret; - if(strength_reg < 0xa0) - *signal_strength = strength_tab [ ( strength_reg & 0xf0 ) >> 4 ] + - ( strength_tab [ ( strength_reg & 0x0f ) ] >> 4 ); - else - *signal_strength = 0xffff; + 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: Signal strength (raw / cooked) = (0x%02x / 0x%04x)\n", - __func__,strength_reg,*signal_strength); + dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, sig_reading, *signal_strength); return 0; } -/* TODO: Not clear how we do this */ +/* 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; + 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", __func__); - *snr = 0; + + 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", __func__, + snr_reading, *snr); 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; + struct cx24116_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); - *ucblocks = 0; + + *ucblocks = ( cx24116_readreg(state, CX24116_REG_UCB8) << 8 ) | + cx24116_readreg(state, CX24116_REG_UCB0); return 0; } @@ -583,6 +801,27 @@ static void cx24116_clone_params(struct dvb_frontend* fe) 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", __func__, + 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", __func__); + + return -ETIMEDOUT; /* -EBUSY ? */ +} + static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { struct cx24116_cmd cmd; @@ -594,6 +833,14 @@ static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t 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; @@ -619,6 +866,9 @@ static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) } cmd.len= 0x04; + /* Min delay time before DiSEqC send */ + msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ + return cx24116_cmd_execute(fe, &cmd); } @@ -626,9 +876,39 @@ static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) 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; - /* Default DiSEqC burst state */ - state->burst = CX24116_DISEQC_MINI_A; + /* 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; } @@ -637,7 +917,6 @@ static int cx24116_diseqc_init(struct dvb_frontend* fe) 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 */ @@ -647,82 +926,134 @@ static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_ma printk("0x%02x", d->msg[i]); if(++i < d->msg_len) printk(", "); - } - printk(")\n"); + } + printk(") toneburst=%d\n", toneburst); } + /* Validate length */ 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; + 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", __func__, state->dsec_cmd.args[CX24116_DISEQC_BURST]); + } - cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; - cmd.len = CX24116_DISEQC_MSGOFS + d->msg_len; + /* Wait for LNB ready */ + ret = cx24116_wait_for_lnb(fe); + if(ret != 0) + return ret; - ret = cx24116_cmd_execute(fe, &cmd); + /* Wait for voltage/min repeat delay */ + msleep(100); - /* Firmware command duration is unknown, so guess... + /* Command */ + ret = cx24116_cmd_execute(fe, &state->dsec_cmd); + if(ret != 0) + return ret; + /* + * Wait for send * * Eutelsat spec: - * >15ms delay + - * 13.5ms per byte + - * >15ms delay + - * 12.5ms burst + - * >15ms delay + * >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) */ - if(ret == 0) - msleep( (cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 ); + msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60) ); - return ret; + 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; - struct cx24116_cmd cmd; int ret; - dprintk("%s(%d)\n",__func__,(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; + dprintk("%s(%d) toneburst=%d\n",__func__, burst, toneburst); + /* DiSEqC burst */ if (burst == SEC_MINI_A) - cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; + state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; else if(burst == SEC_MINI_B) - cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B; + state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B; else return -EINVAL; - /* Cache as previous burst state */ - state->burst= cmd.args[CX24116_DISEQC_BURST]; + /* DiSEqC toneburst */ + if(toneburst != CX24116_DISEQC_MESGCACHE) + /* Burst is cached */ + return 0; - cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; - cmd.len= CX24116_DISEQC_MSGOFS; + /* Burst is to be sent with cached message */ - ret= cx24116_cmd_execute(fe, &cmd); + /* Wait for LNB ready */ + ret = cx24116_wait_for_lnb(fe); + if(ret != 0) + return ret; - /* Firmware command duration is unknown, so guess... */ - if(ret == 0) - msleep(60); + /* Wait for voltage/min repeat delay */ + msleep(100); - return ret; + /* 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) @@ -730,6 +1061,7 @@ static void cx24116_release(struct dvb_frontend* fe) struct cx24116_state* state = fe->demodulator_priv; dprintk("%s\n",__func__); kfree(state); + unregister_sysctl_table(kernel_table_header); } static struct dvb_frontend_ops cx24116_ops; @@ -742,11 +1074,15 @@ struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, dprintk("%s\n",__func__); + 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 error; + goto error2; } /* setup the state */ @@ -759,7 +1095,7 @@ struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, ret = (cx24116_readreg(state, 0xFF) << 8) | cx24116_readreg(state, 0xFE); if (ret != 0x0501) { printk("Invalid probe, probably not a CX24116 device\n"); - goto error; + goto error3; } /* create dvb_frontend */ @@ -767,12 +1103,11 @@ struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, state->frontend.demodulator_priv = state; return &state->frontend; -error: - kfree(state); - - return NULL; +error3: kfree(state); +error2: unregister_sysctl_table(kernel_table_header); +error1: return NULL; } - +#if 0 static int cx24116_get_params(struct dvb_frontend* fe) { struct cx24116_state *state = fe->demodulator_priv; @@ -788,14 +1123,63 @@ static int cx24116_get_params(struct dvb_frontend* fe) return 0; } - +#endif +/* + * 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",__func__); + /* 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",__func__); + + /* 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 int cx24116_set_property(struct dvb_frontend *fe, struct dtv_property* tvp) { dprintk("%s(..)\n", __func__); @@ -817,14 +1201,45 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct cx24116_cmd cmd; fe_status_t tunerstat; - int ret, above30msps; - u8 retune=4; + int i, status, ret, retune = 1; dprintk("%s()\n",__func__); state->dnxt.modulation = c->modulation; state->dnxt.frequency = c->frequency; + switch(c->delivery_system) { + case SYS_DVBS: + dprintk("%s: DVB-S delivery system selected\n",__func__); + state->dnxt.pilot = PILOT_OFF; + state->dnxt.rolloff = CX24116_ROLLOFF_035; + break; + case SYS_DVBS2: + dprintk("%s: DVB-S2 delivery system selected\n",__func__); + if(c->pilot == PILOT_AUTO) + retune++; + state->dnxt.pilot = c->pilot; + switch(c->rolloff) { + case ROLLOFF_20: + state->dnxt.rolloff_val= CX24116_ROLLOFF_020; + break; + case ROLLOFF_25: + state->dnxt.rolloff_val= CX24116_ROLLOFF_025; + break; + case ROLLOFF_35: + state->dnxt.rolloff_val= CX24116_ROLLOFF_035; + break; + case ROLLOFF_AUTO: + return -EOPNOTSUPP; + } + state->dnxt.rolloff = c->rolloff; + break; + default: + dprintk("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + if ((ret = cx24116_set_inversion(state, c->inversion)) != 0) return ret; @@ -837,6 +1252,9 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par /* discard the 'current' tuning parameters and prepare to tune */ cx24116_clone_params(fe); + dprintk("%s: retune = %d\n", __func__, retune); + dprintk("%s: rolloff = %d\n", __func__, state->dcur.rolloff); + dprintk("%s: pilot = %d\n", __func__, state->dcur.pilot); dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__, @@ -844,18 +1262,17 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__, state->dcur.inversion, state->dcur.inversion_val); + /* This is also done in advise/acquire on HVR4000 but not on LITE */ if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); - above30msps = (state->dcur.symbol_rate > 30000000); - - if (above30msps){ - cx24116_writereg(state, 0xF9, 0x01); - cx24116_writereg(state, 0xF3, 0x44); - } else { - cx24116_writereg(state, 0xF9, 0x00); - cx24116_writereg(state, 0xF3, 0x46); - } + /* 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; @@ -875,28 +1292,32 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par /* Modulation / FEC & Pilot Off */ cmd.args[0x07] = state->dcur.fec_val; - if (c->pilot == PILOT_ON) - cmd.args[0x07] |= 0x40; + if (state->dcur.pilot == PILOT_ON) + cmd.args[0x07] |= CX24116_PILOT; 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[0x0c] = state->dcur.rolloff_val; cmd.args[0x0d] = state->dcur.fec_mask; - if (above30msps){ + if (state->dcur.symbol_rate > 30000000) { cmd.args[0x0e] = 0x04; cmd.args[0x0f] = 0x00; cmd.args[0x10] = 0x01; cmd.args[0x11] = 0x77; cmd.args[0x12] = 0x36; + cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44); + cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01); } else { cmd.args[0x0e] = 0x06; cmd.args[0x0f] = 0x00; cmd.args[0x10] = 0x00; cmd.args[0x11] = 0xFA; cmd.args[0x12] = 0x24; + cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); + cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); } cmd.len= 0x13; @@ -906,29 +1327,47 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par * the demod does not support autodetect. */ do { - /* Reset status register? */ - cx24116_writereg(state, 0x9d, 0xc1); + /* 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 ) 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(c->pilot == PILOT_AUTO) - /* Toggle pilot bit */ - cmd.args[0x07] ^= 0x40; + /* + * Wait for up to 500 ms before retrying + * + * If we are able to tune then generally it occurs within 100ms. + * If it takes longer, try a different toneburst setting. + */ + for(i = 0; i < 50 ; i++) { + cx24116_read_status(fe, &tunerstat); + status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC); + if(status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) { + dprintk("%s: Tuned\n",__func__); + goto tuned; + } + msleep(10); } + + dprintk("%s: Not tuned\n",__func__); + + /* Toggle pilot bit when in auto-pilot */ + if(state->dcur.pilot == PILOT_AUTO) + cmd.args[0x07] ^= CX24116_PILOT; } 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; } @@ -953,6 +1392,7 @@ static struct dvb_frontend_ops cx24116_ops = { .release = cx24116_release, .init = cx24116_initfe, + .sleep = cx24116_sleep, .read_status = cx24116_read_status, .read_ber = cx24116_read_ber, .read_signal_strength = cx24116_read_signal_strength, @@ -971,6 +1411,9 @@ static struct dvb_frontend_ops cx24116_ops = { 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"); -- cgit v1.2.3 From 18e7f955906a13d81aa379e24fb28eb68c952694 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 14 Sep 2008 02:56:59 +0300 Subject: Add support for DvbWorld 2004 DVB-S2 PCI adapter From: Igor M. Liplianin Add support for DvbWorld 2004 DVB-S2 PCI adapter. The card contains dm1105 PCI chip and cx24116 demodulator Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/Kconfig | 2 +- linux/drivers/media/dvb/dm1105/Kconfig | 19 ++++++++++--------- linux/drivers/media/dvb/dm1105/dm1105.c | 10 +++------- 3 files changed, 14 insertions(+), 17 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/Kconfig b/linux/drivers/media/dvb/Kconfig index 5858744e7..14488f644 100644 --- a/linux/drivers/media/dvb/Kconfig +++ b/linux/drivers/media/dvb/Kconfig @@ -36,7 +36,7 @@ comment "Supported Pluto2 Adapters" source "drivers/media/dvb/pluto2/Kconfig" comment "Supported SDMC DM1105 Adapters" - depends on DVB_CORE && PCI && I2C + depends on DVB_CORE && PCI && I2C source "drivers/media/dvb/dm1105/Kconfig" comment "Supported DVB Frontends" diff --git a/linux/drivers/media/dvb/dm1105/Kconfig b/linux/drivers/media/dvb/dm1105/Kconfig index fc2ca7dca..4b96bbba9 100644 --- a/linux/drivers/media/dvb/dm1105/Kconfig +++ b/linux/drivers/media/dvb/dm1105/Kconfig @@ -1,14 +1,15 @@ config DVB_DM1105 - tristate "SDMC DM1105 based PCI cards" - depends on DVB_CORE && PCI && I2C + tristate "SDMC DM1105 based PCI cards" + depends on DVB_CORE && PCI && I2C select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE - help - Support for cards based on the SDMC DM1105 PCI chip like - DvbWorld 2002 + select DVB_CX24116 if !DVB_FE_CUSTOMISE + help + Support for cards based on the SDMC DM1105 PCI chip like + DvbWorld 2002 - Since these cards have no MPEG decoder onboard, they transmit - only compressed MPEG data over the PCI bus, so you need - an external software decoder to watch TV on your computer. + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the PCI bus, so you need + an external software decoder to watch TV on your computer. - Say Y or M if you own such a device and want to use it. + Say Y or M if you own such a device and want to use it. diff --git a/linux/drivers/media/dvb/dm1105/dm1105.c b/linux/drivers/media/dvb/dm1105/dm1105.c index 2dd31ee00..7f300d4e7 100644 --- a/linux/drivers/media/dvb/dm1105/dm1105.c +++ b/linux/drivers/media/dvb/dm1105/dm1105.c @@ -41,8 +41,8 @@ #include "stv0299.h" /*#include "stv0288.h" *#include "si21xx.h" - *#include "stb6000.h" - *#include "cx24116.h"*/ + *#include "stb6000.h"*/ +#include "cx24116.h" #include "z0194a.h" /* ----------------------------------------------- */ @@ -613,11 +613,11 @@ static struct si21xx_config serit_config = { .min_delay_ms = 100, }; +#endif /* keep */ static struct cx24116_config serit_sp2633_config = { .demod_address = 0x55, }; -#endif /* keep */ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) { @@ -659,15 +659,11 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) #endif /* keep */ break; case PCI_DEVICE_ID_DW2004: -#if 0 /* keep */ dm1105dvb->fe = dvb_attach( cx24116_attach, &serit_sp2633_config, &dm1105dvb->i2c_adap); if (dm1105dvb->fe) dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage; -#else /* keep */ - dev_err(&dm1105dvb->pdev->dev, "needs cx24116 module\n"); -#endif /* keep */ break; } -- cgit v1.2.3 From a2ce2d3d0a4382c4b67dc92e5fa54e49ead46222 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sun, 14 Sep 2008 09:45:58 -0400 Subject: S2API: cx24116 Rolloff changes, sysctls cleanup, isl power changes. From: Darron Broad Remove the debugging sysctls. Rolloff was broken, not it works as expected and has been tested in kaffeine. Power related changes for the isl6421 are not implemented on the HVR4000/4000LITE. Priority: normal Signed-off-by: Steven Toth Signed-off-by: Darron Broad --- linux/drivers/media/dvb/frontends/cx24116.c | 82 +++-------------------------- 1 file changed, 6 insertions(+), 76 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index 8bc622d95..fc116d6d2 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "dvb_frontend.h" #include "cx24116.h" @@ -108,70 +107,6 @@ static int debug = 0; /* DiSEqC tone burst */ static int toneburst = 1; -/* debug & toneburst sysctl */ -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 = "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}, -}; - enum cmds { CMD_SET_VCO = 0x10, @@ -964,7 +899,7 @@ static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_ma * 2/C/A: E0 10 38 F8..FB * 3/D/B: E0 10 38 FC..FF * - * datebyte[3]= 8421:8421 + * databyte[3]= 8421:8421 * ABCD:WXYZ * CLR :SET * @@ -1061,7 +996,6 @@ static void cx24116_release(struct dvb_frontend* fe) struct cx24116_state* state = fe->demodulator_priv; dprintk("%s\n",__func__); kfree(state); - unregister_sysctl_table(kernel_table_header); } static struct dvb_frontend_ops cx24116_ops; @@ -1074,15 +1008,11 @@ struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, dprintk("%s\n",__func__); - 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; + goto error1; } /* setup the state */ @@ -1095,7 +1025,7 @@ struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, ret = (cx24116_readreg(state, 0xFF) << 8) | cx24116_readreg(state, 0xFE); if (ret != 0x0501) { printk("Invalid probe, probably not a CX24116 device\n"); - goto error3; + goto error2; } /* create dvb_frontend */ @@ -1103,8 +1033,7 @@ struct dvb_frontend* cx24116_attach(const struct cx24116_config* config, state->frontend.demodulator_priv = state; return &state->frontend; -error3: kfree(state); -error2: unregister_sysctl_table(kernel_table_header); +error2: kfree(state); error1: return NULL; } #if 0 @@ -1212,7 +1141,8 @@ static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par case SYS_DVBS: dprintk("%s: DVB-S delivery system selected\n",__func__); state->dnxt.pilot = PILOT_OFF; - state->dnxt.rolloff = CX24116_ROLLOFF_035; + state->dnxt.rolloff_val = CX24116_ROLLOFF_035; + state->dnxt.rolloff = c->rolloff; break; case SYS_DVBS2: dprintk("%s: DVB-S2 delivery system selected\n",__func__); -- cgit v1.2.3 From 4183f9e03abc776d7b346e9bb8df2a0b12b0170b Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 14 Sep 2008 13:43:53 +0300 Subject: History update: MPEG initialization in cx24116. From: Igor M. Liplianin Adjust MPEG initialization in cx24116 in order to accomodate different MPEG CLK position and polarity in different cards. For example, HVR4000 uses 0x02 value, but DvbWorld & TeVii USB cards uses 0x01. Without it MPEG stream was broken on that cards for symbol rates > 30000 kSyms/s. Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/frontends/cx24116.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index fc116d6d2..869215a66 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -13,7 +13,8 @@ Some clean ups. Copyright (C) 2008 Igor Liplianin September, 9th 2008 - Fixed locking on high symbol rates (>30000). + Fixed locking on high symbol rates (>30000). + Implement MPEG initialization parameter. 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 -- cgit v1.2.3 From 22b1f7d6a19414713a231f7d8d50726b3fed9a7c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 15 Sep 2008 21:01:52 +0300 Subject: initial driver for af9013 demodulator From: Antti Palosaari - initial driver for the Afatech AF9013 demodulator Priority: normal Signed-off-by: Antti Palosaari --- linux/drivers/media/dvb/frontends/Kconfig | 6 + linux/drivers/media/dvb/frontends/Makefile | 1 + linux/drivers/media/dvb/frontends/af9013.c | 1691 +++++++++++++++++++++++ linux/drivers/media/dvb/frontends/af9013.h | 107 ++ linux/drivers/media/dvb/frontends/af9013_priv.h | 869 ++++++++++++ 5 files changed, 2674 insertions(+) create mode 100644 linux/drivers/media/dvb/frontends/af9013.c create mode 100644 linux/drivers/media/dvb/frontends/af9013.h create mode 100644 linux/drivers/media/dvb/frontends/af9013_priv.h (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index e2444f270..097d9f001 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -398,4 +398,10 @@ config DVB_DUMMY_FE tristate "Dummy frontend driver" default n +config DVB_AF9013 + tristate "Afatech AF9013 demodulator" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + Say Y when you want to support this frontend. endmenu diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index ca24618d5..1b5478886 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -50,3 +50,4 @@ obj-$(CONFIG_DVB_TDA10048) += tda10048.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o +obj-$(CONFIG_DVB_AF9013) += af9013.o diff --git a/linux/drivers/media/dvb/frontends/af9013.c b/linux/drivers/media/dvb/frontends/af9013.c new file mode 100644 index 000000000..480230b11 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/af9013.c @@ -0,0 +1,1691 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "af9013_priv.h" +#include "af9013.h" + +int debug; + +struct af9013_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + + struct af9013_config config; + + u16 signal_strength; + u32 ber; + u32 ucblocks; + u16 snr; + u32 frequency; + unsigned long next_statistics_check; +}; + +static u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + +static int af9013_write_regs(struct af9013_state *state, u8 mbox, u16 reg, + u8 *val, u8 len) +{ + u8 buf[3+len]; + struct i2c_msg msg = { + .addr = state->config.demod_address, + .flags = 0, + .len = sizeof(buf), + .buf = buf }; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + buf[2] = mbox; + memcpy(&buf[3], val, len); + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + warn("I2C write failed reg:%04x len:%d", reg, len); + return -EREMOTEIO; + } + return 0; +} + +static int af9013_write_ofdm_regs(struct af9013_state *state, u16 reg, u8 *val, + u8 len) +{ + u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(0 << 6)|(0 << 7); + return af9013_write_regs(state, mbox, reg, val, len); +} + +static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val, + u8 len) +{ + u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(1 << 6)|(1 << 7); + return af9013_write_regs(state, mbox, reg, val, len); +} + +/* write single register */ +static int af9013_write_reg(struct af9013_state *state, u16 reg, u8 val) +{ + return af9013_write_ofdm_regs(state, reg, &val, 1); +} + +/* read single register */ +static int af9013_read_reg(struct af9013_state *state, u16 reg, u8 *val) +{ + u8 obuf[3] = { reg >> 8, reg & 0xff, 0 }; + u8 ibuf[1]; + struct i2c_msg msg[2] = { + { + .addr = state->config.demod_address, + .flags = 0, + .len = sizeof(obuf), + .buf = obuf + }, { + .addr = state->config.demod_address, + .flags = I2C_M_RD, + .len = sizeof(ibuf), + .buf = ibuf + } + }; + + if (i2c_transfer(state->i2c, msg, 2) != 2) { + warn("I2C read failed reg:%04x", reg); + return -EREMOTEIO; + } + *val = ibuf[0]; + return 0; +} + +static int af9013_write_reg_bits(struct af9013_state *state, u16 reg, u8 pos, + u8 len, u8 val) +{ + int ret; + u8 tmp, mask; + + ret = af9013_read_reg(state, reg, &tmp); + if (ret) + return ret; + + mask = regmask[len - 1] << pos; + tmp = (tmp & ~mask) | ((val << pos) & mask); + + return af9013_write_reg(state, reg, tmp); +} + +static int af9013_read_reg_bits(struct af9013_state *state, u16 reg, u8 pos, + u8 len, u8 *val) +{ + int ret; + u8 tmp; + + ret = af9013_read_reg(state, reg, &tmp); + if (ret) + return ret; + *val = (tmp >> pos) & regmask[len - 1]; + return 0; +} + +static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval) +{ + int ret; + u8 pos; + u16 addr; + deb_info("%s: gpio:%d gpioval:%02x\n", __func__, gpio, gpioval); + +/* GPIO0 & GPIO1 0xd735 + GPIO2 & GPIO3 0xd736 */ + + switch (gpio) { + case 0: + case 1: + addr = 0xd735; + break; + case 2: + case 3: + addr = 0xd736; + break; + + default: + err("invalid gpio:%d\n", gpio); + ret = -EINVAL; + goto error; + }; + + switch (gpio) { + case 0: + case 2: + pos = 0; + break; + case 1: + case 3: + default: + pos = 4; + break; + }; + + ret = af9013_write_reg_bits(state, addr, pos, 4, gpioval); + +error: + return ret; +} + +static u32 af913_div(u32 a, u32 b, u32 x) +{ + u32 r = 0, c = 0, i; + deb_info("%s: a:%d b:%d x:%d\n", __func__, a, b, x); + + if (a > b) { + c = a / b; + a = a - c * b; + } + + for (i = 0; i < x; i++) { + if (a >= b) { + r += 1; + a -= b; + } + a <<= 1; + r <<= 1; + } + r = (c << (u32)x) + r; + + deb_info("%s: a:%d b:%d x:%d r:%d r:%x\n", __func__, a, b, x, r, r); + return r; +} + +static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw) +{ + int ret = 0; + u8 i = 0; + u8 buf[24]; + u32 ns_coeff1_2048nu; + u32 ns_coeff1_8191nu; + u32 ns_coeff1_8192nu; + u32 ns_coeff1_8193nu; + u32 ns_coeff2_2k; + u32 ns_coeff2_8k; + + deb_info("%s: adc_clock:%d bw:%d\n", __func__, + state->config.adc_clock, bw); + + switch (state->config.adc_clock) { + case 28800: /* 28.800 MHz */ + switch (bw) { + case BANDWIDTH_6_MHZ: + ns_coeff1_2048nu = 0x01e79e7a; + ns_coeff1_8191nu = 0x0079eb6e; + ns_coeff1_8192nu = 0x0079e79e; + ns_coeff1_8193nu = 0x0079e3cf; + ns_coeff2_2k = 0x00f3cf3d; + ns_coeff2_8k = 0x003cf3cf; + break; + case BANDWIDTH_7_MHZ: + ns_coeff1_2048nu = 0x0238e38e; + ns_coeff1_8191nu = 0x008e3d55; + ns_coeff1_8192nu = 0x008e38e4; + ns_coeff1_8193nu = 0x008e3472; + ns_coeff2_2k = 0x011c71c7; + ns_coeff2_8k = 0x00471c72; + break; + case BANDWIDTH_8_MHZ: + ns_coeff1_2048nu = 0x028a28a3; + ns_coeff1_8191nu = 0x00a28f3d; + ns_coeff1_8192nu = 0x00a28a29; + ns_coeff1_8193nu = 0x00a28514; + ns_coeff2_2k = 0x01451451; + ns_coeff2_8k = 0x00514514; + break; + default: + ret = -EINVAL; + } + break; + case 20480: /* 20.480 MHz */ + switch (bw) { + case BANDWIDTH_6_MHZ: + ns_coeff1_2048nu = 0x02adb6dc; + ns_coeff1_8191nu = 0x00ab7313; + ns_coeff1_8192nu = 0x00ab6db7; + ns_coeff1_8193nu = 0x00ab685c; + ns_coeff2_2k = 0x0156db6e; + ns_coeff2_8k = 0x0055b6dc; + break; + case BANDWIDTH_7_MHZ: + ns_coeff1_2048nu = 0x03200001; + ns_coeff1_8191nu = 0x00c80640; + ns_coeff1_8192nu = 0x00c80000; + ns_coeff1_8193nu = 0x00c7f9c0; + ns_coeff2_2k = 0x01900000; + ns_coeff2_8k = 0x00640000; + break; + case BANDWIDTH_8_MHZ: + ns_coeff1_2048nu = 0x03924926; + ns_coeff1_8191nu = 0x00e4996e; + ns_coeff1_8192nu = 0x00e49249; + ns_coeff1_8193nu = 0x00e48b25; + ns_coeff2_2k = 0x01c92493; + ns_coeff2_8k = 0x00724925; + break; + default: + ret = -EINVAL; + } + break; + case 28000: /* 28.000 MHz */ + switch (bw) { + case BANDWIDTH_6_MHZ: + ns_coeff1_2048nu = 0x01f58d10; + ns_coeff1_8191nu = 0x007d672f; + ns_coeff1_8192nu = 0x007d6344; + ns_coeff1_8193nu = 0x007d5f59; + ns_coeff2_2k = 0x00fac688; + ns_coeff2_8k = 0x003eb1a2; + break; + case BANDWIDTH_7_MHZ: + ns_coeff1_2048nu = 0x02492492; + ns_coeff1_8191nu = 0x00924db7; + ns_coeff1_8192nu = 0x00924925; + ns_coeff1_8193nu = 0x00924492; + ns_coeff2_2k = 0x01249249; + ns_coeff2_8k = 0x00492492; + break; + case BANDWIDTH_8_MHZ: + ns_coeff1_2048nu = 0x029cbc15; + ns_coeff1_8191nu = 0x00a7343f; + ns_coeff1_8192nu = 0x00a72f05; + ns_coeff1_8193nu = 0x00a729cc; + ns_coeff2_2k = 0x014e5e0a; + ns_coeff2_8k = 0x00539783; + break; + default: + ret = -EINVAL; + } + break; + case 25000: /* 25.000 MHz */ + switch (bw) { + case BANDWIDTH_6_MHZ: + ns_coeff1_2048nu = 0x0231bcb5; + ns_coeff1_8191nu = 0x008c7391; + ns_coeff1_8192nu = 0x008c6f2d; + ns_coeff1_8193nu = 0x008c6aca; + ns_coeff2_2k = 0x0118de5b; + ns_coeff2_8k = 0x00463797; + break; + case BANDWIDTH_7_MHZ: + ns_coeff1_2048nu = 0x028f5c29; + ns_coeff1_8191nu = 0x00a3dc29; + ns_coeff1_8192nu = 0x00a3d70a; + ns_coeff1_8193nu = 0x00a3d1ec; + ns_coeff2_2k = 0x0147ae14; + ns_coeff2_8k = 0x0051eb85; + break; + case BANDWIDTH_8_MHZ: + ns_coeff1_2048nu = 0x02ecfb9d; + ns_coeff1_8191nu = 0x00bb44c1; + ns_coeff1_8192nu = 0x00bb3ee7; + ns_coeff1_8193nu = 0x00bb390d; + ns_coeff2_2k = 0x01767dce; + ns_coeff2_8k = 0x005d9f74; + break; + default: + ret = -EINVAL; + } + break; + default: + err("invalid xtal"); + return -EINVAL; + } + if (ret) { + err("invalid bandwidth"); + return ret; + } + + buf[i++] = (u8) ((ns_coeff1_2048nu & 0x03000000) >> 24); + buf[i++] = (u8) ((ns_coeff1_2048nu & 0x00ff0000) >> 16); + buf[i++] = (u8) ((ns_coeff1_2048nu & 0x0000ff00) >> 8); + buf[i++] = (u8) ((ns_coeff1_2048nu & 0x000000ff)); + buf[i++] = (u8) ((ns_coeff2_2k & 0x01c00000) >> 22); + buf[i++] = (u8) ((ns_coeff2_2k & 0x003fc000) >> 14); + buf[i++] = (u8) ((ns_coeff2_2k & 0x00003fc0) >> 6); + buf[i++] = (u8) ((ns_coeff2_2k & 0x0000003f)); + buf[i++] = (u8) ((ns_coeff1_8191nu & 0x03000000) >> 24); + buf[i++] = (u8) ((ns_coeff1_8191nu & 0x00ffc000) >> 16); + buf[i++] = (u8) ((ns_coeff1_8191nu & 0x0000ff00) >> 8); + buf[i++] = (u8) ((ns_coeff1_8191nu & 0x000000ff)); + buf[i++] = (u8) ((ns_coeff1_8192nu & 0x03000000) >> 24); + buf[i++] = (u8) ((ns_coeff1_8192nu & 0x00ffc000) >> 16); + buf[i++] = (u8) ((ns_coeff1_8192nu & 0x0000ff00) >> 8); + buf[i++] = (u8) ((ns_coeff1_8192nu & 0x000000ff)); + buf[i++] = (u8) ((ns_coeff1_8193nu & 0x03000000) >> 24); + buf[i++] = (u8) ((ns_coeff1_8193nu & 0x00ffc000) >> 16); + buf[i++] = (u8) ((ns_coeff1_8193nu & 0x0000ff00) >> 8); + buf[i++] = (u8) ((ns_coeff1_8193nu & 0x000000ff)); + buf[i++] = (u8) ((ns_coeff2_8k & 0x01c00000) >> 22); + buf[i++] = (u8) ((ns_coeff2_8k & 0x003fc000) >> 14); + buf[i++] = (u8) ((ns_coeff2_8k & 0x00003fc0) >> 6); + buf[i++] = (u8) ((ns_coeff2_8k & 0x0000003f)); + + deb_info("%s: coeff:", __func__); + debug_dump(buf, sizeof(buf), deb_info); + + /* program */ + for (i = 0; i < sizeof(buf); i++) { + ret = af9013_write_reg(state, 0xae00 + i, buf[i]); + if (ret) + break; + } + + return ret; +} + +static int af9013_set_adc_ctrl(struct af9013_state *state) +{ + int ret; + u8 buf[3], tmp, i; + u32 adc_cw; + + deb_info("%s: adc_clock:%d\n", __func__, state->config.adc_clock); + + /* adc frequency type */ + switch (state->config.adc_clock) { + case 28800: /* 28.800 MHz */ + tmp = 0; + break; + case 20480: /* 20.480 MHz */ + tmp = 1; + break; + case 28000: /* 28.000 MHz */ + tmp = 2; + break; + case 25000: /* 25.000 MHz */ + tmp = 3; + break; + default: + err("invalid xtal"); + return -EINVAL; + } + + adc_cw = af913_div(state->config.adc_clock*1000, 1000000ul, 19ul); + + buf[0] = (u8) ((adc_cw & 0x000000ff)); + buf[1] = (u8) ((adc_cw & 0x0000ff00) >> 8); + buf[2] = (u8) ((adc_cw & 0x00ff0000) >> 16); + + deb_info("%s: adc_cw:", __func__); + debug_dump(buf, sizeof(buf), deb_info); + + /* program */ + for (i = 0; i < sizeof(buf); i++) { + ret = af9013_write_reg(state, 0xd180 + i, buf[i]); + if (ret) + goto error; + } + ret = af9013_write_reg_bits(state, 0x9bd2, 0, 4, tmp); +error: + return ret; +} + +static int af9013_set_freq_ctrl(struct af9013_state *state, fe_bandwidth_t bw) +{ + int ret; + u16 addr; + u8 buf[3], i, j; + u32 adc_freq, freq_cw; + s8 bfs_spec_inv; + int if_sample_freq; + + for (j = 0; j < 3; j++) { + if (j == 0) { + addr = 0xd140; /* fcw normal */ + bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1; + } else if (j == 1) { + addr = 0x9be7; /* fcw dummy ram */ + bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1; + } else { + addr = 0x9bea; /* fcw inverted */ + bfs_spec_inv = state->config.rf_spec_inv ? 1 : -1; + } + + adc_freq = state->config.adc_clock * 1000; + if_sample_freq = state->config.tuner_if * 1000; + + /* TDA18271 uses different sampling freq for every bw */ + if (state->config.tuner == AF9013_TUNER_TDA18271) { + switch (bw) { + case BANDWIDTH_6_MHZ: + if_sample_freq = 3300000; /* 3.3 MHz */ + break; + case BANDWIDTH_7_MHZ: + if_sample_freq = 3800000; /* 3.8 MHz */ + break; + case BANDWIDTH_8_MHZ: + default: + if_sample_freq = 4300000; /* 4.3 MHz */ + break; + } + } + + while (if_sample_freq > (adc_freq / 2)) + if_sample_freq = if_sample_freq - adc_freq; + + if (if_sample_freq >= 0) + bfs_spec_inv = bfs_spec_inv * (-1); + else + if_sample_freq = if_sample_freq * (-1); + + freq_cw = af913_div(if_sample_freq, adc_freq, 23ul); + + if (bfs_spec_inv == -1) + freq_cw = 0x00800000 - freq_cw; + + buf[0] = (u8) ((freq_cw & 0x000000ff)); + buf[1] = (u8) ((freq_cw & 0x0000ff00) >> 8); + buf[2] = (u8) ((freq_cw & 0x007f0000) >> 16); + + + deb_info("%s: freq_cw:", __func__); + debug_dump(buf, sizeof(buf), deb_info); + + /* program */ + for (i = 0; i < sizeof(buf); i++) { + ret = af9013_write_reg(state, addr++, buf[i]); + if (ret) + goto error; + } + } +error: + return ret; +} + +static int af9013_set_ofdm_params(struct af9013_state *state, + struct dvb_ofdm_parameters *params, u8 *auto_mode) +{ + int ret; + u8 i, buf[3] = {0, 0, 0}; + *auto_mode = 0; /* set if parameters are requested to auto set */ + + switch (params->transmission_mode) { + case TRANSMISSION_MODE_AUTO: + *auto_mode = 1; + case TRANSMISSION_MODE_2K: + break; + case TRANSMISSION_MODE_8K: + buf[0] |= (1 << 0); + break; + default: + return -EINVAL; + } + + switch (params->guard_interval) { + case GUARD_INTERVAL_AUTO: + *auto_mode = 1; + case GUARD_INTERVAL_1_32: + break; + case GUARD_INTERVAL_1_16: + buf[0] |= (1 << 2); + break; + case GUARD_INTERVAL_1_8: + buf[0] |= (2 << 2); + break; + case GUARD_INTERVAL_1_4: + buf[0] |= (3 << 2); + break; + default: + return -EINVAL; + } + + switch (params->hierarchy_information) { + case HIERARCHY_AUTO: + *auto_mode = 1; + case HIERARCHY_NONE: + break; + case HIERARCHY_1: + buf[0] |= (1 << 4); + break; + case HIERARCHY_2: + buf[0] |= (2 << 4); + break; + case HIERARCHY_4: + buf[0] |= (3 << 4); + break; + default: + return -EINVAL; + }; + + switch (params->constellation) { + case QAM_AUTO: + *auto_mode = 1; + case QPSK: + break; + case QAM_16: + buf[1] |= (1 << 6); + break; + case QAM_64: + buf[1] |= (2 << 6); + break; + default: + return -EINVAL; + } + + /* Use HP. How and which case we can switch to LP? */ + buf[1] |= (1 << 4); + + switch (params->code_rate_HP) { + case FEC_AUTO: + *auto_mode = 1; + case FEC_1_2: + break; + case FEC_2_3: + buf[2] |= (1 << 0); + break; + case FEC_3_4: + buf[2] |= (2 << 0); + break; + case FEC_5_6: + buf[2] |= (3 << 0); + break; + case FEC_7_8: + buf[2] |= (4 << 0); + break; + default: + return -EINVAL; + } + + switch (params->code_rate_LP) { + case FEC_AUTO: + /* if HIERARCHY_NONE and FEC_NONE then LP FEC is set to FEC_AUTO + by dvb_frontend.c for compatibility */ + if (params->hierarchy_information != HIERARCHY_NONE) + *auto_mode = 1; + case FEC_1_2: + break; + case FEC_2_3: + buf[2] |= (1 << 3); + break; + case FEC_3_4: + buf[2] |= (2 << 3); + break; + case FEC_5_6: + buf[2] |= (3 << 3); + break; + case FEC_7_8: + buf[2] |= (4 << 3); + break; + case FEC_NONE: + if (params->hierarchy_information == HIERARCHY_AUTO) + break; + default: + return -EINVAL; + } + + switch (params->bandwidth) { + case BANDWIDTH_6_MHZ: + break; + case BANDWIDTH_7_MHZ: + buf[1] |= (1 << 2); + break; + case BANDWIDTH_8_MHZ: + buf[1] |= (2 << 2); + break; + default: + return -EINVAL; + } + + /* program */ + for (i = 0; i < sizeof(buf); i++) { + ret = af9013_write_reg(state, 0xd3c0 + i, buf[i]); + if (ret) + break; + } + + return ret; +} + +static int af9013_reset(struct af9013_state *state, u8 sleep) +{ + int ret; + u8 tmp, i; + deb_info("%s\n", __func__); + + /* enable OFDM reset */ + ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 1); + if (ret) + goto error; + + /* start reset mechanism */ + ret = af9013_write_reg(state, 0xaeff, 1); + if (ret) + goto error; + + /* reset is done when bit 1 is set */ + for (i = 0; i < 150; i++) { + ret = af9013_read_reg_bits(state, 0xd417, 1, 1, &tmp); + if (ret) + goto error; + if (tmp) + break; /* reset done */ + msleep(10); + } + if (!tmp) + return -ETIMEDOUT; + + /* don't clear reset when going to sleep */ + if (!sleep) { + /* clear OFDM reset */ + ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0); + if (ret) + goto error; + + /* disable OFDM reset */ + ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0); + } +error: + return ret; +} + +static int af9013_power_ctrl(struct af9013_state *state, u8 onoff) +{ + int ret; + deb_info("%s: onoff:%d\n", __func__, onoff); + + if (onoff) { + /* power on */ + ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 0); + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0); + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0); + } else { + /* power off */ + ret = af9013_reset(state, 1); + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 1); + } +error: + return ret; +} + +static int af9013_lock_led(struct af9013_state *state, u8 onoff) +{ + deb_info("%s: onoff:%d\n", __func__, onoff); + + return af9013_write_reg_bits(state, 0xd730, 0, 1, onoff); +} + +static int af9013_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 auto_mode; /* auto set TPS */ + + deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency, + params->u.ofdm.bandwidth); + + state->frequency = params->frequency; + + /* program CFOE coefficients */ + ret = af9013_set_coeff(state, params->u.ofdm.bandwidth); + if (ret) + goto error; + + /* program frequency control */ + ret = af9013_set_freq_ctrl(state, params->u.ofdm.bandwidth); + if (ret) + goto error; + + /* clear TPS lock flag (inverted flag) */ + ret = af9013_write_reg_bits(state, 0xd330, 3, 1, 1); + if (ret) + goto error; + + /* clear MPEG2 lock flag */ + ret = af9013_write_reg_bits(state, 0xd507, 6, 1, 0); + if (ret) + goto error; + + /* empty channel function */ + ret = af9013_write_reg_bits(state, 0x9bfe, 0, 1, 0); + if (ret) + goto error; + + /* empty DVB-T channel function */ + ret = af9013_write_reg_bits(state, 0x9bc2, 0, 1, 0); + if (ret) + goto error; + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, params); + + /* program TPS and bandwidth, check if auto mode needed */ + ret = af9013_set_ofdm_params(state, ¶ms->u.ofdm, &auto_mode); + if (ret) + goto error; + + if (auto_mode) { + /* clear easy mode flag */ + ret = af9013_write_reg(state, 0xaefd, 0); + deb_info("%s: auto TPS\n", __func__); + } else { + /* set easy mode flag */ + ret = af9013_write_reg(state, 0xaefd, 1); + if (ret) + goto error; + ret = af9013_write_reg(state, 0xaefe, 0); + deb_info("%s: manual TPS\n", __func__); + } + if (ret) + goto error; + + /* everything is set, lets try to receive channel - OFSM GO! */ + ret = af9013_write_reg(state, 0xffff, 0); + if (ret) + goto error; + +error: + return ret; +} + +static int af9013_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 i, buf[3]; + deb_info("%s\n", __func__); + + /* read TPS registers */ + for (i = 0; i < 3; i++) { + ret = af9013_read_reg(state, 0xd3c0 + i, &buf[i]); + if (ret) + goto error; + } + + switch ((buf[1] >> 6) & 3) { + case 0: + p->u.ofdm.constellation = QPSK; + break; + case 1: + p->u.ofdm.constellation = QAM_16; + break; + case 2: + p->u.ofdm.constellation = QAM_64; + break; + } + + switch ((buf[0] >> 0) & 3) { + case 0: + p->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + p->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; + } + + switch ((buf[0] >> 2) & 3) { + case 0: + p->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + p->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + p->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + p->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[0] >> 4) & 7) { + case 0: + p->u.ofdm.hierarchy_information = HIERARCHY_NONE; + break; + case 1: + p->u.ofdm.hierarchy_information = HIERARCHY_1; + break; + case 2: + p->u.ofdm.hierarchy_information = HIERARCHY_2; + break; + case 3: + p->u.ofdm.hierarchy_information = HIERARCHY_4; + break; + } + + switch ((buf[2] >> 0) & 7) { + case 0: + p->u.ofdm.code_rate_HP = FEC_1_2; + break; + case 1: + p->u.ofdm.code_rate_HP = FEC_2_3; + break; + case 2: + p->u.ofdm.code_rate_HP = FEC_3_4; + break; + case 3: + p->u.ofdm.code_rate_HP = FEC_5_6; + break; + case 4: + p->u.ofdm.code_rate_HP = FEC_7_8; + break; + } + + switch ((buf[2] >> 3) & 7) { + case 0: + p->u.ofdm.code_rate_LP = FEC_1_2; + break; + case 1: + p->u.ofdm.code_rate_LP = FEC_2_3; + break; + case 2: + p->u.ofdm.code_rate_LP = FEC_3_4; + break; + case 3: + p->u.ofdm.code_rate_LP = FEC_5_6; + break; + case 4: + p->u.ofdm.code_rate_LP = FEC_7_8; + break; + } + + switch ((buf[1] >> 2) & 3) { + case 0: + p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + break; + case 1: + p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; + break; + case 2: + p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + break; + } + + p->inversion = INVERSION_AUTO; + p->frequency = state->frequency; + +error: + return ret; +} + +static int af9013_update_ber_unc(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 buf[3], i; + u32 error_bit_count = 0; + u32 total_bit_count = 0; + u32 abort_packet_count = 0; + u64 numerator, denominator; + + state->ber = 0; + + /* check if error bit count is ready */ + ret = af9013_read_reg_bits(state, 0xd391, 4, 1, &buf[0]); + if (ret) + goto error; + if (!buf[0]) + goto exit; + + /* get RSD packet abort count */ + for (i = 0; i < 2; i++) { + ret = af9013_read_reg(state, 0xd38a + i, &buf[i]); + if (ret) + goto error; + } + abort_packet_count = (buf[1] << 8) + buf[0]; + + /* get error bit count */ + for (i = 0; i < 3; i++) { + ret = af9013_read_reg(state, 0xd387 + i, &buf[i]); + if (ret) + goto error; + } + error_bit_count = (buf[2] << 16) + (buf[1] << 8) + buf[0]; + error_bit_count = error_bit_count - abort_packet_count * 8 * 8; + + /* get used RSD counting period (10000 RSD packets used) */ + for (i = 0; i < 2; i++) { + ret = af9013_read_reg(state, 0xd385 + i, &buf[i]); + if (ret) + goto error; + } + total_bit_count = (buf[1] << 8) + buf[0]; + total_bit_count = total_bit_count - abort_packet_count; + total_bit_count = total_bit_count * 204 * 8; + + if (total_bit_count) { + numerator = error_bit_count * 1000000000; + denominator = total_bit_count; + state->ber = numerator / denominator; + } + + state->ucblocks += abort_packet_count; + + deb_info("%s: err bits:%d total bits:%d abort count:%d\n", __func__, + error_bit_count, total_bit_count, abort_packet_count); + + /* set BER counting range */ + ret = af9013_write_reg(state, 0xd385, 10000 & 0xff); + if (ret) + goto error; + ret = af9013_write_reg(state, 0xd386, 10000 >> 8); + if (ret) + goto error; + /* reset and start BER counter */ + ret = af9013_write_reg_bits(state, 0xd391, 4, 1, 1); + if (ret) + goto error; + +exit: +error: + return ret; +} + +static int af9013_update_snr(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 buf[3], i, len; + u32 quant = 0; + struct snr_table *snr_table; + + /* check if quantizer ready (for snr) */ + ret = af9013_read_reg_bits(state, 0xd2e1, 3, 1, &buf[0]); + if (ret) + goto error; + if (buf[0]) { + /* quantizer ready - read it */ + for (i = 0; i < 3; i++) { + ret = af9013_read_reg(state, 0xd2e3 + i, &buf[i]); + if (ret) + goto error; + } + quant = (buf[2] << 16) + (buf[1] << 8) + buf[0]; + + /* read current constellation */ + ret = af9013_read_reg(state, 0xd3c1, &buf[0]); + if (ret) + goto error; + + switch ((buf[0] >> 6) & 3) { + case 0: + len = ARRAY_SIZE(qpsk_snr_table); + snr_table = qpsk_snr_table; + break; + case 1: + len = ARRAY_SIZE(qam16_snr_table); + snr_table = qam16_snr_table; + break; + case 2: + len = ARRAY_SIZE(qam64_snr_table); + snr_table = qam64_snr_table; + break; + default: + len = 0; + break; + } + + if (len) { + for (i = 0; i < len; i++) { + if (quant < snr_table[i].val) { + state->snr = snr_table[i].snr * 10; + break; + } + } + } + + /* set quantizer super frame count */ + ret = af9013_write_reg(state, 0xd2e2, 1); + if (ret) + goto error; + + /* check quantizer availability */ + for (i = 0; i < 10; i++) { + msleep(10); + ret = af9013_read_reg_bits(state, 0xd2e6, 0, 1, + &buf[0]); + if (ret) + goto error; + if (!buf[0]) + break; + } + + /* reset quantizer */ + ret = af9013_write_reg_bits(state, 0xd2e1, 3, 1, 1); + if (ret) + goto error; + } + +error: + return ret; +} + +static int af9013_update_signal_strength(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + u8 tmp0; + u8 rf_gain, rf_50, rf_80, if_gain, if_50, if_80; + int signal_strength; + + deb_info("%s\n", __func__); + + state->signal_strength = 0; + + ret = af9013_read_reg_bits(state, 0x9bee, 0, 1, &tmp0); + if (ret) + goto error; + if (tmp0) { + ret = af9013_read_reg(state, 0x9bbd, &rf_50); + if (ret) + goto error; + ret = af9013_read_reg(state, 0x9bd0, &rf_80); + if (ret) + goto error; + ret = af9013_read_reg(state, 0x9be2, &if_50); + if (ret) + goto error; + ret = af9013_read_reg(state, 0x9be4, &if_80); + if (ret) + goto error; + ret = af9013_read_reg(state, 0xd07c, &rf_gain); + if (ret) + goto error; + ret = af9013_read_reg(state, 0xd07d, &if_gain); + if (ret) + goto error; + signal_strength = (0xffff / (9 * (rf_50 + if_50) - \ + 11 * (rf_80 + if_80))) * (10 * (rf_gain + if_gain) - \ + 11 * (rf_80 + if_80)); + if (signal_strength < 0) + signal_strength = 0; + else if (signal_strength > 0xffff) + signal_strength = 0xffff; + + state->signal_strength = signal_strength; + } + +error: + return ret; +} + +static int af9013_update_statistics(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + + if (time_before(jiffies, state->next_statistics_check)) + return 0; + + /* set minimum statistic update interval */ + state->next_statistics_check = jiffies + msecs_to_jiffies(1200); + + ret = af9013_update_signal_strength(fe); + if (ret) + goto error; + ret = af9013_update_snr(fe); + if (ret) + goto error; + ret = af9013_update_ber_unc(fe); + if (ret) + goto error; + +error: + return ret; +} + +static int af9013_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 800; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret = 0; + u8 tmp; + *status = 0; + + /* TPS lock */ + ret = af9013_read_reg_bits(state, 0xd330, 3, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL; + + /* MPEG2 lock */ + ret = af9013_read_reg_bits(state, 0xd507, 6, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_SYNC | FE_HAS_LOCK; + + if (!*status & FE_HAS_SIGNAL) { + /* AGC lock */ + ret = af9013_read_reg_bits(state, 0xd1a0, 6, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_SIGNAL; + } + + if (!*status & FE_HAS_CARRIER) { + /* CFO lock */ + ret = af9013_read_reg_bits(state, 0xd333, 7, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_CARRIER; + } + + if (!*status & FE_HAS_CARRIER) { + /* SFOE lock */ + ret = af9013_read_reg_bits(state, 0xd334, 6, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_CARRIER; + } + + ret = af9013_update_statistics(fe); + +error: + return ret; +} + + +static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + ret = af9013_update_statistics(fe); + *ber = state->ber; + return ret; +} + +static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + ret = af9013_update_statistics(fe); + *strength = state->signal_strength; + return ret; +} + +static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + ret = af9013_update_statistics(fe); + *snr = state->snr; + return ret; +} + +static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + ret = af9013_update_statistics(fe); + *ucblocks = state->ucblocks; + return ret; +} + +static int af9013_sleep(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret; + deb_info("%s\n", __func__); + + ret = af9013_lock_led(state, 0); + if (ret) + goto error; + + ret = af9013_power_ctrl(state, 0); +error: + return ret; +} + +static int af9013_init(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + int ret, i, len; + u8 tmp0, tmp1; + struct regdesc *init; + deb_info("%s\n", __func__); + + /* reset OFDM */ + ret = af9013_reset(state, 0); + if (ret) + goto error; + + /* power on */ + ret = af9013_power_ctrl(state, 1); + if (ret) + goto error; + + /* enable ADC */ + ret = af9013_write_reg(state, 0xd73a, 0xa4); + if (ret) + goto error; + + /* write API version to firmware */ + for (i = 0; i < sizeof(state->config.api_version); i++) { + ret = af9013_write_reg(state, 0x9bf2 + i, + state->config.api_version[i]); + if (ret) + goto error; + } + + /* program ADC control */ + ret = af9013_set_adc_ctrl(state); + if (ret) + goto error; + + /* set I2C master clock */ + ret = af9013_write_reg(state, 0xd416, 0x14); + if (ret) + goto error; + + /* set 16 embx */ + ret = af9013_write_reg_bits(state, 0xd700, 1, 1, 1); + if (ret) + goto error; + + /* set no trigger */ + ret = af9013_write_reg_bits(state, 0xd700, 2, 1, 0); + if (ret) + goto error; + + /* set read-update bit for constellation */ + ret = af9013_write_reg_bits(state, 0xd371, 1, 1, 1); + if (ret) + goto error; + + /* enable FEC monitor */ + ret = af9013_write_reg_bits(state, 0xd392, 1, 1, 1); + if (ret) + goto error; + + /* load OFSM settings */ + deb_info("%s: load ofsm settings\n", __func__); + len = ARRAY_SIZE(ofsm_init); + init = ofsm_init; + for (i = 0; i < len; i++) { + ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos, + init[i].len, init[i].val); + if (ret) + goto error; + } + + /* load tuner specific settings */ + deb_info("%s: load tuner specific settings\n", __func__); + switch (state->config.tuner) { + case AF9013_TUNER_MXL5003D: + len = ARRAY_SIZE(tuner_init_mxl5003d); + init = tuner_init_mxl5003d; + break; + case AF9013_TUNER_MXL5005D: + case AF9013_TUNER_MXL5005R: + len = ARRAY_SIZE(tuner_init_mxl5005); + init = tuner_init_mxl5005; + break; + case AF9013_TUNER_ENV77H11D5: + len = ARRAY_SIZE(tuner_init_env77h11d5); + init = tuner_init_env77h11d5; + break; + case AF9013_TUNER_MT2060: + len = ARRAY_SIZE(tuner_init_mt2060); + init = tuner_init_mt2060; + break; + case AF9013_TUNER_MC44S803: + len = ARRAY_SIZE(tuner_init_mc44s803); + init = tuner_init_mc44s803; + break; + case AF9013_TUNER_QT1010: + case AF9013_TUNER_QT1010A: + len = ARRAY_SIZE(tuner_init_qt1010); + init = tuner_init_qt1010; + break; + case AF9013_TUNER_MT2060_2: + len = ARRAY_SIZE(tuner_init_mt2060_2); + init = tuner_init_mt2060_2; + break; + case AF9013_TUNER_TDA18271: + len = ARRAY_SIZE(tuner_init_tda18271); + init = tuner_init_tda18271; + break; + case AF9013_TUNER_UNKNOWN: + default: + len = ARRAY_SIZE(tuner_init_unknown); + init = tuner_init_unknown; + break; + } + + for (i = 0; i < len; i++) { + ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos, + init[i].len, init[i].val); + if (ret) + goto error; + } + + /* set TS mode */ + deb_info("%s: setting ts mode\n", __func__); + tmp0 = 0; /* parallel mode */ + tmp1 = 0; /* serial mode */ + switch (state->config.output_mode) { + case AF9013_OUTPUT_MODE_PARALLEL: + tmp0 = 1; + break; + case AF9013_OUTPUT_MODE_SERIAL: + tmp1 = 1; + break; + case AF9013_OUTPUT_MODE_USB: + /* usb mode for AF9015 */ + default: + break; + } + ret = af9013_write_reg_bits(state, 0xd500, 1, 1, tmp0); /* parallel */ + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd500, 2, 1, tmp1); /* serial */ + if (ret) + goto error; + + /* enable lock led */ + ret = af9013_lock_led(state, 1); + if (ret) + goto error; + +error: + return ret; +} + +static struct dvb_frontend_ops af9013_ops; + +static int af9013_download_firmware(struct af9013_state *state) +{ + int i, len, packets, remainder, ret; + const struct firmware *fw; + u16 addr = 0x5100; /* firmware start address */ + u16 checksum = 0; + u8 val; + u8 fw_params[4]; + u8 *data; + u8 *fw_file = AF9013_DEFAULT_FIRMWARE; + + msleep(100); + /* check whether firmware is already running */ + ret = af9013_read_reg(state, 0x98be, &val); + if (ret) + goto error; + else + deb_info("%s: firmware status:%02x\n", __func__, val); + + if (val == 0x0c) /* fw is running, no need for download */ + goto exit; + + info("found a '%s' in cold state, will try to load a firmware", + af9013_ops.info.name); + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, &state->i2c->dev); + if (ret) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details" \ + " on firmware-problems. (%d)", + fw_file, ret); + goto error; + } + + info("downloading firmware from file '%s'", fw_file); + + /* calc checksum */ + for (i = 0; i < fw->size; i++) + checksum += fw->data[i]; + + fw_params[0] = checksum >> 8; + fw_params[1] = checksum & 0xff; + fw_params[2] = fw->size >> 8; + fw_params[3] = fw->size & 0xff; + + /* write fw checksum & size */ + ret = af9013_write_ofsm_regs(state, 0x50fc, + fw_params, sizeof(fw_params)); + if (ret) + goto error_release; + + #define FW_PACKET_MAX_DATA 16 + + packets = fw->size / FW_PACKET_MAX_DATA; + remainder = fw->size % FW_PACKET_MAX_DATA; + len = FW_PACKET_MAX_DATA; + for (i = 0; i <= packets; i++) { + if (i == packets) /* set size of the last packet */ + len = remainder; + + data = (fw->data + i * FW_PACKET_MAX_DATA); + ret = af9013_write_ofsm_regs(state, addr, data, len); + addr += FW_PACKET_MAX_DATA; + + if (ret) { + err("firmware download failed at %d with %d", i, ret); + goto error_release; + } + } + + #undef FW_PACKET_MAX_DATA + + /* request boot firmware */ + ret = af9013_write_reg(state, 0xe205, 1); + if (ret) + goto error_release; + + for (i = 0; i < 15; i++) { + msleep(100); + + /* check firmware status */ + ret = af9013_read_reg(state, 0x98be, &val); + if (ret) + goto error_release; + + deb_info("%s: firmware status:%02x\n", __func__, val); + + if (val == 0x0c || val == 0x04) /* success or fail */ + break; + } + + if (val == 0x04) { + err("firmware did not run"); + ret = -1; + } else if (val != 0x0c) { + err("firmware boot timeout"); + ret = -1; + } + +error_release: + release_firmware(fw); +error: +exit: + if (!ret) + info("found a '%s' in warm state.", af9013_ops.info.name); + return ret; +} + +static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + int ret; + struct af9013_state *state = fe->demodulator_priv; + deb_info("%s: enable:%d\n", __func__, enable); + + if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) + ret = af9013_write_reg_bits(state, 0xd417, 3, 1, enable); + else + ret = af9013_write_reg_bits(state, 0xd607, 2, 1, enable); + + return ret; +} + +static void af9013_release(struct dvb_frontend *fe) +{ + struct af9013_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops af9013_ops; + +struct dvb_frontend *af9013_attach(const struct af9013_config *config, + struct i2c_adapter *i2c) +{ + int ret; + struct af9013_state *state = NULL; + u8 buf[3], i; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config, config, sizeof(struct af9013_config)); + + /* chip version */ + ret = af9013_read_reg_bits(state, 0xd733, 4, 4, &buf[2]); + if (ret) + goto error; + + /* ROM version */ + for (i = 0; i < 2; i++) { + ret = af9013_read_reg(state, 0x116b + i, &buf[i]); + if (ret) + goto error; + } + deb_info("%s: chip version:%d ROM version:%d.%d\n", __func__, + buf[2], buf[0], buf[1]); + + /* download firmware */ + if (state->config.output_mode != AF9013_OUTPUT_MODE_USB) { + ret = af9013_download_firmware(state); + if (ret) + goto error; + } + + /* firmware version */ + for (i = 0; i < 3; i++) { + ret = af9013_read_reg(state, 0x5103 + i, &buf[i]); + if (ret) + goto error; + } + info("firmware version:%d.%d.%d", buf[0], buf[1], buf[2]); + + /* settings for mp2if */ + if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) { + /* AF9015 split PSB to 1.5k + 0.5k */ + ret = af9013_write_reg_bits(state, 0xd50b, 2, 1, 1); + } else { + /* AF9013 change the output bit to data7 */ + ret = af9013_write_reg_bits(state, 0xd500, 3, 1, 1); + if (ret) + goto error; + /* AF9013 set mpeg to full speed */ + ret = af9013_write_reg_bits(state, 0xd502, 4, 1, 1); + } + if (ret) + goto error; + ret = af9013_write_reg_bits(state, 0xd520, 4, 1, 1); + if (ret) + goto error; + + /* set GPIOs */ + for (i = 0; i < sizeof(state->config.gpio); i++) { + ret = af9013_set_gpio(state, i, state->config.gpio[i]); + if (ret) + goto error; + } + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &af9013_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(af9013_attach); + +static struct dvb_frontend_ops af9013_ops = { + .info = { + .name = "Afatech AF9013 DVB-T", + .type = FE_OFDM, + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 250000, + .frequency_tolerance = 0, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .release = af9013_release, + .init = af9013_init, + .sleep = af9013_sleep, + .i2c_gate_ctrl = af9013_i2c_gate_ctrl, + + .set_frontend = af9013_set_frontend, + .get_frontend = af9013_get_frontend, + + .get_tune_settings = af9013_get_tune_settings, + + .read_status = af9013_read_status, + .read_ber = af9013_read_ber, + .read_signal_strength = af9013_read_signal_strength, + .read_snr = af9013_read_snr, + .read_ucblocks = af9013_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/af9013.h b/linux/drivers/media/dvb/frontends/af9013.h new file mode 100644 index 000000000..28b90c91c --- /dev/null +++ b/linux/drivers/media/dvb/frontends/af9013.h @@ -0,0 +1,107 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari + * + * Thanks to Afatech who kindly provided information. + * + * 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 _AF9013_H_ +#define _AF9013_H_ + +#include + +enum af9013_ts_mode { + AF9013_OUTPUT_MODE_PARALLEL, + AF9013_OUTPUT_MODE_SERIAL, + AF9013_OUTPUT_MODE_USB, /* only for AF9015 */ +}; + +enum af9013_tuner { + AF9013_TUNER_MXL5003D = 3, /* MaxLinear */ + AF9013_TUNER_MXL5005D = 13, /* MaxLinear */ + AF9013_TUNER_MXL5005R = 30, /* MaxLinear */ + AF9013_TUNER_ENV77H11D5 = 129, /* Panasonic */ + AF9013_TUNER_MT2060 = 130, /* Microtune */ + AF9013_TUNER_MC44S803 = 133, /* Freescale */ + AF9013_TUNER_QT1010 = 134, /* Quantek */ + AF9013_TUNER_UNKNOWN = 140, /* for can tuners ? */ + AF9013_TUNER_MT2060_2 = 147, /* Microtune */ + AF9013_TUNER_TDA18271 = 156, /* NXP */ + AF9013_TUNER_QT1010A = 162, /* Quantek */ +}; + +/* AF9013/5 GPIOs (mostly guessed) + demod#1-gpio#0 - set demod#2 i2c-addr for dual devices + demod#1-gpio#1 - xtal setting (?) + demod#1-gpio#3 - tuner#1 + demod#2-gpio#0 - tuner#2 + demod#2-gpio#1 - xtal setting (?) +*/ +#define AF9013_GPIO_ON (1 << 0) +#define AF9013_GPIO_EN (1 << 1) +#define AF9013_GPIO_O (1 << 2) +#define AF9013_GPIO_I (1 << 3) + +#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN) +#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) + +#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN) +#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) + +struct af9013_config { + /* demodulator's I2C address */ + u8 demod_address; + + /* frequencies in kHz */ + u32 adc_clock; + + /* tuner ID */ + u8 tuner; + + /* tuner IF */ + u16 tuner_if; + + /* TS data output mode */ + u8 output_mode:2; + + /* RF spectrum inversion */ + u8 rf_spec_inv:1; + + /* API version */ + u8 api_version[4]; + + /* GPIOs */ + u8 gpio[4]; +}; + + +#if defined(CONFIG_DVB_AF9013) || \ + (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE)) +extern struct dvb_frontend *af9013_attach(const struct af9013_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *af9013_attach( +const struct af9013_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_AF9013 */ + +#endif /* _AF9013_H_ */ diff --git a/linux/drivers/media/dvb/frontends/af9013_priv.h b/linux/drivers/media/dvb/frontends/af9013_priv.h new file mode 100644 index 000000000..b62e4cc9b --- /dev/null +++ b/linux/drivers/media/dvb/frontends/af9013_priv.h @@ -0,0 +1,869 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari + * + * Thanks to Afatech who kindly provided information. + * + * 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 _AF9013_PRIV_ +#define _AF9013_PRIV_ + +#define LOG_PREFIX "af9013" +extern int debug; + +#define dprintk(var, level, args...) \ + do { if ((var & level)) printk(args); } while (0) + +#define debug_dump(b, l, func) {\ + int loop_; \ + for (loop_ = 0; loop_ < l; loop_++) \ + func("%02x ", b[loop_]); \ + func("\n");\ +} + +#define deb_info(args...) dprintk(debug, 0x01, args) + +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +#define AF9013_DEFAULT_FIRMWARE "dvb-fe-af9013.fw" + +struct regdesc { + u16 addr; + u8 pos:4; + u8 len:4; + u8 val; +}; + +struct snr_table { + u32 val; + u8 snr; +}; + +/* QPSK SNR lookup table */ +static struct snr_table qpsk_snr_table[] = { + { 0x0b4771, 0 }, + { 0x0c1aed, 1 }, + { 0x0d0d27, 2 }, + { 0x0e4d19, 3 }, + { 0x0e5da8, 4 }, + { 0x107097, 5 }, + { 0x116975, 6 }, + { 0x1252d9, 7 }, + { 0x131fa4, 8 }, + { 0x13d5e1, 9 }, + { 0x148e53, 10 }, + { 0x15358b, 11 }, + { 0x15dd29, 12 }, + { 0x168112, 13 }, + { 0x170b61, 14 }, + { 0xffffff, 15 }, +}; + +/* QAM16 SNR lookup table */ +static struct snr_table qam16_snr_table[] = { + { 0x05eb62, 5 }, + { 0x05fecf, 6 }, + { 0x060b80, 7 }, + { 0x062501, 8 }, + { 0x064865, 9 }, + { 0x069604, 10 }, + { 0x06f356, 11 }, + { 0x07706a, 12 }, + { 0x0804d3, 13 }, + { 0x089d1a, 14 }, + { 0x093e3d, 15 }, + { 0x09e35d, 16 }, + { 0x0a7c3c, 17 }, + { 0x0afaf8, 18 }, + { 0x0b719d, 19 }, + { 0xffffff, 20 }, +}; + +/* QAM64 SNR lookup table */ +static struct snr_table qam64_snr_table[] = { + { 0x03109b, 12 }, + { 0x0310d4, 13 }, + { 0x031920, 14 }, + { 0x0322d0, 15 }, + { 0x0339fc, 16 }, + { 0x0364a1, 17 }, + { 0x038bcc, 18 }, + { 0x03c7d3, 19 }, + { 0x0408cc, 20 }, + { 0x043bed, 21 }, + { 0x048061, 22 }, + { 0x04be95, 23 }, + { 0x04fa7d, 24 }, + { 0x052405, 25 }, + { 0x05570d, 26 }, + { 0xffffff, 27 }, +}; + +static struct regdesc ofsm_init[] = { + { 0xd73a, 0, 8, 0xa1 }, + { 0xd73b, 0, 8, 0x1f }, + { 0xd73c, 4, 4, 0x0a }, + { 0xd732, 3, 1, 0x00 }, + { 0xd731, 4, 2, 0x03 }, + { 0xd73d, 7, 1, 0x01 }, + { 0xd740, 0, 1, 0x00 }, + { 0xd740, 1, 1, 0x00 }, + { 0xd740, 2, 1, 0x00 }, + { 0xd740, 3, 1, 0x01 }, + { 0xd3c1, 4, 1, 0x01 }, + { 0xd3a2, 0, 8, 0x00 }, + { 0xd3a3, 0, 8, 0x04 }, + { 0xd305, 0, 8, 0x32 }, + { 0xd306, 0, 8, 0x10 }, + { 0xd304, 0, 8, 0x04 }, + { 0x9112, 0, 1, 0x01 }, + { 0x911d, 0, 1, 0x01 }, + { 0x911a, 0, 1, 0x01 }, + { 0x911b, 0, 1, 0x01 }, + { 0x9bce, 0, 4, 0x02 }, + { 0x9116, 0, 1, 0x01 }, + { 0x9bd1, 0, 1, 0x01 }, + { 0xd2e0, 0, 8, 0xd0 }, + { 0xd2e9, 0, 4, 0x0d }, + { 0xd38c, 0, 8, 0xfc }, + { 0xd38d, 0, 8, 0x00 }, + { 0xd38e, 0, 8, 0x7e }, + { 0xd38f, 0, 8, 0x00 }, + { 0xd390, 0, 8, 0x2f }, + { 0xd145, 4, 1, 0x01 }, + { 0xd1a9, 4, 1, 0x01 }, + { 0xd158, 5, 3, 0x01 }, + { 0xd159, 0, 6, 0x06 }, + { 0xd167, 0, 8, 0x00 }, + { 0xd168, 0, 4, 0x07 }, + { 0xd1c3, 5, 3, 0x00 }, + { 0xd1c4, 0, 6, 0x00 }, + { 0xd1c5, 0, 7, 0x10 }, + { 0xd1c6, 0, 3, 0x02 }, + { 0xd080, 2, 5, 0x03 }, + { 0xd081, 4, 4, 0x09 }, + { 0xd098, 4, 4, 0x0f }, + { 0xd098, 0, 4, 0x03 }, + { 0xdbc0, 3, 1, 0x01 }, + { 0xdbc0, 4, 1, 0x01 }, + { 0xdbc7, 0, 8, 0x08 }, + { 0xdbc8, 4, 4, 0x00 }, + { 0xdbc9, 0, 5, 0x01 }, + { 0xd280, 0, 8, 0xe0 }, + { 0xd281, 0, 8, 0xff }, + { 0xd282, 0, 8, 0xff }, + { 0xd283, 0, 8, 0xc3 }, + { 0xd284, 0, 8, 0xff }, + { 0xd285, 0, 4, 0x01 }, + { 0xd0f0, 0, 7, 0x1a }, + { 0xd0f1, 4, 1, 0x01 }, + { 0xd0f2, 0, 8, 0x0c }, + { 0xd103, 0, 4, 0x08 }, + { 0xd0f8, 0, 7, 0x20 }, + { 0xd111, 5, 1, 0x00 }, + { 0xd111, 6, 1, 0x00 }, + { 0x910b, 0, 8, 0x0a }, + { 0x9115, 0, 8, 0x02 }, + { 0x910c, 0, 8, 0x02 }, + { 0x910d, 0, 8, 0x08 }, + { 0x910e, 0, 8, 0x0a }, + { 0x9bf6, 0, 8, 0x06 }, + { 0x9bf8, 0, 8, 0x02 }, + { 0x9bf7, 0, 8, 0x05 }, + { 0x9bf9, 0, 8, 0x0f }, + { 0x9bfc, 0, 8, 0x13 }, + { 0x9bd3, 0, 8, 0xff }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, +}; + +/* Panasonic ENV77H11D5 tuner init + AF9013_TUNER_ENV77H11D5 = 129 */ +static struct regdesc tuner_init_env77h11d5[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x03 }, + { 0x9bbe, 0, 8, 0x01 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x00 }, + { 0x9be3, 0, 8, 0x00 }, + { 0xd015, 0, 8, 0x50 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0xdf }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x44 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0xeb }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0xf4 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bba, 0, 8, 0xf9 }, + { 0x9bc3, 0, 8, 0xdf }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0xeb }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bc9, 0, 8, 0x52 }, + { 0xd011, 0, 8, 0x3c }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0xf7 }, + { 0xd014, 0, 2, 0x02 }, + { 0xd040, 0, 8, 0x0b }, + { 0xd041, 0, 2, 0x02 }, + { 0xd042, 0, 8, 0x4d }, + { 0xd043, 0, 2, 0x00 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, +}; + +/* Microtune MT2060 tuner init + AF9013_TUNER_MT2060 = 130 */ +static struct regdesc tuner_init_mt2060[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x07 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x00 }, + { 0x9be3, 0, 8, 0x00 }, + { 0x9bbe, 0, 1, 0x00 }, + { 0x9bcc, 0, 1, 0x00 }, + { 0x9bb9, 0, 8, 0x75 }, + { 0x9bcd, 0, 8, 0x24 }, + { 0x9bff, 0, 8, 0x30 }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x32 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x36 }, + { 0xd00d, 0, 2, 0x03 }, + { 0xd00a, 0, 8, 0x35 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x07 }, + { 0x9bc8, 0, 8, 0x90 }, + { 0x9bc3, 0, 8, 0x0f }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x36 }, + { 0x9bc6, 0, 8, 0x03 }, + { 0x9bba, 0, 8, 0xc9 }, + { 0x9bc9, 0, 8, 0x79 }, + { 0xd011, 0, 8, 0x10 }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0x45 }, + { 0xd014, 0, 2, 0x03 }, + { 0xd040, 0, 8, 0x98 }, + { 0xd041, 0, 2, 0x00 }, + { 0xd042, 0, 8, 0xcf }, + { 0xd043, 0, 2, 0x03 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0xcc }, + { 0x9be4, 0, 8, 0xa0 }, + { 0x9bbd, 0, 8, 0x8e }, + { 0x9be2, 0, 8, 0x4d }, + { 0x9bee, 0, 1, 0x01 }, +}; + +/* Microtune MT2060 tuner init + AF9013_TUNER_MT2060_2 = 147 */ +static struct regdesc tuner_init_mt2060_2[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x06 }, + { 0x9bbe, 0, 8, 0x01 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x32 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x36 }, + { 0xd00d, 0, 2, 0x03 }, + { 0xd00a, 0, 8, 0x35 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x07 }, + { 0x9bc8, 0, 8, 0x90 }, + { 0x9bc3, 0, 8, 0x0f }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x36 }, + { 0x9bc6, 0, 8, 0x03 }, + { 0x9bba, 0, 8, 0xc9 }, + { 0x9bc9, 0, 8, 0x79 }, + { 0xd011, 0, 8, 0x10 }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0x45 }, + { 0xd014, 0, 2, 0x03 }, + { 0xd040, 0, 8, 0x98 }, + { 0xd041, 0, 2, 0x00 }, + { 0xd042, 0, 8, 0xcf }, + { 0xd043, 0, 2, 0x03 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 8, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x96 }, + { 0xd054, 0, 8, 0x46 }, + { 0xd045, 7, 1, 0x00 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, +}; + +/* MaxLinear MXL5003 tuner init + AF9013_TUNER_MXL5003D = 3 */ +static struct regdesc tuner_init_mxl5003d[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x09 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x00 }, + { 0x9be3, 0, 8, 0x00 }, + { 0x9bfc, 0, 8, 0x0f }, + { 0x9bf6, 0, 8, 0x01 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0xd015, 0, 8, 0x33 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x40 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x6c }, + { 0xd007, 0, 2, 0x00 }, + { 0xd00c, 0, 8, 0x3d }, + { 0xd00d, 0, 2, 0x00 }, + { 0xd00a, 0, 8, 0x45 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x07 }, + { 0x9bc8, 0, 8, 0x52 }, + { 0x9bc3, 0, 8, 0x0f }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x3d }, + { 0x9bc6, 0, 8, 0x00 }, + { 0x9bba, 0, 8, 0xa2 }, + { 0x9bc9, 0, 8, 0xa0 }, + { 0xd011, 0, 8, 0x56 }, + { 0xd012, 0, 2, 0x00 }, + { 0xd013, 0, 8, 0x50 }, + { 0xd014, 0, 2, 0x00 }, + { 0xd040, 0, 8, 0x56 }, + { 0xd041, 0, 2, 0x00 }, + { 0xd042, 0, 8, 0x50 }, + { 0xd043, 0, 2, 0x00 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 8, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, +}; + +/* MaxLinear MXL5005 tuner init + AF9013_TUNER_MXL5005D = 13 + AF9013_TUNER_MXL5005R = 30 */ +static struct regdesc tuner_init_mxl5005[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x07 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x01 }, + { 0x9be3, 0, 8, 0x01 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x28 }, + { 0x9bff, 0, 8, 0x24 }, + { 0xd015, 0, 8, 0x40 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x40 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x73 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0xfa }, + { 0xd00d, 0, 2, 0x01 }, + { 0xd00a, 0, 8, 0xff }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x23 }, + { 0x9bc8, 0, 8, 0x55 }, + { 0x9bc3, 0, 8, 0x01 }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0xfa }, + { 0x9bc6, 0, 8, 0x01 }, + { 0x9bba, 0, 8, 0xff }, + { 0x9bc9, 0, 8, 0xff }, + { 0x9bd3, 0, 8, 0x95 }, + { 0xd011, 0, 8, 0x70 }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0xfb }, + { 0xd014, 0, 2, 0x01 }, + { 0xd040, 0, 8, 0x70 }, + { 0xd041, 0, 2, 0x01 }, + { 0xd042, 0, 8, 0xfb }, + { 0xd043, 0, 2, 0x01 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0x93 }, + { 0x9be4, 0, 8, 0xfe }, + { 0x9bbd, 0, 8, 0x63 }, + { 0x9be2, 0, 8, 0xfe }, + { 0x9bee, 0, 1, 0x01 }, +}; + +/* Quantek QT1010 tuner init + AF9013_TUNER_QT1010 = 134 + AF9013_TUNER_QT1010A = 162 */ +static struct regdesc tuner_init_qt1010[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x09 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x01 }, + { 0x9be3, 0, 8, 0x01 }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x28 }, + { 0x9bff, 0, 8, 0x20 }, + { 0xd008, 0, 8, 0x0f }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x99 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x0f }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0x50 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x00 }, + { 0x9bc8, 0, 8, 0x00 }, + { 0x9bc3, 0, 8, 0x0f }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x0f }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bba, 0, 8, 0xc5 }, + { 0x9bc9, 0, 8, 0xff }, + { 0xd011, 0, 8, 0x58 }, + { 0xd012, 0, 2, 0x02 }, + { 0xd013, 0, 8, 0x89 }, + { 0xd014, 0, 2, 0x01 }, + { 0xd040, 0, 8, 0x58 }, + { 0xd041, 0, 2, 0x02 }, + { 0xd042, 0, 8, 0x89 }, + { 0xd043, 0, 2, 0x01 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0xcd }, + { 0x9be4, 0, 8, 0xbb }, + { 0x9bbd, 0, 8, 0x93 }, + { 0x9be2, 0, 8, 0x80 }, + { 0x9bee, 0, 1, 0x01 }, +}; + +/* Freescale MC44S803 tuner init + AF9013_TUNER_MC44S803 = 133 */ +static struct regdesc tuner_init_mc44s803[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x06 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x00 }, + { 0x9be3, 0, 8, 0x00 }, + { 0x9bf6, 0, 8, 0x01 }, + { 0x9bf8, 0, 8, 0x02 }, + { 0x9bf9, 0, 8, 0x02 }, + { 0x9bfc, 0, 8, 0x1f }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x24 }, + { 0x9bff, 0, 8, 0x24 }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0x01 }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x7b }, + { 0xd007, 0, 2, 0x00 }, + { 0xd00c, 0, 8, 0x7c }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0xfe }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bc7, 0, 8, 0x08 }, + { 0x9bc8, 0, 8, 0x9a }, + { 0x9bc3, 0, 8, 0x01 }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x7c }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bba, 0, 8, 0xfc }, + { 0x9bc9, 0, 8, 0xaa }, + { 0xd011, 0, 8, 0x6b }, + { 0xd012, 0, 2, 0x00 }, + { 0xd013, 0, 8, 0x88 }, + { 0xd014, 0, 2, 0x02 }, + { 0xd040, 0, 8, 0x6b }, + { 0xd041, 0, 2, 0x00 }, + { 0xd042, 0, 8, 0x7c }, + { 0xd043, 0, 2, 0x02 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0x9e }, + { 0x9be4, 0, 8, 0xff }, + { 0x9bbd, 0, 8, 0x9e }, + { 0x9be2, 0, 8, 0x25 }, + { 0x9bee, 0, 1, 0x01 }, + { 0xd73b, 3, 1, 0x00 }, +}; + +/* unknown, probably for tin can tuner, tuner init + AF9013_TUNER_UNKNOWN = 140 */ +static struct regdesc tuner_init_unknown[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x02 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x01 }, + { 0x9be3, 0, 8, 0x01 }, + { 0xd1a0, 1, 1, 0x00 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x18 }, + { 0x9bff, 0, 8, 0x2c }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0xdf }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x44 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x00 }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0xf6 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bba, 0, 8, 0xf9 }, + { 0x9bc8, 0, 8, 0xaa }, + { 0x9bc3, 0, 8, 0xdf }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x00 }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bc9, 0, 8, 0xf0 }, + { 0xd011, 0, 8, 0x3c }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0xf7 }, + { 0xd014, 0, 2, 0x02 }, + { 0xd040, 0, 8, 0x0b }, + { 0xd041, 0, 2, 0x02 }, + { 0xd042, 0, 8, 0x4d }, + { 0xd043, 0, 2, 0x00 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, +}; + +/* NXP TDA18271 tuner init + AF9013_TUNER_TDA18271 = 156 */ +static struct regdesc tuner_init_tda18271[] = { + { 0x9bd5, 0, 8, 0x01 }, + { 0x9bd6, 0, 8, 0x04 }, + { 0xd1a0, 1, 1, 0x01 }, + { 0xd000, 0, 1, 0x01 }, + { 0xd000, 1, 1, 0x00 }, + { 0xd001, 1, 1, 0x01 }, + { 0xd001, 0, 1, 0x00 }, + { 0xd001, 5, 1, 0x00 }, + { 0xd002, 0, 5, 0x19 }, + { 0xd003, 0, 5, 0x1a }, + { 0xd004, 0, 5, 0x19 }, + { 0xd005, 0, 5, 0x1a }, + { 0xd00e, 0, 5, 0x10 }, + { 0xd00f, 0, 3, 0x04 }, + { 0xd00f, 3, 3, 0x05 }, + { 0xd010, 0, 3, 0x04 }, + { 0xd010, 3, 3, 0x05 }, + { 0xd016, 4, 4, 0x03 }, + { 0xd01f, 0, 6, 0x0a }, + { 0xd020, 0, 6, 0x0a }, + { 0x9bda, 0, 8, 0x01 }, + { 0x9be3, 0, 8, 0x01 }, + { 0xd1a0, 1, 1, 0x00 }, + { 0x9bbe, 0, 1, 0x01 }, + { 0x9bcc, 0, 1, 0x01 }, + { 0x9bb9, 0, 8, 0x00 }, + { 0x9bcd, 0, 8, 0x18 }, + { 0x9bff, 0, 8, 0x2c }, + { 0xd015, 0, 8, 0x46 }, + { 0xd016, 0, 1, 0x00 }, + { 0xd044, 0, 8, 0x46 }, + { 0xd045, 0, 1, 0x00 }, + { 0xd008, 0, 8, 0xdf }, + { 0xd009, 0, 2, 0x02 }, + { 0xd006, 0, 8, 0x44 }, + { 0xd007, 0, 2, 0x01 }, + { 0xd00c, 0, 8, 0x00 }, + { 0xd00d, 0, 2, 0x02 }, + { 0xd00a, 0, 8, 0xf6 }, + { 0xd00b, 0, 2, 0x01 }, + { 0x9bba, 0, 8, 0xf9 }, + { 0x9bc8, 0, 8, 0xaa }, + { 0x9bc3, 0, 8, 0xdf }, + { 0x9bc4, 0, 8, 0x02 }, + { 0x9bc5, 0, 8, 0x00 }, + { 0x9bc6, 0, 8, 0x02 }, + { 0x9bc9, 0, 8, 0xf0 }, + { 0xd011, 0, 8, 0x3c }, + { 0xd012, 0, 2, 0x01 }, + { 0xd013, 0, 8, 0xf7 }, + { 0xd014, 0, 2, 0x02 }, + { 0xd040, 0, 8, 0x0b }, + { 0xd041, 0, 2, 0x02 }, + { 0xd042, 0, 8, 0x4d }, + { 0xd043, 0, 2, 0x00 }, + { 0xd045, 1, 1, 0x00 }, + { 0x9bcf, 0, 1, 0x01 }, + { 0xd045, 2, 1, 0x01 }, + { 0xd04f, 0, 8, 0x9a }, + { 0xd050, 0, 1, 0x01 }, + { 0xd051, 0, 8, 0x5a }, + { 0xd052, 0, 1, 0x01 }, + { 0xd053, 0, 8, 0x50 }, + { 0xd054, 0, 8, 0x46 }, + { 0x9bd7, 0, 8, 0x0a }, + { 0x9bd8, 0, 8, 0x14 }, + { 0x9bd9, 0, 8, 0x08 }, + { 0x9bd0, 0, 8, 0xa8 }, + { 0x9be4, 0, 8, 0x7f }, + { 0x9bbd, 0, 8, 0xa8 }, + { 0x9be2, 0, 8, 0x20 }, + { 0x9bee, 0, 1, 0x01 }, +}; + +#endif /* _AF9013_PRIV_ */ -- cgit v1.2.3 From dc76b0b035cd040558734c9e30257a03f5de19c6 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 15 Sep 2008 23:18:09 +0300 Subject: initial driver for af9015 chipset From: Antti Palosaari - initial driver for the Afatech AF9015 chipset Priority: normal Signed-off-by: Antti Palosaari Thanks-to: Mark Spieth Thanks-to: Lee Essen Thanks-to: Luca Olivetti Thanks-to: Andrew Leech Thanks-to: Nick Andrew Thanks-to: Rafael Antoniello Thanks-to: Jarryd Beck Thanks-to: Jose Alberto Reguero Thanks-to: Benjamin Larsson Thanks-to: Wolfgang Breyha --- linux/drivers/media/dvb/dvb-usb/Kconfig | 12 + linux/drivers/media/dvb/dvb-usb/Makefile | 3 + linux/drivers/media/dvb/dvb-usb/af9015.c | 1462 +++++++++++++++++++++++++ linux/drivers/media/dvb/dvb-usb/af9015.h | 524 +++++++++ linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 17 +- 5 files changed, 2017 insertions(+), 1 deletion(-) create mode 100644 linux/drivers/media/dvb/dvb-usb/af9015.c create mode 100644 linux/drivers/media/dvb/dvb-usb/af9015.h (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig index 1d9e98cee..b35d7f0db 100644 --- a/linux/drivers/media/dvb/dvb-usb/Kconfig +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -271,3 +271,15 @@ config DVB_USB_DTV5100 select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE help Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. + +config DVB_USB_AF9015 + tristate "Afatech AF9015 DVB-T USB2.0 support" + depends on DVB_USB && EXPERIMENTAL + select DVB_AF9013 + select DVB_PLL if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE + help + Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver diff --git a/linux/drivers/media/dvb/dvb-usb/Makefile b/linux/drivers/media/dvb/dvb-usb/Makefile index 116544f42..ece8c9dfe 100644 --- a/linux/drivers/media/dvb/dvb-usb/Makefile +++ b/linux/drivers/media/dvb/dvb-usb/Makefile @@ -70,6 +70,9 @@ obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o dvb-usb-dtv5100-objs = dtv5100.o obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o +dvb-usb-af9015-objs = af9015.o +obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c new file mode 100644 index 000000000..354702b85 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -0,0 +1,1462 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "af9015.h" +#include "af9013.h" +#include "mt2060.h" +#include "qt1010.h" +#include "tda18271.h" +#include "mxl5005s.h" +#if 0 /* keep */ +#include "mc44s80x.h" +#endif + +int dvb_usb_af9015_debug; +module_param_named(debug, dvb_usb_af9015_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +int dvb_usb_af9015_remote; +module_param_named(remote, dvb_usb_af9015_remote, int, 0644); +MODULE_PARM_DESC(remote, "select remote"); +int dvb_usb_af9015_dual_mode; +module_param_named(dual_mode, dvb_usb_af9015_dual_mode, int, 0644); +MODULE_PARM_DESC(dual_mode, "enable dual mode"); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static DEFINE_MUTEX(af9015_usb_mutex); + +static struct af9015_config af9015_config; +static struct dvb_usb_device_properties af9015_properties[2]; +int af9015_properties_count = ARRAY_SIZE(af9015_properties); + +static struct af9013_config af9015_af9013_config[] = { + { + .demod_address = AF9015_I2C_DEMOD, + .output_mode = AF9013_OUTPUT_MODE_USB, + .api_version = { 0, 1, 9, 0 }, + .gpio[0] = AF9013_GPIO_HI, + .gpio[1] = AF9013_GPIO_LO, + .gpio[3] = AF9013_GPIO_TUNER_ON, + + }, { + .output_mode = AF9013_OUTPUT_MODE_SERIAL, + .api_version = { 0, 1, 9, 0 }, + .gpio[0] = AF9013_GPIO_TUNER_ON, + .gpio[1] = AF9013_GPIO_LO, + } +}; + +static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) +{ + int act_len, ret; + u8 buf[64]; + u8 write = 1; + u8 msg_len = 8; + static u8 seq; /* packet sequence number */ + + if (mutex_lock_interruptible(&af9015_usb_mutex) < 0) + return -EAGAIN; + + buf[0] = req->cmd; + buf[1] = seq++; + buf[2] = req->i2c_addr; + buf[3] = req->addr >> 8; + buf[4] = req->addr & 0xff; + buf[5] = req->mbox; + buf[6] = req->addr_len; + buf[7] = req->data_len; + + switch (req->cmd) { + case GET_CONFIG: + case BOOT: + case READ_MEMORY: + case RECONNECT_USB: + case GET_IR_CODE: + write = 0; + break; + case READ_I2C: + write = 0; + buf[2] |= 0x01; /* set I2C direction */ + case WRITE_I2C: + buf[0] = READ_WRITE_I2C; + break; + case WRITE_MEMORY: + if (((req->addr & 0xff00) == 0xff00) || + ((req->addr & 0xae00) == 0xae00)) + buf[0] = WRITE_VIRTUAL_MEMORY; + case WRITE_VIRTUAL_MEMORY: + case COPY_FIRMWARE: + case DOWNLOAD_FIRMWARE: + break; + default: + err("unknown command:%d", req->cmd); + ret = -1; + goto error_unlock; + } + + /* write requested */ + if (write) { + memcpy(&buf[8], req->data, req->data_len); + msg_len += req->data_len; + } + deb_xfer(">>> "); + debug_dump(buf, msg_len, deb_xfer); + + /* send req */ + ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len, + &act_len, AF9015_USB_TIMEOUT); + if (ret) + err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len); + else + if (act_len != msg_len) + ret = -1; /* all data is not send */ + if (ret) + goto error_unlock; + + /* no ack for those packets */ + if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB) + goto exit_unlock; + + /* receive ack and data if read req */ + msg_len = 1 + 1 + req->data_len; /* seq + status + data len */ + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, + &act_len, AF9015_USB_TIMEOUT); + if (ret) { + err("recv bulk message failed:%d", ret); + ret = -1; + goto error_unlock; + } + + deb_xfer("<<< "); + debug_dump(buf, act_len, deb_xfer); + + /* remote controller query status is 1 if remote code is not received */ + if (req->cmd == GET_IR_CODE && buf[1] == 1) { + buf[1] = 0; /* clear command "error" status */ + memset(&buf[2], 0, req->data_len); + buf[3] = 1; /* no remote code received mark */ + } + + /* check status */ + if (buf[1]) { + err("command failed:%d", buf[1]); + ret = -1; + goto error_unlock; + } + + /* read request, copy returned data to return buf */ + if (!write) + memcpy(req->data, &buf[2], req->data_len); + +error_unlock: +exit_unlock: + mutex_unlock(&af9015_usb_mutex); + + return ret; +} + +static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) +{ + return af9015_rw_udev(d->udev, req); +} + +static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val, + u8 len) +{ + struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len, + val}; + return af9015_ctrl_msg(d, &req); +} + +static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val) +{ + return af9015_write_regs(d, addr, &val, 1); +} + +static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val) +{ + struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, 1, val}; + return af9015_ctrl_msg(d, &req); +} + +static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, + u8 val) +{ + struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val}; + + if (addr == af9015_af9013_config[0].demod_address || + addr == af9015_af9013_config[1].demod_address) + req.addr_len = 3; + + return af9015_ctrl_msg(d, &req); +} + +static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, + u8 *val) +{ + struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val}; + + if (addr == af9015_af9013_config[0].demod_address || + addr == af9015_af9013_config[1].demod_address) + req.addr_len = 3; + + return af9015_ctrl_msg(d, &req); +} + +static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret = 0, i = 0; + u16 addr; + u8 mbox, addr_len; + struct req_t req; + +/* TODO: implement bus lock + +The bus lock is needed because there is two tuners both using same I2C-address. +Due to that the only way to select correct tuner is use demodulator I2C-gate. + +................................................ +. AF9015 includes integrated AF9013 demodulator. +. ____________ ____________ . ____________ +.| uC | | demod | . | tuner | +.|------------| |------------| . |------------| +.| AF9015 | | AF9013/5 | . | MXL5003 | +.| |--+----I2C-------|-----/ -----|-.-----I2C-------| | +.| | | | addr 0x38 | . | addr 0xc6 | +.|____________| | |____________| . |____________| +.................|.............................. + | ____________ ____________ + | | demod | | tuner | + | |------------| |------------| + | | AF9013 | | MXL5003 | + +----I2C-------|-----/ -----|-------I2C-------| | + | addr 0x3a | | addr 0xc6 | + |____________| |____________| +*/ + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (msg[i].addr == af9015_af9013_config[0].demod_address || + msg[i].addr == af9015_af9013_config[1].demod_address) { + addr = msg[i].buf[0] << 8; + addr += msg[i].buf[1]; + mbox = msg[i].buf[2]; + addr_len = 3; + } else { + addr = msg[i].buf[0]; + addr_len = 1; + mbox = 0; + } + + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + if (msg[i].addr == + af9015_af9013_config[0].demod_address) + req.cmd = READ_MEMORY; + else + req.cmd = READ_I2C; + req.i2c_addr = msg[i].addr; + req.addr = addr; + req.mbox = mbox; + req.addr_len = addr_len; + req.data_len = msg[i+1].len; + req.data = &msg[i+1].buf[0]; + ret = af9015_ctrl_msg(d, &req); + i += 2; + } else { + if (msg[i].addr == + af9015_af9013_config[0].demod_address) + req.cmd = WRITE_MEMORY; + else + req.cmd = WRITE_I2C; + req.i2c_addr = msg[i].addr; + req.addr = addr; + req.mbox = mbox; + req.addr_len = addr_len; + req.data_len = msg[i].len-addr_len; + req.data = &msg[i].buf[addr_len]; + ret = af9015_ctrl_msg(d, &req); + i += 1; + } + if (ret) + goto error; + + } + ret = i; + +error: + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static u32 af9015_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm af9015_i2c_algo = { + .master_xfer = af9015_i2c_xfer, + .functionality = af9015_i2c_func, +}; + +static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op) +{ + int ret; + u8 val, mask = 0x01; + + ret = af9015_read_reg(d, addr, &val); + if (ret) + return ret; + + mask <<= bit; + if (op) { + /* set bit */ + val |= mask; + } else { + /* clear bit */ + mask ^= 0xff; + val &= mask; + } + + return af9015_write_reg(d, addr, val); +} + +static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) +{ + return af9015_do_reg_bit(d, addr, bit, 1); +} + +static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) +{ + return af9015_do_reg_bit(d, addr, bit, 0); +} + +static int af9015_init_endpoint(struct dvb_usb_device *d) +{ + int ret; + u16 frame_size; + u8 packet_size; + deb_info("%s: USB speed:%d\n", __func__, d->udev->speed); + +#define TS_PACKET_SIZE 188 + +#define TS_USB20_PACKET_COUNT 348 +#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT) + +#define TS_USB11_PACKET_COUNT 21 +#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT) + +#define TS_USB20_MAX_PACKET_SIZE 512 +#define TS_USB11_MAX_PACKET_SIZE 64 + + if (d->udev->speed == USB_SPEED_FULL) { + frame_size = TS_USB11_FRAME_SIZE/4; + packet_size = TS_USB11_MAX_PACKET_SIZE/4; + } else { + frame_size = TS_USB20_FRAME_SIZE/4; + packet_size = TS_USB20_MAX_PACKET_SIZE/4; + } + + ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */ + if (ret) + goto error; + ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */ + if (ret) + goto error; + ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */ + if (ret) + goto error; + ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */ + if (ret) + goto error; + ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */ + if (ret) + goto error; + if (af9015_config.dual_mode) { + ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */ + if (ret) + goto error; + } + ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */ + if (ret) + goto error; + if (af9015_config.dual_mode) { + ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */ + if (ret) + goto error; + } + /* EP4 xfer length */ + ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff); + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd89, frame_size >> 8); + if (ret) + goto error; + /* EP5 xfer length */ + ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff); + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8); + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */ + if (ret) + goto error; + ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */ + if (ret) + goto error; + ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */ + if (ret) + goto error; + if (af9015_config.dual_mode) { + ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */ + if (ret) + goto error; + } + + /* enable / disable mp2if2 */ + if (af9015_config.dual_mode) + ret = af9015_set_reg_bit(d, 0xd50b, 0); + else + ret = af9015_clear_reg_bit(d, 0xd50b, 0); +error: + if (ret) + err("endpoint init failed:%d", ret); + return ret; +} + +static int af9015_copy_firmware(struct dvb_usb_device *d) +{ + int ret; + u8 fw_params[4]; + u8 val, i; + struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params), + fw_params }; + deb_info("%s:\n", __func__); + + fw_params[0] = af9015_config.firmware_size >> 8; + fw_params[1] = af9015_config.firmware_size & 0xff; + fw_params[2] = af9015_config.firmware_checksum >> 8; + fw_params[3] = af9015_config.firmware_checksum & 0xff; + + /* wait 2nd demodulator ready */ + msleep(100); + + ret = af9015_read_reg_i2c(d, 0x3a, 0x98be, &val); + if (ret) + goto error; + else + deb_info("%s: firmware status:%02x\n", __func__, val); + + if (val == 0x0c) /* fw is running, no need for download */ + goto exit; + + /* set I2C master clock to fast (to speed up firmware copy) */ + ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */ + if (ret) + goto error; + + msleep(50); + + /* copy firmware */ + ret = af9015_ctrl_msg(d, &req); + if (ret) + err("firmware copy cmd failed:%d", ret); + deb_info("%s: firmware copy done\n", __func__); + + /* set I2C master clock back to normal */ + ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */ + if (ret) + goto error; + + /* request boot firmware */ + ret = af9015_write_reg_i2c(d, af9015_af9013_config[1].demod_address, + 0xe205, 1); + deb_info("%s: firmware boot cmd status:%d\n", __func__, ret); + if (ret) + goto error; + + for (i = 0; i < 15; i++) { + msleep(100); + + /* check firmware status */ + ret = af9015_read_reg_i2c(d, + af9015_af9013_config[1].demod_address, 0x98be, &val); + deb_info("%s: firmware status cmd status:%d fw status:%02x\n", + __func__, ret, val); + if (ret) + goto error; + + if (val == 0x0c || val == 0x04) /* success or fail */ + break; + } + + if (val == 0x04) { + err("firmware did not run"); + ret = -1; + } else if (val != 0x0c) { + err("firmware boot timeout"); + ret = -1; + } + +error: +exit: + return ret; +} + +/* dump eeprom */ +static int af9015_eeprom_dump(struct dvb_usb_device *d) +{ + char buf[52], buf2[4]; + u8 reg, val; + + for (reg = 0; ; reg++) { + if (reg % 16 == 0) { + if (reg) + deb_info("%s\n", buf); + sprintf(buf, "%02x: ", reg); + } + if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0) + sprintf(buf2, "%02x ", val); + else + strcpy(buf2, "-- "); + strcat(buf, buf2); + if (reg == 0xff) + break; + } + deb_info("%s\n", buf); + return 0; +} + +int af9015_download_ir_table(struct dvb_usb_device *d) +{ + int i, packets = 0, ret; + u16 addr = 0x9a56; /* ir-table start address */ + struct req_t req = {WRITE_MEMORY, 0, 0, 0, 0, 1, NULL}; + u8 *data = NULL; + deb_info("%s:\n", __func__); + + data = af9015_config.ir_table; + packets = af9015_config.ir_table_size; + + /* no remote */ + if (!packets) + goto exit; + + /* load remote ir-table */ + for (i = 0; i < packets; i++) { + req.addr = addr + i; + req.data = &data[i]; + ret = af9015_ctrl_msg(d, &req); + if (ret) { + err("ir-table download failed at packet %d with " \ + "code %d", i, ret); + return ret; + } + } + +exit: + return 0; +} + +static int af9015_init(struct dvb_usb_device *d) +{ + int ret; + deb_info("%s:\n", __func__); + + ret = af9015_init_endpoint(d); + if (ret) + goto error; + + ret = af9015_download_ir_table(d); + if (ret) + goto error; + +error: + return ret; +} + +static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + deb_info("%s: onoff:%d\n", __func__, onoff); + + if (onoff) + ret = af9015_set_reg_bit(adap->dev, 0xd503, 0); + else + ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0); + + return ret; +} + +static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, + int onoff) +{ + int ret; + u8 idx; + + deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n", + __func__, index, pid, onoff); + + ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff)); + if (ret) + goto error; + + ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8)); + if (ret) + goto error; + + idx = ((index & 0x1f) | (1 << 5)); + ret = af9015_write_reg(adap->dev, 0xd504, idx); + +error: + return ret; +} + +static int af9015_download_firmware(struct usb_device *udev, + const struct firmware *fw) +{ + int i, len, packets, remainder, ret; + struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL}; + u16 addr = 0x5100; /* firmware start address */ + u16 checksum = 0; + + deb_info("%s:\n", __func__); + + /* calc checksum */ + for (i = 0; i < fw->size; i++) + checksum += fw->data[i]; + + af9015_config.firmware_size = fw->size; + af9015_config.firmware_checksum = checksum; + + #define FW_PACKET_MAX_DATA 55 + + packets = fw->size / FW_PACKET_MAX_DATA; + remainder = fw->size % FW_PACKET_MAX_DATA; + len = FW_PACKET_MAX_DATA; + for (i = 0; i <= packets; i++) { + if (i == packets) /* set size of the last packet */ + len = remainder; + + req.data_len = len; + req.data = (fw->data + i * FW_PACKET_MAX_DATA); + req.addr = addr; + addr += FW_PACKET_MAX_DATA; + + ret = af9015_rw_udev(udev, &req); + if (ret) { + err("firmware download failed at packet %d with " \ + "code %d", i, ret); + goto error; + } + } + #undef FW_PACKET_MAX_DATA + + /* firmware loaded, request boot */ + req.cmd = BOOT; + ret = af9015_rw_udev(udev, &req); + if (ret) { + err("firmware boot failed:%d", ret); + goto error; + } + + /* firmware is running, reconnect device in the usb bus */ + req.cmd = RECONNECT_USB; + ret = af9015_rw_udev(udev, &req); + if (ret) + err("reconnect failed: %d", ret); + +error: + return ret; +} + +static int af9015_read_config(struct usb_device *udev) +{ + int ret; + u8 val, i, offset = 0; + struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; + char manufacturer[10]; + + /* IR remote controller */ + req.addr = AF9015_EEPROM_IR_MODE; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + deb_info("%s: IR mode:%d\n", __func__, val); + for (i = 0; i < af9015_properties_count; i++) { + if (val == AF9015_IR_MODE_DISABLED || val == 0x04) { + af9015_properties[i].rc_key_map = NULL; + af9015_properties[i].rc_key_map_size = 0; + } else if (dvb_usb_af9015_remote) { + /* load remote defined as module param */ + switch (dvb_usb_af9015_remote) { + case AF9015_REMOTE_A_LINK_DTU_M: + af9015_properties[i].rc_key_map = + af9015_rc_keys_a_link; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_a_link); + af9015_config.ir_table = af9015_ir_table_a_link; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_a_link); + break; + case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3: + af9015_properties[i].rc_key_map = + af9015_rc_keys_msi; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_msi); + af9015_config.ir_table = af9015_ir_table_msi; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_msi); + break; + case AF9015_REMOTE_MYGICTV_U718: + af9015_properties[i].rc_key_map = + af9015_rc_keys_mygictv; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_mygictv); + af9015_config.ir_table = + af9015_ir_table_mygictv; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_mygictv); + break; + } + } else { + switch (udev->descriptor.idVendor) { + case cpu_to_le16(USB_VID_LEADTEK): + af9015_properties[i].rc_key_map = + af9015_rc_keys_leadtek; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_leadtek); + af9015_config.ir_table = + af9015_ir_table_leadtek; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_leadtek); + break; + case cpu_to_le16(USB_VID_VISIONPLUS): + if (udev->descriptor.idProduct == + cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) { + af9015_properties[i].rc_key_map = + af9015_rc_keys_twinhan; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_twinhan); + af9015_config.ir_table = + af9015_ir_table_twinhan; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_twinhan); + } + break; + case cpu_to_le16(USB_VID_KWORLD_2): + /* TODO: use correct rc keys */ + af9015_properties[i].rc_key_map = + af9015_rc_keys_twinhan; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_twinhan); + af9015_config.ir_table = af9015_ir_table_kworld; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_kworld); + break; + /* Check USB manufacturer and product strings and try + to determine correct remote in case of chip vendor + reference IDs are used. */ + case cpu_to_le16(USB_VID_AFATECH): + memset(manufacturer, 0, sizeof(manufacturer)); + usb_string(udev, udev->descriptor.iManufacturer, + manufacturer, sizeof(manufacturer)); + if (!strcmp("Geniatech", manufacturer)) { + /* iManufacturer 1 Geniatech + iProduct 2 AF9015 */ + af9015_properties[i].rc_key_map = + af9015_rc_keys_mygictv; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_mygictv); + af9015_config.ir_table = + af9015_ir_table_mygictv; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_mygictv); + } else if (!strcmp("MSI", manufacturer)) { + /* iManufacturer 1 MSI + iProduct 2 MSI K-VOX */ + af9015_properties[i].rc_key_map = + af9015_rc_keys_msi; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_msi); + af9015_config.ir_table = + af9015_ir_table_msi; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_msi); + } + break; + } + } + } + + /* TS mode - one or two receivers */ + req.addr = AF9015_EEPROM_TS_MODE; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_config.dual_mode = val; + deb_info("%s: TS mode:%d\n", __func__, af9015_config.dual_mode); + /* disable dual mode by default because it is buggy */ + if (!dvb_usb_af9015_dual_mode) + af9015_config.dual_mode = 0; + + /* set buffer size according to USB port speed */ + for (i = 0; i < af9015_properties_count; i++) { + /* USB1.1 set smaller buffersize and disable 2nd adapter */ + if (udev->speed == USB_SPEED_FULL) { + af9015_properties[i].adapter->stream.u.bulk.buffersize = + TS_USB11_MAX_PACKET_SIZE; + /* disable 2nd adapter because we don't have + PID-filters */ + af9015_config.dual_mode = 0; + } else { + af9015_properties[i].adapter->stream.u.bulk.buffersize = + TS_USB20_MAX_PACKET_SIZE; + } + } + + if (af9015_config.dual_mode) { + /* read 2nd demodulator I2C address */ + req.addr = AF9015_EEPROM_DEMOD2_I2C; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_af9013_config[1].demod_address = val; + + /* enable 2nd adapter */ + for (i = 0; i < af9015_properties_count; i++) + af9015_properties[i].num_adapters = 2; + + } else { + /* disable 2nd adapter */ + for (i = 0; i < af9015_properties_count; i++) + af9015_properties[i].num_adapters = 1; + } + + for (i = 0; i < af9015_properties[0].num_adapters; i++) { + if (i == 1) + offset = AF9015_EEPROM_OFFSET; + /* xtal */ + req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + switch (val) { + case 0: + af9015_af9013_config[i].adc_clock = 28800; + break; + case 1: + af9015_af9013_config[i].adc_clock = 20480; + break; + case 2: + af9015_af9013_config[i].adc_clock = 28000; + break; + case 3: + af9015_af9013_config[i].adc_clock = 25000; + break; + }; + deb_info("%s: [%d] xtal:%d set adc_clock:%d\n", __func__, i, + val, af9015_af9013_config[i].adc_clock); + + /* tuner IF */ + req.addr = AF9015_EEPROM_IF1H + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_af9013_config[i].tuner_if = val << 8; + req.addr = AF9015_EEPROM_IF1L + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_af9013_config[i].tuner_if += val; + deb_info("%s: [%d] IF1:%d\n", __func__, i, + af9015_af9013_config[0].tuner_if); + + /* MT2060 IF1 */ + req.addr = AF9015_EEPROM_MT2060_IF1H + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_config.mt2060_if1[i] = val << 8; + req.addr = AF9015_EEPROM_MT2060_IF1L + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + af9015_config.mt2060_if1[i] += val; + deb_info("%s: [%d] MT2060 IF1:%d\n", __func__, i, + af9015_config.mt2060_if1[i]); + + /* tuner */ + req.addr = AF9015_EEPROM_TUNER_ID1 + offset; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto error; + switch (val) { + case AF9013_TUNER_ENV77H11D5: + case AF9013_TUNER_MT2060: + case AF9013_TUNER_MC44S803: + case AF9013_TUNER_QT1010: + case AF9013_TUNER_UNKNOWN: + case AF9013_TUNER_MT2060_2: + case AF9013_TUNER_TDA18271: + case AF9013_TUNER_QT1010A: + af9015_af9013_config[i].rf_spec_inv = 1; + break; + case AF9013_TUNER_MXL5003D: + case AF9013_TUNER_MXL5005D: + case AF9013_TUNER_MXL5005R: + af9015_af9013_config[i].rf_spec_inv = 0; + break; + default: + warn("tuner id:%d not supported, please report!", val); + return -ENODEV; + }; + + af9015_af9013_config[i].tuner = val; + deb_info("%s: [%d] tuner id:%d\n", __func__, i, val); + } + +error: + if (ret) + err("eeprom read failed:%d", ret); + + return ret; +} + +static int af9015_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int ret; + u8 reply; + struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply}; + + ret = af9015_rw_udev(udev, &req); + if (ret) + return ret; + + deb_info("%s: reply:%02x\n", __func__, reply); + if (reply == 0x02) + *cold = 0; + else + *cold = 1; + + return ret; +} + +static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 buf[8]; + struct req_t req = {GET_IR_CODE, 0, 0, 0, 0, sizeof(buf), buf}; + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + int i, ret; + + memset(buf, 0, sizeof(buf)); + + ret = af9015_ctrl_msg(d, &req); + if (ret) + return ret; + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + for (i = 0; i < d->props.rc_key_map_size; i++) { + if (!buf[1] && keymap[i].custom == buf[0] && + keymap[i].data == buf[2]) { + *event = keymap[i].event; + *state = REMOTE_KEY_PRESSED; + break; + } + } + if (!buf[1]) + deb_rc("%s: %02x %02x %02x %02x %02x %02x %02x %02x\n", + __func__, buf[0], buf[1], buf[2], buf[3], buf[4], + buf[5], buf[6], buf[7]); + + return 0; +} + +/* init 2nd I2C adapter */ +int af9015_i2c_init(struct dvb_usb_device *d) +{ + int ret; + struct af9015_state *state = d->priv; + deb_info("%s:\n", __func__); + + strncpy(state->i2c_adap.name, d->desc->name, + sizeof(state->i2c_adap.name)); +#ifdef I2C_ADAP_CLASS_TV_DIGITAL + state->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL, +#else + state->i2c_adap.class = I2C_CLASS_TV_DIGITAL, +#endif + state->i2c_adap.algo = d->props.i2c_algo; + state->i2c_adap.algo_data = NULL; + state->i2c_adap.dev.parent = &d->udev->dev; + + i2c_set_adapdata(&state->i2c_adap, d); + + ret = i2c_add_adapter(&state->i2c_adap); + if (ret < 0) + err("could not add i2c adapter"); + + return ret; +} + +static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct af9015_state *state = adap->dev->priv; + struct i2c_adapter *i2c_adap; + + if (adap->id == 0) { + /* select I2C adapter */ + i2c_adap = &adap->dev->i2c_adap; + + deb_info("%s: init I2C\n", __func__); + ret = af9015_i2c_init(adap->dev); + + /* dump eeprom (debug) */ + ret = af9015_eeprom_dump(adap->dev); + if (ret) + return ret; + } else { + /* select I2C adapter */ + i2c_adap = &state->i2c_adap; + + /* copy firmware to 2nd demodulator */ + if (af9015_config.dual_mode) { + ret = af9015_copy_firmware(adap->dev); + if (ret) { + err("firmware copy to 2nd frontend " \ + "failed, will disable it"); + af9015_config.dual_mode = 0; + return -ENODEV; + } + } else { + return -ENODEV; + } + } + + /* attach demodulator */ + adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id], + i2c_adap); + + return adap->fe == NULL ? -ENODEV : 0; +} + +static struct mt2060_config af9015_mt2060_config = { + .i2c_address = 0xc0, + .clock_out = 0, +}; + +static struct qt1010_config af9015_qt1010_config = { + .i2c_address = 0xc4, +}; + +static struct tda18271_config af9015_tda18271_config = { + .gate = TDA18271_GATE_DIGITAL, + .small_i2c = 1, +}; + +static struct mxl5005s_config af9015_mxl5003_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_DEFAULT, + .rssi_enable = MXL_RSSI_DISABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static struct mxl5005s_config af9015_mxl5005_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_OFF, + .rssi_enable = MXL_RSSI_DISABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static int af9015_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct af9015_state *state = adap->dev->priv; + struct i2c_adapter *i2c_adap; + int ret; + deb_info("%s: \n", __func__); + + /* select I2C adapter */ + if (adap->id == 0) + i2c_adap = &adap->dev->i2c_adap; + else + i2c_adap = &state->i2c_adap; + + switch (af9015_af9013_config[adap->id].tuner) { + case AF9013_TUNER_MT2060: + case AF9013_TUNER_MT2060_2: + ret = dvb_attach(mt2060_attach, adap->fe, i2c_adap, + &af9015_mt2060_config, + af9015_config.mt2060_if1[adap->id]) + == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_QT1010: + case AF9013_TUNER_QT1010A: + ret = dvb_attach(qt1010_attach, adap->fe, i2c_adap, + &af9015_qt1010_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_TDA18271: + ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap, + &af9015_tda18271_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MXL5003D: + ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap, + &af9015_mxl5003_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MXL5005D: + case AF9013_TUNER_MXL5005R: + ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap, + &af9015_mxl5005_config) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_ENV77H11D5: + ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, i2c_adap, + DVB_PLL_TDA665X) == NULL ? -ENODEV : 0; + break; + case AF9013_TUNER_MC44S803: +#if 0 /* keep */ + ret = dvb_attach(mc44s80x_attach, adap->fe, i2c_adap) + == NULL ? -ENODEV : 0; +#else + ret = -ENODEV; + info("Freescale MC44S803 tuner found but no driver for that" \ + "tuner. Look at the Linuxtv.org for tuner driver" \ + "status."); +#endif + break; + case AF9013_TUNER_UNKNOWN: + default: + ret = -ENODEV; + err("Unknown tuner id:%d", + af9015_af9013_config[adap->id].tuner); + } + return ret; +} + +static struct usb_device_id af9015_usb_table[] = { +/* 0 */{USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)}, + {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)}, + {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)}, + {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)}, +/* 5 */{USB_DEVICE(USB_VID_VISIONPLUS, + USB_PID_TINYTWIN)}, + {USB_DEVICE(USB_VID_VISIONPLUS, + USB_PID_AZUREWAVE_AD_TU700)}, + {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)}, + {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)}, +/* 10 */{USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)}, + {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, + {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, + {0}, +}; +MODULE_DEVICE_TABLE(usb, af9015_usb_table); + +static struct dvb_usb_device_properties af9015_properties[] = { + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9015_download_firmware, + .firmware = "dvb-usb-af9015.fw", + + .size_of_priv = sizeof(struct af9015_state), \ + + .num_adapters = 2, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, + + .frontend_attach = + af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + }, + { + .frontend_attach = + af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + }, + } + }, + + .identify_state = af9015_identify_state, + + .rc_query = af9015_rc_query, + .rc_interval = 150, + + .i2c_algo = &af9015_i2c_algo, + + .num_device_descs = 9, + .devices = { + { + .name = "Afatech AF9015 DVB-T USB2.0 stick", + .cold_ids = {&af9015_usb_table[0], + &af9015_usb_table[1], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "Leadtek WinFast DTV Dongle Gold", + .cold_ids = {&af9015_usb_table[2], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "Pinnacle PCTV 71e", + .cold_ids = {&af9015_usb_table[3], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "KWorld PlusTV Dual DVB-T Stick " \ + "(DVB-T 399U)", + .cold_ids = {&af9015_usb_table[4], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "DigitalNow TinyTwin DVB-T Receiver", + .cold_ids = {&af9015_usb_table[5], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "TwinHan AzureWave AD-TU700(704J)", + .cold_ids = {&af9015_usb_table[6], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "TerraTec Cinergy T USB XE", + .cold_ids = {&af9015_usb_table[7], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "KWorld PlusTV Dual DVB-T PCI " \ + "(DVB-T PC160-2T)", + .cold_ids = {&af9015_usb_table[8], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "AVerMedia AVerTV DVB-T Volar X", + .cold_ids = {&af9015_usb_table[9], NULL}, + .warm_ids = {NULL}, + }, + } + }, { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9015_download_firmware, + .firmware = "dvb-usb-af9015.fw", + + .size_of_priv = sizeof(struct af9015_state), \ + + .num_adapters = 2, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, + + .frontend_attach = + af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + }, + { + .frontend_attach = + af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + }, + } + }, + + .identify_state = af9015_identify_state, + + .rc_query = af9015_rc_query, + .rc_interval = 150, + + .i2c_algo = &af9015_i2c_algo, + + .num_device_descs = 3, + .devices = { + { + .name = "Xtensions XD-380", + .cold_ids = {&af9015_usb_table[10], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "MSI DIGIVOX Duo", + .cold_ids = {&af9015_usb_table[11], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "Fujitsu-Siemens Slim Mobile USB DVB-T", + .cold_ids = {&af9015_usb_table[12], NULL}, + .warm_ids = {NULL}, + }, + } + } +}; +#undef AF9015_DEFAULT_PROPERTIES + +static int af9015_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret = 0; + struct dvb_usb_device *d = NULL; + struct usb_device *udev = interface_to_usbdev(intf); + u8 i; + + deb_info("%s: interface:%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + /* interface 0 is used by DVB-T receiver and + interface 1 is for remote controller (HID) */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + ret = af9015_read_config(udev); + if (ret) + return ret; + + for (i = 0; i < af9015_properties_count; i++) { + ret = dvb_usb_device_init(intf, &af9015_properties[i], + THIS_MODULE, &d, adapter_nr); + if (!ret) + break; + if (ret != -ENODEV) + return ret; + } + if (ret) + return ret; + + if (d) + ret = af9015_init(d); + } + + return ret; +} + +void af9015_i2c_exit(struct dvb_usb_device *d) +{ + struct af9015_state *state = d->priv; + deb_info("%s: \n", __func__); + + /* remove 2nd I2C adapter */ + if (d->state & DVB_USB_STATE_I2C) + i2c_del_adapter(&state->i2c_adap); +} + +static void af9015_usb_device_exit(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + deb_info("%s: \n", __func__); + + /* remove 2nd I2C adapter */ + if (d != NULL && d->desc != NULL) + af9015_i2c_exit(d); + + dvb_usb_device_exit(intf); +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver af9015_usb_driver = { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) + .owner = THIS_MODULE, +#endif + .name = "dvb_usb_af9015", + .probe = af9015_usb_probe, + .disconnect = af9015_usb_device_exit, + .id_table = af9015_usb_table, +}; + +/* module stuff */ +static int __init af9015_usb_module_init(void) +{ + int ret; + ret = usb_register(&af9015_usb_driver); + if (ret) + err("module init failed:%d", ret); + + return ret; +} + +static void __exit af9015_usb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&af9015_usb_driver); +} + +module_init(af9015_usb_module_init); +module_exit(af9015_usb_module_exit); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.h b/linux/drivers/media/dvb/dvb-usb/af9015.h new file mode 100644 index 000000000..882e8a4b3 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/af9015.h @@ -0,0 +1,524 @@ +/* + * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari + * + * Thanks to Afatech who kindly provided information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _DVB_USB_AF9015_H_ +#define _DVB_USB_AF9015_H_ + +#define DVB_USB_LOG_PREFIX "af9015" +#include "dvb-usb.h" + +extern int dvb_usb_af9015_debug; +#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args) +#define deb_rc(args...) dprintk(dvb_usb_af9015_debug, 0x02, args) +#define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args) +#define deb_reg(args...) dprintk(dvb_usb_af9015_debug, 0x08, args) +#define deb_i2c(args...) dprintk(dvb_usb_af9015_debug, 0x10, args) +#define deb_fw(args...) dprintk(dvb_usb_af9015_debug, 0x20, args) + +#define AF9015_I2C_EEPROM 0xa0 +#define AF9015_I2C_DEMOD 0x38 +#define AF9015_USB_TIMEOUT 2000 + +/* EEPROM locations */ +#define AF9015_EEPROM_IR_MODE 0x18 +#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34 +#define AF9015_EEPROM_TS_MODE 0x31 +#define AF9015_EEPROM_DEMOD2_I2C 0x32 + +#define AF9015_EEPROM_SAW_BW1 0x35 +#define AF9015_EEPROM_XTAL_TYPE1 0x36 +#define AF9015_EEPROM_SPEC_INV1 0x37 +#define AF9015_EEPROM_IF1L 0x38 +#define AF9015_EEPROM_IF1H 0x39 +#define AF9015_EEPROM_MT2060_IF1L 0x3a +#define AF9015_EEPROM_MT2060_IF1H 0x3b +#define AF9015_EEPROM_TUNER_ID1 0x3c + +#define AF9015_EEPROM_SAW_BW2 0x45 +#define AF9015_EEPROM_XTAL_TYPE2 0x46 +#define AF9015_EEPROM_SPEC_INV2 0x47 +#define AF9015_EEPROM_IF2L 0x48 +#define AF9015_EEPROM_IF2H 0x49 +#define AF9015_EEPROM_MT2060_IF2L 0x4a +#define AF9015_EEPROM_MT2060_IF2H 0x4b +#define AF9015_EEPROM_TUNER_ID2 0x4c + +#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1) + +#define AF9015_GPIO_ON (1 << 0) +#define AF9015_GPIO_EN (1 << 1) +#define AF9015_GPIO_O (1 << 2) +#define AF9015_GPIO_I (1 << 3) + +#define AF9015_GPIO_TUNER_ON (AF9015_GPIO_ON|AF9015_GPIO_EN) +#define AF9015_GPIO_TUNER_OFF (AF9015_GPIO_ON|AF9015_GPIO_EN|AF9015_GPIO_O) + +struct req_t { + u8 cmd; /* [0] */ + /* seq */ /* [1] */ + u8 i2c_addr; /* [2] */ + u16 addr; /* [3|4] */ + u8 mbox; /* [5] */ + u8 addr_len; /* [6] */ + u8 data_len; /* [7] */ + u8 *data; +}; + +enum af9015_cmd { + GET_CONFIG = 0x10, + DOWNLOAD_FIRMWARE = 0x11, + BOOT = 0x13, + READ_MEMORY = 0x20, + WRITE_MEMORY = 0x21, + READ_WRITE_I2C = 0x22, + COPY_FIRMWARE = 0x23, + RECONNECT_USB = 0x5a, + WRITE_VIRTUAL_MEMORY = 0x26, + GET_IR_CODE = 0x27, + READ_I2C, + WRITE_I2C, +}; + +enum af9015_ir_mode { + AF9015_IR_MODE_DISABLED = 0, + AF9015_IR_MODE_HID, + AF9015_IR_MODE_RLC, + AF9015_IR_MODE_RC6, +}; + +struct af9015_state { + struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */ +}; + +struct af9015_config { + u8 dual_mode:1; + u16 mt2060_if1[2]; + u16 firmware_size; + u16 firmware_checksum; + u8 *ir_table; + u16 ir_table_size; +}; + +enum af9015_remote { + AF9015_REMOTE_NONE = 0, + AF9015_REMOTE_A_LINK_DTU_M, + AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, + AF9015_REMOTE_MYGICTV_U718, +}; + +/* Leadtek WinFast DTV Dongle Gold */ +static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = { + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x28, KEY_ENTER }, + { 0x00, 0x4f, KEY_VOLUMEUP }, + { 0x00, 0x50, KEY_VOLUMEDOWN }, + { 0x00, 0x51, KEY_CHANNELDOWN }, + { 0x00, 0x52, KEY_CHANNELUP }, +}; + +static u8 af9015_ir_table_leadtek[] = { + 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00, + 0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00, + 0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00, + 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00, + 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00, + 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00, + 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00, + 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00, + 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00, + 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00, + 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00, + 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00, + 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00, + 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00, + 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00, + 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00, + 0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00, + 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00, + 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00, + 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00, + 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00, + 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00, + 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00, + 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00, + 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00, + 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00, + 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00, + 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00, + 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00, + 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00, + 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00, + 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00, + 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00, + 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00, + 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, + 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00, + 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00, + 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00, + 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00, + 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00, + 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00, + 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00, + 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00, + 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00, + 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00, + 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00, +}; + +/* TwinHan AzureWave AD-TU700(704J) */ +static struct dvb_usb_rc_key af9015_rc_keys_twinhan[] = { + { 0x05, 0x3f, KEY_POWER }, + { 0x00, 0x19, KEY_FAVORITES }, /* Favorite List */ + { 0x00, 0x04, KEY_TEXT }, /* Teletext */ + { 0x00, 0x0e, KEY_POWER }, + { 0x00, 0x0e, KEY_INFO }, /* Preview */ + { 0x00, 0x08, KEY_EPG }, /* Info/EPG */ + { 0x00, 0x0f, KEY_LIST }, /* Record List */ + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x29, KEY_CANCEL }, /* Cancel */ + { 0x00, 0x4c, KEY_CLEAR }, /* Clear */ + { 0x00, 0x2a, KEY_BACK }, /* Back */ + { 0x00, 0x2b, KEY_TAB }, /* Tab */ + { 0x00, 0x52, KEY_UP }, /* up arrow */ + { 0x00, 0x51, KEY_DOWN }, /* down arrow */ + { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */ + { 0x00, 0x50, KEY_LEFT }, /* left arrow */ + { 0x00, 0x28, KEY_ENTER }, /* Enter / ok */ + { 0x02, 0x52, KEY_VOLUMEUP }, + { 0x02, 0x51, KEY_VOLUMEDOWN }, + { 0x00, 0x4e, KEY_CHANNELDOWN }, + { 0x00, 0x4b, KEY_CHANNELUP }, + { 0x00, 0x4a, KEY_RECORD }, + { 0x01, 0x11, KEY_PLAY }, + { 0x00, 0x17, KEY_PAUSE }, + { 0x00, 0x0c, KEY_REWIND }, /* FR << */ + { 0x00, 0x11, KEY_FASTFORWARD }, /* FF >> */ + { 0x01, 0x15, KEY_PREVIOUS }, /* Replay */ + { 0x01, 0x0e, KEY_NEXT }, /* Skip */ + { 0x00, 0x13, KEY_CAMERA }, /* Capture */ + { 0x01, 0x0f, KEY_LANGUAGE }, /* SAP */ + { 0x01, 0x13, KEY_TV2 }, /* PIP */ + { 0x00, 0x1d, KEY_ZOOM }, /* Full Screen */ + { 0x01, 0x17, KEY_SUBTITLE }, /* Subtitle / CC */ + { 0x00, 0x10, KEY_MUTE }, + { 0x01, 0x19, KEY_AUDIO }, /* L/R */ /* TODO better event */ + { 0x01, 0x16, KEY_SLEEP }, /* Hibernate */ + { 0x01, 0x16, KEY_SWITCHVIDEOMODE }, + /* A/V */ /* TODO does not work */ + { 0x00, 0x06, KEY_AGAIN }, /* Recall */ + { 0x01, 0x16, KEY_KPPLUS }, /* Zoom+ */ /* TODO does not work */ + { 0x01, 0x16, KEY_KPMINUS }, /* Zoom- */ /* TODO does not work */ + { 0x02, 0x15, KEY_RED }, + { 0x02, 0x0a, KEY_GREEN }, + { 0x02, 0x1c, KEY_YELLOW }, + { 0x02, 0x05, KEY_BLUE }, +}; + +static u8 af9015_ir_table_twinhan[] = { + 0x00, 0xff, 0x16, 0xe9, 0x3f, 0x05, 0x00, + 0x00, 0xff, 0x07, 0xf8, 0x16, 0x01, 0x00, + 0x00, 0xff, 0x14, 0xeb, 0x11, 0x01, 0x00, + 0x00, 0xff, 0x1a, 0xe5, 0x4d, 0x00, 0x00, + 0x00, 0xff, 0x4c, 0xb3, 0x17, 0x00, 0x00, + 0x00, 0xff, 0x12, 0xed, 0x11, 0x00, 0x00, + 0x00, 0xff, 0x40, 0xbf, 0x0c, 0x00, 0x00, + 0x00, 0xff, 0x11, 0xee, 0x4a, 0x00, 0x00, + 0x00, 0xff, 0x54, 0xab, 0x13, 0x00, 0x00, + 0x00, 0xff, 0x41, 0xbe, 0x15, 0x01, 0x00, + 0x00, 0xff, 0x42, 0xbd, 0x0e, 0x01, 0x00, + 0x00, 0xff, 0x43, 0xbc, 0x17, 0x01, 0x00, + 0x00, 0xff, 0x50, 0xaf, 0x0f, 0x01, 0x00, + 0x00, 0xff, 0x4d, 0xb2, 0x1d, 0x00, 0x00, + 0x00, 0xff, 0x47, 0xb8, 0x13, 0x01, 0x00, + 0x00, 0xff, 0x05, 0xfa, 0x4b, 0x00, 0x00, + 0x00, 0xff, 0x02, 0xfd, 0x4e, 0x00, 0x00, + 0x00, 0xff, 0x0e, 0xf1, 0x06, 0x00, 0x00, + 0x00, 0xff, 0x1e, 0xe1, 0x52, 0x02, 0x00, + 0x00, 0xff, 0x0a, 0xf5, 0x51, 0x02, 0x00, + 0x00, 0xff, 0x10, 0xef, 0x10, 0x00, 0x00, + 0x00, 0xff, 0x49, 0xb6, 0x19, 0x01, 0x00, + 0x00, 0xff, 0x15, 0xea, 0x27, 0x00, 0x00, + 0x00, 0xff, 0x03, 0xfc, 0x1e, 0x00, 0x00, + 0x00, 0xff, 0x01, 0xfe, 0x1f, 0x00, 0x00, + 0x00, 0xff, 0x06, 0xf9, 0x20, 0x00, 0x00, + 0x00, 0xff, 0x09, 0xf6, 0x21, 0x00, 0x00, + 0x00, 0xff, 0x1d, 0xe2, 0x22, 0x00, 0x00, + 0x00, 0xff, 0x1f, 0xe0, 0x23, 0x00, 0x00, + 0x00, 0xff, 0x0d, 0xf2, 0x24, 0x00, 0x00, + 0x00, 0xff, 0x19, 0xe6, 0x25, 0x00, 0x00, + 0x00, 0xff, 0x1b, 0xe4, 0x26, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x2b, 0x00, 0x00, + 0x00, 0xff, 0x4a, 0xb5, 0x4c, 0x00, 0x00, + 0x00, 0xff, 0x4b, 0xb4, 0x52, 0x00, 0x00, + 0x00, 0xff, 0x51, 0xae, 0x51, 0x00, 0x00, + 0x00, 0xff, 0x52, 0xad, 0x4f, 0x00, 0x00, + 0x00, 0xff, 0x4e, 0xb1, 0x50, 0x00, 0x00, + 0x00, 0xff, 0x0c, 0xf3, 0x29, 0x00, 0x00, + 0x00, 0xff, 0x4f, 0xb0, 0x28, 0x00, 0x00, + 0x00, 0xff, 0x13, 0xec, 0x2a, 0x00, 0x00, + 0x00, 0xff, 0x17, 0xe8, 0x19, 0x00, 0x00, + 0x00, 0xff, 0x04, 0xfb, 0x0f, 0x00, 0x00, + 0x00, 0xff, 0x48, 0xb7, 0x0e, 0x00, 0x00, + 0x00, 0xff, 0x0f, 0xf0, 0x04, 0x00, 0x00, + 0x00, 0xff, 0x1c, 0xe3, 0x08, 0x00, 0x00, + 0x00, 0xff, 0x18, 0xe7, 0x15, 0x02, 0x00, + 0x00, 0xff, 0x53, 0xac, 0x0a, 0x02, 0x00, + 0x00, 0xff, 0x5e, 0xa1, 0x1c, 0x02, 0x00, + 0x00, 0xff, 0x5f, 0xa0, 0x05, 0x02, 0x00, +}; + +/* A-Link DTU(m) */ +static struct dvb_usb_rc_key af9015_rc_keys_a_link[] = { + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x2e, KEY_CHANNELUP }, + { 0x00, 0x2d, KEY_CHANNELDOWN }, + { 0x04, 0x28, KEY_ZOOM }, + { 0x00, 0x41, KEY_MUTE }, + { 0x00, 0x42, KEY_VOLUMEDOWN }, + { 0x00, 0x43, KEY_VOLUMEUP }, + { 0x00, 0x44, KEY_GOTO }, /* jump */ + { 0x05, 0x45, KEY_POWER }, +}; + +static u8 af9015_ir_table_a_link[] = { + 0x08, 0xf7, 0x12, 0xed, 0x45, 0x05, 0x00, /* power */ + 0x08, 0xf7, 0x1a, 0xe5, 0x41, 0x00, 0x00, /* mute */ + 0x08, 0xf7, 0x01, 0xfe, 0x1e, 0x00, 0x00, /* 1 */ + 0x08, 0xf7, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */ + 0x08, 0xf7, 0x03, 0xfc, 0x24, 0x00, 0x00, /* 7 */ + 0x08, 0xf7, 0x05, 0xfa, 0x28, 0x04, 0x00, /* zoom */ + 0x08, 0xf7, 0x00, 0xff, 0x43, 0x00, 0x00, /* volume up */ + 0x08, 0xf7, 0x16, 0xe9, 0x42, 0x00, 0x00, /* volume down */ + 0x08, 0xf7, 0x0f, 0xf0, 0x1f, 0x00, 0x00, /* 2 */ + 0x08, 0xf7, 0x0d, 0xf2, 0x22, 0x00, 0x00, /* 5 */ + 0x08, 0xf7, 0x1b, 0xe4, 0x25, 0x00, 0x00, /* 8 */ + 0x08, 0xf7, 0x06, 0xf9, 0x27, 0x00, 0x00, /* 0 */ + 0x08, 0xf7, 0x14, 0xeb, 0x2e, 0x00, 0x00, /* channel up */ + 0x08, 0xf7, 0x1d, 0xe2, 0x2d, 0x00, 0x00, /* channel down */ + 0x08, 0xf7, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */ + 0x08, 0xf7, 0x18, 0xe7, 0x23, 0x00, 0x00, /* 6 */ + 0x08, 0xf7, 0x04, 0xfb, 0x26, 0x00, 0x00, /* 9 */ + 0x08, 0xf7, 0x07, 0xf8, 0x44, 0x00, 0x00, /* jump */ +}; + +/* MSI DIGIVOX mini II V3.0 */ +static struct dvb_usb_rc_key af9015_rc_keys_msi[] = { + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x03, 0x0f, KEY_CHANNELUP }, + { 0x03, 0x0e, KEY_CHANNELDOWN }, + { 0x00, 0x42, KEY_VOLUMEDOWN }, + { 0x00, 0x43, KEY_VOLUMEUP }, + { 0x05, 0x45, KEY_POWER }, + { 0x00, 0x52, KEY_UP }, /* up */ + { 0x00, 0x51, KEY_DOWN }, /* down */ + { 0x00, 0x28, KEY_ENTER }, +}; + +static u8 af9015_ir_table_msi[] = { + 0x03, 0xfc, 0x17, 0xe8, 0x45, 0x05, 0x00, /* power */ + 0x03, 0xfc, 0x0d, 0xf2, 0x51, 0x00, 0x00, /* down */ + 0x03, 0xfc, 0x03, 0xfc, 0x52, 0x00, 0x00, /* up */ + 0x03, 0xfc, 0x1a, 0xe5, 0x1e, 0x00, 0x00, /* 1 */ + 0x03, 0xfc, 0x02, 0xfd, 0x1f, 0x00, 0x00, /* 2 */ + 0x03, 0xfc, 0x04, 0xfb, 0x20, 0x00, 0x00, /* 3 */ + 0x03, 0xfc, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */ + 0x03, 0xfc, 0x08, 0xf7, 0x22, 0x00, 0x00, /* 5 */ + 0x03, 0xfc, 0x1d, 0xe2, 0x23, 0x00, 0x00, /* 6 */ + 0x03, 0xfc, 0x11, 0xee, 0x24, 0x00, 0x00, /* 7 */ + 0x03, 0xfc, 0x0b, 0xf4, 0x25, 0x00, 0x00, /* 8 */ + 0x03, 0xfc, 0x10, 0xef, 0x26, 0x00, 0x00, /* 9 */ + 0x03, 0xfc, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */ + 0x03, 0xfc, 0x14, 0xeb, 0x43, 0x00, 0x00, /* volume up */ + 0x03, 0xfc, 0x1f, 0xe0, 0x42, 0x00, 0x00, /* volume down */ + 0x03, 0xfc, 0x15, 0xea, 0x0f, 0x03, 0x00, /* channel up */ + 0x03, 0xfc, 0x05, 0xfa, 0x0e, 0x03, 0x00, /* channel down */ + 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* enter */ +}; + +/* MYGICTV U718 */ +static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = { + { 0x00, 0x3d, KEY_SWITCHVIDEOMODE }, + /* TV / AV */ + { 0x05, 0x45, KEY_POWER }, + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x41, KEY_MUTE }, + { 0x00, 0x2a, KEY_ESC }, /* Esc */ + { 0x00, 0x2e, KEY_CHANNELUP }, + { 0x00, 0x2d, KEY_CHANNELDOWN }, + { 0x00, 0x42, KEY_VOLUMEDOWN }, + { 0x00, 0x43, KEY_VOLUMEUP }, + { 0x00, 0x52, KEY_UP }, /* up arrow */ + { 0x00, 0x51, KEY_DOWN }, /* down arrow */ + { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */ + { 0x00, 0x50, KEY_LEFT }, /* left arrow */ + { 0x00, 0x28, KEY_ENTER }, /* ok */ + { 0x01, 0x15, KEY_RECORD }, + { 0x03, 0x13, KEY_PLAY }, + { 0x01, 0x13, KEY_PAUSE }, + { 0x01, 0x16, KEY_STOP }, + { 0x03, 0x07, KEY_REWIND }, /* FR << */ + { 0x03, 0x09, KEY_FASTFORWARD }, /* FF >> */ + { 0x00, 0x3b, KEY_TIME }, /* TimeShift */ + { 0x00, 0x3e, KEY_CAMERA }, /* Snapshot */ + { 0x03, 0x16, KEY_CYCLEWINDOWS }, /* yellow, min / max */ + { 0x00, 0x00, KEY_ZOOM }, /* 'select' (?) */ + { 0x03, 0x16, KEY_SHUFFLE }, /* Shuffle */ + { 0x03, 0x45, KEY_POWER }, +}; + +static u8 af9015_ir_table_mygictv[] = { + 0x02, 0xbd, 0x0c, 0xf3, 0x3d, 0x00, 0x00, /* TV / AV */ + 0x02, 0xbd, 0x14, 0xeb, 0x45, 0x05, 0x00, /* power */ + 0x02, 0xbd, 0x00, 0xff, 0x1e, 0x00, 0x00, /* 1 */ + 0x02, 0xbd, 0x01, 0xfe, 0x1f, 0x00, 0x00, /* 2 */ + 0x02, 0xbd, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */ + 0x02, 0xbd, 0x03, 0xfc, 0x21, 0x00, 0x00, /* 4 */ + 0x02, 0xbd, 0x04, 0xfb, 0x22, 0x00, 0x00, /* 5 */ + 0x02, 0xbd, 0x05, 0xfa, 0x23, 0x00, 0x00, /* 6 */ + 0x02, 0xbd, 0x06, 0xf9, 0x24, 0x00, 0x00, /* 7 */ + 0x02, 0xbd, 0x07, 0xf8, 0x25, 0x00, 0x00, /* 8 */ + 0x02, 0xbd, 0x08, 0xf7, 0x26, 0x00, 0x00, /* 9 */ + 0x02, 0xbd, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */ + 0x02, 0xbd, 0x0a, 0xf5, 0x41, 0x00, 0x00, /* mute */ + 0x02, 0xbd, 0x1c, 0xe3, 0x2a, 0x00, 0x00, /* esc */ + 0x02, 0xbd, 0x1f, 0xe0, 0x43, 0x00, 0x00, /* volume up */ + 0x02, 0xbd, 0x12, 0xed, 0x52, 0x00, 0x00, /* up arrow */ + 0x02, 0xbd, 0x11, 0xee, 0x50, 0x00, 0x00, /* left arrow */ + 0x02, 0xbd, 0x15, 0xea, 0x28, 0x00, 0x00, /* ok */ + 0x02, 0xbd, 0x10, 0xef, 0x4f, 0x00, 0x00, /* right arrow */ + 0x02, 0xbd, 0x13, 0xec, 0x51, 0x00, 0x00, /* down arrow */ + 0x02, 0xbd, 0x0e, 0xf1, 0x42, 0x00, 0x00, /* volume down */ + 0x02, 0xbd, 0x19, 0xe6, 0x15, 0x01, 0x00, /* record */ + 0x02, 0xbd, 0x1e, 0xe1, 0x13, 0x03, 0x00, /* play */ + 0x02, 0xbd, 0x16, 0xe9, 0x16, 0x01, 0x00, /* stop */ + 0x02, 0xbd, 0x0b, 0xf4, 0x28, 0x04, 0x00, /* yellow, min / max */ + 0x02, 0xbd, 0x0f, 0xf0, 0x3b, 0x00, 0x00, /* time shift */ + 0x02, 0xbd, 0x18, 0xe7, 0x2e, 0x00, 0x00, /* channel up */ + 0x02, 0xbd, 0x1a, 0xe5, 0x2d, 0x00, 0x00, /* channel down */ + 0x02, 0xbd, 0x17, 0xe8, 0x3e, 0x00, 0x00, /* snapshot */ + 0x02, 0xbd, 0x40, 0xbf, 0x13, 0x01, 0x00, /* pause */ + 0x02, 0xbd, 0x41, 0xbe, 0x09, 0x03, 0x00, /* FF >> */ + 0x02, 0xbd, 0x42, 0xbd, 0x07, 0x03, 0x00, /* FR << */ + 0x02, 0xbd, 0x43, 0xbc, 0x00, 0x00, 0x00, /* 'select' (?) */ + 0x02, 0xbd, 0x44, 0xbb, 0x16, 0x03, 0x00, /* shuffle */ + 0x02, 0xbd, 0x45, 0xba, 0x45, 0x03, 0x00, /* power */ +}; + +/* KWorld PlusTV Dual DVB-T Stick (DVB-T 399U) */ +static u8 af9015_ir_table_kworld[] = { + 0x86, 0x6b, 0x0c, 0xf3, 0x2e, 0x07, 0x00, + 0x86, 0x6b, 0x16, 0xe9, 0x2d, 0x07, 0x00, + 0x86, 0x6b, 0x1d, 0xe2, 0x37, 0x07, 0x00, + 0x86, 0x6b, 0x00, 0xff, 0x1e, 0x07, 0x00, + 0x86, 0x6b, 0x01, 0xfe, 0x1f, 0x07, 0x00, + 0x86, 0x6b, 0x02, 0xfd, 0x20, 0x07, 0x00, + 0x86, 0x6b, 0x03, 0xfc, 0x21, 0x07, 0x00, + 0x86, 0x6b, 0x04, 0xfb, 0x22, 0x07, 0x00, + 0x86, 0x6b, 0x05, 0xfa, 0x23, 0x07, 0x00, + 0x86, 0x6b, 0x06, 0xf9, 0x24, 0x07, 0x00, + 0x86, 0x6b, 0x07, 0xf8, 0x25, 0x07, 0x00, + 0x86, 0x6b, 0x08, 0xf7, 0x26, 0x07, 0x00, + 0x86, 0x6b, 0x09, 0xf6, 0x4d, 0x07, 0x00, + 0x86, 0x6b, 0x0a, 0xf5, 0x4e, 0x07, 0x00, + 0x86, 0x6b, 0x14, 0xeb, 0x4f, 0x07, 0x00, + 0x86, 0x6b, 0x1e, 0xe1, 0x50, 0x07, 0x00, + 0x86, 0x6b, 0x17, 0xe8, 0x52, 0x07, 0x00, + 0x86, 0x6b, 0x1f, 0xe0, 0x51, 0x07, 0x00, + 0x86, 0x6b, 0x0e, 0xf1, 0x0b, 0x07, 0x00, + 0x86, 0x6b, 0x20, 0xdf, 0x0c, 0x07, 0x00, + 0x86, 0x6b, 0x42, 0xbd, 0x0d, 0x07, 0x00, + 0x86, 0x6b, 0x0b, 0xf4, 0x0e, 0x07, 0x00, + 0x86, 0x6b, 0x43, 0xbc, 0x0f, 0x07, 0x00, + 0x86, 0x6b, 0x10, 0xef, 0x10, 0x07, 0x00, + 0x86, 0x6b, 0x21, 0xde, 0x11, 0x07, 0x00, + 0x86, 0x6b, 0x13, 0xec, 0x12, 0x07, 0x00, + 0x86, 0x6b, 0x11, 0xee, 0x13, 0x07, 0x00, + 0x86, 0x6b, 0x12, 0xed, 0x14, 0x07, 0x00, + 0x86, 0x6b, 0x19, 0xe6, 0x15, 0x07, 0x00, + 0x86, 0x6b, 0x1a, 0xe5, 0x16, 0x07, 0x00, + 0x86, 0x6b, 0x1b, 0xe4, 0x17, 0x07, 0x00, + 0x86, 0x6b, 0x4b, 0xb4, 0x18, 0x07, 0x00, + 0x86, 0x6b, 0x40, 0xbf, 0x19, 0x07, 0x00, + 0x86, 0x6b, 0x44, 0xbb, 0x1a, 0x07, 0x00, + 0x86, 0x6b, 0x41, 0xbe, 0x1b, 0x07, 0x00, + 0x86, 0x6b, 0x22, 0xdd, 0x1c, 0x07, 0x00, + 0x86, 0x6b, 0x15, 0xea, 0x1d, 0x07, 0x00, + 0x86, 0x6b, 0x0f, 0xf0, 0x3f, 0x07, 0x00, + 0x86, 0x6b, 0x1c, 0xe3, 0x40, 0x07, 0x00, + 0x86, 0x6b, 0x4a, 0xb5, 0x41, 0x07, 0x00, + 0x86, 0x6b, 0x48, 0xb7, 0x42, 0x07, 0x00, + 0x86, 0x6b, 0x49, 0xb6, 0x43, 0x07, 0x00, + 0x86, 0x6b, 0x18, 0xe7, 0x44, 0x07, 0x00, + 0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00, +}; + +#endif diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 81369b79e..2558f2b51 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -34,12 +34,14 @@ #define USB_VID_HAUPPAUGE 0x2040 #define USB_VID_HYPER_PALTEK 0x1025 #define USB_VID_KWORLD 0xeb2a +#define USB_VID_KWORLD_2 0x1b80 #define USB_VID_KYE 0x0458 #define USB_VID_LEADTEK 0x0413 #define USB_VID_LITEON 0x04ca #define USB_VID_MEDION 0x1660 #define USB_VID_MIGLIA 0x18f3 #define USB_VID_MSI 0x0db0 +#define USB_VID_MSI_2 0x1462 #define USB_VID_OPERA1 0x695c #define USB_VID_PINNACLE 0x2304 #define USB_VID_TECHNOTREND 0x0b48 @@ -51,15 +53,18 @@ #define USB_VID_WIDEVIEW 0x14aa #define USB_VID_GIGABYTE 0x1044 #define USB_VID_YUAN 0x1164 - +#define USB_VID_XTENSIONS 0x1ae7 /* Product IDs */ #define USB_PID_ADSTECH_USB2_COLD 0xa333 #define USB_PID_ADSTECH_USB2_WARM 0xa334 #define USB_PID_AFATECH_AF9005 0x9020 +#define USB_PID_AFATECH_AF9015_9015 0x9015 +#define USB_PID_AFATECH_AF9015_9016 0x9016 #define USB_VID_ALINK_DTU 0xf170 #define USB_PID_ANSONIC_DVBT_USB 0x6000 #define USB_PID_ANYSEE 0x861f +#define USB_PID_AZUREWAVE_AD_TU700 0x3237 #define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 #define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 @@ -89,9 +94,12 @@ #define USB_PID_UNIWILL_STK7700P 0x6003 #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 +#define USB_PID_KWORLD_399U 0xe399 +#define USB_PID_KWORLD_PC160_2T 0xc160 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 +#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069 #define USB_PID_TWINHAN_VP7041_COLD 0x3201 #define USB_PID_TWINHAN_VP7041_WARM 0x3202 #define USB_PID_TWINHAN_VP7020_COLD 0x3203 @@ -100,6 +108,7 @@ #define USB_PID_TWINHAN_VP7045_WARM 0x3206 #define USB_PID_TWINHAN_VP7021_COLD 0x3207 #define USB_PID_TWINHAN_VP7021_WARM 0x3208 +#define USB_PID_TINYTWIN 0x3226 #define USB_PID_DNTV_TINYUSB2_COLD 0x3223 #define USB_PID_DNTV_TINYUSB2_WARM 0x3224 #define USB_PID_ULTIMA_TVBOX_COLD 0x8105 @@ -146,6 +155,8 @@ #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039 +#define USB_PID_AVERMEDIA_VOLAR_X 0xa815 +#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a #define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058 @@ -155,6 +166,7 @@ #define USB_PID_PINNACLE_PCTV2000E 0x022c #define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228 #define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T 0x0229 +#define USB_PID_PINNACLE_PCTV71E 0x022b #define USB_PID_PINNACLE_PCTV72E 0x0236 #define USB_PID_PINNACLE_PCTV73E 0x0237 #define USB_PID_PCTV_200E 0x020e @@ -193,6 +205,7 @@ #define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01 +#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029 #define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200 #define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201 #define USB_PID_GENPIX_8PSK_REV_2 0x0202 @@ -200,6 +213,7 @@ #define USB_PID_GENPIX_SKYWALKER_CW3K 0x0204 #define USB_PID_SIGMATEK_DVB_110 0x6610 #define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513 +#define USB_PID_MSI_DIGIVOX_DUO 0x8801 #define USB_PID_OPERA1_COLD 0x2830 #define USB_PID_OPERA1_WARM 0x3829 #define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514 @@ -209,5 +223,6 @@ #define USB_PID_ASUS_U3100 0x173f #define USB_PID_YUAN_EC372S 0x1edc #define USB_PID_DW2102 0x2102 +#define USB_PID_XTENSIONS_XD_380 0x0381 #endif -- cgit v1.2.3 From 4aeab9ce21556b35420ecbd4761da226ce2f271a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 16 Sep 2008 01:15:30 -0400 Subject: replace xc3028 firmware filenames with defined default firmware names From: Michael Krufky Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index bffb44380..1a746127c 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -773,7 +773,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) .callback = dvico_bluebird_xc2028_callback, }; static struct xc2028_ctrl ctl = { - .fname = "xc3028-v27.fw", + .fname = XC2028_DEFAULT_FIRMWARE, .max_len = 64, .demod = XC3028_FE_ZARLINK456, }; -- cgit v1.2.3 From 60a8c612ed0fc74b4eb8e0a0797c5f467522dd98 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Tue, 16 Sep 2008 20:22:43 +0300 Subject: af9013: fix compile error coming from u64 div From: Antti Palosaari - fix compile error coming from u64 div Priority: normal Signed-off-by: Antti Palosaari --- linux/drivers/media/dvb/frontends/af9013.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/frontends/af9013.c b/linux/drivers/media/dvb/frontends/af9013.c index 480230b11..739d885b3 100644 --- a/linux/drivers/media/dvb/frontends/af9013.c +++ b/linux/drivers/media/dvb/frontends/af9013.c @@ -941,7 +941,6 @@ static int af9013_update_ber_unc(struct dvb_frontend *fe) u32 error_bit_count = 0; u32 total_bit_count = 0; u32 abort_packet_count = 0; - u64 numerator, denominator; state->ber = 0; @@ -979,11 +978,8 @@ static int af9013_update_ber_unc(struct dvb_frontend *fe) total_bit_count = total_bit_count - abort_packet_count; total_bit_count = total_bit_count * 204 * 8; - if (total_bit_count) { - numerator = error_bit_count * 1000000000; - denominator = total_bit_count; - state->ber = numerator / denominator; - } + if (total_bit_count) + state->ber = error_bit_count * 1000000000 / total_bit_count; state->ucblocks += abort_packet_count; -- cgit v1.2.3 From 90cb83f888a0b9beb7123965200ae7e1ce774751 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 16 Sep 2008 15:46:42 -0400 Subject: sms1xxx: Add new USB product ID for Hauppauge WinTV MiniStick From: Michael Krufky 2040:5510 is the same hardware as 2040:5500 Priority: high Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/sms-cards.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index 06297665c..9da260fe3 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -40,6 +40,8 @@ struct usb_device_id smsusb_id_table[] = { .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B }, { USB_DEVICE(0x2040, 0x5500), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0x5510), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { USB_DEVICE(0x2040, 0x5580), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { USB_DEVICE(0x2040, 0x5590), -- cgit v1.2.3 From dc5d724b55e94616801ab8f3c004cd16ed14bc20 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sun, 21 Sep 2008 23:47:20 -0400 Subject: S2API: cx24116 register description fixes. From: Darron Broad From the author: Here is a simple patch detailing some reverse engineered register detail lost in my latest merge. The comments in the code refer to this register but it is never defined. This corrects this. Priority: normal Signed-off-by: Steven Toth Signed-off-by: Darron Broad --- linux/drivers/media/dvb/frontends/cx24116.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index 869215a66..163190d54 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -67,6 +67,13 @@ static int debug = 0; #define CX24116_REG_UCB8 (0xca) #define CX24116_REG_CLKDIV (0xf3) #define CX24116_REG_RATEDIV (0xf9) +#define CX24116_REG_FECSTATUS (0x9c) /* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */ + +/* FECSTATUS bits */ +#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, else DVB-S2 */ +#define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */ +#define CX24116_FEC_PILOT (0x80) /* Pilot mode requested when tuning else always reset when tuned */ /* arg buffer size */ #define CX24116_ARGLEN (0x1e) -- cgit v1.2.3 From 3668eadcd7c33c68a01f1f3333e65a17d944b551 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Wed, 17 Sep 2008 00:21:11 +0300 Subject: Add support for Silicon Laboratories SI2109/2110 demodulators. From: Igor M. Liplianin Add support for Silicon Laboratories SI2109/2110 demodulator and cards with it, such as DvbWorld PCI2002. Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dm1105/Kconfig | 1 + linux/drivers/media/dvb/dm1105/dm1105.c | 7 +- linux/drivers/media/dvb/frontends/Kconfig | 7 + linux/drivers/media/dvb/frontends/Makefile | 1 + linux/drivers/media/dvb/frontends/si21xx.c | 1049 ++++++++++++++++++++++++++++ linux/drivers/media/dvb/frontends/si21xx.h | 37 + 6 files changed, 1098 insertions(+), 4 deletions(-) create mode 100644 linux/drivers/media/dvb/frontends/si21xx.c create mode 100644 linux/drivers/media/dvb/frontends/si21xx.h (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dm1105/Kconfig b/linux/drivers/media/dvb/dm1105/Kconfig index 4b96bbba9..2af5fffe1 100644 --- a/linux/drivers/media/dvb/dm1105/Kconfig +++ b/linux/drivers/media/dvb/dm1105/Kconfig @@ -4,6 +4,7 @@ config DVB_DM1105 select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_CX24116 if !DVB_FE_CUSTOMISE + select DVB_SI21XX if !DVB_FE_CUSTOMISE help Support for cards based on the SDMC DM1105 PCI chip like DvbWorld 2002 diff --git a/linux/drivers/media/dvb/dm1105/dm1105.c b/linux/drivers/media/dvb/dm1105/dm1105.c index 7f300d4e7..767fa530f 100644 --- a/linux/drivers/media/dvb/dm1105/dm1105.c +++ b/linux/drivers/media/dvb/dm1105/dm1105.c @@ -40,8 +40,8 @@ #include "stv0299.h" /*#include "stv0288.h" - *#include "si21xx.h" *#include "stb6000.h"*/ +#include "si21xx.h" #include "cx24116.h" #include "z0194a.h" @@ -608,12 +608,12 @@ static struct stv0288_config earda_config = { .min_delay_ms = 100, }; +#endif /* keep */ static struct si21xx_config serit_config = { .demod_address = 0x68, .min_delay_ms = 100, }; -#endif /* keep */ static struct cx24116_config serit_sp2633_config = { .demod_address = 0x55, @@ -647,7 +647,7 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) &dm1105dvb->i2c_adap); } } - +#endif /* keep */ if (!dm1105dvb->fe) { dm1105dvb->fe = dvb_attach( si21xx_attach, &serit_config, @@ -656,7 +656,6 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage; } -#endif /* keep */ break; case PCI_DEVICE_ID_DW2004: dm1105dvb->fe = dvb_attach( diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 5398641a9..440669a11 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -99,6 +99,13 @@ config DVB_CX24116 help A DVB-S/S2 tuner module. Say Y when you want to support this frontend. +config DVB_SI21XX + tristate "Silicon Labs SI21XX based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. Say Y when you want to support this frontend. + comment "DVB-T (terrestrial) frontends" depends on DVB_CORE diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 9bcf29f86..2c879d2ed 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -50,3 +50,4 @@ obj-$(CONFIG_DVB_TDA10048) += tda10048.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o obj-$(CONFIG_DVB_CX24116) += cx24116.o +obj-$(CONFIG_DVB_SI21XX) += si21xx.o diff --git a/linux/drivers/media/dvb/frontends/si21xx.c b/linux/drivers/media/dvb/frontends/si21xx.c new file mode 100644 index 000000000..bad4ce9b8 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/si21xx.c @@ -0,0 +1,1049 @@ +/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator +* +* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +* +* 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. +* +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "si21xx.h" + +#define REVISION_REG 0x00 +#define SYSTEM_MODE_REG 0x01 +#define TS_CTRL_REG_1 0x02 +#define TS_CTRL_REG_2 0x03 +#define PIN_CTRL_REG_1 0x04 +#define PIN_CTRL_REG_2 0x05 +#define LOCK_STATUS_REG_1 0x0f +#define LOCK_STATUS_REG_2 0x10 +#define ACQ_STATUS_REG 0x11 +#define ACQ_CTRL_REG_1 0x13 +#define ACQ_CTRL_REG_2 0x14 +#define PLL_DIVISOR_REG 0x15 +#define COARSE_TUNE_REG 0x16 +#define FINE_TUNE_REG_L 0x17 +#define FINE_TUNE_REG_H 0x18 + +#define ANALOG_AGC_POWER_LEVEL_REG 0x28 +#define CFO_ESTIMATOR_CTRL_REG_1 0x29 +#define CFO_ESTIMATOR_CTRL_REG_2 0x2a +#define CFO_ESTIMATOR_CTRL_REG_3 0x2b + +#define SYM_RATE_ESTIMATE_REG_L 0x31 +#define SYM_RATE_ESTIMATE_REG_M 0x32 +#define SYM_RATE_ESTIMATE_REG_H 0x33 + +#define CFO_ESTIMATOR_OFFSET_REG_L 0x36 +#define CFO_ESTIMATOR_OFFSET_REG_H 0x37 +#define CFO_ERROR_REG_L 0x38 +#define CFO_ERROR_REG_H 0x39 +#define SYM_RATE_ESTIMATOR_CTRL_REG 0x3a + +#define SYM_RATE_REG_L 0x3f +#define SYM_RATE_REG_M 0x40 +#define SYM_RATE_REG_H 0x41 +#define SYM_RATE_ESTIMATOR_MAXIMUM_REG 0x42 +#define SYM_RATE_ESTIMATOR_MINIMUM_REG 0x43 + +#define C_N_ESTIMATOR_CTRL_REG 0x7c +#define C_N_ESTIMATOR_THRSHLD_REG 0x7d +#define C_N_ESTIMATOR_LEVEL_REG_L 0x7e +#define C_N_ESTIMATOR_LEVEL_REG_H 0x7f + +#define BLIND_SCAN_CTRL_REG 0x80 + +#define LSA_CTRL_REG_1 0x8D +#define SPCTRM_TILT_CORR_THRSHLD_REG 0x8f +#define ONE_DB_BNDWDTH_THRSHLD_REG 0x90 +#define TWO_DB_BNDWDTH_THRSHLD_REG 0x91 +#define THREE_DB_BNDWDTH_THRSHLD_REG 0x92 +#define INBAND_POWER_THRSHLD_REG 0x93 +#define REF_NOISE_LVL_MRGN_THRSHLD_REG 0x94 + +#define VIT_SRCH_CTRL_REG_1 0xa0 +#define VIT_SRCH_CTRL_REG_2 0xa1 +#define VIT_SRCH_CTRL_REG_3 0xa2 +#define VIT_SRCH_STATUS_REG 0xa3 +#define VITERBI_BER_COUNT_REG_L 0xab +#define REED_SOLOMON_CTRL_REG 0xb0 +#define REED_SOLOMON_ERROR_COUNT_REG_L 0xb1 +#define PRBS_CTRL_REG 0xb5 + +#define LNB_CTRL_REG_1 0xc0 +#define LNB_CTRL_REG_2 0xc1 +#define LNB_CTRL_REG_3 0xc2 +#define LNB_CTRL_REG_4 0xc3 +#define LNB_CTRL_STATUS_REG 0xc4 +#define LNB_FIFO_REGS_0 0xc5 +#define LNB_FIFO_REGS_1 0xc6 +#define LNB_FIFO_REGS_2 0xc7 +#define LNB_FIFO_REGS_3 0xc8 +#define LNB_FIFO_REGS_4 0xc9 +#define LNB_FIFO_REGS_5 0xca +#define LNB_SUPPLY_CTRL_REG_1 0xcb +#define LNB_SUPPLY_CTRL_REG_2 0xcc +#define LNB_SUPPLY_CTRL_REG_3 0xcd +#define LNB_SUPPLY_CTRL_REG_4 0xce +#define LNB_SUPPLY_STATUS_REG 0xcf + +#define FALSE 0 +#define TRUE 1 +#define FAIL -1 +#define PASS 0 + +#define ALLOWABLE_FS_COUNT 10 +#define STATUS_BER 0 +#define STATUS_UCBLOCKS 1 + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "si21xx: " args); \ + } while (0) + +enum { + ACTIVE_HIGH, + ACTIVE_LOW +}; +enum { + BYTE_WIDE, + BIT_WIDE +}; +enum { + CLK_GAPPED_MODE, + CLK_CONTINUOUS_MODE +}; +enum { + RISING_EDGE, + FALLING_EDGE +}; +enum { + MSB_FIRST, + LSB_FIRST +}; +enum { + SERIAL, + PARALLEL +}; + +struct si21xx_state { + struct i2c_adapter *i2c; + const struct si21xx_config *config; + struct dvb_frontend frontend; + u8 initialised:1; + int errmode; + int fs; /*Sampling rate of the ADC in MHz*/ +}; + +/* register default initialization */ +static u8 serit_sp1511lhb_inittab[] = { + 0x01, 0x28, /* set i2c_inc_disable */ + 0x20, 0x03, + 0x27, 0x20, + 0xe0, 0x45, + 0xe1, 0x08, + 0xfe, 0x01, + 0x01, 0x28, + 0x89, 0x09, + 0x04, 0x80, + 0x05, 0x01, + 0x06, 0x00, + 0x20, 0x03, + 0x24, 0x88, + 0x29, 0x09, + 0x2a, 0x0f, + 0x2c, 0x10, + 0x2d, 0x19, + 0x2e, 0x08, + 0x2f, 0x10, + 0x30, 0x19, + 0x34, 0x20, + 0x35, 0x03, + 0x45, 0x02, + 0x46, 0x45, + 0x47, 0xd0, + 0x48, 0x00, + 0x49, 0x40, + 0x4a, 0x03, + 0x4c, 0xfd, + 0x4f, 0x2e, + 0x50, 0x2e, + 0x51, 0x10, + 0x52, 0x10, + 0x56, 0x92, + 0x59, 0x00, + 0x5a, 0x2d, + 0x5b, 0x33, + 0x5c, 0x1f, + 0x5f, 0x76, + 0x62, 0xc0, + 0x63, 0xc0, + 0x64, 0xf3, + 0x65, 0xf3, + 0x79, 0x40, + 0x6a, 0x40, + 0x6b, 0x0a, + 0x6c, 0x80, + 0x6d, 0x27, + 0x71, 0x06, + 0x75, 0x60, + 0x78, 0x00, + 0x79, 0xb5, + 0x7c, 0x05, + 0x7d, 0x1a, + 0x87, 0x55, + 0x88, 0x72, + 0x8f, 0x08, + 0x90, 0xe0, + 0x94, 0x40, + 0xa0, 0x3f, + 0xa1, 0xc0, + 0xa4, 0xcc, + 0xa5, 0x66, + 0xa6, 0x66, + 0xa7, 0x7b, + 0xa8, 0x7b, + 0xa9, 0x7b, + 0xaa, 0x9a, + 0xed, 0x04, + 0xad, 0x00, + 0xae, 0x03, + 0xcc, 0xab, + 0x01, 0x08, + 0xff, 0xff +}; + +/* low level read/writes */ +static int si21_writeregs(struct si21xx_state *state, u8 reg1, + u8 *data, int len) +{ + int ret; + u8 buf[60];/* = { reg1, data };*/ + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = len + 1 + }; + + msg.buf[0] = reg1; + memcpy(msg.buf + 1, data, len); + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, " + "ret == %i)\n", __func__, reg1, data[0], ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, " + "ret == %i)\n", __func__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int si21_write(struct dvb_frontend *fe, u8 *buf, int len) +{ + struct si21xx_state *state = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return si21_writereg(state, buf[0], buf[1]); +} + +static u8 si21_readreg(struct si21xx_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) + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __func__, reg, ret); + + return b1[0]; +} + +static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len) +{ + int ret; + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = ®1, + .len = 1 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b, + .len = len + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + dprintk("%s: readreg error (ret == %i)\n", __func__, ret); + + return ret == 2 ? 0 : -1; +} +#if 0 +static int si21xx_wait_diseqc_fifo(struct si21xx_state *state, int timeout) +{ + unsigned long start = jiffies; + + dprintk("%s\n", __func__); + + while (((si21_readreg(state, 0xc4) >> 7) & 1) == 0) { + if (jiffies - start > timeout) { + dprintk("%s: timeout!!\n", __func__); + return -ETIMEDOUT; + } + msleep(10); + }; + + return 0; +} +#endif + +static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout) +{ + unsigned long start = jiffies; + + dprintk("%s\n", __func__); + + while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) { + if (jiffies - start > timeout) { + dprintk("%s: timeout!!\n", __func__); + return -ETIMEDOUT; + } + msleep(10); + }; + + return 0; +} + +static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate) +{ + struct si21xx_state *state = fe->demodulator_priv; + u32 sym_rate, data_rate; + int i; + u8 sym_rate_bytes[3]; + + dprintk("%s : srate = %i\n", __func__ , srate); + + if ((srate < 1000000) || (srate > 45000000)) + return -EINVAL; + + data_rate = srate; + sym_rate = 0; + + for (i = 0; i < 4; ++i) { + sym_rate /= 100; + sym_rate = sym_rate + ((data_rate % 100) * 0x800000) / + state->fs; + data_rate /= 100; + } + for (i = 0; i < 3; ++i) + sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff); + + si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03); + + return 0; +} + +static int si21xx_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *m) +{ + struct si21xx_state *state = fe->demodulator_priv; +#if 0 + u8 val; + int i; + + if (si21xx_wait_diseqc_idle(state, 100) < 0) + return -ETIMEDOUT; + + val = si21_readreg(state, 0x08); + /* DiSEqC mode */ + if (si21_writereg(state, 0x08, (val & ~0x7) | 0x6)) + return -EREMOTEIO; + + for (i = 0; i < m->msg_len; i++) { + if (si21xx_wait_diseqc_fifo(state, 100) < 0) + return -ETIMEDOUT; + + if (si21_writereg(state, 0x09, m->msg[i])) + return -EREMOTEIO; + } + + if (si21xx_wait_diseqc_idle(state, 100) < 0) + return -ETIMEDOUT; + + return 0; + } + +int si21xx_set_lnb_msg(state, lnb_cmd) +{ +#endif + u8 lnb_status; + u8 LNB_CTRL_1; + int status; + + dprintk("%s\n", __func__); + + status = PASS; + LNB_CTRL_1 = 0; + + status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01); + status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01); + + /*fill the FIFO*/ + status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len); + + LNB_CTRL_1 = (lnb_status & 0x70); +#if 0 + LNB_CTRL_1 |= voltage << 6; /*voltage select*/ + LNB_CTRL_1 |= tone << 5; /*continuous tone selection*/ + LNB_CTRL_1 |= burst << 4; /*tone burst selection*/ + LNB_CTRL_1 |= mmsg << 3; /*more messages indicator*/ +#endif + LNB_CTRL_1 |= m->msg_len; + + LNB_CTRL_1 |= 0x80; /* begin LNB signaling */ + + status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01); + + return status; +} + +static int si21xx_send_diseqc_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t burst) +{ + struct si21xx_state *state = fe->demodulator_priv; + u8 val; + + dprintk("%s\n", __func__); + + if (si21xx_wait_diseqc_idle(state, 100) < 0) + return -ETIMEDOUT; + + val = (0x80 | si21_readreg(state, 0xc1)); +#if 0 + /* burst mode */ + if (si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x10)) + return -EREMOTEIO; +#endif + if (si21_writereg(state, LNB_CTRL_REG_1, + burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10))) + return -EREMOTEIO; + + if (si21xx_wait_diseqc_idle(state, 100) < 0) + return -ETIMEDOUT; + + if (si21_writereg(state, LNB_CTRL_REG_1, val)) + return -EREMOTEIO; + + return 0; +} +/* 30.06.2008 */ +static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct si21xx_state *state = fe->demodulator_priv; + u8 val; + + dprintk("%s\n", __func__); +#if 0 + if (si21xx_wait_diseqc_idle(state, 100) < 0) + return -ETIMEDOUT; +#endif + val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); + + switch (tone) { + case SEC_TONE_ON: + return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20); + + case SEC_TONE_OFF: + return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20)); + + default: + return -EINVAL; + } +} + +static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +{ + struct si21xx_state *state = fe->demodulator_priv; + + u8 val; + dprintk("%s: %s\n", __func__, + volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : + volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); + + + val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); + + switch (volt) { + case SEC_VOLTAGE_18: + return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40); + break; + case SEC_VOLTAGE_13: + return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40)); + break; + default: + return -EINVAL; + }; +} + +static int si21xx_init(struct dvb_frontend *fe) +{ + struct si21xx_state *state = fe->demodulator_priv; + int i; + int status = 0; + u8 reg1; + u8 val; + u8 reg2[2]; + + dprintk("%s\n", __func__); + + for (i = 0; ; i += 2) { + reg1 = serit_sp1511lhb_inittab[i]; + val = serit_sp1511lhb_inittab[i+1]; + if (reg1 == 0xff && val == 0xff) + break; + si21_writeregs(state, reg1, &val, 1); + } + + /*DVB QPSK SYSTEM MODE REG*/ + reg1 = 0x08; + si21_writeregs(state, SYSTEM_MODE_REG, ®1, 0x01); + + /*transport stream config*/ + /* + mode = PARALLEL; + sdata_form = LSB_FIRST; + clk_edge = FALLING_EDGE; + clk_mode = CLK_GAPPED_MODE; + strt_len = BYTE_WIDE; + sync_pol = ACTIVE_HIGH; + val_pol = ACTIVE_HIGH; + err_pol = ACTIVE_HIGH; + sclk_rate = 0x00; + parity = 0x00 ; + data_delay = 0x00; + clk_delay = 0x00; + pclk_smooth = 0x00; + */ + reg2[0] = + PARALLEL + (LSB_FIRST << 1) + + (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3) + + (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5) + + (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7); + + reg2[1] = 0; + /* sclk_rate + (parity << 2) + + (data_delay << 3) + (clk_delay << 4) + + (pclk_smooth << 5); + */ + status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02); + if (status != 0) + dprintk(" %s : TS Set Error\n", __func__); + +#if 0 + lnb_cmd.tone = ON; /* 22khz continuous */ + lnb_cmd.mmsg = OFF; /* diseqc more message */ + /* diseqc command */ + lnb_cmd.msg[6] = { "0xE0", "0x10", "0x38", "0xF0" }; + lnb_cmd.msg_len = OFF; /* diseqc command length */ + lnb_cmd.burst = OFF; /* tone burst a,b */ + lnb_cmd.volt = OFF; /* 13v 18v select */ + + status |= si21xx_set_lnb_msg(state, lnb_cmd); + if (status != PASS) + dprintk("%s LNB Set Error\n", __func__); +#endif + return 0; + +} + +static int si21_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct si21xx_state *state = fe->demodulator_priv; + u8 regs_read[2]; + u8 reg_read; + u8 i; + u8 lock; + u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG); + + si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02); + reg_read = 0; + + for (i = 0; i < 7; ++i) + reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i); + + lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80)); + + dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock); + *status = 0; + + if (signal > 10) + *status |= FE_HAS_SIGNAL; + + if (lock & 0x2) + *status |= FE_HAS_CARRIER; + + if (lock & 0x20) + *status |= FE_HAS_VITERBI; + + if (lock & 0x40) + *status |= FE_HAS_SYNC; + + if ((lock & 0x7b) == 0x7b) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct si21xx_state *state = fe->demodulator_priv; + + /*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG, + (u8*)agclevel, 0x01);*/ + + u16 signal = (3 * si21_readreg(state, 0x27) * + si21_readreg(state, 0x28)); + + dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__, + si21_readreg(state, 0x27), + si21_readreg(state, 0x28), (int) signal); + + signal <<= 4; + *strength = signal; + + return 0; +} + +static int si21_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct si21xx_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (state->errmode != STATUS_BER) + return 0; + + *ber = (si21_readreg(state, 0x1d) << 8) | + si21_readreg(state, 0x1e); + + return 0; +} + +static int si21_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct si21xx_state *state = fe->demodulator_priv; + + s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) | + si21_readreg(state, 0x25)); + xsnr = 3 * (xsnr - 0xa100); + *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; + + dprintk("%s\n", __func__); + + return 0; +} + +static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct si21xx_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (state->errmode != STATUS_UCBLOCKS) + *ucblocks = 0; + else + *ucblocks = (si21_readreg(state, 0x1d) << 8) | + si21_readreg(state, 0x1e); + + return 0; +} + +/* initiates a channel acquisition sequence + using the specified symbol rate and code rate */ +static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate, + fe_code_rate_t crate) +{ + + struct si21xx_state *state = fe->demodulator_priv; + u8 coderates[] = { + 0x0, 0x01, 0x02, 0x04, 0x00, + 0x8, 0x10, 0x20, 0x00, 0x3f + }; + + u8 coderate_ptr; + int status; + u8 start_acq = 0x80; + u8 reg, regs[3]; + + dprintk("%s\n", __func__); + + status = PASS; + coderate_ptr = coderates[crate]; + + si21xx_set_symbolrate(fe, symbrate); + + /* write code rates to use in the Viterbi search */ + status |= si21_writeregs(state, + VIT_SRCH_CTRL_REG_1, + &coderate_ptr, 0x01); + + /* clear acq_start bit */ + status |= si21_readregs(state, ACQ_CTRL_REG_2, ®, 0x01); + reg &= ~start_acq; + status |= si21_writeregs(state, ACQ_CTRL_REG_2, ®, 0x01); + + /* use new Carrier Frequency Offset Estimator (QuickLock) */ + regs[0] = 0xCB; + regs[1] = 0x40; + regs[2] = 0xCB; + + status |= si21_writeregs(state, + TWO_DB_BNDWDTH_THRSHLD_REG, + ®s[0], 0x03); + reg = 0x56; + status |= si21_writeregs(state, + LSA_CTRL_REG_1, ®, 1); + reg = 0x05; + status |= si21_writeregs(state, + BLIND_SCAN_CTRL_REG, ®, 1); + /* start automatic acq */ + status |= si21_writeregs(state, + ACQ_CTRL_REG_2, &start_acq, 0x01); + + return status; +} + +static int si21xx_set_property(struct dvb_frontend *fe, struct dtv_property *p) +{ + dprintk("%s(..)\n", __func__); + return 0; +} + +static int si21xx_get_property(struct dvb_frontend *fe, struct dtv_property *p) +{ + dprintk("%s(..)\n", __func__); + return 0; +} + +static int si21xx_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *dfp) +{ + struct si21xx_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + /* freq Channel carrier frequency in KHz (i.e. 1550000 KHz) + datarate Channel symbol rate in Sps (i.e. 22500000 Sps)*/ + + /* in MHz */ + unsigned char coarse_tune_freq; + int fine_tune_freq; + unsigned char sample_rate = 0; + /* boolean */ + unsigned int inband_interferer_ind; + + /* INTERMEDIATE VALUES */ + int icoarse_tune_freq; /* MHz */ + int ifine_tune_freq; /* MHz */ + unsigned int band_high; + unsigned int band_low; + unsigned int x1; + unsigned int x2; + int i; + unsigned int inband_interferer_div2[ALLOWABLE_FS_COUNT] = { + FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, FALSE + }; + unsigned int inband_interferer_div4[ALLOWABLE_FS_COUNT] = { + FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, FALSE + }; + + int status; + + /* allowable sample rates for ADC in MHz */ + int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195, + 196, 204, 205, 206, 207 + }; + /* in MHz */ + int if_limit_high; + int if_limit_low; + int lnb_lo; + int lnb_uncertanity; + + int rf_freq; + int data_rate; + unsigned char regs[4]; + + dprintk("%s : FE_SET_FRONTEND\n", __func__); + + if (c->delivery_system != SYS_DVBS) { + dprintk("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) + inband_interferer_div2[i] = inband_interferer_div4[i] = FALSE; + + if_limit_high = -700000; + if_limit_low = -100000; + /* in MHz */ + lnb_lo = 0; + lnb_uncertanity = 0; + + rf_freq = 10 * c->frequency ; + data_rate = c->symbol_rate / 100; + + status = PASS; + + band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200) + + (data_rate * 135)) / 200; + + band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200) + + (data_rate * 135)) / 200; + + + icoarse_tune_freq = 100000 * + (((rf_freq - lnb_lo) - + (if_limit_low + if_limit_high) / 2) + / 100000); + + ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ; + + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * + (afs[i] * 2500) + afs[i] * 2500; + + x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * + (afs[i] * 2500); + + if (((band_low < x1) && (x1 < band_high)) || + ((band_low < x2) && (x2 < band_high))) + inband_interferer_div4[i] = TRUE; + + } + + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * + (afs[i] * 5000) + afs[i] * 5000; + + x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * + (afs[i] * 5000); + + if (((band_low < x1) && (x1 < band_high)) || + ((band_low < x2) && (x2 < band_high))) + inband_interferer_div2[i] = TRUE; + } + + inband_interferer_ind = TRUE; + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) + inband_interferer_ind &= inband_interferer_div2[i] | + inband_interferer_div4[i]; + + if (inband_interferer_ind) { + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + if (inband_interferer_div2[i] == FALSE) { + sample_rate = (u8) afs[i]; + break; + } + } + } else { + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + if ((inband_interferer_div2[i] | + inband_interferer_div4[i]) == FALSE) { + sample_rate = (u8) afs[i]; + break; + } + } + + } + + if (sample_rate > 207 || sample_rate < 192) + sample_rate = 200; + + fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) / + ((sample_rate) * 1000)); + + coarse_tune_freq = (u8)(icoarse_tune_freq / 100000); + + regs[0] = sample_rate; + regs[1] = coarse_tune_freq; + regs[2] = fine_tune_freq & 0xFF; + regs[3] = fine_tune_freq >> 8 & 0xFF; + + status |= si21_writeregs(state, PLL_DIVISOR_REG, ®s[0], 0x04); + + state->fs = sample_rate;/*ADC MHz*/ + si21xx_setacquire(fe, c->symbol_rate, c->fec_inner); + + return 0; +} + +static int si21xx_sleep(struct dvb_frontend *fe) +{ + struct si21xx_state *state = fe->demodulator_priv; + u8 regdata; + + dprintk("%s\n", __func__); + + si21_readregs(state, SYSTEM_MODE_REG, ®data, 0x01); + regdata |= 1 << 6; + si21_writeregs(state, SYSTEM_MODE_REG, ®data, 0x01); + state->initialised = 0; + + return 0; +} + +static void si21xx_release(struct dvb_frontend *fe) +{ + struct si21xx_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + kfree(state); +} + +static struct dvb_frontend_ops si21xx_ops = { + + .info = { + .name = "SL SI21XX DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 125, /* kHz for QPSK frontends */ + .frequency_tolerance = 0, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO + }, + + .release = si21xx_release, + .init = si21xx_init, + .sleep = si21xx_sleep, + .write = si21_write, + .read_status = si21_read_status, + .read_ber = si21_read_ber, + .read_signal_strength = si21_read_signal_strength, + .read_snr = si21_read_snr, + .read_ucblocks = si21_read_ucblocks, + .diseqc_send_master_cmd = si21xx_send_diseqc_msg, + .diseqc_send_burst = si21xx_send_diseqc_burst, + .set_tone = si21xx_set_tone, + .set_voltage = si21xx_set_voltage, + + .set_property = si21xx_set_property, + .get_property = si21xx_get_property, + .set_frontend = si21xx_set_frontend, +}; + +struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, + struct i2c_adapter *i2c) +{ + struct si21xx_state *state = NULL; + int id; + + dprintk("%s\n", __func__); + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->initialised = 0; + state->errmode = STATUS_BER; + + /* check if the demod is there */ + id = si21_readreg(state, SYSTEM_MODE_REG); + si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */ + msleep(200); + id = si21_readreg(state, 0x00); + + /* register 0x00 contains: + 0x34 for SI2107 + 0x24 for SI2108 + 0x14 for SI2109 + 0x04 for SI2110 + */ + if (id != 0x04 && id != 0x14) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &si21xx_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(si21xx_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver"); +MODULE_AUTHOR("Igor M. Liplianin"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/si21xx.h b/linux/drivers/media/dvb/frontends/si21xx.h new file mode 100644 index 000000000..141b5b8a5 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/si21xx.h @@ -0,0 +1,37 @@ +#ifndef SI21XX_H +#define SI21XX_H + +#include +#include "dvb_frontend.h" + +struct si21xx_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* minimum delay before retuning */ + int min_delay_ms; +}; + +#if defined(CONFIG_DVB_SI21XX) || \ + (defined(CONFIG_DVB_SI21XX_MODULE) && defined(MODULE)) +extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *si21xx_attach( + const struct si21xx_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +static inline int si21xx_writeregister(struct dvb_frontend *fe, u8 reg, u8 val) +{ + int r = 0; + u8 buf[] = {reg, val}; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 2); + return r; +} + +#endif -- cgit v1.2.3 From ed6c8fe1f1e74e4a4a4412d5cb564f5486b4e9f4 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Thu, 18 Sep 2008 01:19:19 +0300 Subject: Add support for USB card modification with SI2109/2110 demodulator. From: Igor M. Liplianin Add support for USB card modification with SI2109/2110 demodulator. Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dvb-usb/dw2102.c | 260 ++++++++++++++++++++----------- 1 file changed, 173 insertions(+), 87 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c index 169951fc2..b5f81f761 100644 --- a/linux/drivers/media/dvb/dvb-usb/dw2102.c +++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c @@ -11,6 +11,7 @@ */ #include #include "dw2102.h" +#include "si21xx.h" #include "stv0299.h" #include "z0194a.h" #include "cx24116.h" @@ -23,8 +24,8 @@ #define USB_PID_DW2104 0x2104 #endif -#define DW2102_READ_MSG 0 -#define DW2102_WRITE_MSG 1 +#define DW210X_READ_MSG 0 +#define DW210X_WRITE_MSG 1 #define REG_1F_SYMBOLRATE_BYTE0 0x1f #define REG_20_SYMBOLRATE_BYTE1 0x20 @@ -33,10 +34,10 @@ #define DW2102_VOLTAGE_CTRL (0x1800) #define DW2102_RC_QUERY (0x1a00) -struct dw2102_state { +struct dw210x_state { u32 last_key_pressed; }; -struct dw2102_rc_keys { +struct dw210x_rc_keys { u32 keycode; u32 event; }; @@ -48,28 +49,27 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer (or-able))." DVB_USB DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -static int dw2102_op_rw(struct usb_device *dev, u8 request, u16 value, +static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value, u16 index, u8 * data, u16 len, int flags) { int ret; u8 u8buf[len]; - unsigned int pipe = (flags == DW2102_READ_MSG) ? + unsigned int pipe = (flags == DW210X_READ_MSG) ? usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0); - u8 request_type = (flags == DW2102_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; + u8 request_type = (flags == DW210X_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; - if (flags == DW2102_WRITE_MSG) + if (flags == DW210X_WRITE_MSG) memcpy(u8buf, data, len); ret = usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR, value, index , u8buf, len, 2000); - if (flags == DW2102_READ_MSG) + if (flags == DW210X_READ_MSG) memcpy(data, u8buf, len); return ret; } /* I2C */ - static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { @@ -89,10 +89,9 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap); value = msg[0].buf[0];/* register */ for (i = 0; i < msg[1].len; i++) { value = value + i; - ret = dw2102_op_rw(d->udev, 0xb5, value, 0, - buf6, 2, DW2102_READ_MSG); + ret = dw210x_op_rw(d->udev, 0xb5, value, 0, + buf6, 2, DW210X_READ_MSG); msg[1].buf[i] = buf6[0]; - } break; case 1: @@ -102,8 +101,8 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap); buf6[0] = 0x2a; buf6[1] = msg[0].buf[0]; buf6[2] = msg[0].buf[1]; - ret = dw2102_op_rw(d->udev, 0xb2, 0, 0, - buf6, 3, DW2102_WRITE_MSG); + ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + buf6, 3, DW210X_WRITE_MSG); break; case 0x60: if (msg[0].flags == 0) { @@ -115,26 +114,26 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap); buf6[4] = msg[0].buf[1]; buf6[5] = msg[0].buf[2]; buf6[6] = msg[0].buf[3]; - ret = dw2102_op_rw(d->udev, 0xb2, 0, 0, - buf6, 7, DW2102_WRITE_MSG); + ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + buf6, 7, DW210X_WRITE_MSG); } else { /* read from tuner */ - ret = dw2102_op_rw(d->udev, 0xb5, 0,0, - buf6, 1, DW2102_READ_MSG); + ret = dw210x_op_rw(d->udev, 0xb5, 0, 0, + buf6, 1, DW210X_READ_MSG); msg[0].buf[0] = buf6[0]; } break; case (DW2102_RC_QUERY): - ret = dw2102_op_rw(d->udev, 0xb8, 0, 0, - buf6, 2, DW2102_READ_MSG); + ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + buf6, 2, DW210X_READ_MSG); msg[0].buf[0] = buf6[0]; msg[0].buf[1] = buf6[1]; break; case (DW2102_VOLTAGE_CTRL): buf6[0] = 0x30; buf6[1] = msg[0].buf[0]; - ret = dw2102_op_rw(d->udev, 0xb2, 0, 0, - buf6, 2, DW2102_WRITE_MSG); + ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + buf6, 2, DW210X_WRITE_MSG); break; } @@ -145,6 +144,62 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap); return num; } +static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret = 0; + u8 buf6[] = {0, 0, 0, 0, 0, 0, 0}; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + switch (num) { + case 2: + /* read si2109 register by number */ + buf6[0] = 0xd0; + buf6[1] = msg[0].len; + buf6[2] = msg[0].buf[0]; + ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + buf6, msg[0].len + 2, DW210X_WRITE_MSG); + /* read si2109 register */ + ret = dw210x_op_rw(d->udev, 0xc3, 0xd0, 0, + buf6, msg[1].len + 2, DW210X_READ_MSG); + memcpy(msg[1].buf, buf6 + 2, msg[1].len); + + break; + case 1: + switch (msg[0].addr) { + case 0x68: + /* write to si2109 register */ + buf6[0] = 0xd0; + buf6[1] = msg[0].len; + memcpy(buf6 + 2, msg[0].buf, msg[0].len); + ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, + msg[0].len + 2, DW210X_WRITE_MSG); + break; + case(DW2102_RC_QUERY): + ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + buf6, 2, DW210X_READ_MSG); + msg[0].buf[0] = buf6[0]; + msg[0].buf[1] = buf6[1]; + break; + case(DW2102_VOLTAGE_CTRL): + buf6[0] = 0x30; + buf6[1] = msg[0].buf[0]; + ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + buf6, 2, DW210X_WRITE_MSG); + break; + } + break; + } + + mutex_unlock(&d->i2c_mutex); + return num; +} + static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); @@ -164,11 +219,11 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i obuf[0] = 0xaa; obuf[1] = msg[0].len; obuf[2] = msg[0].buf[0]; - ret = dw2102_op_rw(d->udev, 0xc2, 0, 0, - obuf, msg[0].len + 2, DW2102_WRITE_MSG); + ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[0].len + 2, DW210X_WRITE_MSG); /* second read registers */ - ret = dw2102_op_rw(d->udev, 0xc3, 0xab , 0, - ibuf, msg[1].len + 2, DW2102_READ_MSG); + ret = dw210x_op_rw(d->udev, 0xc3, 0xab , 0, + ibuf, msg[1].len + 2, DW210X_READ_MSG); memcpy(msg[1].buf, ibuf + 2, msg[1].len); break; @@ -187,8 +242,8 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i i = 1; do { memcpy(obuf + 3, msg[0].buf + i, (len > 16 ? 16 : len)); - ret = dw2102_op_rw(d->udev, 0xc2, 0, 0, - obuf, (len > 16 ? 16 : len) + 3, DW2102_WRITE_MSG); + ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG); i += 16; len -= 16; } while (len > 0); @@ -198,15 +253,15 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i obuf[0] = 0xaa; obuf[1] = msg[0].len; memcpy(obuf + 2, msg[0].buf, msg[0].len); - ret = dw2102_op_rw(d->udev, 0xc2, 0, 0, - obuf, msg[0].len + 2, DW2102_WRITE_MSG); + ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + obuf, msg[0].len + 2, DW210X_WRITE_MSG); } break; } case(DW2102_RC_QUERY): { u8 ibuf[2]; - ret = dw2102_op_rw(d->udev, 0xb8, 0, 0, - ibuf, 2, DW2102_READ_MSG); + ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + ibuf, 2, DW210X_READ_MSG); memcpy(msg[0].buf, ibuf , 2); break; } @@ -214,8 +269,8 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i u8 obuf[2]; obuf[0] = 0x30; obuf[1] = msg[0].buf[0]; - ret = dw2102_op_rw(d->udev, 0xb2, 0, 0, - obuf, 2, DW2102_WRITE_MSG); + ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + obuf, 2, DW210X_WRITE_MSG); break; } } @@ -227,29 +282,34 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i return num; } -static u32 dw2102_i2c_func(struct i2c_adapter *adapter) +static u32 dw210x_i2c_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C; } static struct i2c_algorithm dw2102_i2c_algo = { .master_xfer = dw2102_i2c_transfer, - .functionality = dw2102_i2c_func, + .functionality = dw210x_i2c_func, +}; + +static struct i2c_algorithm dw2102_serit_i2c_algo = { + .master_xfer = dw2102_serit_i2c_transfer, + .functionality = dw210x_i2c_func, }; static struct i2c_algorithm dw2104_i2c_algo = { .master_xfer = dw2104_i2c_transfer, - .functionality = dw2102_i2c_func, + .functionality = dw210x_i2c_func, }; -static int dw2102_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) { int i; u8 ibuf[] = {0, 0}; u8 eeprom[256], eepromline[16]; for (i = 0; i < 256; i++) { - if (dw2102_op_rw(d->udev, 0xb6, 0xa0 , i, ibuf, 2, DW2102_READ_MSG) < 0) { + if (dw210x_op_rw(d->udev, 0xb6, 0xa0 , i, ibuf, 2, DW210X_READ_MSG) < 0) { err("read eeprom failed."); return -1; } else { @@ -265,7 +325,7 @@ static int dw2102_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) return 0; }; -static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { static u8 command_13v[1] = {0x00}; static u8 command_18v[1] = {0x01}; @@ -287,25 +347,46 @@ static struct cx24116_config dw2104_config = { .mpg_clk_pos_pol = 0x01, }; +static struct si21xx_config serit_sp1511lhb_config = { + .demod_address = 0x68, + .min_delay_ms = 100, + +}; + static int dw2104_frontend_attach(struct dvb_usb_adapter *d) { if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config, &d->dev->i2c_adap)) != NULL) { - d->fe->ops.set_voltage = dw2102_set_voltage; + d->fe->ops.set_voltage = dw210x_set_voltage; info("Attached cx24116!\n"); return 0; } return -EIO; } +static struct dvb_usb_device_properties dw2102_properties; + static int dw2102_frontend_attach(struct dvb_usb_adapter *d) { - d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config, - &d->dev->i2c_adap); - if (d->fe != NULL) { - d->fe->ops.set_voltage = dw2102_set_voltage; - info("Attached stv0299!\n"); - return 0; + if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) { + /*dw2102_properties.adapter->tuner_attach = NULL;*/ + d->fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config, + &d->dev->i2c_adap); + if (d->fe != NULL) { + d->fe->ops.set_voltage = dw210x_set_voltage; + info("Attached si21xx!\n"); + return 0; + } + } + if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) { + /*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/ + d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config, + &d->dev->i2c_adap); + if (d->fe != NULL) { + d->fe->ops.set_voltage = dw210x_set_voltage; + info("Attached stv0299!\n"); + return 0; + } } return -EIO; } @@ -317,7 +398,7 @@ static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) return 0; } -static struct dvb_usb_rc_key dw2102_rc_keys[] = { +static struct dvb_usb_rc_key dw210x_rc_keys[] = { { 0xf8, 0x0a, KEY_Q }, /*power*/ { 0xf8, 0x0c, KEY_M }, /*mute*/ { 0xf8, 0x11, KEY_1 }, @@ -356,7 +437,7 @@ static struct dvb_usb_rc_key dw2102_rc_keys[] = { static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { - struct dw2102_state *st = d->priv; + struct dw210x_state *st = d->priv; u8 key[2]; struct i2c_msg msg[] = { {.addr = DW2102_RC_QUERY, .flags = I2C_M_RD, .buf = key, @@ -366,12 +447,12 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) *state = REMOTE_NO_KEY_PRESSED; if (dw2102_i2c_transfer(&d->i2c_adap, msg, 1) == 1) { - for (i = 0; i < ARRAY_SIZE(dw2102_rc_keys); i++) { - if (dw2102_rc_keys[i].data == msg[0].buf[0]) { + for (i = 0; i < ARRAY_SIZE(dw210x_rc_keys); i++) { + if (dw210x_rc_keys[i].data == msg[0].buf[0]) { *state = REMOTE_KEY_PRESSED; - *event = dw2102_rc_keys[i].event; + *event = dw210x_rc_keys[i].event; st->last_key_pressed = - dw2102_rc_keys[i].event; + dw210x_rc_keys[i].event; break; } st->last_key_pressed = 0; @@ -418,15 +499,15 @@ static int dw2102_load_firmware(struct usb_device *dev, p = kmalloc(fw->size, GFP_KERNEL); reset = 1; /*stop the CPU*/ - dw2102_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, DW2102_WRITE_MSG); - dw2102_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, DW2102_WRITE_MSG); + dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, DW210X_WRITE_MSG); + dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, DW210X_WRITE_MSG); if (p != NULL) { memcpy(p, fw->data, fw->size); for (i = 0; i < fw->size; i += 0x40) { b = (u8 *) p + i; - if (dw2102_op_rw(dev, 0xa0, i, 0, b , 0x40, - DW2102_WRITE_MSG) != 0x40) { + if (dw210x_op_rw(dev, 0xa0, i, 0, b , 0x40, + DW210X_WRITE_MSG) != 0x40) { err("error while transferring firmware"); ret = -EINVAL; break; @@ -434,13 +515,13 @@ static int dw2102_load_firmware(struct usb_device *dev, } /* restart the CPU */ reset = 0; - if (ret || dw2102_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, - DW2102_WRITE_MSG) != 1) { + if (ret || dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, + DW210X_WRITE_MSG) != 1) { err("could not restart the USB controller CPU."); ret = -EINVAL; } - if (ret || dw2102_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, - DW2102_WRITE_MSG) != 1) { + if (ret || dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, + DW210X_WRITE_MSG) != 1) { err("could not restart the USB controller CPU."); ret = -EINVAL; } @@ -449,27 +530,32 @@ static int dw2102_load_firmware(struct usb_device *dev, case USB_PID_DW2104: case 0xd650: reset = 1; - dw2102_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1, - DW2102_WRITE_MSG); + dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1, + DW210X_WRITE_MSG); reset = 0; - dw2102_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, - DW2102_WRITE_MSG); + dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, + DW210X_WRITE_MSG); break; case USB_PID_DW2102: - dw2102_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, - DW2102_WRITE_MSG); - dw2102_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, - DW2102_READ_MSG); + dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, + DW210X_WRITE_MSG); + dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, + DW210X_READ_MSG); + /* check STV0299 frontend */ + dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2, + DW210X_READ_MSG); + if (reset16[0] == 0xa1) + dw2102_properties.i2c_algo = &dw2102_i2c_algo; break; case 0x2101: - dw2102_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2, - DW2102_READ_MSG); - dw2102_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, - DW2102_READ_MSG); - dw2102_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, - DW2102_READ_MSG); - dw2102_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, - DW2102_READ_MSG); + dw210x_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2, + DW210X_READ_MSG); + dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, + DW210X_READ_MSG); + dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, + DW210X_READ_MSG); + dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, + DW210X_READ_MSG); break; } msleep(100); @@ -482,12 +568,12 @@ static struct dvb_usb_device_properties dw2102_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-dw2102.fw", - .size_of_priv = sizeof(struct dw2102_state), + .size_of_priv = sizeof(struct dw210x_state), .no_reconnect = 1, - .i2c_algo = &dw2102_i2c_algo, - .rc_key_map = dw2102_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dw2102_rc_keys), + .i2c_algo = &dw2102_serit_i2c_algo, + .rc_key_map = dw210x_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys), .rc_interval = 150, .rc_query = dw2102_rc_query, @@ -495,7 +581,7 @@ static struct dvb_usb_device_properties dw2102_properties = { /* parameter for the MPEG2-data transfer */ .num_adapters = 1, .download_firmware = dw2102_load_firmware, - .read_mac_address = dw2102_read_mac_address, + .read_mac_address = dw210x_read_mac_address, .adapter = { { .frontend_attach = dw2102_frontend_attach, @@ -530,12 +616,12 @@ static struct dvb_usb_device_properties dw2104_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-dw2104.fw", - .size_of_priv = sizeof(struct dw2102_state), + .size_of_priv = sizeof(struct dw210x_state), .no_reconnect = 1, .i2c_algo = &dw2104_i2c_algo, - .rc_key_map = dw2102_rc_keys, - .rc_key_map_size = ARRAY_SIZE(dw2102_rc_keys), + .rc_key_map = dw210x_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys), .rc_interval = 150, .rc_query = dw2102_rc_query, @@ -543,7 +629,7 @@ static struct dvb_usb_device_properties dw2104_properties = { /* parameter for the MPEG2-data transfer */ .num_adapters = 1, .download_firmware = dw2102_load_firmware, - .read_mac_address = dw2102_read_mac_address, + .read_mac_address = dw210x_read_mac_address, .adapter = { { .frontend_attach = dw2104_frontend_attach, -- cgit v1.2.3 From 3bc9aa94edd979ac9d24a7143676b92d98afb7c4 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 20 Sep 2008 00:26:05 +0300 Subject: af9015: Add USB ID for Telestar Starstick 2 From: Mikko Ohtamaa - Add USB ID for Telestar Starstick 2 (10b9:8000) Priority: normal Signed-off-by: Mikko Ohtamaa Signed-off-by: Antti Palosaari --- linux/drivers/media/dvb/dvb-usb/af9015.c | 8 +++++++- linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index 354702b85..576d505ad 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -1196,6 +1196,7 @@ static struct usb_device_id af9015_usb_table[] = { /* 10 */{USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)}, {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, + {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1346,7 +1347,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .i2c_algo = &af9015_i2c_algo, - .num_device_descs = 3, + .num_device_descs = 4, .devices = { { .name = "Xtensions XD-380", @@ -1363,6 +1364,11 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = {&af9015_usb_table[12], NULL}, .warm_ids = {NULL}, }, + { + .name = "Telestar USB DVB-T", + .cold_ids = {&af9015_usb_table[13], NULL}, + .warm_ids = {NULL}, + }, } } }; diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 2558f2b51..60c241e02 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -46,6 +46,7 @@ #define USB_VID_PINNACLE 0x2304 #define USB_VID_TECHNOTREND 0x0b48 #define USB_VID_TERRATEC 0x0ccd +#define USB_VID_TELESTAR 0x10b9 #define USB_VID_VISIONPLUS 0x13d3 #define USB_VID_TWINHAN 0x1822 #define USB_VID_ULTIMA_ELECTRONIC 0x05d8 @@ -224,5 +225,6 @@ #define USB_PID_YUAN_EC372S 0x1edc #define USB_PID_DW2102 0x2102 #define USB_PID_XTENSIONS_XD_380 0x0381 +#define USB_PID_TELESTAR 0x8000 #endif -- cgit v1.2.3 From 6fb16d04b141706431a0b86a4dd47c6f96464144 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 20 Sep 2008 00:34:06 +0300 Subject: af9015: cleanup From: Antti Palosaari - cleanup Telestar Starstick 2 patch Priority: normal Signed-off-by: Antti Palosaari --- linux/drivers/media/dvb/dvb-usb/af9015.c | 4 ++-- linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index 576d505ad..1bef67b51 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -1196,7 +1196,7 @@ static struct usb_device_id af9015_usb_table[] = { /* 10 */{USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)}, {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, - {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR)}, + {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1365,7 +1365,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .warm_ids = {NULL}, }, { - .name = "Telestar USB DVB-T", + .name = "Telestar Starstick 2", .cold_ids = {&af9015_usb_table[13], NULL}, .warm_ids = {NULL}, }, diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 60c241e02..66dc000d4 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -225,6 +225,6 @@ #define USB_PID_YUAN_EC372S 0x1edc #define USB_PID_DW2102 0x2102 #define USB_PID_XTENSIONS_XD_380 0x0381 -#define USB_PID_TELESTAR 0x8000 +#define USB_PID_TELESTAR_STARSTICK_2 0x8000 #endif -- cgit v1.2.3 From 06f7624336c58113d2f32b2ebd34a78c1d9d1b80 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Mon, 22 Sep 2008 01:50:11 +0200 Subject: dvb/budget: push adapter_nr mod option down to individual drivers From: Janne Grunau adapter_nr mod option does not make sense for budget-core since it is only common code shared by all budget drivers Priority: normal Signed-off-by: Janne Grunau --- linux/drivers/media/dvb/ttpci/budget-av.c | 6 +++++- linux/drivers/media/dvb/ttpci/budget-ci.c | 5 ++++- linux/drivers/media/dvb/ttpci/budget-core.c | 6 ++---- linux/drivers/media/dvb/ttpci/budget-patch.c | 7 +++++-- linux/drivers/media/dvb/ttpci/budget.c | 5 ++++- linux/drivers/media/dvb/ttpci/budget.h | 2 +- 6 files changed, 21 insertions(+), 10 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index 839c94101..1032ea778 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -57,6 +57,8 @@ #define SLOTSTATUS_READY 8 #define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + struct budget_av { struct budget budget; struct video_device *vd; @@ -1127,7 +1129,9 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio dev->ext_priv = budget_av; - if ((err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE))) { + err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE, + adapter_nr); + if (err) { kfree(budget_av); return err; } diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index 0ccc9db6e..969877f58 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -96,6 +96,8 @@ static int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + struct budget_ci_ir { struct input_dev *dev; struct tasklet_struct msp430_irq_tasklet; @@ -1191,7 +1193,8 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio dev->ext_priv = budget_ci; - err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE); + err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE, + adapter_nr); if (err) goto out2; diff --git a/linux/drivers/media/dvb/ttpci/budget-core.c b/linux/drivers/media/dvb/ttpci/budget-core.c index 6f4ddb643..ba18e56d5 100644 --- a/linux/drivers/media/dvb/ttpci/budget-core.c +++ b/linux/drivers/media/dvb/ttpci/budget-core.c @@ -57,8 +57,6 @@ module_param_named(bufsize, dma_buffer_size, int, 0444); MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - /**************************************************************************** * TT budget / WinTV Nova ****************************************************************************/ @@ -411,7 +409,7 @@ static void budget_unregister(struct budget *budget) int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, struct saa7146_pci_extension_data *info, - struct module *owner) + struct module *owner, short *adapter_nums) { int ret = 0; struct budget_info *bi = info->ext_priv; @@ -474,7 +472,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, - owner, &budget->dev->pci->dev, adapter_nr); + owner, &budget->dev->pci->dev, adapter_nums); if (ret < 0) return ret; diff --git a/linux/drivers/media/dvb/ttpci/budget-patch.c b/linux/drivers/media/dvb/ttpci/budget-patch.c index b53b4b6d9..0f0bfcad1 100644 --- a/linux/drivers/media/dvb/ttpci/budget-patch.c +++ b/linux/drivers/media/dvb/ttpci/budget-patch.c @@ -39,6 +39,8 @@ #include "bsru6.h" +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + #define budget_patch budget static struct saa7146_extension budget_extension; @@ -592,8 +594,9 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte dprintk(2, "budget: %p\n", budget); - if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) { - kfree (budget); + err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); + if (err) { + kfree(budget); return err; } diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c index d95203d55..1638e1d9f 100644 --- a/linux/drivers/media/dvb/ttpci/budget.c +++ b/linux/drivers/media/dvb/ttpci/budget.c @@ -52,6 +52,8 @@ static int diseqc_method; module_param(diseqc_method, int, 0444); MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + static void Set22K (struct budget *budget, int state) { struct saa7146_dev *dev=budget->dev; @@ -598,7 +600,8 @@ static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_ dev->ext_priv = budget; - if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) { + err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); + if (err) { printk("==> failed\n"); kfree (budget); return err; diff --git a/linux/drivers/media/dvb/ttpci/budget.h b/linux/drivers/media/dvb/ttpci/budget.h index 6539c0171..6129196b8 100644 --- a/linux/drivers/media/dvb/ttpci/budget.h +++ b/linux/drivers/media/dvb/ttpci/budget.h @@ -110,7 +110,7 @@ static struct saa7146_pci_extension_data x_var = { \ extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, struct saa7146_pci_extension_data *info, - struct module *owner); + struct module *owner, short *adapter_nums); extern void ttpci_budget_init_hooks(struct budget *budget); extern int ttpci_budget_deinit(struct budget *budget); extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr); -- cgit v1.2.3 From 3c4d506a9fddb6c4a5611b12468a335356e3d25f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 22 Sep 2008 18:32:37 +0300 Subject: af9015: Add USB ID for AVerMedia A309 From: Antti Palosaari - AVerMedia A309 Mini Card (07ca:a309) Priority: normal Signed-off-by: Antti Palosaari Thanks-to: Dirk Vornheder --- linux/drivers/media/dvb/dvb-usb/af9015.c | 8 +++++++- linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index 1bef67b51..776d01d4a 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -1197,6 +1197,7 @@ static struct usb_device_id af9015_usb_table[] = { {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)}, + {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1347,7 +1348,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .i2c_algo = &af9015_i2c_algo, - .num_device_descs = 4, + .num_device_descs = 5, .devices = { { .name = "Xtensions XD-380", @@ -1369,6 +1370,11 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = {&af9015_usb_table[13], NULL}, .warm_ids = {NULL}, }, + { + .name = "AVerMedia A309", + .cold_ids = {&af9015_usb_table[14], NULL}, + .warm_ids = {NULL}, + }, } } }; diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 66dc000d4..7ae262e08 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -158,6 +158,7 @@ #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039 #define USB_PID_AVERMEDIA_VOLAR_X 0xa815 #define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150 +#define USB_PID_AVERMEDIA_A309 0xa309 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a #define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058 -- cgit v1.2.3 From b5c6a451c061b83cfe6f69eb794646879afd7025 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Tue, 23 Sep 2008 21:21:26 -0400 Subject: S2API: Deactivate the ISDB-T definitions From: Steven Toth We don't want to push the ISDB-T definitions into the kernel until we have a high level of confidence in the ISDB-T API. More testing is required before this code is released. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 11 ++++++++--- linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 98aaa1a65..5c1193524 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -840,6 +840,7 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_DELIVERY_SYSTEM, .set = 1, }, +#if 0 [DTV_ISDB_SEGMENT_IDX] = { .name = "DTV_ISDB_SEGMENT_IDX", .cmd = DTV_ISDB_SEGMENT_IDX, @@ -850,7 +851,7 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_ISDB_SEGMENT_WIDTH, .set = 1, }, - +#endif /* Get */ [DTV_DISEQC_SLAVE_REPLY] = { .name = "DTV_DISEQC_SLAVE_REPLY", @@ -858,6 +859,7 @@ struct dtv_cmds_h dtv_cmds[] = { .set = 0, .buffer = 1, }, +#if 0 [DTV_ISDB_LAYERA_FEC] = { .name = "DTV_ISDB_LAYERA_FEC", .cmd = DTV_ISDB_LAYERA_FEC, @@ -903,6 +905,7 @@ struct dtv_cmds_h dtv_cmds[] = { .cmd = DTV_ISDB_LAYERC_SEGMENT_WIDTH, .set = 0, }, +#endif }; void dtv_property_dump(struct dtv_property *tvp) @@ -1165,7 +1168,7 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_DELIVERY_SYSTEM: tvp->u.data = fe->dtv_property_cache.delivery_system; break; - +#if 0 /* ISDB-T Support here */ case DTV_ISDB_SEGMENT_IDX: tvp->u.data = fe->dtv_property_cache.isdb_segment_idx; @@ -1200,6 +1203,7 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_ISDB_LAYERC_SEGMENT_WIDTH: tvp->u.data = fe->dtv_property_cache.isdb_layerc_segment_width; break; +#endif case DTV_VOLTAGE: tvp->u.data = fe->dtv_property_cache.voltage; break; @@ -1277,7 +1281,7 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_DELIVERY_SYSTEM: fe->dtv_property_cache.delivery_system = tvp->u.data; break; - +#if 0 /* ISDB-T Support here */ case DTV_ISDB_SEGMENT_IDX: fe->dtv_property_cache.isdb_segment_idx = tvp->u.data; @@ -1285,6 +1289,7 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_ISDB_SEGMENT_WIDTH: fe->dtv_property_cache.isdb_segment_width = tvp->u.data; break; +#endif case DTV_VOLTAGE: fe->dtv_property_cache.voltage = tvp->u.data; r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE, diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 2fa37f5a0..1207f29f2 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -209,7 +209,7 @@ struct dtv_frontend_properties { fe_rolloff_t rolloff; fe_delivery_system_t delivery_system; - +#if 0 /* ISDB-T specifics */ u32 isdb_segment_idx; u32 isdb_segment_width; @@ -222,6 +222,7 @@ struct dtv_frontend_properties { fe_code_rate_t isdb_layerc_fec; fe_modulation_t isdb_layerc_modulation; u32 isdb_layerc_segment_width; +#endif }; struct dvb_frontend { -- cgit v1.2.3 From 60068c572f087679b3b93155ec10c660ea932da8 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 23 Sep 2008 21:43:57 +0300 Subject: Add support for ST STV0288 demodulator and cards with it. From: Georg Acher From: Igor M. Liplianin Add support for ST STV0288 demodulator and cards with it, such as TeVii S420. Signed-off by: Georg Acher Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dm1105/dm1105.c | 11 +- linux/drivers/media/dvb/frontends/Kconfig | 14 + linux/drivers/media/dvb/frontends/Makefile | 2 + linux/drivers/media/dvb/frontends/stb6000.c | 255 ++++++++++++ linux/drivers/media/dvb/frontends/stb6000.h | 51 +++ linux/drivers/media/dvb/frontends/stv0288.c | 606 ++++++++++++++++++++++++++++ linux/drivers/media/dvb/frontends/stv0288.h | 65 +++ linux/drivers/media/dvb/frontends/stv0299.c | 2 + linux/drivers/media/dvb/frontends/stv0299.h | 13 +- 9 files changed, 1008 insertions(+), 11 deletions(-) create mode 100644 linux/drivers/media/dvb/frontends/stb6000.c create mode 100644 linux/drivers/media/dvb/frontends/stb6000.h create mode 100644 linux/drivers/media/dvb/frontends/stv0288.c create mode 100644 linux/drivers/media/dvb/frontends/stv0288.h (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dm1105/dm1105.c b/linux/drivers/media/dvb/dm1105/dm1105.c index 767fa530f..07ebe639f 100644 --- a/linux/drivers/media/dvb/dm1105/dm1105.c +++ b/linux/drivers/media/dvb/dm1105/dm1105.c @@ -39,8 +39,8 @@ #include "dvb-pll.h" #include "stv0299.h" -/*#include "stv0288.h" - *#include "stb6000.h"*/ +#include "stv0288.h" +#include "stb6000.h" #include "si21xx.h" #include "cx24116.h" #include "z0194a.h" @@ -602,13 +602,12 @@ static void dm1105dvb_hw_exit(struct dm1105dvb *dm1105dvb) dm1105dvb_dma_unmap(dm1105dvb); } -#if 0 /* keep */ + static struct stv0288_config earda_config = { .demod_address = 0x68, .min_delay_ms = 100, }; -#endif /* keep */ static struct si21xx_config serit_config = { .demod_address = 0x68, .min_delay_ms = 100, @@ -635,7 +634,7 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) dvb_attach(dvb_pll_attach, dm1105dvb->fe, 0x60, &dm1105dvb->i2c_adap, DVB_PLL_OPERA1); } -#if 0 /* keep */ + if (!dm1105dvb->fe) { dm1105dvb->fe = dvb_attach( stv0288_attach, &earda_config, @@ -647,7 +646,7 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) &dm1105dvb->i2c_adap); } } -#endif /* keep */ + if (!dm1105dvb->fe) { dm1105dvb->fe = dvb_attach( si21xx_attach, &serit_config, diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 440669a11..9e90962b5 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -43,6 +43,20 @@ config DVB_S5H1420 help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_STV0288 + tristate "ST STV0288 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. Say Y when you want to support this frontend. + +config DVB_STB6000 + tristate "ST STB6000 silicon tuner" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S silicon tuner module. Say Y when you want to support this tuner. + config DVB_STV0299 tristate "ST STV0299 based" depends on DVB_CORE && I2C diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 2c879d2ed..f4624baed 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -51,3 +51,5 @@ obj-$(CONFIG_DVB_S5H1411) += s5h1411.o obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o obj-$(CONFIG_DVB_CX24116) += cx24116.o obj-$(CONFIG_DVB_SI21XX) += si21xx.o +obj-$(CONFIG_DVB_STV0299) += stv0288.o +obj-$(CONFIG_DVB_STB6000) += stb6000.o diff --git a/linux/drivers/media/dvb/frontends/stb6000.c b/linux/drivers/media/dvb/frontends/stb6000.c new file mode 100644 index 000000000..a0ece4278 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/stb6000.c @@ -0,0 +1,255 @@ + /* + Driver for ST STB6000 DVBS Silicon tuner + + Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#include +#include "compat.h" +#include +#include + +#include "stb6000.h" + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "stb6000: " args); \ + } while (0) + +struct stb6000_priv { + /* i2c details */ + int i2c_address; + struct i2c_adapter *i2c; + u32 frequency; +}; + +static int stb6000_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int stb6000_sleep(struct dvb_frontend *fe) +{ + struct stb6000_priv *priv = fe->tuner_priv; + int ret; + u8 buf[] = { 10, 0 }; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + dprintk("%s:\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret != 1) + dprintk("%s: i2c error\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return (ret == 1) ? 0 : ret; +} + +static int stb6000_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct stb6000_priv *priv = fe->tuner_priv; + unsigned int n, m; + int ret; + u32 freq_mhz; + int bandwidth; + u8 buf[12]; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 12 + }; + + dprintk("%s:\n", __func__); + + freq_mhz = params->frequency / 1000; + bandwidth = params->u.qpsk.symbol_rate / 1000000; + + if (bandwidth > 31) + bandwidth = 31; + + if ((freq_mhz > 949) && (freq_mhz < 2151)) { + buf[0] = 0x01; + buf[1] = 0xac; + if (freq_mhz < 1950) + buf[1] = 0xaa; + if (freq_mhz < 1800) + buf[1] = 0xa8; + if (freq_mhz < 1650) + buf[1] = 0xa6; + if (freq_mhz < 1530) + buf[1] = 0xa5; + if (freq_mhz < 1470) + buf[1] = 0xa4; + if (freq_mhz < 1370) + buf[1] = 0xa2; + if (freq_mhz < 1300) + buf[1] = 0xa1; + if (freq_mhz < 1200) + buf[1] = 0xa0; + if (freq_mhz < 1075) + buf[1] = 0xbc; + if (freq_mhz < 1000) + buf[1] = 0xba; + if (freq_mhz < 1075) { + n = freq_mhz / 8; /* vco=lo*4 */ + m = 2; + } else { + n = freq_mhz / 16; /* vco=lo*2 */ + m = 1; + } + buf[2] = n >> 1; + buf[3] = (unsigned char)(((n & 1) << 7) | + (m * freq_mhz - n * 16) | 0x60); + buf[4] = 0x04; + buf[5] = 0x0e; + + buf[6] = (unsigned char)(bandwidth); + + buf[7] = 0xd8; + buf[8] = 0xd0; + buf[9] = 0x50; + buf[10] = 0xeb; + buf[11] = 0x4f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret != 1) + dprintk("%s: i2c error\n", __func__); + + udelay(10); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + buf[0] = 0x07; + buf[1] = 0xdf; + buf[2] = 0xd0; + buf[3] = 0x50; + buf[4] = 0xfb; + msg.len = 5; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret != 1) + dprintk("%s: i2c error\n", __func__); + + udelay(10); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + priv->frequency = freq_mhz * 1000; + + return (ret == 1) ? 0 : ret; + } + return -1; +} + +static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct stb6000_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static struct dvb_tuner_ops stb6000_tuner_ops = { + .info = { + .name = "ST STB6000", + .frequency_min = 950000, + .frequency_max = 2150000 + }, + .release = stb6000_release, + .sleep = stb6000_sleep, + .set_params = stb6000_set_params, + .get_frequency = stb6000_get_frequency, +}; + +struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c) +{ + struct stb6000_priv *priv = NULL; + u8 b1[] = { 0, 0 }; + struct i2c_msg msg[2] = { + { + .addr = addr, + .flags = 0, + .buf = NULL, + .len = 0 + }, { + .addr = addr, + .flags = I2C_M_RD, + .buf = b1, + .len = 2 + } + }; + int ret; + + dprintk("%s:\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* is some i2c device here ? */ + ret = i2c_transfer(i2c, msg, 2); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 2) + return NULL; + + priv = kzalloc(sizeof(struct stb6000_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_address = addr; + priv->i2c = i2c; + + memcpy(&fe->ops.tuner_ops, &stb6000_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + return fe; +} +EXPORT_SYMBOL(stb6000_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("DVB STB6000 driver"); +MODULE_AUTHOR("Igor M. Liplianin "); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/stb6000.h b/linux/drivers/media/dvb/frontends/stb6000.h new file mode 100644 index 000000000..7be479c22 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/stb6000.h @@ -0,0 +1,51 @@ + /* + Driver for ST stb6000 DVBS Silicon tuner + + Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#ifndef __DVB_STB6000_H__ +#define __DVB_STB6000_H__ + +#include +#include "dvb_frontend.h" + +/** + * Attach a stb6000 tuner to the supplied frontend structure. + * + * @param fe Frontend to attach to. + * @param addr i2c address of the tuner. + * @param i2c i2c adapter to use. + * @return FE pointer on success, NULL on failure. + */ +#if defined(CONFIG_DVB_STB6000) || (defined(CONFIG_DVB_STB6000_MODULE) \ + && defined(MODULE)) +extern struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, + int addr, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_STB6000 */ + +#endif /* __DVB_STB6000_H__ */ diff --git a/linux/drivers/media/dvb/frontends/stv0288.c b/linux/drivers/media/dvb/frontends/stv0288.c new file mode 100644 index 000000000..90e72e771 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/stv0288.c @@ -0,0 +1,606 @@ +/* + Driver for ST STV0288 demodulator + Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de + for Reel Multimedia + Copyright (C) 2008 TurboSight.com, Bob Liu + Copyright (C) 2008 Igor M. Liplianin + Removed stb6000 specific tuner code and revised some + procedures. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "stv0288.h" + +struct stv0288_state { + struct i2c_adapter *i2c; + const struct stv0288_config *config; + struct dvb_frontend frontend; + + u8 initialised:1; + u32 tuner_frequency; + u32 symbol_rate; + fe_code_rate_t fec_inner; + int errmode; +}; + +#define STATUS_BER 0 +#define STATUS_UCBLOCKS 1 + +static int debug; +static int debug_legacy_dish_switch; +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "stv0288: " args); \ + } while (0) + + +static int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 2 + }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " + "ret == %i)\n", __func__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + +static int stv0288_write(struct dvb_frontend *fe, u8 *buf, int len) +{ + struct stv0288_state *state = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return stv0288_writeregI(state, buf[0], buf[1]); +} + +static u8 stv0288_readreg(struct stv0288_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) + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __func__, reg, ret); + + return b1[0]; +} + +static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate) +{ + struct stv0288_state *state = fe->demodulator_priv; + unsigned int temp; + unsigned char b[3]; + + if ((srate < 1000000) || (srate > 45000000)) + return -EINVAL; + + temp = (unsigned int)srate / 1000; + + temp = temp * 32768; + temp = temp / 25; + temp = temp / 125; + b[0] = (unsigned char)((temp >> 12) & 0xff); + b[1] = (unsigned char)((temp >> 4) & 0xff); + b[2] = (unsigned char)((temp << 4) & 0xf0); + stv0288_writeregI(state, 0x28, 0x80); /* SFRH */ + stv0288_writeregI(state, 0x29, 0); /* SFRM */ + stv0288_writeregI(state, 0x2a, 0); /* SFRL */ + + stv0288_writeregI(state, 0x28, b[0]); + stv0288_writeregI(state, 0x29, b[1]); + stv0288_writeregI(state, 0x2a, b[2]); + dprintk("stv0288: stv0288_set_symbolrate\n"); + + return 0; +} + +static int stv0288_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *m) +{ + struct stv0288_state *state = fe->demodulator_priv; + + int i; + + dprintk("%s\n", __func__); + + stv0288_writeregI(state, 0x09, 0); + msleep(30); + stv0288_writeregI(state, 0x05, 0x16); + + for (i = 0; i < m->msg_len; i++) { + if (stv0288_writeregI(state, 0x06, m->msg[i])) + return -EREMOTEIO; + msleep(12); + } + + return 0; +} + +static int stv0288_send_diseqc_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t burst) +{ + struct stv0288_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + if (stv0288_writeregI(state, 0x05, 0x16))/* burst mode */ + return -EREMOTEIO; + + if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff)) + return -EREMOTEIO; + + if (stv0288_writeregI(state, 0x06, 0x12)) + return -EREMOTEIO; + + return 0; +} + +static int stv0288_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct stv0288_state *state = fe->demodulator_priv; + + switch (tone) { + case SEC_TONE_ON: + if (stv0288_writeregI(state, 0x05, 0x10))/* burst mode */ + return -EREMOTEIO; + return stv0288_writeregI(state, 0x06, 0xff); + + case SEC_TONE_OFF: + if (stv0288_writeregI(state, 0x05, 0x13))/* burst mode */ + return -EREMOTEIO; + return stv0288_writeregI(state, 0x06, 0x00); + + default: + return -EINVAL; + } +} + +static u8 stv0288_inittab[] = { + 0x01, 0x15, + 0x02, 0x20, + 0x09, 0x0, + 0x0a, 0x4, + 0x0b, 0x0, + 0x0c, 0x0, + 0x0d, 0x0, + 0x0e, 0xd4, + 0x0f, 0x30, + 0x11, 0x80, + 0x12, 0x03, + 0x13, 0x48, + 0x14, 0x84, + 0x15, 0x45, + 0x16, 0xb7, + 0x17, 0x9c, + 0x18, 0x0, + 0x19, 0xa6, + 0x1a, 0x88, + 0x1b, 0x8f, + 0x1c, 0xf0, + 0x20, 0x0b, + 0x21, 0x54, + 0x22, 0x0, + 0x23, 0x0, + 0x2b, 0xff, + 0x2c, 0xf7, + 0x30, 0x0, + 0x31, 0x1e, + 0x32, 0x14, + 0x33, 0x0f, + 0x34, 0x09, + 0x35, 0x0c, + 0x36, 0x05, + 0x37, 0x2f, + 0x38, 0x16, + 0x39, 0xbe, + 0x3a, 0x0, + 0x3b, 0x13, + 0x3c, 0x11, + 0x3d, 0x30, + 0x40, 0x63, + 0x41, 0x04, + 0x42, 0x60, + 0x43, 0x00, + 0x44, 0x00, + 0x45, 0x00, + 0x46, 0x00, + 0x47, 0x00, + 0x4a, 0x00, + 0x50, 0x10, + 0x51, 0x38, + 0x52, 0x21, + 0x58, 0x54, + 0x59, 0x86, + 0x5a, 0x0, + 0x5b, 0x9b, + 0x5c, 0x08, + 0x5d, 0x7f, + 0x5e, 0x0, + 0x5f, 0xff, + 0x70, 0x0, + 0x71, 0x0, + 0x72, 0x0, + 0x74, 0x0, + 0x75, 0x0, + 0x76, 0x0, + 0x81, 0x0, + 0x82, 0x3f, + 0x83, 0x3f, + 0x84, 0x0, + 0x85, 0x0, + 0x88, 0x0, + 0x89, 0x0, + 0x8a, 0x0, + 0x8b, 0x0, + 0x8c, 0x0, + 0x90, 0x0, + 0x91, 0x0, + 0x92, 0x0, + 0x93, 0x0, + 0x94, 0x1c, + 0x97, 0x0, + 0xa0, 0x48, + 0xa1, 0x0, + 0xb0, 0xb8, + 0xb1, 0x3a, + 0xb2, 0x10, + 0xb3, 0x82, + 0xb4, 0x80, + 0xb5, 0x82, + 0xb6, 0x82, + 0xb7, 0x82, + 0xb8, 0x20, + 0xb9, 0x0, + 0xf0, 0x0, + 0xf1, 0x0, + 0xf2, 0xc0, + 0x51, 0x36, + 0x52, 0x09, + 0x53, 0x94, + 0x54, 0x62, + 0x55, 0x29, + 0x56, 0x64, + 0x57, 0x2b, + 0xff, 0xff, +}; + +static int stv0288_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +{ + dprintk("%s: %s\n", __func__, + volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : + volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); + + return 0; +} + +static int stv0288_init(struct dvb_frontend *fe) +{ + struct stv0288_state *state = fe->demodulator_priv; + int i; + + dprintk("stv0288: init chip\n"); + stv0288_writeregI(state, 0x41, 0x04); + msleep(50); + + for (i = 0; !(stv0288_inittab[i] == 0xff && + stv0288_inittab[i + 1] == 0xff); i += 2) + stv0288_writeregI(state, stv0288_inittab[i], + stv0288_inittab[i + 1]); + + return 0; +} + +static int stv0288_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct stv0288_state *state = fe->demodulator_priv; + + u8 sync = stv0288_readreg(state, 0x24); + if (sync == 255) + sync = 0; + + dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync); + + *status = 0; + + if ((sync & 0x08) == 0x08) { + *status |= FE_HAS_LOCK; + dprintk("stv0288 has locked\n"); + } + + return 0; +} + +static int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct stv0288_state *state = fe->demodulator_priv; + + if (state->errmode != STATUS_BER) + return 0; + *ber = (stv0288_readreg(state, 0x26) << 8) | + stv0288_readreg(state, 0x27); + dprintk("stv0288_read_ber %d\n", *ber); + + return 0; +} + + +static int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct stv0288_state *state = fe->demodulator_priv; + + s32 signal = 0xffff - ((stv0288_readreg(state, 0x10) << 8)); + + + signal = signal * 5 / 4; + *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; + dprintk("stv0288_read_signal_strength %d\n", *strength); + + return 0; +} +static int stv0288_sleep(struct dvb_frontend *fe) +{ + struct stv0288_state *state = fe->demodulator_priv; + + stv0288_writeregI(state, 0x41, 0x84); + state->initialised = 0; + + return 0; +} +static int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct stv0288_state *state = fe->demodulator_priv; + + s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8) + | stv0288_readreg(state, 0x2e)); + xsnr = 3 * (xsnr - 0xa100); + *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; + dprintk("stv0288_read_snr %d\n", *snr); + + return 0; +} + +static int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct stv0288_state *state = fe->demodulator_priv; + + if (state->errmode != STATUS_BER) + return 0; + *ucblocks = (stv0288_readreg(state, 0x26) << 8) | + stv0288_readreg(state, 0x27); + dprintk("stv0288_read_ber %d\n", *ucblocks); + + return 0; +} + +static int stv0288_set_property(struct dvb_frontend *fe, struct dtv_property *p) +{ + dprintk("%s(..)\n", __func__); + return 0; +} + +static int stv0288_get_property(struct dvb_frontend *fe, struct dtv_property *p) +{ + dprintk("%s(..)\n", __func__); + return 0; +} + +static int stv0288_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *dfp) +{ + struct stv0288_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + char tm; + unsigned char tda[3]; + + dprintk("%s : FE_SET_FRONTEND\n", __func__); + + if (c->delivery_system != SYS_DVBS) { + dprintk("%s: unsupported delivery " + "system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); + + /* only frequency & symbol_rate are used for tuner*/ + dfp->frequency = c->frequency; + dfp->u.qpsk.symbol_rate = c->symbol_rate; + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, dfp); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + udelay(10); + stv0288_set_symbolrate(fe, c->symbol_rate); + /* Carrier lock control register */ + stv0288_writeregI(state, 0x15, 0xc5); + + tda[0] = 0x2b; /* CFRM */ + tda[2] = 0x0; /* CFRL */ + for (tm = -6; tm < 7;) { + /* Viterbi status */ + if (stv0288_readreg(state, 0x24) & 0x80) + break; + + tda[2] += 40; + if (tda[2] < 40) + tm++; + tda[1] = (unsigned char)tm; + stv0288_writeregI(state, 0x2b, tda[1]); + stv0288_writeregI(state, 0x2c, tda[2]); + udelay(30); + } + + state->tuner_frequency = c->frequency; + state->fec_inner = FEC_AUTO; + state->symbol_rate = c->symbol_rate; + + return 0; +} + +static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct stv0288_state *state = fe->demodulator_priv; + + if (enable) + stv0288_writeregI(state, 0x01, 0xb5); + else + stv0288_writeregI(state, 0x01, 0x35); + + udelay(1); + + return 0; +} + +static void stv0288_release(struct dvb_frontend *fe) +{ + struct stv0288_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops stv0288_ops = { + + .info = { + .name = "ST STV0288 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1000, /* kHz for QPSK frontends */ + .frequency_tolerance = 0, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .symbol_rate_tolerance = 500, /* ppm */ + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO + }, + + .release = stv0288_release, + .init = stv0288_init, + .sleep = stv0288_sleep, + .write = stv0288_write, + .i2c_gate_ctrl = stv0288_i2c_gate_ctrl, + .read_status = stv0288_read_status, + .read_ber = stv0288_read_ber, + .read_signal_strength = stv0288_read_signal_strength, + .read_snr = stv0288_read_snr, + .read_ucblocks = stv0288_read_ucblocks, + .diseqc_send_master_cmd = stv0288_send_diseqc_msg, + .diseqc_send_burst = stv0288_send_diseqc_burst, + .set_tone = stv0288_set_tone, + .set_voltage = stv0288_set_voltage, + + .set_property = stv0288_set_property, + .get_property = stv0288_get_property, + .set_frontend = stv0288_set_frontend, +}; + +struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, + struct i2c_adapter *i2c) +{ + struct stv0288_state *state = NULL; + int id; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + state->initialised = 0; + state->tuner_frequency = 0; + state->symbol_rate = 0; + state->fec_inner = 0; + state->errmode = STATUS_BER; + + stv0288_writeregI(state, 0x41, 0x04); + msleep(200); + id = stv0288_readreg(state, 0x00); + dprintk("stv0288 id %x\n", id); + + /* register 0x00 contains 0x11 for STV0288 */ + if (id != 0x11) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &stv0288_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + + return NULL; +} +EXPORT_SYMBOL(stv0288_attach); + +module_param(debug_legacy_dish_switch, int, 0444); +MODULE_PARM_DESC(debug_legacy_dish_switch, + "Enable timing analysis for Dish Network legacy switches"); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver"); +MODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin"); +MODULE_LICENSE("GPL"); + diff --git a/linux/drivers/media/dvb/frontends/stv0288.h b/linux/drivers/media/dvb/frontends/stv0288.h new file mode 100644 index 000000000..aa0cdd273 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/stv0288.h @@ -0,0 +1,65 @@ +/* + Driver for ST STV0288 demodulator + + Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de + for Reel Multimedia + Copyright (C) 2008 TurboSight.com, + Copyright (C) 2008 Igor M. Liplianin + Removed stb6000 specific tuner code and revised some + procedures. + + 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 STV0288_H +#define STV0288_H + +#include +#include "dvb_frontend.h" + +struct stv0288_config { + /* the demodulator's i2c address */ + u8 demod_address; + + /* minimum delay before retuning */ + int min_delay_ms; + + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); +}; + +#if defined(CONFIG_DVB_STV0288) || (defined(CONFIG_DVB_STV0288_MODULE) && \ + defined(MODULE)) +extern struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_STV0288 */ + +static inline int stv0288_writereg(struct dvb_frontend *fe, u8 reg, u8 val) +{ + int r = 0; + u8 buf[] = { reg, val }; + if (fe->ops.write) + r = fe->ops.write(fe, buf, 2); + return r; +} + +#endif /* STV0288_H */ diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c index 35435bef8..6c1cb1973 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.c +++ b/linux/drivers/media/dvb/frontends/stv0299.c @@ -559,6 +559,8 @@ static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par int invval = 0; dprintk ("%s : FE_SET_FRONTEND\n", __func__); + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); // set the inversion if (p->inversion == INVERSION_OFF) invval = 0; diff --git a/linux/drivers/media/dvb/frontends/stv0299.h b/linux/drivers/media/dvb/frontends/stv0299.h index 3282f4302..0fd96e22b 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.h +++ b/linux/drivers/media/dvb/frontends/stv0299.h @@ -89,15 +89,18 @@ struct stv0299_config int min_delay_ms; /* Set the symbol rate */ - int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio); + int (*set_symbol_rate)(struct dvb_frontend *fe, u32 srate, u32 ratio); + + /* Set device param to start dma */ + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); }; #if defined(CONFIG_DVB_STV0299) || (defined(CONFIG_DVB_STV0299_MODULE) && defined(MODULE)) -extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, - struct i2c_adapter* i2c); +extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, + struct i2c_adapter *i2c); #else -static inline struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, - struct i2c_adapter* i2c) +static inline struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, + struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; -- cgit v1.2.3 From 57ba167023f217da9151306b24ac58d039441bad Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 24 Sep 2008 10:00:37 +0200 Subject: Fix deadlock in demux code From: Andreas Oberritter The functions dvb_dmxdev_section_callback, dvb_dmxdev_ts_callback, dvb_dmx_swfilter_packet, dvb_dmx_swfilter_packets, dvb_dmx_swfilter and dvb_dmx_swfilter_204 may be called from both interrupt and process context. Therefore they need to be protected by spin_lock_irqsave() instead of spin_lock(). This fixes a deadlock discovered by lockdep. Signed-off-by: Andreas Oberritter Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dmxdev.c | 16 +++++++++------- linux/drivers/media/dvb/dvb-core/dvb_demux.c | 16 ++++++++++------ 2 files changed, 19 insertions(+), 13 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c index 069d847ba..0c733c66a 100644 --- a/linux/drivers/media/dvb/dvb-core/dmxdev.c +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c @@ -364,15 +364,16 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, enum dmx_success success) { struct dmxdev_filter *dmxdevfilter = filter->priv; + unsigned long flags; int ret; if (dmxdevfilter->buffer.error) { wake_up(&dmxdevfilter->buffer.queue); return 0; } - spin_lock(&dmxdevfilter->dev->lock); + spin_lock_irqsave(&dmxdevfilter->dev->lock, flags); if (dmxdevfilter->state != DMXDEV_STATE_GO) { - spin_unlock(&dmxdevfilter->dev->lock); + spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); return 0; } del_timer(&dmxdevfilter->timer); @@ -391,7 +392,7 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, } if (dmxdevfilter->params.sec.flags & DMX_ONESHOT) dmxdevfilter->state = DMXDEV_STATE_DONE; - spin_unlock(&dmxdevfilter->dev->lock); + spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); wake_up(&dmxdevfilter->buffer.queue); return 0; } @@ -403,11 +404,12 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, { struct dmxdev_filter *dmxdevfilter = feed->priv; struct dvb_ringbuffer *buffer; + unsigned long flags; int ret; - spin_lock(&dmxdevfilter->dev->lock); + spin_lock_irqsave(&dmxdevfilter->dev->lock, flags); if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) { - spin_unlock(&dmxdevfilter->dev->lock); + spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); return 0; } @@ -417,7 +419,7 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, else buffer = &dmxdevfilter->dev->dvr_buffer; if (buffer->error) { - spin_unlock(&dmxdevfilter->dev->lock); + spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); wake_up(&buffer->queue); return 0; } @@ -428,7 +430,7 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, dvb_ringbuffer_flush(buffer); buffer->error = ret; } - spin_unlock(&dmxdevfilter->dev->lock); + spin_unlock_irqrestore(&dmxdevfilter->dev->lock, flags); wake_up(&buffer->queue); return 0; } diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.c b/linux/drivers/media/dvb/dvb-core/dvb_demux.c index e2eca0b1f..a2c1fd5d2 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.c @@ -399,7 +399,9 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, size_t count) { - spin_lock(&demux->lock); + unsigned long flags; + + spin_lock_irqsave(&demux->lock, flags); while (count--) { if (buf[0] == 0x47) @@ -407,16 +409,17 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, buf += 188; } - spin_unlock(&demux->lock); + spin_unlock_irqrestore(&demux->lock, flags); } EXPORT_SYMBOL(dvb_dmx_swfilter_packets); void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) { + unsigned long flags; int p = 0, i, j; - spin_lock(&demux->lock); + spin_lock_irqsave(&demux->lock, flags); if (demux->tsbufp) { i = demux->tsbufp; @@ -449,17 +452,18 @@ void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) } bailout: - spin_unlock(&demux->lock); + spin_unlock_irqrestore(&demux->lock, flags); } EXPORT_SYMBOL(dvb_dmx_swfilter); void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) { + unsigned long flags; int p = 0, i, j; u8 tmppack[188]; - spin_lock(&demux->lock); + spin_lock_irqsave(&demux->lock, flags); if (demux->tsbufp) { i = demux->tsbufp; @@ -500,7 +504,7 @@ void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) } bailout: - spin_unlock(&demux->lock); + spin_unlock_irqrestore(&demux->lock, flags); } EXPORT_SYMBOL(dvb_dmx_swfilter_204); -- cgit v1.2.3 From 458d337d90095b5d546c442e37de79db65fc9dfd Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 10 Sep 2008 00:39:20 -0400 Subject: add a general-purpose callback pointer to struct dvb_frontend From: Michael Krufky Remove tuner_callback pointers from tuner driver configuration and private state structures, replaced with a general-purpose callback pointer within struct dvb_frontend. A new parameter is added to the callback function, called component. This allows us to use this callback pointer by frontend components other than the tuner, if need be. So far, this is only used by tuner drivers. Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 1207f29f2..a0c591b3b 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -234,6 +234,8 @@ struct dvb_frontend { void *sec_priv; void *analog_demod_priv; struct dtv_frontend_properties dtv_property_cache; +#define DVB_FRONTEND_COMPONENT_TUNER 0 + int (*callback)(void *adapter_priv, int component, int cmd, int arg); }; extern int dvb_register_frontend(struct dvb_adapter *dvb, -- cgit v1.2.3 From 9385e4874ef72f62d77800fe2eb47429d0854125 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 12 Sep 2008 12:31:45 -0400 Subject: convert tuner drivers to use dvb_frontend->callback From: Michael Krufky Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 7 +++++-- linux/drivers/media/dvb/dvb-usb/dib0700_devices.c | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'linux/drivers/media/dvb') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index 1a746127c..406d7fba3 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -742,7 +742,8 @@ static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap) return 0; } -static int dvico_bluebird_xc2028_callback(void *ptr, int command, int arg) +static int dvico_bluebird_xc2028_callback(void *ptr, int component, + int command, int arg) { struct dvb_usb_adapter *adap = ptr; struct dvb_usb_device *d = adap->dev; @@ -770,7 +771,6 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) struct xc2028_config cfg = { .i2c_adap = &adap->dev->i2c_adap, .i2c_addr = 0x61, - .callback = dvico_bluebird_xc2028_callback, }; static struct xc2028_ctrl ctl = { .fname = XC2028_DEFAULT_FIRMWARE, @@ -778,6 +778,9 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) .demod = XC3028_FE_ZARLINK456, }; + /* FIXME: generalize & move to common area */ + adap->fe->callback = dvico_bluebird_xc2028_callback; + fe = dvb_attach(xc2028_attach, adap->fe, &cfg); if (fe == NULL || fe->ops.tuner_ops.set_config == NULL) return -EIO; diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c index 6c0e5c5f4..88925c9ba 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -366,7 +366,8 @@ static struct dib7000p_config stk7700ph_dib7700_xc3028_config = { .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, }; -static int stk7700ph_xc3028_callback(void *ptr, int command, int arg) +static int stk7700ph_xc3028_callback(void *ptr, int component, + int command, int arg) { struct dvb_usb_adapter *adap = ptr; @@ -394,7 +395,6 @@ static struct xc2028_ctrl stk7700ph_xc3028_ctrl = { static struct xc2028_config stk7700ph_xc3028_config = { .i2c_addr = 0x61, - .callback = stk7700ph_xc3028_callback, .ctrl = &stk7700ph_xc3028_ctrl, }; @@ -435,7 +435,9 @@ static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap) DIBX000_I2C_INTERFACE_TUNER, 1); stk7700ph_xc3028_config.i2c_adap = tun_i2c; - stk7700ph_xc3028_config.video_dev = adap; + + /* FIXME: generalize & move to common area */ + adap->fe->callback = stk7700ph_xc3028_callback; return dvb_attach(xc2028_attach, adap->fe, &stk7700ph_xc3028_config) == NULL ? -ENODEV : 0; -- cgit v1.2.3