xref: /linux/drivers/media/dvb-frontends/au8522_common.c (revision ebf68996de0ab250c5d520eb2291ab65643e9a1e)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3     Auvitek AU8522 QAM/8VSB demodulator driver
4 
5     Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
6     Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
7     Copyright (C) 2005-2008 Auvitek International, Ltd.
8     Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org>
9 
10 
11 */
12 
13 #include <linux/i2c.h>
14 #include <media/dvb_frontend.h>
15 #include "au8522_priv.h"
16 
17 static int debug;
18 
19 #define dprintk(arg...)\
20   do { if (debug)\
21 	 printk(arg);\
22   } while (0)
23 
24 /* Despite the name "hybrid_tuner", the framework works just as well for
25    hybrid demodulators as well... */
26 static LIST_HEAD(hybrid_tuner_instance_list);
27 static DEFINE_MUTEX(au8522_list_mutex);
28 
29 /* 16 bit registers, 8 bit values */
30 int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
31 {
32 	int ret;
33 	u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
34 
35 	struct i2c_msg msg = { .addr = state->config.demod_address,
36 			       .flags = 0, .buf = buf, .len = 3 };
37 
38 	ret = i2c_transfer(state->i2c, &msg, 1);
39 
40 	if (ret != 1)
41 		printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
42 		       __func__, reg, data, ret);
43 
44 	return (ret != 1) ? -1 : 0;
45 }
46 EXPORT_SYMBOL(au8522_writereg);
47 
48 u8 au8522_readreg(struct au8522_state *state, u16 reg)
49 {
50 	int ret;
51 	u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
52 	u8 b1[] = { 0 };
53 
54 	struct i2c_msg msg[] = {
55 		{ .addr = state->config.demod_address, .flags = 0,
56 		  .buf = b0, .len = 2 },
57 		{ .addr = state->config.demod_address, .flags = I2C_M_RD,
58 		  .buf = b1, .len = 1 } };
59 
60 	ret = i2c_transfer(state->i2c, msg, 2);
61 
62 	if (ret != 2)
63 		printk(KERN_ERR "%s: readreg error (ret == %i)\n",
64 		       __func__, ret);
65 	return b1[0];
66 }
67 EXPORT_SYMBOL(au8522_readreg);
68 
69 int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
70 {
71 	struct au8522_state *state = fe->demodulator_priv;
72 
73 	dprintk("%s(%d)\n", __func__, enable);
74 
75 	if (state->operational_mode == AU8522_ANALOG_MODE) {
76 		/* We're being asked to manage the gate even though we're
77 		   not in digital mode.  This can occur if we get switched
78 		   over to analog mode before the dvb_frontend kernel thread
79 		   has completely shutdown */
80 		return 0;
81 	}
82 
83 	if (enable)
84 		return au8522_writereg(state, 0x106, 1);
85 	else
86 		return au8522_writereg(state, 0x106, 0);
87 }
88 EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
89 
90 int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
91 {
92 	struct au8522_state *state = fe->demodulator_priv;
93 
94 	dprintk("%s(%d)\n", __func__, enable);
95 
96 	if (enable)
97 		return au8522_writereg(state, 0x106, 1);
98 	else
99 		return au8522_writereg(state, 0x106, 0);
100 }
101 EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl);
102 
103 /* Reset the demod hardware and reset all of the configuration registers
104    to a default state. */
105 int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
106 		     u8 client_address)
107 {
108 	int ret;
109 
110 	mutex_lock(&au8522_list_mutex);
111 	ret = hybrid_tuner_request_state(struct au8522_state, (*state),
112 					 hybrid_tuner_instance_list,
113 					 i2c, client_address, "au8522");
114 	mutex_unlock(&au8522_list_mutex);
115 
116 	return ret;
117 }
118 EXPORT_SYMBOL(au8522_get_state);
119 
120 void au8522_release_state(struct au8522_state *state)
121 {
122 	mutex_lock(&au8522_list_mutex);
123 	if (state != NULL)
124 		hybrid_tuner_release_state(state);
125 	mutex_unlock(&au8522_list_mutex);
126 }
127 EXPORT_SYMBOL(au8522_release_state);
128 
129 static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
130 {
131 	struct au8522_led_config *led_config = state->config.led_cfg;
132 	u8 val;
133 
134 	/* bail out if we can't control an LED */
135 	if (!led_config || !led_config->gpio_output ||
136 	    !led_config->gpio_output_enable || !led_config->gpio_output_disable)
137 		return 0;
138 
139 	val = au8522_readreg(state, 0x4000 |
140 			     (led_config->gpio_output & ~0xc000));
141 	if (onoff) {
142 		/* enable GPIO output */
143 		val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
144 		val |=  (led_config->gpio_output_enable & 0xff);
145 	} else {
146 		/* disable GPIO output */
147 		val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
148 		val |=  (led_config->gpio_output_disable & 0xff);
149 	}
150 	return au8522_writereg(state, 0x8000 |
151 			       (led_config->gpio_output & ~0xc000), val);
152 }
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  */
159 int 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 }
212 EXPORT_SYMBOL(au8522_led_ctrl);
213 
214 int 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 	state->current_modulation = VSB_8;
226 
227 	au8522_writereg(state, 0xa4, 1 << 5);
228 
229 	au8522_i2c_gate_ctrl(fe, 1);
230 
231 	return 0;
232 }
233 EXPORT_SYMBOL(au8522_init);
234 
235 int 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 }
259 EXPORT_SYMBOL(au8522_sleep);
260 
261 module_param(debug, int, 0644);
262 MODULE_PARM_DESC(debug, "Enable verbose debug messages");
263 
264 MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
265 MODULE_AUTHOR("Steven Toth");
266 MODULE_LICENSE("GPL");
267