1 /* 2 * isl6421.h - driver for lnb supply and control ic ISL6421 3 * 4 * Copyright (C) 2006 Andrew de Quincey 5 * Copyright (C) 2006 Oliver Endriss 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * To obtain the license, point your browser to 19 * http://www.gnu.org/copyleft/gpl.html 20 * 21 * 22 * the project's page is at https://linuxtv.org 23 */ 24 #include <linux/delay.h> 25 #include <linux/errno.h> 26 #include <linux/init.h> 27 #include <linux/kernel.h> 28 #include <linux/module.h> 29 #include <linux/string.h> 30 #include <linux/slab.h> 31 32 #include <media/dvb_frontend.h> 33 #include "isl6421.h" 34 35 struct isl6421 { 36 u8 config; 37 u8 override_or; 38 u8 override_and; 39 struct i2c_adapter *i2c; 40 u8 i2c_addr; 41 bool is_off; 42 }; 43 44 static int isl6421_set_voltage(struct dvb_frontend *fe, 45 enum fe_sec_voltage voltage) 46 { 47 int ret; 48 u8 buf; 49 bool is_off; 50 struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; 51 struct i2c_msg msg[2] = { 52 { 53 .addr = isl6421->i2c_addr, 54 .flags = 0, 55 .buf = &isl6421->config, 56 .len = 1, 57 }, { 58 .addr = isl6421->i2c_addr, 59 .flags = I2C_M_RD, 60 .buf = &buf, 61 .len = 1, 62 } 63 64 }; 65 66 isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1); 67 68 switch(voltage) { 69 case SEC_VOLTAGE_OFF: 70 is_off = true; 71 break; 72 case SEC_VOLTAGE_13: 73 is_off = false; 74 isl6421->config |= ISL6421_EN1; 75 break; 76 case SEC_VOLTAGE_18: 77 is_off = false; 78 isl6421->config |= (ISL6421_EN1 | ISL6421_VSEL1); 79 break; 80 default: 81 return -EINVAL; 82 } 83 84 /* 85 * If LNBf were not powered on, disable dynamic current limit, as, 86 * according with datasheet, highly capacitive load on the output may 87 * cause a difficult start-up. 88 */ 89 if (isl6421->is_off && !is_off) 90 isl6421->config |= ISL6421_DCL; 91 92 isl6421->config |= isl6421->override_or; 93 isl6421->config &= isl6421->override_and; 94 95 ret = i2c_transfer(isl6421->i2c, msg, 2); 96 if (ret < 0) 97 return ret; 98 if (ret != 2) 99 return -EIO; 100 101 /* Store off status now incase future commands fail */ 102 isl6421->is_off = is_off; 103 104 /* On overflow, the device will try again after 900 ms (typically) */ 105 if (!is_off && (buf & ISL6421_OLF1)) 106 msleep(1000); 107 108 /* Re-enable dynamic current limit */ 109 if ((isl6421->config & ISL6421_DCL) && 110 !(isl6421->override_or & ISL6421_DCL)) { 111 isl6421->config &= ~ISL6421_DCL; 112 113 ret = i2c_transfer(isl6421->i2c, msg, 2); 114 if (ret < 0) 115 return ret; 116 if (ret != 2) 117 return -EIO; 118 } 119 120 /* Check if overload flag is active. If so, disable power */ 121 if (!is_off && (buf & ISL6421_OLF1)) { 122 isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1); 123 ret = i2c_transfer(isl6421->i2c, msg, 1); 124 if (ret < 0) 125 return ret; 126 if (ret != 1) 127 return -EIO; 128 isl6421->is_off = true; 129 130 dev_warn(&isl6421->i2c->dev, 131 "Overload current detected. disabling LNBf power\n"); 132 return -EINVAL; 133 } 134 135 return 0; 136 } 137 138 static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) 139 { 140 struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; 141 struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, 142 .buf = &isl6421->config, 143 .len = sizeof(isl6421->config) }; 144 145 if (arg) 146 isl6421->config |= ISL6421_LLC1; 147 else 148 isl6421->config &= ~ISL6421_LLC1; 149 150 isl6421->config |= isl6421->override_or; 151 isl6421->config &= isl6421->override_and; 152 153 return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; 154 } 155 156 static int isl6421_set_tone(struct dvb_frontend *fe, 157 enum fe_sec_tone_mode tone) 158 { 159 struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; 160 struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, 161 .buf = &isl6421->config, 162 .len = sizeof(isl6421->config) }; 163 164 switch (tone) { 165 case SEC_TONE_ON: 166 isl6421->config |= ISL6421_ENT1; 167 break; 168 case SEC_TONE_OFF: 169 isl6421->config &= ~ISL6421_ENT1; 170 break; 171 default: 172 return -EINVAL; 173 } 174 175 isl6421->config |= isl6421->override_or; 176 isl6421->config &= isl6421->override_and; 177 178 return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; 179 } 180 181 static void isl6421_release(struct dvb_frontend *fe) 182 { 183 /* power off */ 184 isl6421_set_voltage(fe, SEC_VOLTAGE_OFF); 185 186 /* free */ 187 kfree(fe->sec_priv); 188 fe->sec_priv = NULL; 189 } 190 191 struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, 192 u8 override_set, u8 override_clear, bool override_tone) 193 { 194 struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL); 195 if (!isl6421) 196 return NULL; 197 198 /* default configuration */ 199 isl6421->config = ISL6421_ISEL1; 200 isl6421->i2c = i2c; 201 isl6421->i2c_addr = i2c_addr; 202 fe->sec_priv = isl6421; 203 204 /* bits which should be forced to '1' */ 205 isl6421->override_or = override_set; 206 207 /* bits which should be forced to '0' */ 208 isl6421->override_and = ~override_clear; 209 210 /* detect if it is present or not */ 211 if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) { 212 kfree(isl6421); 213 fe->sec_priv = NULL; 214 return NULL; 215 } 216 217 isl6421->is_off = true; 218 219 /* install release callback */ 220 fe->ops.release_sec = isl6421_release; 221 222 /* override frontend ops */ 223 fe->ops.set_voltage = isl6421_set_voltage; 224 fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage; 225 if (override_tone) 226 fe->ops.set_tone = isl6421_set_tone; 227 228 return fe; 229 } 230 EXPORT_SYMBOL(isl6421_attach); 231 232 MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6421"); 233 MODULE_AUTHOR("Andrew de Quincey & Oliver Endriss"); 234 MODULE_LICENSE("GPL"); 235