xref: /OK3568_Linux_fs/kernel/drivers/media/dvb-frontends/au8522_common.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun     Auvitek AU8522 QAM/8VSB demodulator driver
4*4882a593Smuzhiyun 
5*4882a593Smuzhiyun     Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
6*4882a593Smuzhiyun     Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
7*4882a593Smuzhiyun     Copyright (C) 2005-2008 Auvitek International, Ltd.
8*4882a593Smuzhiyun     Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org>
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/i2c.h>
14*4882a593Smuzhiyun #include <media/dvb_frontend.h>
15*4882a593Smuzhiyun #include "au8522_priv.h"
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun static int debug;
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #define dprintk(arg...)\
20*4882a593Smuzhiyun   do { if (debug)\
21*4882a593Smuzhiyun 	 printk(arg);\
22*4882a593Smuzhiyun   } while (0)
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun /* Despite the name "hybrid_tuner", the framework works just as well for
25*4882a593Smuzhiyun    hybrid demodulators as well... */
26*4882a593Smuzhiyun static LIST_HEAD(hybrid_tuner_instance_list);
27*4882a593Smuzhiyun static DEFINE_MUTEX(au8522_list_mutex);
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun /* 16 bit registers, 8 bit values */
au8522_writereg(struct au8522_state * state,u16 reg,u8 data)30*4882a593Smuzhiyun int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	int ret;
33*4882a593Smuzhiyun 	u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun 	struct i2c_msg msg = { .addr = state->config.demod_address,
36*4882a593Smuzhiyun 			       .flags = 0, .buf = buf, .len = 3 };
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	ret = i2c_transfer(state->i2c, &msg, 1);
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	if (ret != 1)
41*4882a593Smuzhiyun 		printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
42*4882a593Smuzhiyun 		       __func__, reg, data, ret);
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	return (ret != 1) ? -1 : 0;
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun EXPORT_SYMBOL(au8522_writereg);
47*4882a593Smuzhiyun 
au8522_readreg(struct au8522_state * state,u16 reg)48*4882a593Smuzhiyun u8 au8522_readreg(struct au8522_state *state, u16 reg)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	int ret;
51*4882a593Smuzhiyun 	u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
52*4882a593Smuzhiyun 	u8 b1[] = { 0 };
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	struct i2c_msg msg[] = {
55*4882a593Smuzhiyun 		{ .addr = state->config.demod_address, .flags = 0,
56*4882a593Smuzhiyun 		  .buf = b0, .len = 2 },
57*4882a593Smuzhiyun 		{ .addr = state->config.demod_address, .flags = I2C_M_RD,
58*4882a593Smuzhiyun 		  .buf = b1, .len = 1 } };
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	ret = i2c_transfer(state->i2c, msg, 2);
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	if (ret != 2)
63*4882a593Smuzhiyun 		printk(KERN_ERR "%s: readreg error (ret == %i)\n",
64*4882a593Smuzhiyun 		       __func__, ret);
65*4882a593Smuzhiyun 	return b1[0];
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun EXPORT_SYMBOL(au8522_readreg);
68*4882a593Smuzhiyun 
au8522_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)69*4882a593Smuzhiyun int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun 	struct au8522_state *state = fe->demodulator_priv;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	dprintk("%s(%d)\n", __func__, enable);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	if (state->operational_mode == AU8522_ANALOG_MODE) {
76*4882a593Smuzhiyun 		/* We're being asked to manage the gate even though we're
77*4882a593Smuzhiyun 		   not in digital mode.  This can occur if we get switched
78*4882a593Smuzhiyun 		   over to analog mode before the dvb_frontend kernel thread
79*4882a593Smuzhiyun 		   has completely shutdown */
80*4882a593Smuzhiyun 		return 0;
81*4882a593Smuzhiyun 	}
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	if (enable)
84*4882a593Smuzhiyun 		return au8522_writereg(state, 0x106, 1);
85*4882a593Smuzhiyun 	else
86*4882a593Smuzhiyun 		return au8522_writereg(state, 0x106, 0);
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
89*4882a593Smuzhiyun 
au8522_analog_i2c_gate_ctrl(struct dvb_frontend * fe,int enable)90*4882a593Smuzhiyun int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun 	struct au8522_state *state = fe->demodulator_priv;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	dprintk("%s(%d)\n", __func__, enable);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	if (enable)
97*4882a593Smuzhiyun 		return au8522_writereg(state, 0x106, 1);
98*4882a593Smuzhiyun 	else
99*4882a593Smuzhiyun 		return au8522_writereg(state, 0x106, 0);
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl);
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun /* Reset the demod hardware and reset all of the configuration registers
104*4882a593Smuzhiyun    to a default state. */
au8522_get_state(struct au8522_state ** state,struct i2c_adapter * i2c,u8 client_address)105*4882a593Smuzhiyun int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
106*4882a593Smuzhiyun 		     u8 client_address)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun 	int ret;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	mutex_lock(&au8522_list_mutex);
111*4882a593Smuzhiyun 	ret = hybrid_tuner_request_state(struct au8522_state, (*state),
112*4882a593Smuzhiyun 					 hybrid_tuner_instance_list,
113*4882a593Smuzhiyun 					 i2c, client_address, "au8522");
114*4882a593Smuzhiyun 	mutex_unlock(&au8522_list_mutex);
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	return ret;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun EXPORT_SYMBOL(au8522_get_state);
119*4882a593Smuzhiyun 
au8522_release_state(struct au8522_state * state)120*4882a593Smuzhiyun void au8522_release_state(struct au8522_state *state)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun 	mutex_lock(&au8522_list_mutex);
123*4882a593Smuzhiyun 	if (state != NULL)
124*4882a593Smuzhiyun 		hybrid_tuner_release_state(state);
125*4882a593Smuzhiyun 	mutex_unlock(&au8522_list_mutex);
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun EXPORT_SYMBOL(au8522_release_state);
128*4882a593Smuzhiyun 
au8522_led_gpio_enable(struct au8522_state * state,int onoff)129*4882a593Smuzhiyun static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun 	struct au8522_led_config *led_config = state->config.led_cfg;
132*4882a593Smuzhiyun 	u8 val;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	/* bail out if we can't control an LED */
135*4882a593Smuzhiyun 	if (!led_config || !led_config->gpio_output ||
136*4882a593Smuzhiyun 	    !led_config->gpio_output_enable || !led_config->gpio_output_disable)
137*4882a593Smuzhiyun 		return 0;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	val = au8522_readreg(state, 0x4000 |
140*4882a593Smuzhiyun 			     (led_config->gpio_output & ~0xc000));
141*4882a593Smuzhiyun 	if (onoff) {
142*4882a593Smuzhiyun 		/* enable GPIO output */
143*4882a593Smuzhiyun 		val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
144*4882a593Smuzhiyun 		val |=  (led_config->gpio_output_enable & 0xff);
145*4882a593Smuzhiyun 	} else {
146*4882a593Smuzhiyun 		/* disable GPIO output */
147*4882a593Smuzhiyun 		val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
148*4882a593Smuzhiyun 		val |=  (led_config->gpio_output_disable & 0xff);
149*4882a593Smuzhiyun 	}
150*4882a593Smuzhiyun 	return au8522_writereg(state, 0x8000 |
151*4882a593Smuzhiyun 			       (led_config->gpio_output & ~0xc000), val);
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun /* led = 0 | off
155*4882a593Smuzhiyun  * led = 1 | signal ok
156*4882a593Smuzhiyun  * led = 2 | signal strong
157*4882a593Smuzhiyun  * led < 0 | only light led if leds are currently off
158*4882a593Smuzhiyun  */
au8522_led_ctrl(struct au8522_state * state,int led)159*4882a593Smuzhiyun int au8522_led_ctrl(struct au8522_state *state, int led)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	struct au8522_led_config *led_config = state->config.led_cfg;
162*4882a593Smuzhiyun 	int i, ret = 0;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	/* bail out if we can't control an LED */
165*4882a593Smuzhiyun 	if (!led_config || !led_config->gpio_leds ||
166*4882a593Smuzhiyun 	    !led_config->num_led_states || !led_config->led_states)
167*4882a593Smuzhiyun 		return 0;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	if (led < 0) {
170*4882a593Smuzhiyun 		/* if LED is already lit, then leave it as-is */
171*4882a593Smuzhiyun 		if (state->led_state)
172*4882a593Smuzhiyun 			return 0;
173*4882a593Smuzhiyun 		else
174*4882a593Smuzhiyun 			led *= -1;
175*4882a593Smuzhiyun 	}
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	/* toggle LED if changing state */
178*4882a593Smuzhiyun 	if (state->led_state != led) {
179*4882a593Smuzhiyun 		u8 val;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 		dprintk("%s: %d\n", __func__, led);
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 		au8522_led_gpio_enable(state, 1);
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 		val = au8522_readreg(state, 0x4000 |
186*4882a593Smuzhiyun 				     (led_config->gpio_leds & ~0xc000));
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 		/* start with all leds off */
189*4882a593Smuzhiyun 		for (i = 0; i < led_config->num_led_states; i++)
190*4882a593Smuzhiyun 			val &= ~led_config->led_states[i];
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 		/* set selected LED state */
193*4882a593Smuzhiyun 		if (led < led_config->num_led_states)
194*4882a593Smuzhiyun 			val |= led_config->led_states[led];
195*4882a593Smuzhiyun 		else if (led_config->num_led_states)
196*4882a593Smuzhiyun 			val |=
197*4882a593Smuzhiyun 			led_config->led_states[led_config->num_led_states - 1];
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 		ret = au8522_writereg(state, 0x8000 |
200*4882a593Smuzhiyun 				      (led_config->gpio_leds & ~0xc000), val);
201*4882a593Smuzhiyun 		if (ret < 0)
202*4882a593Smuzhiyun 			return ret;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 		state->led_state = led;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 		if (led == 0)
207*4882a593Smuzhiyun 			au8522_led_gpio_enable(state, 0);
208*4882a593Smuzhiyun 	}
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	return 0;
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun EXPORT_SYMBOL(au8522_led_ctrl);
213*4882a593Smuzhiyun 
au8522_init(struct dvb_frontend * fe)214*4882a593Smuzhiyun int au8522_init(struct dvb_frontend *fe)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun 	struct au8522_state *state = fe->demodulator_priv;
217*4882a593Smuzhiyun 	dprintk("%s()\n", __func__);
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	state->operational_mode = AU8522_DIGITAL_MODE;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	/* Clear out any state associated with the digital side of the
222*4882a593Smuzhiyun 	   chip, so that when it gets powered back up it won't think
223*4882a593Smuzhiyun 	   that it is already tuned */
224*4882a593Smuzhiyun 	state->current_frequency = 0;
225*4882a593Smuzhiyun 	state->current_modulation = VSB_8;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	au8522_writereg(state, 0xa4, 1 << 5);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	au8522_i2c_gate_ctrl(fe, 1);
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	return 0;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun EXPORT_SYMBOL(au8522_init);
234*4882a593Smuzhiyun 
au8522_sleep(struct dvb_frontend * fe)235*4882a593Smuzhiyun int au8522_sleep(struct dvb_frontend *fe)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun 	struct au8522_state *state = fe->demodulator_priv;
238*4882a593Smuzhiyun 	dprintk("%s()\n", __func__);
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	/* Only power down if the digital side is currently using the chip */
241*4882a593Smuzhiyun 	if (state->operational_mode == AU8522_ANALOG_MODE) {
242*4882a593Smuzhiyun 		/* We're not in one of the expected power modes, which means
243*4882a593Smuzhiyun 		   that the DVB thread is probably telling us to go to sleep
244*4882a593Smuzhiyun 		   even though the analog frontend has already started using
245*4882a593Smuzhiyun 		   the chip.  So ignore the request */
246*4882a593Smuzhiyun 		return 0;
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	/* turn off led */
250*4882a593Smuzhiyun 	au8522_led_ctrl(state, 0);
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	/* Power down the chip */
253*4882a593Smuzhiyun 	au8522_writereg(state, 0xa4, 1 << 5);
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	state->current_frequency = 0;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	return 0;
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun EXPORT_SYMBOL(au8522_sleep);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun module_param(debug, int, 0644);
262*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "Enable verbose debug messages");
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
265*4882a593Smuzhiyun MODULE_AUTHOR("Steven Toth");
266*4882a593Smuzhiyun MODULE_LICENSE("GPL");
267