au8522_common.c (6944B)
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 17static 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... */ 26static LIST_HEAD(hybrid_tuner_instance_list); 27static DEFINE_MUTEX(au8522_list_mutex); 28 29/* 16 bit registers, 8 bit values */ 30int 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} 46EXPORT_SYMBOL(au8522_writereg); 47 48u8 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} 67EXPORT_SYMBOL(au8522_readreg); 68 69int 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} 88EXPORT_SYMBOL(au8522_i2c_gate_ctrl); 89 90int 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} 101EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl); 102 103/* Reset the demod hardware and reset all of the configuration registers 104 to a default state. */ 105int 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} 118EXPORT_SYMBOL(au8522_get_state); 119 120void 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} 127EXPORT_SYMBOL(au8522_release_state); 128 129static 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 */ 159int 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} 212EXPORT_SYMBOL(au8522_led_ctrl); 213 214int 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} 233EXPORT_SYMBOL(au8522_init); 234 235int 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} 259EXPORT_SYMBOL(au8522_sleep); 260 261module_param(debug, int, 0644); 262MODULE_PARM_DESC(debug, "Enable verbose debug messages"); 263 264MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver"); 265MODULE_AUTHOR("Steven Toth"); 266MODULE_LICENSE("GPL");