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