linux/drivers/media/dvb-frontends/au8522_common.c
<<
>>
Prefs
   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");
 267