aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb/frontends/au8522_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/dvb/frontends/au8522_common.c')
-rw-r--r--drivers/media/dvb/frontends/au8522_common.c259
1 files changed, 259 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/au8522_common.c b/drivers/media/dvb/frontends/au8522_common.c
new file mode 100644
index 000000000000..5cfe151ee394
--- /dev/null
+++ b/drivers/media/dvb/frontends/au8522_common.c
@@ -0,0 +1,259 @@
1/*
2 Auvitek AU8522 QAM/8VSB demodulator driver
3
4 Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
5 Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
6 Copyright (C) 2005-2008 Auvitek International, Ltd.
7 Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23*/
24
25#include <linux/i2c.h>
26#include "dvb_frontend.h"
27#include "au8522_priv.h"
28
29MODULE_LICENSE("GPL");
30
31static int debug;
32
33#define dprintk(arg...)\
34 do { if (debug)\
35 printk(arg);\
36 } while (0)
37
38/* Despite the name "hybrid_tuner", the framework works just as well for
39 hybrid demodulators as well... */
40static LIST_HEAD(hybrid_tuner_instance_list);
41static DEFINE_MUTEX(au8522_list_mutex);
42
43/* 16 bit registers, 8 bit values */
44int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
45{
46 int ret;
47 u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
48
49 struct i2c_msg msg = { .addr = state->config->demod_address,
50 .flags = 0, .buf = buf, .len = 3 };
51
52 ret = i2c_transfer(state->i2c, &msg, 1);
53
54 if (ret != 1)
55 printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
56 "ret == %i)\n", __func__, reg, data, ret);
57
58 return (ret != 1) ? -1 : 0;
59}
60EXPORT_SYMBOL(au8522_writereg);
61
62u8 au8522_readreg(struct au8522_state *state, u16 reg)
63{
64 int ret;
65 u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
66 u8 b1[] = { 0 };
67
68 struct i2c_msg msg[] = {
69 { .addr = state->config->demod_address, .flags = 0,
70 .buf = b0, .len = 2 },
71 { .addr = state->config->demod_address, .flags = I2C_M_RD,
72 .buf = b1, .len = 1 } };
73
74 ret = i2c_transfer(state->i2c, msg, 2);
75
76 if (ret != 2)
77 printk(KERN_ERR "%s: readreg error (ret == %i)\n",
78 __func__, ret);
79 return b1[0];
80}
81EXPORT_SYMBOL(au8522_readreg);
82
83int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
84{
85 struct au8522_state *state = fe->demodulator_priv;
86
87 dprintk("%s(%d)\n", __func__, enable);
88
89 if (state->operational_mode == AU8522_ANALOG_MODE) {
90 /* We're being asked to manage the gate even though we're
91 not in digital mode. This can occur if we get switched
92 over to analog mode before the dvb_frontend kernel thread
93 has completely shutdown */
94 return 0;
95 }
96
97 if (enable)
98 return au8522_writereg(state, 0x106, 1);
99 else
100 return au8522_writereg(state, 0x106, 0);
101}
102EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
103
104/* Reset the demod hardware and reset all of the configuration registers
105 to a default state. */
106int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
107 u8 client_address)
108{
109 int ret;
110
111 mutex_lock(&au8522_list_mutex);
112 ret = hybrid_tuner_request_state(struct au8522_state, (*state),
113 hybrid_tuner_instance_list,
114 i2c, client_address, "au8522");
115 mutex_unlock(&au8522_list_mutex);
116
117 return ret;
118}
119EXPORT_SYMBOL(au8522_get_state);
120
121void au8522_release_state(struct au8522_state *state)
122{
123 mutex_lock(&au8522_list_mutex);
124 if (state != NULL)
125 hybrid_tuner_release_state(state);
126 mutex_unlock(&au8522_list_mutex);
127}
128EXPORT_SYMBOL(au8522_release_state);
129
130static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
131{
132 struct au8522_led_config *led_config = state->config->led_cfg;
133 u8 val;
134
135 /* bail out if we can't control an LED */
136 if (!led_config || !led_config->gpio_output ||
137 !led_config->gpio_output_enable || !led_config->gpio_output_disable)
138 return 0;
139
140 val = au8522_readreg(state, 0x4000 |
141 (led_config->gpio_output & ~0xc000));
142 if (onoff) {
143 /* enable GPIO output */
144 val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
145 val |= (led_config->gpio_output_enable & 0xff);
146 } else {
147 /* disable GPIO output */
148 val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
149 val |= (led_config->gpio_output_disable & 0xff);
150 }
151 return au8522_writereg(state, 0x8000 |
152 (led_config->gpio_output & ~0xc000), val);
153}
154
155/* led = 0 | off
156 * led = 1 | signal ok
157 * led = 2 | signal strong
158 * led < 0 | only light led if leds are currently off
159 */
160int au8522_led_ctrl(struct au8522_state *state, int led)
161{
162 struct au8522_led_config *led_config = state->config->led_cfg;
163 int i, ret = 0;
164
165 /* bail out if we can't control an LED */
166 if (!led_config || !led_config->gpio_leds ||
167 !led_config->num_led_states || !led_config->led_states)
168 return 0;
169
170 if (led < 0) {
171 /* if LED is already lit, then leave it as-is */
172 if (state->led_state)
173 return 0;
174 else
175 led *= -1;
176 }
177
178 /* toggle LED if changing state */
179 if (state->led_state != led) {
180 u8 val;
181
182 dprintk("%s: %d\n", __func__, led);
183
184 au8522_led_gpio_enable(state, 1);
185
186 val = au8522_readreg(state, 0x4000 |
187 (led_config->gpio_leds & ~0xc000));
188
189 /* start with all leds off */
190 for (i = 0; i < led_config->num_led_states; i++)
191 val &= ~led_config->led_states[i];
192
193 /* set selected LED state */
194 if (led < led_config->num_led_states)
195 val |= led_config->led_states[led];
196 else if (led_config->num_led_states)
197 val |=
198 led_config->led_states[led_config->num_led_states - 1];
199
200 ret = au8522_writereg(state, 0x8000 |
201 (led_config->gpio_leds & ~0xc000), val);
202 if (ret < 0)
203 return ret;
204
205 state->led_state = led;
206
207 if (led == 0)
208 au8522_led_gpio_enable(state, 0);
209 }
210
211 return 0;
212}
213EXPORT_SYMBOL(au8522_led_ctrl);
214
215int au8522_init(struct dvb_frontend *fe)
216{
217 struct au8522_state *state = fe->demodulator_priv;
218 dprintk("%s()\n", __func__);
219
220 state->operational_mode = AU8522_DIGITAL_MODE;
221
222 /* Clear out any state associated with the digital side of the
223 chip, so that when it gets powered back up it won't think
224 that it is already tuned */
225 state->current_frequency = 0;
226
227 au8522_writereg(state, 0xa4, 1 << 5);
228
229 au8522_i2c_gate_ctrl(fe, 1);
230
231 return 0;
232}
233EXPORT_SYMBOL(au8522_init);
234
235int au8522_sleep(struct dvb_frontend *fe)
236{
237 struct au8522_state *state = fe->demodulator_priv;
238 dprintk("%s()\n", __func__);
239
240 /* Only power down if the digital side is currently using the chip */
241 if (state->operational_mode == AU8522_ANALOG_MODE) {
242 /* We're not in one of the expected power modes, which means
243 that the DVB thread is probably telling us to go to sleep
244 even though the analog frontend has already started using
245 the chip. So ignore the request */
246 return 0;
247 }
248
249 /* turn off led */
250 au8522_led_ctrl(state, 0);
251
252 /* Power down the chip */
253 au8522_writereg(state, 0xa4, 1 << 5);
254
255 state->current_frequency = 0;
256
257 return 0;
258}
259EXPORT_SYMBOL(au8522_sleep);