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