Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003     Auvitek AU8522 QAM/8VSB demodulator driver
0004 
0005     Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
0006     Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
0007     Copyright (C) 2005-2008 Auvitek International, Ltd.
0008     Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org>
0009 
0010 
0011 */
0012 
0013 #include <linux/i2c.h>
0014 #include <media/dvb_frontend.h>
0015 #include "au8522_priv.h"
0016 
0017 static int debug;
0018 
0019 #define dprintk(arg...)\
0020   do { if (debug)\
0021      printk(arg);\
0022   } while (0)
0023 
0024 /* Despite the name "hybrid_tuner", the framework works just as well for
0025    hybrid demodulators as well... */
0026 static LIST_HEAD(hybrid_tuner_instance_list);
0027 static DEFINE_MUTEX(au8522_list_mutex);
0028 
0029 /* 16 bit registers, 8 bit values */
0030 int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
0031 {
0032     int ret;
0033     u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
0034 
0035     struct i2c_msg msg = { .addr = state->config.demod_address,
0036                    .flags = 0, .buf = buf, .len = 3 };
0037 
0038     ret = i2c_transfer(state->i2c, &msg, 1);
0039 
0040     if (ret != 1)
0041         printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
0042                __func__, reg, data, ret);
0043 
0044     return (ret != 1) ? -1 : 0;
0045 }
0046 EXPORT_SYMBOL(au8522_writereg);
0047 
0048 u8 au8522_readreg(struct au8522_state *state, u16 reg)
0049 {
0050     int ret;
0051     u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
0052     u8 b1[] = { 0 };
0053 
0054     struct i2c_msg msg[] = {
0055         { .addr = state->config.demod_address, .flags = 0,
0056           .buf = b0, .len = 2 },
0057         { .addr = state->config.demod_address, .flags = I2C_M_RD,
0058           .buf = b1, .len = 1 } };
0059 
0060     ret = i2c_transfer(state->i2c, msg, 2);
0061 
0062     if (ret != 2)
0063         printk(KERN_ERR "%s: readreg error (ret == %i)\n",
0064                __func__, ret);
0065     return b1[0];
0066 }
0067 EXPORT_SYMBOL(au8522_readreg);
0068 
0069 int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
0070 {
0071     struct au8522_state *state = fe->demodulator_priv;
0072 
0073     dprintk("%s(%d)\n", __func__, enable);
0074 
0075     if (state->operational_mode == AU8522_ANALOG_MODE) {
0076         /* We're being asked to manage the gate even though we're
0077            not in digital mode.  This can occur if we get switched
0078            over to analog mode before the dvb_frontend kernel thread
0079            has completely shutdown */
0080         return 0;
0081     }
0082 
0083     if (enable)
0084         return au8522_writereg(state, 0x106, 1);
0085     else
0086         return au8522_writereg(state, 0x106, 0);
0087 }
0088 EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
0089 
0090 int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
0091 {
0092     struct au8522_state *state = fe->demodulator_priv;
0093 
0094     dprintk("%s(%d)\n", __func__, enable);
0095 
0096     if (enable)
0097         return au8522_writereg(state, 0x106, 1);
0098     else
0099         return au8522_writereg(state, 0x106, 0);
0100 }
0101 EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl);
0102 
0103 /* Reset the demod hardware and reset all of the configuration registers
0104    to a default state. */
0105 int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
0106              u8 client_address)
0107 {
0108     int ret;
0109 
0110     mutex_lock(&au8522_list_mutex);
0111     ret = hybrid_tuner_request_state(struct au8522_state, (*state),
0112                      hybrid_tuner_instance_list,
0113                      i2c, client_address, "au8522");
0114     mutex_unlock(&au8522_list_mutex);
0115 
0116     return ret;
0117 }
0118 EXPORT_SYMBOL(au8522_get_state);
0119 
0120 void au8522_release_state(struct au8522_state *state)
0121 {
0122     mutex_lock(&au8522_list_mutex);
0123     if (state != NULL)
0124         hybrid_tuner_release_state(state);
0125     mutex_unlock(&au8522_list_mutex);
0126 }
0127 EXPORT_SYMBOL(au8522_release_state);
0128 
0129 static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
0130 {
0131     struct au8522_led_config *led_config = state->config.led_cfg;
0132     u8 val;
0133 
0134     /* bail out if we can't control an LED */
0135     if (!led_config || !led_config->gpio_output ||
0136         !led_config->gpio_output_enable || !led_config->gpio_output_disable)
0137         return 0;
0138 
0139     val = au8522_readreg(state, 0x4000 |
0140                  (led_config->gpio_output & ~0xc000));
0141     if (onoff) {
0142         /* enable GPIO output */
0143         val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
0144         val |=  (led_config->gpio_output_enable & 0xff);
0145     } else {
0146         /* disable GPIO output */
0147         val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
0148         val |=  (led_config->gpio_output_disable & 0xff);
0149     }
0150     return au8522_writereg(state, 0x8000 |
0151                    (led_config->gpio_output & ~0xc000), val);
0152 }
0153 
0154 /* led = 0 | off
0155  * led = 1 | signal ok
0156  * led = 2 | signal strong
0157  * led < 0 | only light led if leds are currently off
0158  */
0159 int au8522_led_ctrl(struct au8522_state *state, int led)
0160 {
0161     struct au8522_led_config *led_config = state->config.led_cfg;
0162     int i, ret = 0;
0163 
0164     /* bail out if we can't control an LED */
0165     if (!led_config || !led_config->gpio_leds ||
0166         !led_config->num_led_states || !led_config->led_states)
0167         return 0;
0168 
0169     if (led < 0) {
0170         /* if LED is already lit, then leave it as-is */
0171         if (state->led_state)
0172             return 0;
0173         else
0174             led *= -1;
0175     }
0176 
0177     /* toggle LED if changing state */
0178     if (state->led_state != led) {
0179         u8 val;
0180 
0181         dprintk("%s: %d\n", __func__, led);
0182 
0183         au8522_led_gpio_enable(state, 1);
0184 
0185         val = au8522_readreg(state, 0x4000 |
0186                      (led_config->gpio_leds & ~0xc000));
0187 
0188         /* start with all leds off */
0189         for (i = 0; i < led_config->num_led_states; i++)
0190             val &= ~led_config->led_states[i];
0191 
0192         /* set selected LED state */
0193         if (led < led_config->num_led_states)
0194             val |= led_config->led_states[led];
0195         else if (led_config->num_led_states)
0196             val |=
0197             led_config->led_states[led_config->num_led_states - 1];
0198 
0199         ret = au8522_writereg(state, 0x8000 |
0200                       (led_config->gpio_leds & ~0xc000), val);
0201         if (ret < 0)
0202             return ret;
0203 
0204         state->led_state = led;
0205 
0206         if (led == 0)
0207             au8522_led_gpio_enable(state, 0);
0208     }
0209 
0210     return 0;
0211 }
0212 EXPORT_SYMBOL(au8522_led_ctrl);
0213 
0214 int au8522_init(struct dvb_frontend *fe)
0215 {
0216     struct au8522_state *state = fe->demodulator_priv;
0217     dprintk("%s()\n", __func__);
0218 
0219     state->operational_mode = AU8522_DIGITAL_MODE;
0220 
0221     /* Clear out any state associated with the digital side of the
0222        chip, so that when it gets powered back up it won't think
0223        that it is already tuned */
0224     state->current_frequency = 0;
0225     state->current_modulation = VSB_8;
0226 
0227     au8522_writereg(state, 0xa4, 1 << 5);
0228 
0229     au8522_i2c_gate_ctrl(fe, 1);
0230 
0231     return 0;
0232 }
0233 EXPORT_SYMBOL(au8522_init);
0234 
0235 int au8522_sleep(struct dvb_frontend *fe)
0236 {
0237     struct au8522_state *state = fe->demodulator_priv;
0238     dprintk("%s()\n", __func__);
0239 
0240     /* Only power down if the digital side is currently using the chip */
0241     if (state->operational_mode == AU8522_ANALOG_MODE) {
0242         /* We're not in one of the expected power modes, which means
0243            that the DVB thread is probably telling us to go to sleep
0244            even though the analog frontend has already started using
0245            the chip.  So ignore the request */
0246         return 0;
0247     }
0248 
0249     /* turn off led */
0250     au8522_led_ctrl(state, 0);
0251 
0252     /* Power down the chip */
0253     au8522_writereg(state, 0xa4, 1 << 5);
0254 
0255     state->current_frequency = 0;
0256 
0257     return 0;
0258 }
0259 EXPORT_SYMBOL(au8522_sleep);
0260 
0261 module_param(debug, int, 0644);
0262 MODULE_PARM_DESC(debug, "Enable verbose debug messages");
0263 
0264 MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
0265 MODULE_AUTHOR("Steven Toth");
0266 MODULE_LICENSE("GPL");