1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * lnbp21.c - driver for lnb supply and control ic lnbp21 4 * 5 * Copyright (C) 2006, 2009 Oliver Endriss <o.endriss@gmx.de> 6 * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> 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 "lnbp21.h" 20 #include "lnbh24.h" 21 22 struct lnbp21 { 23 u8 config; 24 u8 override_or; 25 u8 override_and; 26 struct i2c_adapter *i2c; 27 u8 i2c_addr; 28 }; 29 30 static int lnbp21_set_voltage(struct dvb_frontend *fe, 31 enum fe_sec_voltage voltage) 32 { 33 struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; 34 struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, 35 .buf = &lnbp21->config, 36 .len = sizeof(lnbp21->config) }; 37 38 lnbp21->config &= ~(LNBP21_VSEL | LNBP21_EN); 39 40 switch(voltage) { 41 case SEC_VOLTAGE_OFF: 42 break; 43 case SEC_VOLTAGE_13: 44 lnbp21->config |= LNBP21_EN; 45 break; 46 case SEC_VOLTAGE_18: 47 lnbp21->config |= (LNBP21_EN | LNBP21_VSEL); 48 break; 49 default: 50 return -EINVAL; 51 } 52 53 lnbp21->config |= lnbp21->override_or; 54 lnbp21->config &= lnbp21->override_and; 55 56 return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; 57 } 58 59 static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) 60 { 61 struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; 62 struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, 63 .buf = &lnbp21->config, 64 .len = sizeof(lnbp21->config) }; 65 66 if (arg) 67 lnbp21->config |= LNBP21_LLC; 68 else 69 lnbp21->config &= ~LNBP21_LLC; 70 71 lnbp21->config |= lnbp21->override_or; 72 lnbp21->config &= lnbp21->override_and; 73 74 return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; 75 } 76 77 static int lnbp21_set_tone(struct dvb_frontend *fe, 78 enum fe_sec_tone_mode tone) 79 { 80 struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; 81 struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, 82 .buf = &lnbp21->config, 83 .len = sizeof(lnbp21->config) }; 84 85 switch (tone) { 86 case SEC_TONE_OFF: 87 lnbp21->config &= ~LNBP21_TEN; 88 break; 89 case SEC_TONE_ON: 90 lnbp21->config |= LNBP21_TEN; 91 break; 92 default: 93 return -EINVAL; 94 } 95 96 lnbp21->config |= lnbp21->override_or; 97 lnbp21->config &= lnbp21->override_and; 98 99 return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; 100 } 101 102 static void lnbp21_release(struct dvb_frontend *fe) 103 { 104 /* LNBP power off */ 105 lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF); 106 107 /* free data */ 108 kfree(fe->sec_priv); 109 fe->sec_priv = NULL; 110 } 111 112 static struct dvb_frontend *lnbx2x_attach(struct dvb_frontend *fe, 113 struct i2c_adapter *i2c, u8 override_set, 114 u8 override_clear, u8 i2c_addr, u8 config) 115 { 116 struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL); 117 if (!lnbp21) 118 return NULL; 119 120 /* default configuration */ 121 lnbp21->config = config; 122 lnbp21->i2c = i2c; 123 lnbp21->i2c_addr = i2c_addr; 124 fe->sec_priv = lnbp21; 125 126 /* bits which should be forced to '1' */ 127 lnbp21->override_or = override_set; 128 129 /* bits which should be forced to '0' */ 130 lnbp21->override_and = ~override_clear; 131 132 /* detect if it is present or not */ 133 if (lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF)) { 134 kfree(lnbp21); 135 return NULL; 136 } 137 138 /* install release callback */ 139 fe->ops.release_sec = lnbp21_release; 140 141 /* override frontend ops */ 142 fe->ops.set_voltage = lnbp21_set_voltage; 143 fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage; 144 if (!(override_clear & LNBH24_TEN)) /*22kHz logic controlled by demod*/ 145 fe->ops.set_tone = lnbp21_set_tone; 146 printk(KERN_INFO "LNBx2x attached on addr=%x\n", lnbp21->i2c_addr); 147 148 return fe; 149 } 150 151 struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, 152 struct i2c_adapter *i2c, u8 override_set, 153 u8 override_clear, u8 i2c_addr) 154 { 155 return lnbx2x_attach(fe, i2c, override_set, override_clear, 156 i2c_addr, LNBH24_TTX); 157 } 158 EXPORT_SYMBOL(lnbh24_attach); 159 160 struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, 161 struct i2c_adapter *i2c, u8 override_set, 162 u8 override_clear) 163 { 164 return lnbx2x_attach(fe, i2c, override_set, override_clear, 165 0x08, LNBP21_ISEL); 166 } 167 EXPORT_SYMBOL(lnbp21_attach); 168 169 MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp21, lnbh24"); 170 MODULE_AUTHOR("Oliver Endriss, Igor M. Liplianin"); 171 MODULE_LICENSE("GPL"); 172