cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");