Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * isl6421.h - driver for lnb supply and control ic ISL6421
0004  *
0005  * Copyright (C) 2006 Andrew de Quincey
0006  * Copyright (C) 2006 Oliver Endriss
0007  *
0008  * the project's page is at https://linuxtv.org
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      * If LNBf were not powered on, disable dynamic current limit, as,
0072      * according with datasheet, highly capacitive load on the output may
0073      * cause a difficult start-up.
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     /* Store off status now in case future commands fail */
0088     isl6421->is_off = is_off;
0089 
0090     /* On overflow, the device will try again after 900 ms (typically) */
0091     if (!is_off && (buf & ISL6421_OLF1))
0092         msleep(1000);
0093 
0094     /* Re-enable dynamic current limit */
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     /* Check if overload flag is active. If so, disable power */
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     /* power off */
0170     isl6421_set_voltage(fe, SEC_VOLTAGE_OFF);
0171 
0172     /* free */
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     /* default configuration */
0185     isl6421->config = ISL6421_ISEL1;
0186     isl6421->i2c = i2c;
0187     isl6421->i2c_addr = i2c_addr;
0188     fe->sec_priv = isl6421;
0189 
0190     /* bits which should be forced to '1' */
0191     isl6421->override_or = override_set;
0192 
0193     /* bits which should be forced to '0' */
0194     isl6421->override_and = ~override_clear;
0195 
0196     /* detect if it is present or not */
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     /* install release callback */
0206     fe->ops.release_sec = isl6421_release;
0207 
0208     /* override frontend ops */
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");