blob: 3559ff230045450ad885bf8e118aaa27a80c50df [file] [log] [blame]
Michael Krufkyb31506c2012-01-27 13:18:29 -03001/*
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
Michael Krufkyb31506c2012-01-27 13:18:29 -030029static 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
Devin Heitmueller4a03daf2012-08-06 22:47:02 -0300102int 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}
113EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl);
114
Michael Krufkyb31506c2012-01-27 13:18:29 -0300115/* Reset the demod hardware and reset all of the configuration registers
116 to a default state. */
117int 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}
130EXPORT_SYMBOL(au8522_get_state);
131
132void 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}
139EXPORT_SYMBOL(au8522_release_state);
140
Michael Krufky52dbb572012-04-09 18:51:08 -0300141static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
Michael Krufkyb31506c2012-01-27 13:18:29 -0300142{
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}
Michael Krufkyb31506c2012-01-27 13:18:29 -0300165
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 */
171int 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}
224EXPORT_SYMBOL(au8522_led_ctrl);
225
226int 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}
244EXPORT_SYMBOL(au8522_init);
245
246int 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}
270EXPORT_SYMBOL(au8522_sleep);
Devin Heitmuellerd6b82672012-08-06 22:47:00 -0300271
272module_param(debug, int, 0644);
273MODULE_PARM_DESC(debug, "Enable verbose debug messages");
274
275MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
276MODULE_AUTHOR("Steven Toth");
277MODULE_LICENSE("GPL");