1 // SPDX-License-Identifier: GPL-2.0-or-later
3 Auvitek AU8522 QAM/8VSB demodulator driver
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>
13 #include <linux/i2c.h>
14 #include <media/dvb_frontend.h>
15 #include "au8522_priv.h"
19 #define dprintk(arg...)\
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
);
29 /* 16 bit registers, 8 bit values */
30 int au8522_writereg(struct au8522_state
*state
, u16 reg
, u8 data
)
33 u8 buf
[] = { (reg
>> 8) | 0x80, reg
& 0xff, data
};
35 struct i2c_msg msg
= { .addr
= state
->config
.demod_address
,
36 .flags
= 0, .buf
= buf
, .len
= 3 };
38 ret
= i2c_transfer(state
->i2c
, &msg
, 1);
41 printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
42 __func__
, reg
, data
, ret
);
44 return (ret
!= 1) ? -1 : 0;
46 EXPORT_SYMBOL(au8522_writereg
);
48 u8
au8522_readreg(struct au8522_state
*state
, u16 reg
)
51 u8 b0
[] = { (reg
>> 8) | 0x40, reg
& 0xff };
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 } };
60 ret
= i2c_transfer(state
->i2c
, msg
, 2);
63 printk(KERN_ERR
"%s: readreg error (ret == %i)\n",
67 EXPORT_SYMBOL(au8522_readreg
);
69 int au8522_i2c_gate_ctrl(struct dvb_frontend
*fe
, int enable
)
71 struct au8522_state
*state
= fe
->demodulator_priv
;
73 dprintk("%s(%d)\n", __func__
, enable
);
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 */
84 return au8522_writereg(state
, 0x106, 1);
86 return au8522_writereg(state
, 0x106, 0);
88 EXPORT_SYMBOL(au8522_i2c_gate_ctrl
);
90 int au8522_analog_i2c_gate_ctrl(struct dvb_frontend
*fe
, int enable
)
92 struct au8522_state
*state
= fe
->demodulator_priv
;
94 dprintk("%s(%d)\n", __func__
, enable
);
97 return au8522_writereg(state
, 0x106, 1);
99 return au8522_writereg(state
, 0x106, 0);
101 EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl
);
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
,
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
);
118 EXPORT_SYMBOL(au8522_get_state
);
120 void au8522_release_state(struct au8522_state
*state
)
122 mutex_lock(&au8522_list_mutex
);
124 hybrid_tuner_release_state(state
);
125 mutex_unlock(&au8522_list_mutex
);
127 EXPORT_SYMBOL(au8522_release_state
);
129 static int au8522_led_gpio_enable(struct au8522_state
*state
, int onoff
)
131 struct au8522_led_config
*led_config
= state
->config
.led_cfg
;
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
)
139 val
= au8522_readreg(state
, 0x4000 |
140 (led_config
->gpio_output
& ~0xc000));
142 /* enable GPIO output */
143 val
&= ~((led_config
->gpio_output_enable
>> 8) & 0xff);
144 val
|= (led_config
->gpio_output_enable
& 0xff);
146 /* disable GPIO output */
147 val
&= ~((led_config
->gpio_output_disable
>> 8) & 0xff);
148 val
|= (led_config
->gpio_output_disable
& 0xff);
150 return au8522_writereg(state
, 0x8000 |
151 (led_config
->gpio_output
& ~0xc000), val
);
155 * led = 1 | signal ok
156 * led = 2 | signal strong
157 * led < 0 | only light led if leds are currently off
159 int au8522_led_ctrl(struct au8522_state
*state
, int led
)
161 struct au8522_led_config
*led_config
= state
->config
.led_cfg
;
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
)
170 /* if LED is already lit, then leave it as-is */
171 if (state
->led_state
)
177 /* toggle LED if changing state */
178 if (state
->led_state
!= led
) {
181 dprintk("%s: %d\n", __func__
, led
);
183 au8522_led_gpio_enable(state
, 1);
185 val
= au8522_readreg(state
, 0x4000 |
186 (led_config
->gpio_leds
& ~0xc000));
188 /* start with all leds off */
189 for (i
= 0; i
< led_config
->num_led_states
; i
++)
190 val
&= ~led_config
->led_states
[i
];
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
)
197 led_config
->led_states
[led_config
->num_led_states
- 1];
199 ret
= au8522_writereg(state
, 0x8000 |
200 (led_config
->gpio_leds
& ~0xc000), val
);
204 state
->led_state
= led
;
207 au8522_led_gpio_enable(state
, 0);
212 EXPORT_SYMBOL(au8522_led_ctrl
);
214 int au8522_init(struct dvb_frontend
*fe
)
216 struct au8522_state
*state
= fe
->demodulator_priv
;
217 dprintk("%s()\n", __func__
);
219 state
->operational_mode
= AU8522_DIGITAL_MODE
;
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
;
227 au8522_writereg(state
, 0xa4, 1 << 5);
229 au8522_i2c_gate_ctrl(fe
, 1);
233 EXPORT_SYMBOL(au8522_init
);
235 int au8522_sleep(struct dvb_frontend
*fe
)
237 struct au8522_state
*state
= fe
->demodulator_priv
;
238 dprintk("%s()\n", __func__
);
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 */
250 au8522_led_ctrl(state
, 0);
252 /* Power down the chip */
253 au8522_writereg(state
, 0xa4, 1 << 5);
255 state
->current_frequency
= 0;
259 EXPORT_SYMBOL(au8522_sleep
);
261 module_param(debug
, int, 0644);
262 MODULE_PARM_DESC(debug
, "Enable verbose debug messages");
264 MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
265 MODULE_AUTHOR("Steven Toth");
266 MODULE_LICENSE("GPL");