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(adap: isl6405->i2c, msgs: &msg, num: 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(adap: isl6405->i2c, msgs: &msg, num: 1) == 1) ? 0 : -EIO; |
94 | } |
95 | |
96 | static void isl6405_release(struct dvb_frontend *fe) |
97 | { |
98 | /* power off */ |
99 | isl6405_set_voltage(fe, voltage: SEC_VOLTAGE_OFF); |
100 | |
101 | /* free */ |
102 | kfree(objp: 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(size: 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, voltage: SEC_VOLTAGE_OFF)) { |
130 | kfree(objp: 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_GPL(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 | |