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
29 static 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... */
38 static LIST_HEAD(hybrid_tuner_instance_list);
39 static DEFINE_MUTEX(au8522_list_mutex);
40
41 /* 16 bit registers, 8 bit values */
au8522_writereg(struct au8522_state * state,u16 reg,u8 data)42 int 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 }
58 EXPORT_SYMBOL(au8522_writereg);
59
au8522_readreg(struct au8522_state * state,u16 reg)60 u8 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 }
79 EXPORT_SYMBOL(au8522_readreg);
80
au8522_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)81 int 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 }
100 EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
101
au8522_analog_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)102 int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
103 {
104 struct au8522_state *state = fe->demodulator_priv;
105
106 dprintk("%s(%d)\n", __func__, enable);
107
108 if (enable)
109 return au8522_writereg(state, 0x106, 1);
110 else
111 return au8522_writereg(state, 0x106, 0);
112 }
113 EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl);
114
115 /* Reset the demod hardware and reset all of the configuration registers
116 to a default state. */
au8522_get_state(struct au8522_state ** state,struct i2c_adapter * i2c,u8 client_address)117 int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
118 u8 client_address)
119 {
120 int ret;
121
122 mutex_lock(&au8522_list_mutex);
123 ret = hybrid_tuner_request_state(struct au8522_state, (*state),
124 hybrid_tuner_instance_list,
125 i2c, client_address, "au8522");
126 mutex_unlock(&au8522_list_mutex);
127
128 return ret;
129 }
130 EXPORT_SYMBOL(au8522_get_state);
131
au8522_release_state(struct au8522_state * state)132 void au8522_release_state(struct au8522_state *state)
133 {
134 mutex_lock(&au8522_list_mutex);
135 if (state != NULL)
136 hybrid_tuner_release_state(state);
137 mutex_unlock(&au8522_list_mutex);
138 }
139 EXPORT_SYMBOL(au8522_release_state);
140
au8522_led_gpio_enable(struct au8522_state * state,int onoff)141 static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
142 {
143 struct au8522_led_config *led_config = state->config.led_cfg;
144 u8 val;
145
146 /* bail out if we can't control an LED */
147 if (!led_config || !led_config->gpio_output ||
148 !led_config->gpio_output_enable || !led_config->gpio_output_disable)
149 return 0;
150
151 val = au8522_readreg(state, 0x4000 |
152 (led_config->gpio_output & ~0xc000));
153 if (onoff) {
154 /* enable GPIO output */
155 val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
156 val |= (led_config->gpio_output_enable & 0xff);
157 } else {
158 /* disable GPIO output */
159 val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
160 val |= (led_config->gpio_output_disable & 0xff);
161 }
162 return au8522_writereg(state, 0x8000 |
163 (led_config->gpio_output & ~0xc000), val);
164 }
165
166 /* led = 0 | off
167 * led = 1 | signal ok
168 * led = 2 | signal strong
169 * led < 0 | only light led if leds are currently off
170 */
au8522_led_ctrl(struct au8522_state * state,int led)171 int au8522_led_ctrl(struct au8522_state *state, int led)
172 {
173 struct au8522_led_config *led_config = state->config.led_cfg;
174 int i, ret = 0;
175
176 /* bail out if we can't control an LED */
177 if (!led_config || !led_config->gpio_leds ||
178 !led_config->num_led_states || !led_config->led_states)
179 return 0;
180
181 if (led < 0) {
182 /* if LED is already lit, then leave it as-is */
183 if (state->led_state)
184 return 0;
185 else
186 led *= -1;
187 }
188
189 /* toggle LED if changing state */
190 if (state->led_state != led) {
191 u8 val;
192
193 dprintk("%s: %d\n", __func__, led);
194
195 au8522_led_gpio_enable(state, 1);
196
197 val = au8522_readreg(state, 0x4000 |
198 (led_config->gpio_leds & ~0xc000));
199
200 /* start with all leds off */
201 for (i = 0; i < led_config->num_led_states; i++)
202 val &= ~led_config->led_states[i];
203
204 /* set selected LED state */
205 if (led < led_config->num_led_states)
206 val |= led_config->led_states[led];
207 else if (led_config->num_led_states)
208 val |=
209 led_config->led_states[led_config->num_led_states - 1];
210
211 ret = au8522_writereg(state, 0x8000 |
212 (led_config->gpio_leds & ~0xc000), val);
213 if (ret < 0)
214 return ret;
215
216 state->led_state = led;
217
218 if (led == 0)
219 au8522_led_gpio_enable(state, 0);
220 }
221
222 return 0;
223 }
224 EXPORT_SYMBOL(au8522_led_ctrl);
225
au8522_init(struct dvb_frontend * fe)226 int au8522_init(struct dvb_frontend *fe)
227 {
228 struct au8522_state *state = fe->demodulator_priv;
229 dprintk("%s()\n", __func__);
230
231 state->operational_mode = AU8522_DIGITAL_MODE;
232
233 /* Clear out any state associated with the digital side of the
234 chip, so that when it gets powered back up it won't think
235 that it is already tuned */
236 state->current_frequency = 0;
237
238 au8522_writereg(state, 0xa4, 1 << 5);
239
240 au8522_i2c_gate_ctrl(fe, 1);
241
242 return 0;
243 }
244 EXPORT_SYMBOL(au8522_init);
245
au8522_sleep(struct dvb_frontend * fe)246 int au8522_sleep(struct dvb_frontend *fe)
247 {
248 struct au8522_state *state = fe->demodulator_priv;
249 dprintk("%s()\n", __func__);
250
251 /* Only power down if the digital side is currently using the chip */
252 if (state->operational_mode == AU8522_ANALOG_MODE) {
253 /* We're not in one of the expected power modes, which means
254 that the DVB thread is probably telling us to go to sleep
255 even though the analog frontend has already started using
256 the chip. So ignore the request */
257 return 0;
258 }
259
260 /* turn off led */
261 au8522_led_ctrl(state, 0);
262
263 /* Power down the chip */
264 au8522_writereg(state, 0xa4, 1 << 5);
265
266 state->current_frequency = 0;
267
268 return 0;
269 }
270 EXPORT_SYMBOL(au8522_sleep);
271
272 module_param(debug, int, 0644);
273 MODULE_PARM_DESC(debug, "Enable verbose debug messages");
274
275 MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
276 MODULE_AUTHOR("Steven Toth");
277 MODULE_LICENSE("GPL");
278