V4L/DVB (7347): tuner-simple: add basic support for digital tuning of hybrid devices
authorMichael Krufky <mkrufky@linuxtv.org>
Tue, 22 Apr 2008 17:45:52 +0000 (14:45 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Thu, 24 Apr 2008 17:07:49 +0000 (14:07 -0300)
Add entry points used for digital tuning via the dvb_frontend.

Share state data between multiple instances of the driver for hybrid
tuners.

Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/tuner-simple.c
include/media/tuner-types.h

index ee5ef860700ac9b7f077847e2a6c73185956097b..d6d922c32bcef63a8780cc3b1cfa2adf92d2e34b 100644 (file)
@@ -88,14 +88,20 @@ MODULE_PARM_DESC(offset, "Allows to specify an offset for tuner");
 #define TUNER_PLL_LOCKED   0x40
 #define TUNER_STEREO_MK3   0x04
 
+static DEFINE_MUTEX(tuner_simple_list_mutex);
+static LIST_HEAD(hybrid_tuner_instance_list);
+
 struct tuner_simple_priv {
        u16 last_div;
+
        struct tuner_i2c_props i2c_props;
+       struct list_head hybrid_tuner_instance_list;
 
        unsigned int type;
        struct tunertype *tun;
 
        u32 frequency;
+       u32 bandwidth;
 };
 
 /* ---------------------------------------------------------------------- */
@@ -188,6 +194,9 @@ static inline char *tuner_param_name(enum param_type type)
        case TUNER_PARAM_TYPE_NTSC:
                name = "ntsc";
                break;
+       case TUNER_PARAM_TYPE_DIGITAL:
+               name = "digital";
+               break;
        default:
                name = "unknown";
                break;
@@ -687,14 +696,118 @@ static int simple_set_params(struct dvb_frontend *fe,
                priv->frequency = params->frequency * 62500;
                break;
        }
+       priv->bandwidth = 0;
 
        return ret;
 }
 
+static int simple_dvb_configure(struct dvb_frontend *fe, u8 *buf,
+                               const struct dvb_frontend_parameters *params)
+{
+       struct tuner_simple_priv *priv = fe->tuner_priv;
+       struct tunertype *tun = priv->tun;
+       static struct tuner_params *t_params;
+       u8 config, cb;
+       u32 div;
+       int ret, frequency = params->frequency / 62500;
+
+       t_params = simple_tuner_params(fe, TUNER_PARAM_TYPE_DIGITAL);
+       ret = simple_config_lookup(fe, t_params, &frequency, &config, &cb);
+       if (ret < 0)
+               return ret;
+
+       div = ((frequency + t_params->iffreq) * 62500 + offset +
+              tun->stepsize/2) / tun->stepsize;
+
+       buf[0] = div >> 8;
+       buf[1] = div & 0xff;
+       buf[2] = config;
+       buf[3] = cb;
+
+       tuner_dbg("%s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
+                 tun->name, div, buf[0], buf[1], buf[2], buf[3]);
+
+       /* calculate the frequency we set it to */
+       return (div * tun->stepsize) - t_params->iffreq;
+}
+
+static int simple_dvb_calc_regs(struct dvb_frontend *fe,
+                               struct dvb_frontend_parameters *params,
+                               u8 *buf, int buf_len)
+{
+       struct tuner_simple_priv *priv = fe->tuner_priv;
+       int ret;
+       u32 frequency;
+
+       if (buf_len < 5)
+               return -EINVAL;
+
+       ret = simple_dvb_configure(fe, buf+1, params);
+       if (ret < 0)
+               return ret;
+       else
+               frequency = ret;
+
+       buf[0] = priv->i2c_props.addr;
+
+       priv->frequency = frequency;
+       priv->bandwidth = (fe->ops.info.type == FE_OFDM) ?
+               params->u.ofdm.bandwidth : 0;
+
+       return 5;
+}
+
+static int simple_dvb_set_params(struct dvb_frontend *fe,
+                                struct dvb_frontend_parameters *params)
+{
+       struct tuner_simple_priv *priv = fe->tuner_priv;
+       u32 prev_freq, prev_bw;
+       int ret;
+       u8 buf[5];
+
+       if (priv->i2c_props.adap == NULL)
+               return -EINVAL;
+
+       prev_freq = priv->frequency;
+       prev_bw   = priv->bandwidth;
+
+       ret = simple_dvb_calc_regs(fe, params, buf, 5);
+       if (ret != 5)
+               goto fail;
+
+       /* put analog demod in standby when tuning digital */
+       if (fe->ops.analog_ops.standby)
+               fe->ops.analog_ops.standby(fe);
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1);
+
+       /* buf[0] contains the i2c address, but *
+        * we already have it in i2c_props.addr */
+       ret = tuner_i2c_xfer_send(&priv->i2c_props, buf+1, 4);
+       if (ret != 4)
+               goto fail;
+
+       return 0;
+fail:
+       /* calc_regs sets frequency and bandwidth. if we failed, unset them */
+       priv->frequency = prev_freq;
+       priv->bandwidth = prev_bw;
+
+       return ret;
+}
 
 static int simple_release(struct dvb_frontend *fe)
 {
-       kfree(fe->tuner_priv);
+       struct tuner_simple_priv *priv = fe->tuner_priv;
+
+       mutex_lock(&tuner_simple_list_mutex);
+
+       if (priv)
+               hybrid_tuner_release_state(priv);
+
+       mutex_unlock(&tuner_simple_list_mutex);
+
        fe->tuner_priv = NULL;
 
        return 0;
@@ -707,10 +820,20 @@ static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency)
        return 0;
 }
 
+static int simple_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+       struct tuner_simple_priv *priv = fe->tuner_priv;
+       *bandwidth = priv->bandwidth;
+       return 0;
+}
+
 static struct dvb_tuner_ops simple_tuner_ops = {
        .set_analog_params = simple_set_params,
+       .set_params        = simple_dvb_set_params,
+       .calc_regs         = simple_dvb_calc_regs,
        .release           = simple_release,
        .get_frequency     = simple_get_frequency,
+       .get_bandwidth     = simple_get_bandwidth,
        .get_status        = simple_get_status,
        .get_rf_strength   = simple_get_rf_strength,
 };
@@ -721,6 +844,7 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
                                         unsigned int type)
 {
        struct tuner_simple_priv *priv = NULL;
+       int instance;
 
        if (type >= tuner_count) {
                printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n",
@@ -728,22 +852,34 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
                return NULL;
        }
 
-       priv = kzalloc(sizeof(struct tuner_simple_priv), GFP_KERNEL);
-       if (priv == NULL)
+       mutex_lock(&tuner_simple_list_mutex);
+
+       instance = hybrid_tuner_request_state(struct tuner_simple_priv, priv,
+                                             hybrid_tuner_instance_list,
+                                             i2c_adap, i2c_addr,
+                                             "tuner-simple");
+       switch (instance) {
+       case 0:
+               mutex_unlock(&tuner_simple_list_mutex);
                return NULL;
-       fe->tuner_priv = priv;
+               break;
+       case 1:
+               fe->tuner_priv = priv;
 
-       priv->i2c_props.addr = i2c_addr;
-       priv->i2c_props.adap = i2c_adap;
-       priv->i2c_props.name = "tuner-simple";
+               priv->type = type;
+               priv->tun  = &tuners[type];
+               break;
+       default:
+               fe->tuner_priv = priv;
+               break;
+       }
 
-       priv->type = type;
-       priv->tun  = &tuners[type];
+       mutex_unlock(&tuner_simple_list_mutex);
 
        memcpy(&fe->ops.tuner_ops, &simple_tuner_ops,
               sizeof(struct dvb_tuner_ops));
 
-       tuner_info("type set to %d (%s)\n", priv->type, priv->tun->name);
+       tuner_info("type set to %d (%s)\n", type, priv->tun->name);
 
        strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name,
                sizeof(fe->ops.tuner_ops.info.name));
index b201371416a0c793473b1fdd96dc9885f06746d5..4b5e5cf780c87ad9bd76aa0f131f2159ab602d76 100644 (file)
@@ -6,10 +6,11 @@
 #define __TUNER_TYPES_H__
 
 enum param_type {
-       TUNER_PARAM_TYPE_RADIO, \
-       TUNER_PARAM_TYPE_PAL, \
-       TUNER_PARAM_TYPE_SECAM, \
-       TUNER_PARAM_TYPE_NTSC
+       TUNER_PARAM_TYPE_RADIO,
+       TUNER_PARAM_TYPE_PAL,
+       TUNER_PARAM_TYPE_SECAM,
+       TUNER_PARAM_TYPE_NTSC,
+       TUNER_PARAM_TYPE_DIGITAL,
 };
 
 struct tuner_range {
@@ -105,6 +106,7 @@ struct tuner_params {
           the SECAM-L/L' standards. Range: -16:+15 */
        signed int default_top_secam_high:5;
 
+       u16 iffreq;
 
        unsigned int count;
        struct tuner_range *ranges;
@@ -114,6 +116,10 @@ struct tunertype {
        char *name;
        unsigned int count;
        struct tuner_params *params;
+
+       u16 min;
+       u16 max;
+       u16 stepsize;
 };
 
 extern struct tunertype tuners[];