root/drivers/media/dvb-frontends/au8522_common.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. au8522_writereg
  2. au8522_readreg
  3. au8522_i2c_gate_ctrl
  4. au8522_analog_i2c_gate_ctrl
  5. au8522_get_state
  6. au8522_release_state
  7. au8522_led_gpio_enable
  8. au8522_led_ctrl
  9. au8522_init
  10. au8522_sleep

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

/* [<][>][^][v][top][bottom][index][help] */