0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/delay.h>
0011 #include <linux/errno.h>
0012 #include <linux/init.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/string.h>
0016 #include <linux/slab.h>
0017
0018 #include <media/dvb_frontend.h>
0019 #include "isl6421.h"
0020
0021 struct isl6421 {
0022 u8 config;
0023 u8 override_or;
0024 u8 override_and;
0025 struct i2c_adapter *i2c;
0026 u8 i2c_addr;
0027 bool is_off;
0028 };
0029
0030 static int isl6421_set_voltage(struct dvb_frontend *fe,
0031 enum fe_sec_voltage voltage)
0032 {
0033 int ret;
0034 u8 buf;
0035 bool is_off;
0036 struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
0037 struct i2c_msg msg[2] = {
0038 {
0039 .addr = isl6421->i2c_addr,
0040 .flags = 0,
0041 .buf = &isl6421->config,
0042 .len = 1,
0043 }, {
0044 .addr = isl6421->i2c_addr,
0045 .flags = I2C_M_RD,
0046 .buf = &buf,
0047 .len = 1,
0048 }
0049
0050 };
0051
0052 isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1);
0053
0054 switch(voltage) {
0055 case SEC_VOLTAGE_OFF:
0056 is_off = true;
0057 break;
0058 case SEC_VOLTAGE_13:
0059 is_off = false;
0060 isl6421->config |= ISL6421_EN1;
0061 break;
0062 case SEC_VOLTAGE_18:
0063 is_off = false;
0064 isl6421->config |= (ISL6421_EN1 | ISL6421_VSEL1);
0065 break;
0066 default:
0067 return -EINVAL;
0068 }
0069
0070
0071
0072
0073
0074
0075 if (isl6421->is_off && !is_off)
0076 isl6421->config |= ISL6421_DCL;
0077
0078 isl6421->config |= isl6421->override_or;
0079 isl6421->config &= isl6421->override_and;
0080
0081 ret = i2c_transfer(isl6421->i2c, msg, 2);
0082 if (ret < 0)
0083 return ret;
0084 if (ret != 2)
0085 return -EIO;
0086
0087
0088 isl6421->is_off = is_off;
0089
0090
0091 if (!is_off && (buf & ISL6421_OLF1))
0092 msleep(1000);
0093
0094
0095 if ((isl6421->config & ISL6421_DCL) &&
0096 !(isl6421->override_or & ISL6421_DCL)) {
0097 isl6421->config &= ~ISL6421_DCL;
0098
0099 ret = i2c_transfer(isl6421->i2c, msg, 2);
0100 if (ret < 0)
0101 return ret;
0102 if (ret != 2)
0103 return -EIO;
0104 }
0105
0106
0107 if (!is_off && (buf & ISL6421_OLF1)) {
0108 isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1);
0109 ret = i2c_transfer(isl6421->i2c, msg, 1);
0110 if (ret < 0)
0111 return ret;
0112 if (ret != 1)
0113 return -EIO;
0114 isl6421->is_off = true;
0115
0116 dev_warn(&isl6421->i2c->dev,
0117 "Overload current detected. disabling LNBf power\n");
0118 return -EINVAL;
0119 }
0120
0121 return 0;
0122 }
0123
0124 static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
0125 {
0126 struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
0127 struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
0128 .buf = &isl6421->config,
0129 .len = sizeof(isl6421->config) };
0130
0131 if (arg)
0132 isl6421->config |= ISL6421_LLC1;
0133 else
0134 isl6421->config &= ~ISL6421_LLC1;
0135
0136 isl6421->config |= isl6421->override_or;
0137 isl6421->config &= isl6421->override_and;
0138
0139 return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO;
0140 }
0141
0142 static int isl6421_set_tone(struct dvb_frontend *fe,
0143 enum fe_sec_tone_mode tone)
0144 {
0145 struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
0146 struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
0147 .buf = &isl6421->config,
0148 .len = sizeof(isl6421->config) };
0149
0150 switch (tone) {
0151 case SEC_TONE_ON:
0152 isl6421->config |= ISL6421_ENT1;
0153 break;
0154 case SEC_TONE_OFF:
0155 isl6421->config &= ~ISL6421_ENT1;
0156 break;
0157 default:
0158 return -EINVAL;
0159 }
0160
0161 isl6421->config |= isl6421->override_or;
0162 isl6421->config &= isl6421->override_and;
0163
0164 return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO;
0165 }
0166
0167 static void isl6421_release(struct dvb_frontend *fe)
0168 {
0169
0170 isl6421_set_voltage(fe, SEC_VOLTAGE_OFF);
0171
0172
0173 kfree(fe->sec_priv);
0174 fe->sec_priv = NULL;
0175 }
0176
0177 struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
0178 u8 override_set, u8 override_clear, bool override_tone)
0179 {
0180 struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL);
0181 if (!isl6421)
0182 return NULL;
0183
0184
0185 isl6421->config = ISL6421_ISEL1;
0186 isl6421->i2c = i2c;
0187 isl6421->i2c_addr = i2c_addr;
0188 fe->sec_priv = isl6421;
0189
0190
0191 isl6421->override_or = override_set;
0192
0193
0194 isl6421->override_and = ~override_clear;
0195
0196
0197 if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) {
0198 kfree(isl6421);
0199 fe->sec_priv = NULL;
0200 return NULL;
0201 }
0202
0203 isl6421->is_off = true;
0204
0205
0206 fe->ops.release_sec = isl6421_release;
0207
0208
0209 fe->ops.set_voltage = isl6421_set_voltage;
0210 fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage;
0211 if (override_tone)
0212 fe->ops.set_tone = isl6421_set_tone;
0213
0214 return fe;
0215 }
0216 EXPORT_SYMBOL(isl6421_attach);
0217
0218 MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6421");
0219 MODULE_AUTHOR("Andrew de Quincey & Oliver Endriss");
0220 MODULE_LICENSE("GPL");