Merge tag 'docs-5.15' of git://git.lwn.net/linux
[linux-2.6-microblaze.git] / drivers / media / dvb-frontends / isl6423.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3         Intersil ISL6423 SEC and LNB Power supply controller
4
5         Copyright (C) Manu Abraham <abraham.manu@gmail.com>
6
7 */
8
9 #include <linux/delay.h>
10 #include <linux/errno.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/string.h>
15 #include <linux/slab.h>
16
17 #include <media/dvb_frontend.h>
18 #include "isl6423.h"
19
20 static unsigned int verbose;
21 module_param(verbose, int, 0644);
22 MODULE_PARM_DESC(verbose, "Set Verbosity level");
23
24 #define FE_ERROR                                0
25 #define FE_NOTICE                               1
26 #define FE_INFO                                 2
27 #define FE_DEBUG                                3
28 #define FE_DEBUGREG                             4
29
30 #define dprintk(__y, __z, format, arg...) do {                                          \
31         if (__z) {                                                                      \
32                 if      ((verbose > FE_ERROR) && (verbose > __y))                       \
33                         printk(KERN_ERR "%s: " format "\n", __func__ , ##arg);          \
34                 else if ((verbose > FE_NOTICE) && (verbose > __y))                      \
35                         printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg);       \
36                 else if ((verbose > FE_INFO) && (verbose > __y))                        \
37                         printk(KERN_INFO "%s: " format "\n", __func__ , ##arg);         \
38                 else if ((verbose > FE_DEBUG) && (verbose > __y))                       \
39                         printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg);        \
40         } else {                                                                        \
41                 if (verbose > __y)                                                      \
42                         printk(format, ##arg);                                          \
43         }                                                                               \
44 } while (0)
45
46 struct isl6423_dev {
47         const struct isl6423_config     *config;
48         struct i2c_adapter              *i2c;
49
50         u8 reg_3;
51         u8 reg_4;
52
53         unsigned int verbose;
54 };
55
56 static int isl6423_write(struct isl6423_dev *isl6423, u8 reg)
57 {
58         struct i2c_adapter *i2c = isl6423->i2c;
59         u8 addr                 = isl6423->config->addr;
60         int err = 0;
61
62         struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = &reg, .len = 1 };
63
64         dprintk(FE_DEBUG, 1, "write reg %02X", reg);
65         err = i2c_transfer(i2c, &msg, 1);
66         if (err < 0)
67                 goto exit;
68         return 0;
69
70 exit:
71         dprintk(FE_ERROR, 1, "I/O error <%d>", err);
72         return err;
73 }
74
75 static int isl6423_set_modulation(struct dvb_frontend *fe)
76 {
77         struct isl6423_dev *isl6423             = (struct isl6423_dev *) fe->sec_priv;
78         const struct isl6423_config *config     = isl6423->config;
79         int err = 0;
80         u8 reg_2 = 0;
81
82         reg_2 = 0x01 << 5;
83
84         if (config->mod_extern)
85                 reg_2 |= (1 << 3);
86         else
87                 reg_2 |= (1 << 4);
88
89         err = isl6423_write(isl6423, reg_2);
90         if (err < 0)
91                 goto exit;
92         return 0;
93
94 exit:
95         dprintk(FE_ERROR, 1, "I/O error <%d>", err);
96         return err;
97 }
98
99 static int isl6423_voltage_boost(struct dvb_frontend *fe, long arg)
100 {
101         struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv;
102         u8 reg_3 = isl6423->reg_3;
103         u8 reg_4 = isl6423->reg_4;
104         int err = 0;
105
106         if (arg) {
107                 /* EN = 1, VSPEN = 1, VBOT = 1 */
108                 reg_4 |= (1 << 4);
109                 reg_4 |= 0x1;
110                 reg_3 |= (1 << 3);
111         } else {
112                 /* EN = 1, VSPEN = 1, VBOT = 0 */
113                 reg_4 |= (1 << 4);
114                 reg_4 &= ~0x1;
115                 reg_3 |= (1 << 3);
116         }
117         err = isl6423_write(isl6423, reg_3);
118         if (err < 0)
119                 goto exit;
120
121         err = isl6423_write(isl6423, reg_4);
122         if (err < 0)
123                 goto exit;
124
125         isl6423->reg_3 = reg_3;
126         isl6423->reg_4 = reg_4;
127
128         return 0;
129 exit:
130         dprintk(FE_ERROR, 1, "I/O error <%d>", err);
131         return err;
132 }
133
134
135 static int isl6423_set_voltage(struct dvb_frontend *fe,
136                                enum fe_sec_voltage voltage)
137 {
138         struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv;
139         u8 reg_3 = isl6423->reg_3;
140         u8 reg_4 = isl6423->reg_4;
141         int err = 0;
142
143         switch (voltage) {
144         case SEC_VOLTAGE_OFF:
145                 /* EN = 0 */
146                 reg_4 &= ~(1 << 4);
147                 break;
148
149         case SEC_VOLTAGE_13:
150                 /* EN = 1, VSPEN = 1, VTOP = 0, VBOT = 0 */
151                 reg_4 |= (1 << 4);
152                 reg_4 &= ~0x3;
153                 reg_3 |= (1 << 3);
154                 break;
155
156         case SEC_VOLTAGE_18:
157                 /* EN = 1, VSPEN = 1, VTOP = 1, VBOT = 0 */
158                 reg_4 |= (1 << 4);
159                 reg_4 |=  0x2;
160                 reg_4 &= ~0x1;
161                 reg_3 |= (1 << 3);
162                 break;
163
164         default:
165                 break;
166         }
167         err = isl6423_write(isl6423, reg_3);
168         if (err < 0)
169                 goto exit;
170
171         err = isl6423_write(isl6423, reg_4);
172         if (err < 0)
173                 goto exit;
174
175         isl6423->reg_3 = reg_3;
176         isl6423->reg_4 = reg_4;
177
178         return 0;
179 exit:
180         dprintk(FE_ERROR, 1, "I/O error <%d>", err);
181         return err;
182 }
183
184 static int isl6423_set_current(struct dvb_frontend *fe)
185 {
186         struct isl6423_dev *isl6423             = (struct isl6423_dev *) fe->sec_priv;
187         u8 reg_3 = isl6423->reg_3;
188         const struct isl6423_config *config     = isl6423->config;
189         int err = 0;
190
191         switch (config->current_max) {
192         case SEC_CURRENT_275m:
193                 /* 275mA */
194                 /* ISELH = 0, ISELL = 0 */
195                 reg_3 &= ~0x3;
196                 break;
197
198         case SEC_CURRENT_515m:
199                 /* 515mA */
200                 /* ISELH = 0, ISELL = 1 */
201                 reg_3 &= ~0x2;
202                 reg_3 |=  0x1;
203                 break;
204
205         case SEC_CURRENT_635m:
206                 /* 635mA */
207                 /* ISELH = 1, ISELL = 0 */
208                 reg_3 &= ~0x1;
209                 reg_3 |=  0x2;
210                 break;
211
212         case SEC_CURRENT_800m:
213                 /* 800mA */
214                 /* ISELH = 1, ISELL = 1 */
215                 reg_3 |= 0x3;
216                 break;
217         }
218
219         err = isl6423_write(isl6423, reg_3);
220         if (err < 0)
221                 goto exit;
222
223         switch (config->curlim) {
224         case SEC_CURRENT_LIM_ON:
225                 /* DCL = 0 */
226                 reg_3 &= ~0x10;
227                 break;
228
229         case SEC_CURRENT_LIM_OFF:
230                 /* DCL = 1 */
231                 reg_3 |= 0x10;
232                 break;
233         }
234
235         err = isl6423_write(isl6423, reg_3);
236         if (err < 0)
237                 goto exit;
238
239         isl6423->reg_3 = reg_3;
240
241         return 0;
242 exit:
243         dprintk(FE_ERROR, 1, "I/O error <%d>", err);
244         return err;
245 }
246
247 static void isl6423_release(struct dvb_frontend *fe)
248 {
249         isl6423_set_voltage(fe, SEC_VOLTAGE_OFF);
250
251         kfree(fe->sec_priv);
252         fe->sec_priv = NULL;
253 }
254
255 struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe,
256                                     struct i2c_adapter *i2c,
257                                     const struct isl6423_config *config)
258 {
259         struct isl6423_dev *isl6423;
260
261         isl6423 = kzalloc(sizeof(struct isl6423_dev), GFP_KERNEL);
262         if (!isl6423)
263                 return NULL;
264
265         isl6423->config = config;
266         isl6423->i2c    = i2c;
267         fe->sec_priv    = isl6423;
268
269         /* SR3H = 0, SR3M = 1, SR3L = 0 */
270         isl6423->reg_3 = 0x02 << 5;
271         /* SR4H = 0, SR4M = 1, SR4L = 1 */
272         isl6423->reg_4 = 0x03 << 5;
273
274         if (isl6423_set_current(fe))
275                 goto exit;
276
277         if (isl6423_set_modulation(fe))
278                 goto exit;
279
280         fe->ops.release_sec             = isl6423_release;
281         fe->ops.set_voltage             = isl6423_set_voltage;
282         fe->ops.enable_high_lnb_voltage = isl6423_voltage_boost;
283         isl6423->verbose                = verbose;
284
285         return fe;
286
287 exit:
288         kfree(isl6423);
289         fe->sec_priv = NULL;
290         return NULL;
291 }
292 EXPORT_SYMBOL(isl6423_attach);
293
294 MODULE_DESCRIPTION("ISL6423 SEC");
295 MODULE_AUTHOR("Manu Abraham");
296 MODULE_LICENSE("GPL");