unsigned int  size;
 };
 
+struct firmware_properties {
+       unsigned int    type;
+       v4l2_std_id     id;
+       v4l2_std_id     std_req;
+       unsigned int    scode_table;
+       int             scode_nr;
+};
+
 struct xc2028_data {
        struct list_head        xc2028_list;
        struct tuner_i2c_props  i2c_props;
 
        struct xc2028_ctrl      ctrl;
 
-       v4l2_std_id             firm_type;         /* video stds supported
-                                                       by current firmware */
-       fe_bandwidth_t          bandwidth;         /* Firmware bandwidth:
-                                                             6M, 7M or 8M */
-       int                     need_load_generic; /* The generic firmware
-                                                             were loaded? */
-       enum tuner_mode mode;
-       struct i2c_client       *i2c_client;
+       struct firmware_properties cur_fw;
 
        struct mutex lock;
 };
 
        priv->firm = NULL;
        priv->firm_size = 0;
-       priv->need_load_generic = 1;
+
+       memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
 }
 
 static int load_all_firmwares(struct dvb_frontend *fe)
        if (((type & ~SCODE) == 0) && (*id == 0))
                *id = V4L2_STD_PAL;
 
+       if (type & BASE)
+               type &= BASE_TYPES;
+       else if (type & SCODE)
+               type &= SCODE_TYPES;
+       else if (type & DTV_TYPES)
+               type = type & DTV_TYPES;
+
        /* Seek for exact match */
        for (i = 0; i < priv->firm_size; i++) {
                if ((type == priv->firm[i].type) && (*id == priv->firm[i].id))
                          v4l2_std_id std, fe_bandwidth_t bandwidth)
 {
        struct xc2028_data      *priv = fe->tuner_priv;
-       int                     rc;
+       int                     rc = 0;
+       unsigned int            type = 0;
+       struct firmware_properties new_fw;
        u16                     version, hwmodel;
-       v4l2_std_id             std0 = 0;
-       unsigned int            type0 = 0, type = 0;
-       int                     change_digital_bandwidth;
 
        tuner_dbg("%s called\n", __FUNCTION__);
 
                        return rc;
        }
 
-       tuner_dbg("I am in mode %u and I should switch to mode %i\n",
-                  priv->mode, new_mode);
-
-       /* first of all, determine whether we have switched the mode */
-       if (new_mode != priv->mode) {
-               priv->mode = new_mode;
-               priv->need_load_generic = 1;
-       }
-
-       change_digital_bandwidth = (priv->mode == T_DIGITAL_TV
-                                   && bandwidth != priv->bandwidth) ? 1 : 0;
-       tuner_dbg("old bandwidth %u, new bandwidth %u\n", priv->bandwidth,
-                  bandwidth);
-
-       if (priv->need_load_generic) {
-               /* Reset is needed before loading firmware */
-               rc = priv->tuner_callback(priv->video_dev,
-                                         XC2028_TUNER_RESET, 0);
-               if (rc < 0)
-                       return rc;
-
-               type0 = BASE;
-
-               if (priv->ctrl.type == XC2028_FIRM_MTS)
-                       type0 |= MTS;
-
-               if (bandwidth == BANDWIDTH_7_MHZ ||
-                   bandwidth == BANDWIDTH_8_MHZ)
-                       type0 |= F8MHZ;
-
-               /* FIXME: How to load FM and FM|INPUT1 firmwares? */
-
-               rc = load_firmware(fe, type0, &std0);
-               if (rc < 0) {
-                       tuner_err("Error %d while loading generic firmware\n",
-                                 rc);
-                       return rc;
-               }
-
-               priv->need_load_generic = 0;
-               priv->firm_type = 0;
-               if (priv->mode == T_DIGITAL_TV)
-                       change_digital_bandwidth = 1;
-       }
+       if (priv->ctrl.type == XC2028_FIRM_MTS)
+               type |= MTS;
+       if (bandwidth == BANDWIDTH_7_MHZ || bandwidth == BANDWIDTH_8_MHZ)
+               type |= F8MHZ;
 
-       tuner_dbg("I should change bandwidth %u\n", change_digital_bandwidth);
+       /* FIXME: How to load FM and FM|INPUT1 firmwares? */
 
-       if (change_digital_bandwidth) {
+       if (new_mode == T_DIGITAL_TV) {
                if (priv->ctrl.d2633)
                        type |= D2633;
                else
                        type |= D2620;
 
-               /* FIXME: When should select a DTV78 firmware?
-                */
                switch (bandwidth) {
                case BANDWIDTH_8_MHZ:
                        type |= DTV8;
                        /* FIXME: Should allow select also ATSC */
                        type |= DTV6 | QAM;
                        break;
-
                default:
                        tuner_err("error: bandwidth not supported.\n");
                };
-               priv->bandwidth = bandwidth;
        }
 
-       if (!change_digital_bandwidth && priv->mode == T_DIGITAL_TV)
-               return 0;
+       new_fw.type = type;
+       new_fw.id = std;
+       new_fw.std_req = std;
+       new_fw.scode_table = SCODE | priv->ctrl.scode_table;
+       new_fw.scode_nr = 0;
+
+       tuner_dbg("checking firmware, user requested type=");
+       if (debug) {
+               dump_firm_type(new_fw.type);
+               printk("(%x), id %016llx, scode_tbl ", new_fw.type,
+                      (unsigned long long)new_fw.std_req);
+               dump_firm_type(priv->ctrl.scode_table);
+               printk("(%x), scode_nr %d\n", priv->ctrl.scode_table,
+                      new_fw.scode_nr);
+       }
+
+       /* No need to reload base firmware if it matches */
+       if (((BASE | new_fw.type) & BASE_TYPES) ==
+           (priv->cur_fw.type & BASE_TYPES)) {
+               tuner_dbg("BASE firmware not changed.\n");
+               goto skip_base;
+       }
+
+       /* Updating BASE - forget about all currently loaded firmware */
+       memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+
+       /* Reset is needed before loading firmware */
+       rc = priv->tuner_callback(priv->video_dev,
+                                 XC2028_TUNER_RESET, 0);
+       if (rc < 0)
+               goto fail;
+
+       rc = load_firmware(fe, BASE | new_fw.type, &new_fw.id);
+       if (rc < 0) {
+               tuner_err("Error %d while loading base firmware\n",
+                         rc);
+               goto fail;
+       }
 
        /* Load INIT1, if needed */
        tuner_dbg("Load init1 firmware, if exists\n");
-       type0 = BASE | INIT1;
-       if (priv->ctrl.type == XC2028_FIRM_MTS)
-               type0 |= MTS;
 
-       /* FIXME: Should handle errors - if INIT1 found */
-       rc = load_firmware(fe, type0, &std0);
+       rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &new_fw.id);
+       if (rc < 0 && rc != -ENOENT) {
+               tuner_err("Error %d while loading init1 firmware\n",
+                         rc);
+               goto fail;
+       }
 
-       /* FIXME: Should add support for FM radio
+skip_base:
+       /*
+        * No need to reload standard specific firmware if base firmware
+        * was not reloaded and requested video standards have not changed.
         */
-
-       if (priv->ctrl.type == XC2028_FIRM_MTS)
-               type |= MTS;
-
-       if (priv->firm_type & std) {
+       if (priv->cur_fw.type == (BASE | new_fw.type) &&
+           priv->cur_fw.std_req == std) {
                tuner_dbg("Std-specific firmware already loaded.\n");
-               return 0;
+               goto skip_std_specific;
        }
 
+       /* Reloading std-specific firmware forces a SCODE update */
+       priv->cur_fw.scode_table = 0;
+
        /* Add audio hack to std mask */
-       std |= parse_audio_std_option();
+       if (new_mode == T_ANALOG_TV)
+               new_fw.id |= parse_audio_std_option();
 
-       rc = load_firmware(fe, type, &std);
+       rc = load_firmware(fe, new_fw.type, &new_fw.id);
        if (rc < 0)
-               return rc;
+               goto fail;
+
+skip_std_specific:
+       if (priv->cur_fw.scode_table == new_fw.scode_table &&
+           priv->cur_fw.scode_nr == new_fw.scode_nr) {
+               tuner_dbg("SCODE firmware already loaded.\n");
+               goto check_device;
+       }
 
        /* Load SCODE firmware, if exists */
-       tuner_dbg("Trying to load scode 0\n");
-       type |= SCODE;
+       tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
 
-       rc = load_scode(fe, type, &std, 0);
+       rc = load_scode(fe, new_fw.type | new_fw.scode_table,
+                       &new_fw.id, new_fw.scode_nr);
 
+check_device:
        xc2028_get_reg(priv, 0x0004, &version);
        xc2028_get_reg(priv, 0x0008, &hwmodel);
 
                   hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
                   (version & 0xf0) >> 4, version & 0xf);
 
-       priv->firm_type = std;
+       memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
+
+       /*
+        * By setting BASE in cur_fw.type only after successfully loading all
+        * firmwares, we can:
+        * 1. Identify that BASE firmware with type=0 has been loaded;
+        * 2. Tell whether BASE firmware was just changed the next time through.
+        */
+       priv->cur_fw.type |= BASE;
 
        return 0;
+
+fail:
+       memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+       if (rc == -ENOENT)
+               rc = -EINVAL;
+       return rc;
 }
 
 static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
        mutex_lock(&priv->lock);
 
        /* HACK: It seems that specific firmware need to be reloaded
-          when freq is changed */
-
-       priv->firm_type = 0;
-
-       /* Reset GPIO 1 */
-       rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0);
-       if (rc < 0)
-               goto ret;
+          when watching analog TV and freq is changed */
+       if (new_mode != T_DIGITAL_TV)
+               priv->cur_fw.type = 0;
 
-       msleep(10);
        tuner_dbg("should set frequency %d kHz\n", freq / 1000);
 
        if (check_firmware(fe, new_mode, std, bandwidth) < 0)
 
        if (new_mode == T_DIGITAL_TV) {
                offset = 2750000;
-               if (priv->bandwidth == BANDWIDTH_7_MHZ)
+               if (priv->cur_fw.type & DTV7)
                        offset -= 500000;
        }
 
                        return NULL;
                }
 
-               priv->bandwidth = BANDWIDTH_6_MHZ;
-               priv->need_load_generic = 1;
-               priv->mode = T_UNINITIALIZED;
                priv->i2c_props.addr = cfg->i2c_addr;
                priv->i2c_props.adap = cfg->i2c_adap;
                priv->video_dev = video_dev;