aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb/frontends/au8522_common.c
diff options
context:
space:
mode:
authorMichael Krufky <mkrufky@kernellabs.com>2012-01-27 11:18:29 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-04-19 08:49:06 -0400
commitb31506c47c5ae67a82fa72e6d763a8f34413aac8 (patch)
treee624f86b51079252a8f6b7617ceb5c2c80a1bcd9 /drivers/media/dvb/frontends/au8522_common.c
parentbe183dc3f73d7e8e0091c54fc3fa04d9ccb91903 (diff)
[media] au8522: build ATV/DTV demodulators as separate modules
au8522_dig.o and au8522_decoder.o function independentantly of each other, each for a different hardware function using a different software subsystem api, each with its own set of subsystem module dependencies. Since these drivers do not depend on each other, and it is in fact possible to build hardware designs using one function and not the other, lets split this module into two, allowing system integrators to enable the hardware without dragging in undesired dependencies. Signed-off-by: Michael Krufky <mkrufky@kernellabs.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/dvb/frontends/au8522_common.c')
-rw-r--r--drivers/media/dvb/frontends/au8522_common.c258
1 files changed, 258 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..befdff919a89
--- /dev/null
+++ b/drivers/media/dvb/frontends/au8522_common.c
@@ -0,0 +1,258 @@
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
29static int debug;
30
31#define dprintk(arg...)\
32 do { if (debug)\
33 printk(arg);\
34 } while (0)
35
36/* Despite the name "hybrid_tuner", the framework works just as well for
37 hybrid demodulators as well... */
38static LIST_HEAD(hybrid_tuner_instance_list);
39static DEFINE_MUTEX(au8522_list_mutex);
40
41/* 16 bit registers, 8 bit values */
42int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
43{
44 int ret;
45 u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
46
47 struct i2c_msg msg = { .addr = state->config->demod_address,
48 .flags = 0, .buf = buf, .len = 3 };
49
50 ret = i2c_transfer(state->i2c, &msg, 1);
51
52 if (ret != 1)
53 printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
54 "ret == %i)\n", __func__, reg, data, ret);
55
56 return (ret != 1) ? -1 : 0;
57}
58EXPORT_SYMBOL(au8522_writereg);
59
60u8 au8522_readreg(struct au8522_state *state, u16 reg)
61{
62 int ret;
63 u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
64 u8 b1[] = { 0 };
65
66 struct i2c_msg msg[] = {
67 { .addr = state->config->demod_address, .flags = 0,
68 .buf = b0, .len = 2 },
69 { .addr = state->config->demod_address, .flags = I2C_M_RD,
70 .buf = b1, .len = 1 } };
71
72 ret = i2c_transfer(state->i2c, msg, 2);
73
74 if (ret != 2)
75 printk(KERN_ERR "%s: readreg error (ret == %i)\n",
76 __func__, ret);
77 return b1[0];
78}
79EXPORT_SYMBOL(au8522_readreg);
80
81int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
82{
83 struct au8522_state *state = fe->demodulator_priv;
84
85 dprintk("%s(%d)\n", __func__, enable);
86
87 if (state->operational_mode == AU8522_ANALOG_MODE) {
88 /* We're being asked to manage the gate even though we're
89 not in digital mode. This can occur if we get switched
90 over to analog mode before the dvb_frontend kernel thread
91 has completely shutdown */
92 return 0;
93 }
94
95 if (enable)
96 return au8522_writereg(state, 0x106, 1);
97 else
98 return au8522_writereg(state, 0x106, 0);
99}
100EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
101
102/* Reset the demod hardware and reset all of the configuration registers
103 to a default state. */
104int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
105 u8 client_address)
106{
107 int ret;
108
109 mutex_lock(&au8522_list_mutex);
110 ret = hybrid_tuner_request_state(struct au8522_state, (*state),
111 hybrid_tuner_instance_list,
112 i2c, client_address, "au8522");
113 mutex_unlock(&au8522_list_mutex);
114
115 return ret;
116}
117EXPORT_SYMBOL(au8522_get_state);
118
119void au8522_release_state(struct au8522_state *state)
120{
121 mutex_lock(&au8522_list_mutex);
122 if (state != NULL)
123 hybrid_tuner_release_state(state);
124 mutex_unlock(&au8522_list_mutex);
125}
126EXPORT_SYMBOL(au8522_release_state);
127
128int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
129{
130 struct au8522_led_config *led_config = state->config->led_cfg;
131 u8 val;
132
133 /* bail out if we can't control an LED */
134 if (!led_config || !led_config->gpio_output ||
135 !led_config->gpio_output_enable || !led_config->gpio_output_disable)
136 return 0;
137
138 val = au8522_readreg(state, 0x4000 |
139 (led_config->gpio_output & ~0xc000));
140 if (onoff) {
141 /* enable GPIO output */
142 val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
143 val |= (led_config->gpio_output_enable & 0xff);
144 } else {
145 /* disable GPIO output */
146 val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
147 val |= (led_config->gpio_output_disable & 0xff);
148 }
149 return au8522_writereg(state, 0x8000 |
150 (led_config->gpio_output & ~0xc000), val);
151}
152EXPORT_SYMBOL(au8522_led_gpio_enable);
153
154/* led = 0 | off
155 * led = 1 | signal ok
156 * led = 2 | signal strong
157 * led < 0 | only light led if leds are currently off
158 */
159int au8522_led_ctrl(struct au8522_state *state, int led)
160{
161 struct au8522_led_config *led_config = state->config->led_cfg;
162 int i, ret = 0;
163
164 /* bail out if we can't control an LED */
165 if (!led_config || !led_config->gpio_leds ||
166 !led_config->num_led_states || !led_config->led_states)
167 return 0;
168
169 if (led < 0) {
170 /* if LED is already lit, then leave it as-is */
171 if (state->led_state)
172 return 0;
173 else
174 led *= -1;
175 }
176
177 /* toggle LED if changing state */
178 if (state->led_state != led) {
179 u8 val;
180
181 dprintk("%s: %d\n", __func__, led);
182
183 au8522_led_gpio_enable(state, 1);
184
185 val = au8522_readreg(state, 0x4000 |
186 (led_config->gpio_leds & ~0xc000));
187
188 /* start with all leds off */
189 for (i = 0; i < led_config->num_led_states; i++)
190 val &= ~led_config->led_states[i];
191
192 /* set selected LED state */
193 if (led < led_config->num_led_states)
194 val |= led_config->led_states[led];
195 else if (led_config->num_led_states)
196 val |=
197 led_config->led_states[led_config->num_led_states - 1];
198
199 ret = au8522_writereg(state, 0x8000 |
200 (led_config->gpio_leds & ~0xc000), val);
201 if (ret < 0)
202 return ret;
203
204 state->led_state = led;
205
206 if (led == 0)
207 au8522_led_gpio_enable(state, 0);
208 }
209
210 return 0;
211}
212EXPORT_SYMBOL(au8522_led_ctrl);
213
214int au8522_init(struct dvb_frontend *fe)
215{
216 struct au8522_state *state = fe->demodulator_priv;
217 dprintk("%s()\n", __func__);
218
219 state->operational_mode = AU8522_DIGITAL_MODE;
220
221 /* Clear out any state associated with the digital side of the
222 chip, so that when it gets powered back up it won't think
223 that it is already tuned */
224 state->current_frequency = 0;
225
226 au8522_writereg(state, 0xa4, 1 << 5);
227
228 au8522_i2c_gate_ctrl(fe, 1);
229
230 return 0;
231}
232EXPORT_SYMBOL(au8522_init);
233
234int au8522_sleep(struct dvb_frontend *fe)
235{
236 struct au8522_state *state = fe->demodulator_priv;
237 dprintk("%s()\n", __func__);
238
239 /* Only power down if the digital side is currently using the chip */
240 if (state->operational_mode == AU8522_ANALOG_MODE) {
241 /* We're not in one of the expected power modes, which means
242 that the DVB thread is probably telling us to go to sleep
243 even though the analog frontend has already started using
244 the chip. So ignore the request */
245 return 0;
246 }
247
248 /* turn off led */
249 au8522_led_ctrl(state, 0);
250
251 /* Power down the chip */
252 au8522_writereg(state, 0xa4, 1 << 5);
253
254 state->current_frequency = 0;
255
256 return 0;
257}
258EXPORT_SYMBOL(au8522_sleep);