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, "
  54                       "ret == %i)\n", __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                        gmb();
 207                        val |= led_config->led_states[led];
 208                } else if (led_config->num_led_states) {
 209                        val |=
 210                        led_config->led_states[led_config->num_led_states - 1];
 211                }
 212
 213                ret = au8522_writereg(state, 0x8000 |
 214                                      (led_config->gpio_leds & ~0xc000), val);
 215                if (ret < 0)
 216                        return ret;
 217
 218                state->led_state = led;
 219
 220                if (led == 0)
 221                        au8522_led_gpio_enable(state, 0);
 222        }
 223
 224        return 0;
 225}
 226EXPORT_SYMBOL(au8522_led_ctrl);
 227
 228int au8522_init(struct dvb_frontend *fe)
 229{
 230        struct au8522_state *state = fe->demodulator_priv;
 231        dprintk("%s()\n", __func__);
 232
 233        state->operational_mode = AU8522_DIGITAL_MODE;
 234
 235        /* Clear out any state associated with the digital side of the
 236           chip, so that when it gets powered back up it won't think
 237           that it is already tuned */
 238        state->current_frequency = 0;
 239
 240        au8522_writereg(state, 0xa4, 1 << 5);
 241
 242        au8522_i2c_gate_ctrl(fe, 1);
 243
 244        return 0;
 245}
 246EXPORT_SYMBOL(au8522_init);
 247
 248int au8522_sleep(struct dvb_frontend *fe)
 249{
 250        struct au8522_state *state = fe->demodulator_priv;
 251        dprintk("%s()\n", __func__);
 252
 253        /* Only power down if the digital side is currently using the chip */
 254        if (state->operational_mode == AU8522_ANALOG_MODE) {
 255                /* We're not in one of the expected power modes, which means
 256                   that the DVB thread is probably telling us to go to sleep
 257                   even though the analog frontend has already started using
 258                   the chip.  So ignore the request */
 259                return 0;
 260        }
 261
 262        /* turn off led */
 263        au8522_led_ctrl(state, 0);
 264
 265        /* Power down the chip */
 266        au8522_writereg(state, 0xa4, 1 << 5);
 267
 268        state->current_frequency = 0;
 269
 270        return 0;
 271}
 272EXPORT_SYMBOL(au8522_sleep);
 273
 274module_param(debug, int, 0644);
 275MODULE_PARM_DESC(debug, "Enable verbose debug messages");
 276
 277MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
 278MODULE_AUTHOR("Steven Toth");
 279MODULE_LICENSE("GPL");
 280