1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * isl6405.c - driver for dual lnb supply and control ic ISL6405 4 * 5 * Copyright (C) 2008 Hartmut Hackmann 6 * Copyright (C) 2006 Oliver Endriss 7 * 8 * the project's page is at https://linuxtv.org 9 */ 10 #include <linux/delay.h> 11 #include <linux/errno.h> 12 #include <linux/init.h> 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/string.h> 16 #include <linux/slab.h> 17 18 #include <media/dvb_frontend.h> 19 #include "isl6405.h" 20 21 struct isl6405 { 22 u8 config; 23 u8 override_or; 24 u8 override_and; 25 struct i2c_adapter *i2c; 26 u8 i2c_addr; 27 }; 28 29 static int isl6405_set_voltage(struct dvb_frontend *fe, 30 enum fe_sec_voltage voltage) 31 { 32 struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv; 33 struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0, 34 .buf = &isl6405->config, 35 .len = sizeof(isl6405->config) }; 36 37 if (isl6405->override_or & 0x80) { 38 isl6405->config &= ~(ISL6405_VSEL2 | ISL6405_EN2); 39 switch (voltage) { 40 case SEC_VOLTAGE_OFF: 41 break; 42 case SEC_VOLTAGE_13: 43 isl6405->config |= ISL6405_EN2; 44 break; 45 case SEC_VOLTAGE_18: 46 isl6405->config |= (ISL6405_EN2 | ISL6405_VSEL2); 47 break; 48 default: 49 return -EINVAL; 50 } 51 } else { 52 isl6405->config &= ~(ISL6405_VSEL1 | ISL6405_EN1); 53 switch (voltage) { 54 case SEC_VOLTAGE_OFF: 55 break; 56 case SEC_VOLTAGE_13: 57 isl6405->config |= ISL6405_EN1; 58 break; 59 case SEC_VOLTAGE_18: 60 isl6405->config |= (ISL6405_EN1 | ISL6405_VSEL1); 61 break; 62 default: 63 return -EINVAL; 64 } 65 } 66 isl6405->config |= isl6405->override_or; 67 isl6405->config &= isl6405->override_and; 68 69 return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO; 70 } 71 72 static int isl6405_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) 73 { 74 struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv; 75 struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0, 76 .buf = &isl6405->config, 77 .len = sizeof(isl6405->config) }; 78 79 if (isl6405->override_or & 0x80) { 80 if (arg) 81 isl6405->config |= ISL6405_LLC2; 82 else 83 isl6405->config &= ~ISL6405_LLC2; 84 } else { 85 if (arg) 86 isl6405->config |= ISL6405_LLC1; 87 else 88 isl6405->config &= ~ISL6405_LLC1; 89 } 90 isl6405->config |= isl6405->override_or; 91 isl6405->config &= isl6405->override_and; 92 93 return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO; 94 } 95 96 static void isl6405_release(struct dvb_frontend *fe) 97 { 98 /* power off */ 99 isl6405_set_voltage(fe, SEC_VOLTAGE_OFF); 100 101 /* free */ 102 kfree(fe->sec_priv); 103 fe->sec_priv = NULL; 104 } 105 106 struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, 107 u8 i2c_addr, u8 override_set, u8 override_clear) 108 { 109 struct isl6405 *isl6405 = kmalloc(sizeof(struct isl6405), GFP_KERNEL); 110 if (!isl6405) 111 return NULL; 112 113 /* default configuration */ 114 if (override_set & 0x80) 115 isl6405->config = ISL6405_ISEL2; 116 else 117 isl6405->config = ISL6405_ISEL1; 118 isl6405->i2c = i2c; 119 isl6405->i2c_addr = i2c_addr; 120 fe->sec_priv = isl6405; 121 122 /* bits which should be forced to '1' */ 123 isl6405->override_or = override_set; 124 125 /* bits which should be forced to '0' */ 126 isl6405->override_and = ~override_clear; 127 128 /* detect if it is present or not */ 129 if (isl6405_set_voltage(fe, SEC_VOLTAGE_OFF)) { 130 kfree(isl6405); 131 fe->sec_priv = NULL; 132 return NULL; 133 } 134 135 /* install release callback */ 136 fe->ops.release_sec = isl6405_release; 137 138 /* override frontend ops */ 139 fe->ops.set_voltage = isl6405_set_voltage; 140 fe->ops.enable_high_lnb_voltage = isl6405_enable_high_lnb_voltage; 141 142 return fe; 143 } 144 EXPORT_SYMBOL(isl6405_attach); 145 146 MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6405"); 147 MODULE_AUTHOR("Hartmut Hackmann & Oliver Endriss"); 148 MODULE_LICENSE("GPL"); 149