@@ -3,6 +3,9 @@
*
* Copyright (C) 2006 Andrew de Quincey
* Copyright (C) 2006 Oliver Endriss
+ * Copyright (C) 2009 Ales Jurik and Jan Petrous (added optional 22k tone
+ * support and temporary diseqc overcurrent enable until
+ * next command - set voltage or tone)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -36,37 +39,91 @@
#include "isl6421.h"
struct isl6421 {
- u8 config;
- u8 override_or;
- u8 override_and;
- struct i2c_adapter *i2c;
- u8 i2c_addr;
+ const struct isl6421_config *config;
+ u8 sys_reg;
+
+ struct i2c_adapter *i2c;
+
+ int (*diseqc_send_master_cmd_orig)(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *cmd);
};
static int isl6421_set_voltage(struct dvb_frontend *fe,
fe_sec_voltage_t voltage)
{
struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
- struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
- .buf = &isl6421->config,
- .len = sizeof(isl6421->config) };
+ struct i2c_msg msg = { .addr = isl6421->config->i2c_addr, .flags = 0,
+ .buf = &isl6421->sys_reg,
+ .len = sizeof(isl6421->sys_reg) };
- isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1);
+ isl6421->sys_reg &= ~(ISL6421_VSEL1 | ISL6421_EN1);
switch(voltage) {
case SEC_VOLTAGE_OFF:
break;
case SEC_VOLTAGE_13:
- isl6421->config |= ISL6421_EN1;
+ isl6421->sys_reg |= ISL6421_EN1;
break;
case SEC_VOLTAGE_18:
- isl6421->config |= (ISL6421_EN1 | ISL6421_VSEL1);
+ isl6421->sys_reg |= (ISL6421_EN1 | ISL6421_VSEL1);
break;
default:
return -EINVAL;
};
- isl6421->config |= isl6421->override_or;
- isl6421->config &= isl6421->override_and;
+ isl6421->sys_reg |= isl6421->config->override_set;
+ isl6421->sys_reg &= ~isl6421->config->override_clear;
+
+ return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+/* Hooked to fe->ops.diseqc_send_master_cmd() only
+ when disable_overcurrent_protection=1 */
+static int isl6421_send_diseqc(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *cmd)
+{
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
+ struct i2c_msg msg = { .addr = isl6421->config->i2c_addr, .flags = 0,
+ .buf = &isl6421->sys_reg,
+ .len = sizeof(isl6421->sys_reg) };
+
+ isl6421->sys_reg |= ISL6421_DCL;
+
+ isl6421->sys_reg |= isl6421->config->override_set;
+ isl6421->sys_reg &= ~isl6421->config->override_clear;
+
+ if (i2c_transfer(isl6421->i2c, &msg, 1) != 1)
+ return -EIO;
+
+ isl6421->sys_reg &= ~ISL6421_DCL;
+
+ return isl6421->diseqc_send_master_cmd_orig ?
+ isl6421->diseqc_send_master_cmd_orig(fe, cmd) : -EIO;
+}
+
+static int isl6421_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
+ struct i2c_msg msg = { .addr = isl6421->config->i2c_addr, .flags = 0,
+ .buf = &isl6421->sys_reg,
+ .len = sizeof(isl6421->sys_reg) };
+
+ isl6421->sys_reg &= ~(ISL6421_ENT1);
+
+ printk(KERN_INFO "%s: %s\n", __func__, ((tone == SEC_TONE_OFF) ?
+ "Off" : "On"));
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ isl6421->sys_reg |= ISL6421_ENT1;
+ break;
+ case SEC_TONE_OFF:
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ isl6421->sys_reg |= isl6421->config->override_set;
+ isl6421->sys_reg &= ~isl6421->config->override_clear;
return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO;
}
@@ -74,49 +131,52 @@ static int isl6421_enable_high_lnb_volta
static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
{
struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
- struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
- .buf = &isl6421->config,
- .len = sizeof(isl6421->config) };
+ struct i2c_msg msg = { .addr = isl6421->config->i2c_addr, .flags = 0,
+ .buf = &isl6421->sys_reg,
+ .len = sizeof(isl6421->sys_reg) };
if (arg)
- isl6421->config |= ISL6421_LLC1;
+ isl6421->sys_reg |= ISL6421_LLC1;
else
- isl6421->config &= ~ISL6421_LLC1;
+ isl6421->sys_reg &= ~ISL6421_LLC1;
- isl6421->config |= isl6421->override_or;
- isl6421->config &= isl6421->override_and;
+ isl6421->sys_reg |= isl6421->config->override_set;
+ isl6421->sys_reg &= ~isl6421->config->override_clear;
return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO;
}
static void isl6421_release(struct dvb_frontend *fe)
{
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
+
/* power off */
isl6421_set_voltage(fe, SEC_VOLTAGE_OFF);
+
+ if (isl6421->config->disable_overcurrent_protection)
+ fe->ops.diseqc_send_master_cmd =
+ isl6421->diseqc_send_master_cmd_orig;
/* free */
kfree(fe->sec_priv);
fe->sec_priv = NULL;
}
-struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct
i2c_adapter *i2c, u8 i2c_addr,
- u8 override_set, u8 override_clear)
+struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ const struct isl6421_config *config)
{
struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL);
+
if (!isl6421)
return NULL;
- /* default configuration */
- isl6421->config = ISL6421_ISEL1;
+ isl6421->config = config;
isl6421->i2c = i2c;
- isl6421->i2c_addr = i2c_addr;
fe->sec_priv = isl6421;
- /* bits which should be forced to '1' */
- isl6421->override_or = override_set;
-
- /* bits which should be forced to '0' */
- isl6421->override_and = ~override_clear;
+ /* default configuration */
+ isl6421->sys_reg = ISL6421_ISEL1;
/* detect if it is present or not */
if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) {
@@ -131,11 +191,38 @@ struct dvb_frontend *isl6421_attach(stru
/* override frontend ops */
fe->ops.set_voltage = isl6421_set_voltage;
fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage;
+ if (config->tone_control)
+ fe->ops.set_tone = isl6421_set_tone;
+
+ printk(KERN_INFO "ISL6421 attached on addr=%x\n", config->i2c_addr);
+
+ if (config->disable_overcurrent_protection) {
+ if ((config->override_set & ISL6421_DCL) ||
+ (config->override_clear & ISL6421_DCL)) {
+ /* there is no sense to use overcurrent_enable
+ * with DCL bit set in any override byte */
+ if (config->override_set & ISL6421_DCL)
+ printk(KERN_WARNING "ISL6421 overcurrent_enable"
+ " with DCL bit in override_set,"
+ " overcurrent_enable ignored\n");
+ if (config->override_clear & ISL6421_DCL)
+ printk(KERN_WARNING "ISL6421 overcurrent_enable"
+ " with DCL bit in override_clear,"
+ " overcurrent_enable ignored\n");
+ } else {
+ printk(KERN_WARNING "ISL6421 overcurrent_enable "
+ " activated. WARNING: it can be "
+ " dangerous for your hardware!");
+ isl6421->diseqc_send_master_cmd_orig =
+ fe->ops.diseqc_send_master_cmd;
+ fe->ops.diseqc_send_master_cmd = isl6421_send_diseqc;
+ }
+ }
return fe;
}
EXPORT_SYMBOL(isl6421_attach);
MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6421");
-MODULE_AUTHOR("Andrew de Quincey & Oliver Endriss");
+MODULE_AUTHOR("Andrew de Quincey,Oliver Endriss,Ales Jurik,Jan Petrous");
MODULE_LICENSE("GPL");
@@ -39,14 +39,40 @@
#define ISL6421_ISEL1 0x20
#define ISL6421_DCL 0x40
+struct isl6421_config {
+ /* I2C address */
+ u8 i2c_addr;
+
+ /* Enable DISEqC tone control mode */
+ bool tone_control;
+
+ /*
+ * Disable isl6421 overcurrent protection.
+ *
+ * WARNING: Don't disable the protection unless you are 100% sure about
+ * what you're doing, otherwise you may damage your board.
+ * Only a few designs require to disable the protection, since
+ * the hardware designer opted to use a hardware protection instead
+ */
+ bool disable_overcurrent_protection;
+
+ /* bits which should be forced to '1' */
+ u8 override_set;
+
+ /* bits which should be forced to '0' */
+ u8 override_clear;
+};
+
+
#if defined(CONFIG_DVB_ISL6421) ||
(defined(CONFIG_DVB_ISL6421_MODULE) && defined(MODULE))
/* override_set and override_clear control which system register bits
(above) to always set & clear */
-extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c, u8 i2c_addr,
- u8 override_set, u8 override_clear);
+extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ const struct isl6421_config *config);
#else
-static inline struct dvb_frontend *isl6421_attach(struct dvb_frontend
*fe, struct i2c_adapter *i2c, u8 i2c_addr,
- u8 override_set, u8 override_clear)
-{
+static struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ const struct isl6421_config *config);
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
@@ -456,6 +456,11 @@ static struct cx24123_config hauppauge_n
.set_ts_params = cx24123_set_ts_param,
};
+static struct isl6421_config hauppauge_novas_isl6421_config = {
+ .i2c_address = 0x08,
+ .override_set = ISL6421_DCL,
+};
+
static struct cx24123_config kworld_dvbs_100_config = {
.demod_address = 0x15,
.set_ts_params = cx24123_set_ts_param,
@@ -614,6 +619,11 @@ static struct cx24116_config hauppauge_h
.reset_device = cx24116_reset_device,
};
+static struct isl6421_config hauppauge_hvr4000_isl6421_config = {
+ .i2c_address = 0x08,
+ .override_set = ISL6421_DCL,
+};
+
static struct cx24116_config tevii_s460_config = {
.demod_address = 0x55,
.set_ts_params = cx24116_set_ts_param,
@@ -757,7 +767,7 @@ static int dvb_register(struct cx8802_de
if (!dvb_attach(isl6421_attach,
fe0->dvb.frontend,
&dev->core->i2c_adap,
- 0x08, ISL6421_DCL, 0x00))
+ &hauppauge_novas_isl6421_config))
goto frontend_detach;
}
/* MFE frontend 2 */
@@ -995,7 +1005,8 @@ static int dvb_register(struct cx8802_de
&core->i2c_adap);
if (fe0->dvb.frontend) {
if (!dvb_attach(isl6421_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x08, ISL6421_DCL, 0x00))
+ &core->i2c_adap,
+ &hauppauge_novas_isl6421_config))
goto frontend_detach;
}
break;
@@ -1100,7 +1111,7 @@ static int dvb_register(struct cx8802_de
if (!dvb_attach(isl6421_attach,
fe0->dvb.frontend,
&dev->core->i2c_adap,
- 0x08, ISL6421_DCL, 0x00))
+ &hauppauge_hvr4000_isl6421_config))
goto frontend_detach;
}
/* MFE frontend 2 */
@@ -1128,7 +1139,7 @@ static int dvb_register(struct cx8802_de
if (!dvb_attach(isl6421_attach,
fe0->dvb.frontend,
&dev->core->i2c_adap,
- 0x08, ISL6421_DCL, 0x00))
+ &hauppauge_hvr4000_isl6421_config))
goto frontend_detach;
}
break;