xref: /linux/drivers/media/dvb-frontends/au8522_common.c (revision e2be04c7f9958dde770eeb8b30e829ca969b37bb)
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 */
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, ret == %i)\n",
54 		       __func__, reg, data, ret);
55 
56 	return (ret != 1) ? -1 : 0;
57 }
58 EXPORT_SYMBOL(au8522_writereg);
59 
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 
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 
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. */
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 
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 
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  */
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 
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 	state->current_modulation = VSB_8;
238 
239 	au8522_writereg(state, 0xa4, 1 << 5);
240 
241 	au8522_i2c_gate_ctrl(fe, 1);
242 
243 	return 0;
244 }
245 EXPORT_SYMBOL(au8522_init);
246 
247 int au8522_sleep(struct dvb_frontend *fe)
248 {
249 	struct au8522_state *state = fe->demodulator_priv;
250 	dprintk("%s()\n", __func__);
251 
252 	/* Only power down if the digital side is currently using the chip */
253 	if (state->operational_mode == AU8522_ANALOG_MODE) {
254 		/* We're not in one of the expected power modes, which means
255 		   that the DVB thread is probably telling us to go to sleep
256 		   even though the analog frontend has already started using
257 		   the chip.  So ignore the request */
258 		return 0;
259 	}
260 
261 	/* turn off led */
262 	au8522_led_ctrl(state, 0);
263 
264 	/* Power down the chip */
265 	au8522_writereg(state, 0xa4, 1 << 5);
266 
267 	state->current_frequency = 0;
268 
269 	return 0;
270 }
271 EXPORT_SYMBOL(au8522_sleep);
272 
273 module_param(debug, int, 0644);
274 MODULE_PARM_DESC(debug, "Enable verbose debug messages");
275 
276 MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
277 MODULE_AUTHOR("Steven Toth");
278 MODULE_LICENSE("GPL");
279