Merge branch 'x86/mm' into core/percpu
[linux-2.6] / drivers / media / dvb / frontends / isl6405.c
1 /*
2  * isl6405.c - driver for dual lnb supply and control ic ISL6405
3  *
4  * Copyright (C) 2008 Hartmut Hackmann
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  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
23  *
24  *
25  * the project's page is at http://www.linuxtv.org
26  */
27 #include <linux/delay.h>
28 #include <linux/errno.h>
29 #include <linux/init.h>
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/string.h>
33 #include <linux/slab.h>
34
35 #include "dvb_frontend.h"
36 #include "isl6405.h"
37
38 struct isl6405 {
39         u8                      config;
40         u8                      override_or;
41         u8                      override_and;
42         struct i2c_adapter      *i2c;
43         u8                      i2c_addr;
44 };
45
46 static int isl6405_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
47 {
48         struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv;
49         struct i2c_msg msg = {  .addr = isl6405->i2c_addr, .flags = 0,
50                                 .buf = &isl6405->config,
51                                 .len = sizeof(isl6405->config) };
52
53         if (isl6405->override_or & 0x80) {
54                 isl6405->config &= ~(ISL6405_VSEL2 | ISL6405_EN2);
55                 switch (voltage) {
56                 case SEC_VOLTAGE_OFF:
57                         break;
58                 case SEC_VOLTAGE_13:
59                         isl6405->config |= ISL6405_EN2;
60                         break;
61                 case SEC_VOLTAGE_18:
62                         isl6405->config |= (ISL6405_EN2 | ISL6405_VSEL2);
63                         break;
64                 default:
65                         return -EINVAL;
66                 }
67         } else {
68                 isl6405->config &= ~(ISL6405_VSEL1 | ISL6405_EN1);
69                 switch (voltage) {
70                 case SEC_VOLTAGE_OFF:
71                         break;
72                 case SEC_VOLTAGE_13:
73                         isl6405->config |= ISL6405_EN1;
74                         break;
75                 case SEC_VOLTAGE_18:
76                         isl6405->config |= (ISL6405_EN1 | ISL6405_VSEL1);
77                         break;
78                 default:
79                         return -EINVAL;
80                 };
81         }
82         isl6405->config |= isl6405->override_or;
83         isl6405->config &= isl6405->override_and;
84
85         return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO;
86 }
87
88 static int isl6405_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
89 {
90         struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv;
91         struct i2c_msg msg = {  .addr = isl6405->i2c_addr, .flags = 0,
92                                 .buf = &isl6405->config,
93                                 .len = sizeof(isl6405->config) };
94
95         if (isl6405->override_or & 0x80) {
96                 if (arg)
97                         isl6405->config |= ISL6405_LLC2;
98                 else
99                         isl6405->config &= ~ISL6405_LLC2;
100         } else {
101                 if (arg)
102                         isl6405->config |= ISL6405_LLC1;
103                 else
104                         isl6405->config &= ~ISL6405_LLC1;
105         }
106         isl6405->config |= isl6405->override_or;
107         isl6405->config &= isl6405->override_and;
108
109         return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO;
110 }
111
112 static void isl6405_release(struct dvb_frontend *fe)
113 {
114         /* power off */
115         isl6405_set_voltage(fe, SEC_VOLTAGE_OFF);
116
117         /* free */
118         kfree(fe->sec_priv);
119         fe->sec_priv = NULL;
120 }
121
122 struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c,
123                                     u8 i2c_addr, u8 override_set, u8 override_clear)
124 {
125         struct isl6405 *isl6405 = kmalloc(sizeof(struct isl6405), GFP_KERNEL);
126         if (!isl6405)
127                 return NULL;
128
129         /* default configuration */
130         if (override_set & 0x80)
131                 isl6405->config = ISL6405_ISEL2;
132         else
133                 isl6405->config = ISL6405_ISEL1;
134         isl6405->i2c = i2c;
135         isl6405->i2c_addr = i2c_addr;
136         fe->sec_priv = isl6405;
137
138         /* bits which should be forced to '1' */
139         isl6405->override_or = override_set;
140
141         /* bits which should be forced to '0' */
142         isl6405->override_and = ~override_clear;
143
144         /* detect if it is present or not */
145         if (isl6405_set_voltage(fe, SEC_VOLTAGE_OFF)) {
146                 kfree(isl6405);
147                 fe->sec_priv = NULL;
148                 return NULL;
149         }
150
151         /* install release callback */
152         fe->ops.release_sec = isl6405_release;
153
154         /* override frontend ops */
155         fe->ops.set_voltage = isl6405_set_voltage;
156         fe->ops.enable_high_lnb_voltage = isl6405_enable_high_lnb_voltage;
157
158         return fe;
159 }
160 EXPORT_SYMBOL(isl6405_attach);
161
162 MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6405");
163 MODULE_AUTHOR("Hartmut Hackmann & Oliver Endriss");
164 MODULE_LICENSE("GPL");