]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/media/common/tuners/xc5000.c
[media] xc5000: support 32MHz & 31.875MHz xtal using the 41.024.5 firmware
[linux-2.6.git] / drivers / media / common / tuners / xc5000.c
index 8d3e31b6effc45ddb6191bbb2c83b99aa14c06c9..eab2ea42420090c7847cd5d4eaa889659bf351b4 100644 (file)
@@ -3,6 +3,7 @@
  *
  *  Copyright (c) 2007 Xceive Corporation
  *  Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ *  Copyright (c) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -36,28 +37,35 @@ static int debug;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
 
+static int no_poweroff;
+module_param(no_poweroff, int, 0644);
+MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
+       "\t\t1 keep device energized and with tuner ready all the times.\n"
+       "\t\tFaster, but consumes more power and keeps the device hotter");
+
 static DEFINE_MUTEX(xc5000_list_mutex);
 static LIST_HEAD(hybrid_tuner_instance_list);
 
 #define dprintk(level, fmt, arg...) if (debug >= level) \
        printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
 
-#define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.4.68.fw"
-#define XC5000_DEFAULT_FIRMWARE_SIZE 12378
-
 struct xc5000_priv {
        struct tuner_i2c_props i2c_props;
        struct list_head hybrid_tuner_instance_list;
 
        u32 if_khz;
+       u32 xtal_khz;
        u32 freq_hz;
        u32 bandwidth;
        u8  video_standard;
        u8  rf_mode;
+       u8  radio_input;
+
+       int chip_id;
 };
 
 /* Misc Defines */
-#define MAX_TV_STANDARD                        23
+#define MAX_TV_STANDARD                        24
 #define XC_MAX_I2C_WRITE_LENGTH                64
 
 /* Signal Types */
@@ -84,10 +92,12 @@ struct xc5000_priv {
 #define XREG_IF_OUT       0x05
 #define XREG_SEEK_MODE    0x07
 #define XREG_POWER_DOWN   0x0A /* Obsolete */
+/* Set the output amplitude - SIF for analog, DTVP/DTVN for digital */
+#define XREG_OUTPUT_AMP   0x0B
 #define XREG_SIGNALSOURCE 0x0D /* 0=Air, 1=Cable */
 #define XREG_SMOOTHEDCVBS 0x0E
 #define XREG_XTALFREQ     0x0F
-#define XREG_FINERFFREQ   0x10
+#define XREG_FINERFREQ    0x10
 #define XREG_DDIMODE      0x11
 
 #define XREG_ADC_ENV      0x00
@@ -165,6 +175,7 @@ struct XC_TV_STANDARD {
 #define DTV7                   20
 #define FM_Radio_INPUT2        21
 #define FM_Radio_INPUT1        22
+#define FM_Radio_INPUT1_MONO   23
 
 static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
        {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020},
@@ -189,9 +200,37 @@ static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
        {"DTV7/8",            0x00C0, 0x801B},
        {"DTV7",              0x00C0, 0x8007},
        {"FM Radio-INPUT2",   0x9802, 0x9002},
-       {"FM Radio-INPUT1",   0x0208, 0x9002}
+       {"FM Radio-INPUT1",   0x0208, 0x9002},
+       {"FM Radio-INPUT1_MONO", 0x0278, 0x9002}
+};
+
+
+struct xc5000_fw_cfg {
+       char *name;
+       u16 size;
+};
+
+static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
+       .name = "dvb-fe-xc5000-1.6.114.fw",
+       .size = 12401,
+};
+
+static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
+       .name = "dvb-fe-xc5000c-41.024.5.fw",
+       .size = 16497,
 };
 
+static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
+{
+       switch (chip_id) {
+       default:
+       case XC5000A:
+               return &xc5000a_1_6_114;
+       case XC5000C:
+               return &xc5000c_41_024_5;
+       }
+}
+
 static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
 static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
 static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
@@ -209,6 +248,7 @@ static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
        return XC_RESULT_SUCCESS;
 }
 
+#if 0
 /* This routine is never used because the only time we read data from the
    i2c bus is when we read registers, and we want that to be an atomic i2c
    transaction in case we are on a multi-master bus */
@@ -223,6 +263,27 @@ static int xc_read_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
        }
        return 0;
 }
+#endif
+
+static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
+{
+       u8 buf[2] = { reg >> 8, reg & 0xff };
+       u8 bval[2] = { 0, 0 };
+       struct i2c_msg msg[2] = {
+               { .addr = priv->i2c_props.addr,
+                       .flags = 0, .buf = &buf[0], .len = 2 },
+               { .addr = priv->i2c_props.addr,
+                       .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
+       };
+
+       if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
+               printk(KERN_WARNING "xc5000: I2C read failed\n");
+               return -EREMOTEIO;
+       }
+
+       *val = (bval[0] << 8) | bval[1];
+       return XC_RESULT_SUCCESS;
+}
 
 static void xc_wait(int wait_ms)
 {
@@ -256,7 +317,7 @@ static int xc5000_TunerReset(struct dvb_frontend *fe)
 static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData)
 {
        u8 buf[4];
-       int WatchDogTimer = 5;
+       int WatchDogTimer = 100;
        int result;
 
        buf[0] = (regAddr >> 8) & 0xFF;
@@ -267,20 +328,14 @@ static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData)
        if (result == XC_RESULT_SUCCESS) {
                /* wait for busy flag to clear */
                while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) {
-                       buf[0] = 0;
-                       buf[1] = XREG_BUSY;
-
-                       result = xc_send_i2c_data(priv, buf, 2);
+                       result = xc5000_readreg(priv, XREG_BUSY, (u16 *)buf);
                        if (result == XC_RESULT_SUCCESS) {
-                               result = xc_read_i2c_data(priv, buf, 2);
-                               if (result == XC_RESULT_SUCCESS) {
-                                       if ((buf[0] == 0) && (buf[1] == 0)) {
-                                               /* busy flag cleared */
+                               if ((buf[0] == 0) && (buf[1] == 0)) {
+                                       /* busy flag cleared */
                                        break;
-                                       } else {
-                                               xc_wait(100); /* wait 5 ms */
-                                               WatchDogTimer--;
-                                       }
+                               } else {
+                                       xc_wait(5); /* wait 5 ms */
+                                       WatchDogTimer--;
                                }
                        }
                }
@@ -395,7 +450,10 @@ static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz)
 
        freq_code = (u16)(freq_hz / 15625);
 
-       return xc_write_reg(priv, XREG_RF_FREQ, freq_code);
+       /* Starting in firmware version 1.1.44, Xceive recommends using the
+          FINERFREQ for all normal tuning (the doc indicates reg 0x03 should
+          only be used for fast scanning for channel lock) */
+       return xc_write_reg(priv, XREG_FINERFREQ, freq_code);
 }
 
 
@@ -515,24 +573,30 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode)
        return found;
 }
 
-static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
+static int xc_set_xtal(struct dvb_frontend *fe)
 {
-       u8 buf[2] = { reg >> 8, reg & 0xff };
-       u8 bval[2] = { 0, 0 };
-       struct i2c_msg msg[2] = {
-               { .addr = priv->i2c_props.addr,
-                       .flags = 0, .buf = &buf[0], .len = 2 },
-               { .addr = priv->i2c_props.addr,
-                       .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
-       };
+       struct xc5000_priv *priv = fe->tuner_priv;
+       int ret = XC_RESULT_SUCCESS;
 
-       if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
-               printk(KERN_WARNING "xc5000: I2C read failed\n");
-               return -EREMOTEIO;
+       switch (priv->chip_id) {
+       default:
+       case XC5000A:
+               /* 32.000 MHz xtal is default */
+               break;
+       case XC5000C:
+               switch (priv->xtal_khz) {
+               default:
+               case 32000:
+                       /* 32.000 MHz xtal is default */
+                       break;
+               case 31875:
+                       /* 31.875 MHz xtal configuration */
+                       ret = xc_write_reg(priv, 0x000f, 0x8081);
+                       break;
+               }
+               break;
        }
-
-       *val = (bval[0] << 8) | bval[1];
-       return XC_RESULT_SUCCESS;
+       return ret;
 }
 
 static int xc5000_fwupload(struct dvb_frontend *fe)
@@ -540,29 +604,34 @@ static int xc5000_fwupload(struct dvb_frontend *fe)
        struct xc5000_priv *priv = fe->tuner_priv;
        const struct firmware *fw;
        int ret;
+       const struct xc5000_fw_cfg *desired_fw =
+               xc5000_assign_firmware(priv->chip_id);
 
        /* request the firmware, this will block and timeout */
        printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
-               XC5000_DEFAULT_FIRMWARE);
+               desired_fw->name);
 
-       ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE,
+       ret = request_firmware(&fw, desired_fw->name,
                priv->i2c_props.adap->dev.parent);
        if (ret) {
                printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
                ret = XC_RESULT_RESET_FAILURE;
                goto out;
        } else {
-               printk(KERN_INFO "xc5000: firmware read %Zu bytes.\n",
+               printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n",
                       fw->size);
                ret = XC_RESULT_SUCCESS;
        }
 
-       if (fw->size != XC5000_DEFAULT_FIRMWARE_SIZE) {
+       if (fw->size != desired_fw->size) {
                printk(KERN_ERR "xc5000: firmware incorrect size\n");
                ret = XC_RESULT_RESET_FAILURE;
        } else {
-               printk(KERN_INFO "xc5000: firmware upload\n");
+               printk(KERN_INFO "xc5000: firmware uploading...\n");
                ret = xc_load_i2c_sequence(fe,  fw->data);
+               if (XC_RESULT_SUCCESS == ret)
+                       ret = xc_set_xtal(fe);
+               printk(KERN_INFO "xc5000: firmware upload complete...\n");
        }
 
 out:
@@ -615,41 +684,84 @@ static void xc_debug_dump(struct xc5000_priv *priv)
        dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality);
 }
 
-static int xc5000_set_params(struct dvb_frontend *fe,
-       struct dvb_frontend_parameters *params)
+static int xc5000_set_params(struct dvb_frontend *fe)
 {
+       int ret, b;
        struct xc5000_priv *priv = fe->tuner_priv;
-       int ret;
+       u32 bw = fe->dtv_property_cache.bandwidth_hz;
+       u32 freq = fe->dtv_property_cache.frequency;
+       u32 delsys  = fe->dtv_property_cache.delivery_system;
 
-       if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS)
-               xc_load_fw_and_init_tuner(fe);
+       if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+               if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+                       dprintk(1, "Unable to load firmware and init tuner\n");
+                       return -EINVAL;
+               }
+       }
 
-       dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency);
+       dprintk(1, "%s() frequency=%d (Hz)\n", __func__, freq);
 
-       switch (params->u.vsb.modulation) {
-       case VSB_8:
-       case VSB_16:
+       switch (delsys) {
+       case SYS_ATSC:
                dprintk(1, "%s() VSB modulation\n", __func__);
                priv->rf_mode = XC_RF_MODE_AIR;
-               priv->freq_hz = params->frequency - 1750000;
-               priv->bandwidth = BANDWIDTH_6_MHZ;
+               priv->freq_hz = freq - 1750000;
                priv->video_standard = DTV6;
                break;
-       case QAM_64:
-       case QAM_256:
-       case QAM_AUTO:
+       case SYS_DVBC_ANNEX_B:
                dprintk(1, "%s() QAM modulation\n", __func__);
                priv->rf_mode = XC_RF_MODE_CABLE;
-               priv->freq_hz = params->frequency - 1750000;
-               priv->bandwidth = BANDWIDTH_6_MHZ;
+               priv->freq_hz = freq - 1750000;
                priv->video_standard = DTV6;
                break;
+       case SYS_DVBT:
+       case SYS_DVBT2:
+               dprintk(1, "%s() OFDM\n", __func__);
+               switch (bw) {
+               case 6000000:
+                       priv->video_standard = DTV6;
+                       priv->freq_hz = freq - 1750000;
+                       break;
+               case 7000000:
+                       priv->video_standard = DTV7;
+                       priv->freq_hz = freq - 2250000;
+                       break;
+               case 8000000:
+                       priv->video_standard = DTV8;
+                       priv->freq_hz = freq - 2750000;
+                       break;
+               default:
+                       printk(KERN_ERR "xc5000 bandwidth not set!\n");
+                       return -EINVAL;
+               }
+               priv->rf_mode = XC_RF_MODE_AIR;
+       case SYS_DVBC_ANNEX_A:
+       case SYS_DVBC_ANNEX_C:
+               dprintk(1, "%s() QAM modulation\n", __func__);
+               priv->rf_mode = XC_RF_MODE_CABLE;
+               if (bw <= 6000000) {
+                       priv->video_standard = DTV6;
+                       priv->freq_hz = freq - 1750000;
+                       b = 6;
+               } else if (bw <= 7000000) {
+                       priv->video_standard = DTV7;
+                       priv->freq_hz = freq - 2250000;
+                       b = 7;
+               } else {
+                       priv->video_standard = DTV7_8;
+                       priv->freq_hz = freq - 2750000;
+                       b = 8;
+               }
+               dprintk(1, "%s() Bandwidth %dMHz (%d)\n", __func__,
+                       b, bw);
+               break;
        default:
+               printk(KERN_ERR "xc5000: delivery system is not supported!\n");
                return -EINVAL;
        }
 
-       dprintk(1, "%s() frequency=%d (compensated)\n",
-               __func__, priv->freq_hz);
+       dprintk(1, "%s() frequency=%d (compensated to %d)\n",
+               __func__, freq, priv->freq_hz);
 
        ret = xc_SetSignalSource(priv, priv->rf_mode);
        if (ret != XC_RESULT_SUCCESS) {
@@ -674,11 +786,15 @@ static int xc5000_set_params(struct dvb_frontend *fe,
                return -EIO;
        }
 
+       xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a);
+
        xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL);
 
        if (debug)
                xc_debug_dump(priv);
 
+       priv->bandwidth = bw;
+
        return 0;
 }
 
@@ -701,15 +817,12 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)
        return ret;
 }
 
-static int xc5000_set_analog_params(struct dvb_frontend *fe,
+static int xc5000_set_tv_freq(struct dvb_frontend *fe,
        struct analog_parameters *params)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
        int ret;
 
-       if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS)
-               xc_load_fw_and_init_tuner(fe);
-
        dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
                __func__, params->frequency);
 
@@ -781,6 +894,8 @@ tune_channel:
                return -EREMOTEIO;
        }
 
+       xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09);
+
        xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
 
        if (debug)
@@ -789,6 +904,94 @@ tune_channel:
        return 0;
 }
 
+static int xc5000_set_radio_freq(struct dvb_frontend *fe,
+       struct analog_parameters *params)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       int ret = -EINVAL;
+       u8 radio_input;
+
+       dprintk(1, "%s() frequency=%d (in units of khz)\n",
+               __func__, params->frequency);
+
+       if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) {
+               dprintk(1, "%s() radio input not configured\n", __func__);
+               return -EINVAL;
+       }
+
+       if (priv->radio_input == XC5000_RADIO_FM1)
+               radio_input = FM_Radio_INPUT1;
+       else if  (priv->radio_input == XC5000_RADIO_FM2)
+               radio_input = FM_Radio_INPUT2;
+       else if  (priv->radio_input == XC5000_RADIO_FM1_MONO)
+               radio_input = FM_Radio_INPUT1_MONO;
+       else {
+               dprintk(1, "%s() unknown radio input %d\n", __func__,
+                       priv->radio_input);
+               return -EINVAL;
+       }
+
+       priv->freq_hz = params->frequency * 125 / 2;
+
+       priv->rf_mode = XC_RF_MODE_AIR;
+
+       ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode,
+                              XC5000_Standard[radio_input].AudioMode);
+
+       if (ret != XC_RESULT_SUCCESS) {
+               printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+               return -EREMOTEIO;
+       }
+
+       ret = xc_SetSignalSource(priv, priv->rf_mode);
+       if (ret != XC_RESULT_SUCCESS) {
+               printk(KERN_ERR
+                       "xc5000: xc_SetSignalSource(%d) failed\n",
+                       priv->rf_mode);
+               return -EREMOTEIO;
+       }
+
+       if ((priv->radio_input == XC5000_RADIO_FM1) ||
+                               (priv->radio_input == XC5000_RADIO_FM2))
+               xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09);
+       else if  (priv->radio_input == XC5000_RADIO_FM1_MONO)
+               xc_write_reg(priv, XREG_OUTPUT_AMP, 0x06);
+
+       xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
+
+       return 0;
+}
+
+static int xc5000_set_analog_params(struct dvb_frontend *fe,
+                            struct analog_parameters *params)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       int ret = -EINVAL;
+
+       if (priv->i2c_props.adap == NULL)
+               return -EINVAL;
+
+       if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+               if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+                       dprintk(1, "Unable to load firmware and init tuner\n");
+                       return -EINVAL;
+               }
+       }
+
+       switch (params->mode) {
+       case V4L2_TUNER_RADIO:
+               ret = xc5000_set_radio_freq(fe, params);
+               break;
+       case V4L2_TUNER_ANALOG_TV:
+       case V4L2_TUNER_DIGITAL_TV:
+               ret = xc5000_set_tv_freq(fe, params);
+               break;
+       }
+
+       return ret;
+}
+
+
 static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
@@ -797,6 +1000,14 @@ static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
        return 0;
 }
 
+static int xc5000_get_if_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       dprintk(1, "%s()\n", __func__);
+       *freq = priv->if_khz * 1000;
+       return 0;
+}
+
 static int xc5000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
@@ -853,6 +1064,10 @@ static int xc5000_sleep(struct dvb_frontend *fe)
 
        dprintk(1, "%s()\n", __func__);
 
+       /* Avoid firmware reload on slow devices */
+       if (no_poweroff)
+               return 0;
+
        /* According to Xceive technical support, the "powerdown" register
           was removed in newer versions of the firmware.  The "supported"
           way to sleep the tuner is to pull the reset pin low for 10ms */
@@ -900,6 +1115,23 @@ static int xc5000_release(struct dvb_frontend *fe)
        return 0;
 }
 
+static int xc5000_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       struct xc5000_config *p = priv_cfg;
+
+       dprintk(1, "%s()\n", __func__);
+
+       if (p->if_khz)
+               priv->if_khz = p->if_khz;
+
+       if (p->radio_input)
+               priv->radio_input = p->radio_input;
+
+       return 0;
+}
+
+
 static const struct dvb_tuner_ops xc5000_tuner_ops = {
        .info = {
                .name           = "Xceive XC5000",
@@ -912,16 +1144,18 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = {
        .init              = xc5000_init,
        .sleep             = xc5000_sleep,
 
+       .set_config        = xc5000_set_config,
        .set_params        = xc5000_set_params,
        .set_analog_params = xc5000_set_analog_params,
        .get_frequency     = xc5000_get_frequency,
+       .get_if_frequency  = xc5000_get_if_frequency,
        .get_bandwidth     = xc5000_get_bandwidth,
        .get_status        = xc5000_get_status
 };
 
 struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
                                   struct i2c_adapter *i2c,
-                                  struct xc5000_config *cfg)
+                                  const struct xc5000_config *cfg)
 {
        struct xc5000_priv *priv = NULL;
        int instance;
@@ -942,7 +1176,7 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
                break;
        case 1:
                /* new tuner instance */
-               priv->bandwidth = BANDWIDTH_6_MHZ;
+               priv->bandwidth = 6000000;
                fe->tuner_priv = priv;
                break;
        default:
@@ -958,6 +1192,19 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
                priv->if_khz = cfg->if_khz;
        }
 
+       if (priv->xtal_khz == 0)
+               priv->xtal_khz = cfg->xtal_khz;
+
+       if (priv->radio_input == 0)
+               priv->radio_input = cfg->radio_input;
+
+       /* don't override chip id if it's already been set
+          unless explicitly specified */
+       if ((priv->chip_id == 0) || (cfg->chip_id))
+               /* use default chip id if none specified, set to 0 so
+                  it can be overridden if this is a hybrid driver */
+               priv->chip_id = (cfg->chip_id) ? cfg->chip_id : 0;
+
        /* Check if firmware has been loaded. It is possible that another
           instance of the driver has loaded the firmware.
         */