"%s firmware -> %s\n",
                intel_uc_fw_type_repr(uc_fw->type),
                status == INTEL_UC_FIRMWARE_SELECTED ?
-               uc_fw->path : intel_uc_fw_status_repr(status));
+               uc_fw->file_selected.path : intel_uc_fw_status_repr(status));
 }
 #endif
 
  *
  * Note that RKL and ADL-S have the same GuC/HuC device ID's and use the same
  * firmware as TGL.
+ *
+ * Version numbers:
+ * Originally, the driver required an exact match major/minor/patch furmware
+ * file and only supported that one version for any given platform. However,
+ * the new direction from upstream is to be backwards compatible with all
+ * prior releases and to be as flexible as possible as to what firmware is
+ * loaded.
+ *
+ * For GuC, the major version number signifies a backwards breaking API change.
+ * So, new format GuC firmware files are labelled by their major version only.
+ * For HuC, there is no KMD interaction, hence no version matching requirement.
+ * So, new format HuC firmware files have no version number at all.
+ *
+ * All of which means that the table below must keep all old format files with
+ * full three point version number. But newer files have reduced requirements.
+ * Having said that, the driver still needs to track the minor version number
+ * for GuC at least. As it is useful to report to the user that they are not
+ * running with a recent enough version for all KMD supported features,
+ * security fixes, etc. to be enabled.
  */
-#define INTEL_GUC_FIRMWARE_DEFS(fw_def, guc_def) \
-       fw_def(DG2,          0, guc_def(dg2,  70, 4, 1)) \
-       fw_def(ALDERLAKE_P,  0, guc_def(adlp, 70, 1, 1)) \
-       fw_def(ALDERLAKE_S,  0, guc_def(tgl,  70, 1, 1)) \
-       fw_def(DG1,          0, guc_def(dg1,  70, 1, 1)) \
-       fw_def(ROCKETLAKE,   0, guc_def(tgl,  70, 1, 1)) \
-       fw_def(TIGERLAKE,    0, guc_def(tgl,  70, 1, 1)) \
-       fw_def(JASPERLAKE,   0, guc_def(ehl,  70, 1, 1)) \
-       fw_def(ELKHARTLAKE,  0, guc_def(ehl,  70, 1, 1)) \
-       fw_def(ICELAKE,      0, guc_def(icl,  70, 1, 1)) \
-       fw_def(COMETLAKE,    5, guc_def(cml,  70, 1, 1)) \
-       fw_def(COMETLAKE,    0, guc_def(kbl,  70, 1, 1)) \
-       fw_def(COFFEELAKE,   0, guc_def(kbl,  70, 1, 1)) \
-       fw_def(GEMINILAKE,   0, guc_def(glk,  70, 1, 1)) \
-       fw_def(KABYLAKE,     0, guc_def(kbl,  70, 1, 1)) \
-       fw_def(BROXTON,      0, guc_def(bxt,  70, 1, 1)) \
-       fw_def(SKYLAKE,      0, guc_def(skl,  70, 1, 1))
-
-#define INTEL_GUC_FIRMWARE_DEFS_FALLBACK(fw_def, guc_def) \
-       fw_def(ALDERLAKE_P,  0, guc_def(adlp, 69, 0, 3)) \
-       fw_def(ALDERLAKE_S,  0, guc_def(tgl,  69, 0, 3))
-
-#define INTEL_HUC_FIRMWARE_DEFS(fw_def, huc_def) \
-       fw_def(ALDERLAKE_P,  0, huc_def(tgl,  7, 9, 3)) \
-       fw_def(ALDERLAKE_S,  0, huc_def(tgl,  7, 9, 3)) \
-       fw_def(DG1,          0, huc_def(dg1,  7, 9, 3)) \
-       fw_def(ROCKETLAKE,   0, huc_def(tgl,  7, 9, 3)) \
-       fw_def(TIGERLAKE,    0, huc_def(tgl,  7, 9, 3)) \
-       fw_def(JASPERLAKE,   0, huc_def(ehl,  9, 0, 0)) \
-       fw_def(ELKHARTLAKE,  0, huc_def(ehl,  9, 0, 0)) \
-       fw_def(ICELAKE,      0, huc_def(icl,  9, 0, 0)) \
-       fw_def(COMETLAKE,    5, huc_def(cml,  4, 0, 0)) \
-       fw_def(COMETLAKE,    0, huc_def(kbl,  4, 0, 0)) \
-       fw_def(COFFEELAKE,   0, huc_def(kbl,  4, 0, 0)) \
-       fw_def(GEMINILAKE,   0, huc_def(glk,  4, 0, 0)) \
-       fw_def(KABYLAKE,     0, huc_def(kbl,  4, 0, 0)) \
-       fw_def(BROXTON,      0, huc_def(bxt,  2, 0, 0)) \
-       fw_def(SKYLAKE,      0, huc_def(skl,  2, 0, 0))
-
-#define __MAKE_UC_FW_PATH(prefix_, name_, major_, minor_, patch_) \
+#define INTEL_GUC_FIRMWARE_DEFS(fw_def, guc_maj, guc_mmp) \
+       fw_def(DG2,          0, guc_mmp(dg2,  70, 4, 1)) \
+       fw_def(ALDERLAKE_P,  0, guc_mmp(adlp, 70, 1, 1)) \
+       fw_def(ALDERLAKE_P,  0, guc_mmp(adlp, 69, 0, 3)) \
+       fw_def(ALDERLAKE_S,  0, guc_mmp(tgl,  70, 1, 1)) \
+       fw_def(ALDERLAKE_S,  0, guc_mmp(tgl,  69, 0, 3)) \
+       fw_def(DG1,          0, guc_mmp(dg1,  70, 1, 1)) \
+       fw_def(ROCKETLAKE,   0, guc_mmp(tgl,  70, 1, 1)) \
+       fw_def(TIGERLAKE,    0, guc_mmp(tgl,  70, 1, 1)) \
+       fw_def(JASPERLAKE,   0, guc_mmp(ehl,  70, 1, 1)) \
+       fw_def(ELKHARTLAKE,  0, guc_mmp(ehl,  70, 1, 1)) \
+       fw_def(ICELAKE,      0, guc_mmp(icl,  70, 1, 1)) \
+       fw_def(COMETLAKE,    5, guc_mmp(cml,  70, 1, 1)) \
+       fw_def(COMETLAKE,    0, guc_mmp(kbl,  70, 1, 1)) \
+       fw_def(COFFEELAKE,   0, guc_mmp(kbl,  70, 1, 1)) \
+       fw_def(GEMINILAKE,   0, guc_mmp(glk,  70, 1, 1)) \
+       fw_def(KABYLAKE,     0, guc_mmp(kbl,  70, 1, 1)) \
+       fw_def(BROXTON,      0, guc_mmp(bxt,  70, 1, 1)) \
+       fw_def(SKYLAKE,      0, guc_mmp(skl,  70, 1, 1))
+
+#define INTEL_HUC_FIRMWARE_DEFS(fw_def, huc_raw, huc_mmp) \
+       fw_def(ALDERLAKE_P,  0, huc_mmp(tgl,  7, 9, 3)) \
+       fw_def(ALDERLAKE_S,  0, huc_mmp(tgl,  7, 9, 3)) \
+       fw_def(DG1,          0, huc_mmp(dg1,  7, 9, 3)) \
+       fw_def(ROCKETLAKE,   0, huc_mmp(tgl,  7, 9, 3)) \
+       fw_def(TIGERLAKE,    0, huc_mmp(tgl,  7, 9, 3)) \
+       fw_def(JASPERLAKE,   0, huc_mmp(ehl,  9, 0, 0)) \
+       fw_def(ELKHARTLAKE,  0, huc_mmp(ehl,  9, 0, 0)) \
+       fw_def(ICELAKE,      0, huc_mmp(icl,  9, 0, 0)) \
+       fw_def(COMETLAKE,    5, huc_mmp(cml,  4, 0, 0)) \
+       fw_def(COMETLAKE,    0, huc_mmp(kbl,  4, 0, 0)) \
+       fw_def(COFFEELAKE,   0, huc_mmp(kbl,  4, 0, 0)) \
+       fw_def(GEMINILAKE,   0, huc_mmp(glk,  4, 0, 0)) \
+       fw_def(KABYLAKE,     0, huc_mmp(kbl,  4, 0, 0)) \
+       fw_def(BROXTON,      0, huc_mmp(bxt,  2, 0, 0)) \
+       fw_def(SKYLAKE,      0, huc_mmp(skl,  2, 0, 0))
+
+/*
+ * Set of macros for producing a list of filenames from the above table.
+ */
+#define __MAKE_UC_FW_PATH_BLANK(prefix_, name_) \
+       "i915/" \
+       __stringify(prefix_) name_ ".bin"
+
+#define __MAKE_UC_FW_PATH_MAJOR(prefix_, name_, major_) \
+       "i915/" \
+       __stringify(prefix_) name_ \
+       __stringify(major_) ".bin"
+
+#define __MAKE_UC_FW_PATH_MMP(prefix_, name_, major_, minor_, patch_) \
        "i915/" \
        __stringify(prefix_) name_ \
        __stringify(major_) "." \
        __stringify(minor_) "." \
        __stringify(patch_) ".bin"
 
-#define MAKE_GUC_FW_PATH(prefix_, major_, minor_, patch_) \
-       __MAKE_UC_FW_PATH(prefix_, "_guc_", major_, minor_, patch_)
+/* Minor for internal driver use, not part of file name */
+#define MAKE_GUC_FW_PATH_MAJOR(prefix_, major_, minor_) \
+       __MAKE_UC_FW_PATH_MAJOR(prefix_, "_guc_", major_)
 
-#define MAKE_HUC_FW_PATH(prefix_, major_, minor_, bld_num_) \
-       __MAKE_UC_FW_PATH(prefix_, "_huc_", major_, minor_, bld_num_)
+#define MAKE_GUC_FW_PATH_MMP(prefix_, major_, minor_, patch_) \
+       __MAKE_UC_FW_PATH_MMP(prefix_, "_guc_", major_, minor_, patch_)
 
-/* All blobs need to be declared via MODULE_FIRMWARE() */
+#define MAKE_HUC_FW_PATH_BLANK(prefix_) \
+       __MAKE_UC_FW_PATH_BLANK(prefix_, "_huc")
+
+#define MAKE_HUC_FW_PATH_MMP(prefix_, major_, minor_, patch_) \
+       __MAKE_UC_FW_PATH_MMP(prefix_, "_huc_", major_, minor_, patch_)
+
+/*
+ * All blobs need to be declared via MODULE_FIRMWARE().
+ * This first expansion of the table macros is solely to provide
+ * that declaration.
+ */
 #define INTEL_UC_MODULE_FW(platform_, revid_, uc_) \
        MODULE_FIRMWARE(uc_);
 
-INTEL_GUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_GUC_FW_PATH)
-INTEL_GUC_FIRMWARE_DEFS_FALLBACK(INTEL_UC_MODULE_FW, MAKE_GUC_FW_PATH)
-INTEL_HUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_HUC_FW_PATH)
+INTEL_GUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_GUC_FW_PATH_MAJOR, MAKE_GUC_FW_PATH_MMP)
+INTEL_HUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_HUC_FW_PATH_BLANK, MAKE_HUC_FW_PATH_MMP)
 
-/* The below structs and macros are used to iterate across the list of blobs */
+/*
+ * The next expansion of the table macros (in __uc_fw_auto_select below) provides
+ * actual data structures with both the filename and the version information.
+ * These structure arrays are then iterated over to the list of suitable files
+ * for the current platform and to then attempt to load those files, in the order
+ * listed, until one is successfully found.
+ */
 struct __packed uc_fw_blob {
+       const char *path;
+       bool legacy;
        u8 major;
        u8 minor;
-       const char *path;
+       u8 patch;
 };
 
-#define UC_FW_BLOB(major_, minor_, path_) \
-       { .major = major_, .minor = minor_, .path = path_ }
+#define UC_FW_BLOB_BASE(major_, minor_, patch_, path_) \
+       .major = major_, \
+       .minor = minor_, \
+       .patch = patch_, \
+       .path = path_,
+
+#define UC_FW_BLOB_NEW(major_, minor_, patch_, path_) \
+       { UC_FW_BLOB_BASE(major_, minor_, patch_, path_) \
+         .legacy = false }
+
+#define UC_FW_BLOB_OLD(major_, minor_, patch_, path_) \
+       { UC_FW_BLOB_BASE(major_, minor_, patch_, path_) \
+         .legacy = true }
 
-#define GUC_FW_BLOB(prefix_, major_, minor_, patch_) \
-       UC_FW_BLOB(major_, minor_, \
-                  MAKE_GUC_FW_PATH(prefix_, major_, minor_, patch_))
+#define GUC_FW_BLOB(prefix_, major_, minor_) \
+       UC_FW_BLOB_NEW(major_, minor_, 0, \
+                      MAKE_GUC_FW_PATH_MAJOR(prefix_, major_, minor_))
 
-#define HUC_FW_BLOB(prefix_, major_, minor_, bld_num_) \
-       UC_FW_BLOB(major_, minor_, \
-                  MAKE_HUC_FW_PATH(prefix_, major_, minor_, bld_num_))
+#define GUC_FW_BLOB_MMP(prefix_, major_, minor_, patch_) \
+       UC_FW_BLOB_OLD(major_, minor_, patch_, \
+                      MAKE_GUC_FW_PATH_MMP(prefix_, major_, minor_, patch_))
+
+#define HUC_FW_BLOB(prefix_) \
+       UC_FW_BLOB_NEW(0, 0, 0, MAKE_HUC_FW_PATH_BLANK(prefix_))
+
+#define HUC_FW_BLOB_MMP(prefix_, major_, minor_, patch_) \
+       UC_FW_BLOB_OLD(major_, minor_, patch_, \
+                      MAKE_HUC_FW_PATH_MMP(prefix_, major_, minor_, patch_))
 
 struct __packed uc_fw_platform_requirement {
        enum intel_platform p;
 __uc_fw_auto_select(struct drm_i915_private *i915, struct intel_uc_fw *uc_fw)
 {
        static const struct uc_fw_platform_requirement blobs_guc[] = {
-               INTEL_GUC_FIRMWARE_DEFS(MAKE_FW_LIST, GUC_FW_BLOB)
-       };
-       static const struct uc_fw_platform_requirement blobs_guc_fallback[] = {
-               INTEL_GUC_FIRMWARE_DEFS_FALLBACK(MAKE_FW_LIST, GUC_FW_BLOB)
+               INTEL_GUC_FIRMWARE_DEFS(MAKE_FW_LIST, GUC_FW_BLOB, GUC_FW_BLOB_MMP)
        };
        static const struct uc_fw_platform_requirement blobs_huc[] = {
-               INTEL_HUC_FIRMWARE_DEFS(MAKE_FW_LIST, HUC_FW_BLOB)
+               INTEL_HUC_FIRMWARE_DEFS(MAKE_FW_LIST, HUC_FW_BLOB, HUC_FW_BLOB_MMP)
        };
        static const struct fw_blobs_by_type blobs_all[INTEL_UC_FW_NUM_TYPES] = {
                [INTEL_UC_FW_TYPE_GUC] = { blobs_guc, ARRAY_SIZE(blobs_guc) },
                [INTEL_UC_FW_TYPE_HUC] = { blobs_huc, ARRAY_SIZE(blobs_huc) },
        };
+       static bool verified;
        const struct uc_fw_platform_requirement *fw_blobs;
        enum intel_platform p = INTEL_INFO(i915)->platform;
        u32 fw_count;
        fw_count = blobs_all[uc_fw->type].count;
 
        for (i = 0; i < fw_count && p <= fw_blobs[i].p; i++) {
-               if (p == fw_blobs[i].p && rev >= fw_blobs[i].rev) {
-                       const struct uc_fw_blob *blob = &fw_blobs[i].blob;
-                       uc_fw->path = blob->path;
-                       uc_fw->wanted_path = blob->path;
-                       uc_fw->major_ver_wanted = blob->major;
-                       uc_fw->minor_ver_wanted = blob->minor;
-                       break;
-               }
-       }
+               const struct uc_fw_blob *blob = &fw_blobs[i].blob;
 
-       if (uc_fw->type == INTEL_UC_FW_TYPE_GUC) {
-               const struct uc_fw_platform_requirement *blobs = blobs_guc_fallback;
-               u32 count = ARRAY_SIZE(blobs_guc_fallback);
+               if (p != fw_blobs[i].p)
+                       continue;
 
-               for (i = 0; i < count && p <= blobs[i].p; i++) {
-                       if (p == blobs[i].p && rev >= blobs[i].rev) {
-                               const struct uc_fw_blob *blob = &blobs[i].blob;
+               if (rev < fw_blobs[i].rev)
+                       continue;
 
-                               uc_fw->fallback.path = blob->path;
-                               uc_fw->fallback.major_ver = blob->major;
-                               uc_fw->fallback.minor_ver = blob->minor;
-                               break;
-                       }
+               if (uc_fw->file_selected.path) {
+                       if (uc_fw->file_selected.path == blob->path)
+                               uc_fw->file_selected.path = NULL;
+
+                       continue;
                }
+
+               uc_fw->file_selected.path = blob->path;
+               uc_fw->file_wanted.path = blob->path;
+               uc_fw->file_wanted.major_ver = blob->major;
+               uc_fw->file_wanted.minor_ver = blob->minor;
+               break;
        }
 
        /* make sure the list is ordered as expected */
-       if (IS_ENABLED(CONFIG_DRM_I915_SELFTEST)) {
+       if (IS_ENABLED(CONFIG_DRM_I915_SELFTEST) && !verified) {
+               verified = true;
+
                for (i = 1; i < fw_count; i++) {
+                       /* Next platform is good: */
                        if (fw_blobs[i].p < fw_blobs[i - 1].p)
                                continue;
 
+                       /* Next platform revision is good: */
                        if (fw_blobs[i].p == fw_blobs[i - 1].p &&
                            fw_blobs[i].rev < fw_blobs[i - 1].rev)
                                continue;
 
-                       drm_err(&i915->drm, "Invalid FW blob order: %s r%u comes before %s r%u\n",
-                               intel_platform_name(fw_blobs[i - 1].p),
-                               fw_blobs[i - 1].rev,
-                               intel_platform_name(fw_blobs[i].p),
-                               fw_blobs[i].rev);
+                       /* Platform/revision must be in order: */
+                       if (fw_blobs[i].p != fw_blobs[i - 1].p ||
+                           fw_blobs[i].rev != fw_blobs[i - 1].rev)
+                               goto bad;
+
+                       /* Next major version is good: */
+                       if (fw_blobs[i].blob.major < fw_blobs[i - 1].blob.major)
+                               continue;
+
+                       /* New must be before legacy: */
+                       if (!fw_blobs[i].blob.legacy && fw_blobs[i - 1].blob.legacy)
+                               goto bad;
+
+                       /* New to legacy also means 0.0 to X.Y (HuC), or X.0 to X.Y (GuC) */
+                       if (fw_blobs[i].blob.legacy && !fw_blobs[i - 1].blob.legacy) {
+                               if (!fw_blobs[i - 1].blob.major)
+                                       continue;
+
+                               if (fw_blobs[i].blob.major == fw_blobs[i - 1].blob.major)
+                                       continue;
+                       }
+
+                       /* Major versions must be in order: */
+                       if (fw_blobs[i].blob.major != fw_blobs[i - 1].blob.major)
+                               goto bad;
+
+                       /* Next minor version is good: */
+                       if (fw_blobs[i].blob.minor < fw_blobs[i - 1].blob.minor)
+                               continue;
 
-                       uc_fw->path = NULL;
+                       /* Minor versions must be in order: */
+                       if (fw_blobs[i].blob.minor != fw_blobs[i - 1].blob.minor)
+                               goto bad;
+
+                       /* Patch versions must be in order: */
+                       if (fw_blobs[i].blob.patch <= fw_blobs[i - 1].blob.patch)
+                               continue;
+
+bad:
+                       drm_err(&i915->drm, "\x1B[35;1mInvalid FW blob order: %s r%u %s%d.%d.%d comes before %s r%u %s%d.%d.%d\n",
+                               intel_platform_name(fw_blobs[i - 1].p), fw_blobs[i - 1].rev,
+                               fw_blobs[i - 1].blob.legacy ? "L" : "v",
+                               fw_blobs[i - 1].blob.major,
+                               fw_blobs[i - 1].blob.minor,
+                               fw_blobs[i - 1].blob.patch,
+                               intel_platform_name(fw_blobs[i].p), fw_blobs[i].rev,
+                               fw_blobs[i].blob.legacy ? "L" : "v",
+                               fw_blobs[i].blob.major,
+                               fw_blobs[i].blob.minor,
+                               fw_blobs[i].blob.patch);
+
+                       uc_fw->file_selected.path = NULL;
                }
        }
 }
        }
 
        if (unlikely(path)) {
-               uc_fw->path = path;
+               uc_fw->file_selected.path = path;
                uc_fw->user_overridden = true;
        }
 }
         */
        BUILD_BUG_ON(INTEL_UC_FIRMWARE_UNINITIALIZED);
        GEM_BUG_ON(uc_fw->status);
-       GEM_BUG_ON(uc_fw->path);
+       GEM_BUG_ON(uc_fw->file_selected.path);
 
        uc_fw->type = type;
 
                __uc_fw_user_override(i915, uc_fw);
        }
 
-       intel_uc_fw_change_status(uc_fw, uc_fw->path ? *uc_fw->path ?
+       intel_uc_fw_change_status(uc_fw, uc_fw->file_selected.path ? *uc_fw->file_selected.path ?
                                  INTEL_UC_FIRMWARE_SELECTED :
                                  INTEL_UC_FIRMWARE_DISABLED :
                                  INTEL_UC_FIRMWARE_NOT_SUPPORTED);
 
        if (i915_inject_probe_error(i915, e)) {
                /* non-existing blob */
-               uc_fw->path = "<invalid>";
+               uc_fw->file_selected.path = "<invalid>";
                uc_fw->user_overridden = user;
        } else if (i915_inject_probe_error(i915, e)) {
                /* require next major version */
-               uc_fw->major_ver_wanted += 1;
-               uc_fw->minor_ver_wanted = 0;
+               uc_fw->file_wanted.major_ver += 1;
+               uc_fw->file_wanted.minor_ver = 0;
                uc_fw->user_overridden = user;
        } else if (i915_inject_probe_error(i915, e)) {
                /* require next minor version */
-               uc_fw->minor_ver_wanted += 1;
+               uc_fw->file_wanted.minor_ver += 1;
                uc_fw->user_overridden = user;
-       } else if (uc_fw->major_ver_wanted &&
+       } else if (uc_fw->file_wanted.major_ver &&
                   i915_inject_probe_error(i915, e)) {
                /* require prev major version */
-               uc_fw->major_ver_wanted -= 1;
-               uc_fw->minor_ver_wanted = 0;
+               uc_fw->file_wanted.major_ver -= 1;
+               uc_fw->file_wanted.minor_ver = 0;
                uc_fw->user_overridden = user;
-       } else if (uc_fw->minor_ver_wanted &&
+       } else if (uc_fw->file_wanted.minor_ver &&
                   i915_inject_probe_error(i915, e)) {
                /* require prev minor version - hey, this should work! */
-               uc_fw->minor_ver_wanted -= 1;
+               uc_fw->file_wanted.minor_ver -= 1;
                uc_fw->user_overridden = user;
        } else if (user && i915_inject_probe_error(i915, e)) {
                /* officially unsupported platform */
-               uc_fw->major_ver_wanted = 0;
-               uc_fw->minor_ver_wanted = 0;
+               uc_fw->file_wanted.major_ver = 0;
+               uc_fw->file_wanted.minor_ver = 0;
                uc_fw->user_overridden = true;
        }
 }
        u32 *dw = (u32 *)fw->data;
        u32 version = dw[HUC_GSC_VERSION_DW];
 
-       uc_fw->major_ver_found = FIELD_GET(HUC_GSC_MAJOR_VER_MASK, version);
-       uc_fw->minor_ver_found = FIELD_GET(HUC_GSC_MINOR_VER_MASK, version);
+       uc_fw->file_selected.major_ver = FIELD_GET(HUC_GSC_MAJOR_VER_MASK, version);
+       uc_fw->file_selected.minor_ver = FIELD_GET(HUC_GSC_MINOR_VER_MASK, version);
 
        return 0;
 }
        /* Check the size of the blob before examining buffer contents */
        if (unlikely(fw->size < sizeof(struct uc_css_header))) {
                drm_warn(&i915->drm, "%s firmware %s: invalid size: %zu < %zu\n",
-                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->path,
+                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
                         fw->size, sizeof(struct uc_css_header));
                return -ENODATA;
        }
        if (unlikely(size != sizeof(struct uc_css_header))) {
                drm_warn(&i915->drm,
                         "%s firmware %s: unexpected header size: %zu != %zu\n",
-                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->path,
+                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
                         fw->size, sizeof(struct uc_css_header));
                return -EPROTO;
        }
        size = sizeof(struct uc_css_header) + uc_fw->ucode_size + uc_fw->rsa_size;
        if (unlikely(fw->size < size)) {
                drm_warn(&i915->drm, "%s firmware %s: invalid size: %zu < %zu\n",
-                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->path,
+                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
                         fw->size, size);
                return -ENOEXEC;
        }
        size = __intel_uc_fw_get_upload_size(uc_fw);
        if (unlikely(size >= i915->wopcm.size)) {
                drm_warn(&i915->drm, "%s firmware %s: invalid size: %zu > %zu\n",
-                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->path,
+                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
                         size, (size_t)i915->wopcm.size);
                return -E2BIG;
        }
 
        /* Get version numbers from the CSS header */
-       uc_fw->major_ver_found = FIELD_GET(CSS_SW_VERSION_UC_MAJOR,
-                                          css->sw_version);
-       uc_fw->minor_ver_found = FIELD_GET(CSS_SW_VERSION_UC_MINOR,
-                                          css->sw_version);
+       uc_fw->file_selected.major_ver = FIELD_GET(CSS_SW_VERSION_UC_MAJOR,
+                                                  css->sw_version);
+       uc_fw->file_selected.minor_ver = FIELD_GET(CSS_SW_VERSION_UC_MINOR,
+                                                  css->sw_version);
 
        if (uc_fw->type == INTEL_UC_FW_TYPE_GUC)
                uc_fw->private_data_size = css->private_data_size;
 int intel_uc_fw_fetch(struct intel_uc_fw *uc_fw)
 {
        struct drm_i915_private *i915 = __uc_fw_to_gt(uc_fw)->i915;
+       struct intel_uc_fw_file file_ideal;
        struct device *dev = i915->drm.dev;
        struct drm_i915_gem_object *obj;
        const struct firmware *fw = NULL;
+       bool old_ver = false;
        int err;
 
        GEM_BUG_ON(!i915->wopcm.size);
        __force_fw_fetch_failures(uc_fw, -EINVAL);
        __force_fw_fetch_failures(uc_fw, -ESTALE);
 
-       err = firmware_request_nowarn(&fw, uc_fw->path, dev);
-       if (err && !intel_uc_fw_is_overridden(uc_fw) && uc_fw->fallback.path) {
-               err = firmware_request_nowarn(&fw, uc_fw->fallback.path, dev);
-               if (!err) {
-                       drm_notice(&i915->drm,
-                                  "%s firmware %s is recommended, but only %s was found\n",
-                                  intel_uc_fw_type_repr(uc_fw->type),
-                                  uc_fw->wanted_path,
-                                  uc_fw->fallback.path);
-                       drm_info(&i915->drm,
-                                "Consider updating your linux-firmware pkg or downloading from %s\n",
-                                INTEL_UC_FIRMWARE_URL);
-
-                       uc_fw->path = uc_fw->fallback.path;
-                       uc_fw->major_ver_wanted = uc_fw->fallback.major_ver;
-                       uc_fw->minor_ver_wanted = uc_fw->fallback.minor_ver;
+       err = firmware_request_nowarn(&fw, uc_fw->file_selected.path, dev);
+       memcpy(&file_ideal, &uc_fw->file_wanted, sizeof(file_ideal));
+       if (!err || intel_uc_fw_is_overridden(uc_fw))
+               goto done;
+
+       while (err == -ENOENT) {
+               __uc_fw_auto_select(i915, uc_fw);
+               if (!uc_fw->file_selected.path) {
+                       /*
+                        * No more options! But set the path back to something
+                        * valid just in case it gets dereferenced.
+                        */
+                       uc_fw->file_selected.path = file_ideal.path;
+
+                       /* Also, preserve the version that was really wanted */
+                       memcpy(&uc_fw->file_wanted, &file_ideal, sizeof(uc_fw->file_wanted));
+                       break;
                }
+
+               err = firmware_request_nowarn(&fw, uc_fw->file_selected.path, dev);
        }
+
        if (err)
                goto fail;
 
+       old_ver = true;
+done:
        if (uc_fw->loaded_via_gsc)
                err = check_gsc_manifest(fw, uc_fw);
        else
        if (err)
                goto fail;
 
-       if (uc_fw->major_ver_found != uc_fw->major_ver_wanted ||
-           uc_fw->minor_ver_found < uc_fw->minor_ver_wanted) {
-               drm_notice(&i915->drm, "%s firmware %s: unexpected version: %u.%u != %u.%u\n",
-                          intel_uc_fw_type_repr(uc_fw->type), uc_fw->path,
-                          uc_fw->major_ver_found, uc_fw->minor_ver_found,
-                          uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted);
-               if (!intel_uc_fw_is_overridden(uc_fw)) {
-                       err = -ENOEXEC;
-                       goto fail;
+       if (uc_fw->file_wanted.major_ver) {
+               /* Check the file's major version was as it claimed */
+               if (uc_fw->file_selected.major_ver != uc_fw->file_wanted.major_ver) {
+                       drm_notice(&i915->drm, "%s firmware %s: unexpected version: %u.%u != %u.%u\n",
+                                  intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
+                                  uc_fw->file_selected.major_ver, uc_fw->file_selected.minor_ver,
+                                  uc_fw->file_wanted.major_ver, uc_fw->file_wanted.minor_ver);
+                       if (!intel_uc_fw_is_overridden(uc_fw)) {
+                               err = -ENOEXEC;
+                               goto fail;
+                       }
+               } else {
+                       if (uc_fw->file_selected.minor_ver < uc_fw->file_wanted.minor_ver)
+                               old_ver = true;
                }
        }
 
+       if (old_ver) {
+               /* Preserve the version that was really wanted */
+               memcpy(&uc_fw->file_wanted, &file_ideal, sizeof(uc_fw->file_wanted));
+
+               drm_notice(&i915->drm,
+                          "%s firmware %s (%d.%d) is recommended, but only %s (%d.%d) was found\n",
+                          intel_uc_fw_type_repr(uc_fw->type),
+                          uc_fw->file_wanted.path,
+                          uc_fw->file_wanted.major_ver, uc_fw->file_wanted.minor_ver,
+                          uc_fw->file_selected.path,
+                          uc_fw->file_selected.major_ver, uc_fw->file_selected.minor_ver);
+               drm_info(&i915->drm,
+                        "Consider updating your linux-firmware pkg or downloading from %s\n",
+                        INTEL_UC_FIRMWARE_URL);
+       }
+
        if (HAS_LMEM(i915)) {
                obj = i915_gem_object_create_lmem_from_data(i915, fw->data, fw->size);
                if (!IS_ERR(obj))
                                  INTEL_UC_FIRMWARE_ERROR);
 
        i915_probe_error(i915, "%s firmware %s: fetch failed with error %d\n",
-                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->path, err);
+                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path, err);
        drm_info(&i915->drm, "%s firmware(s) can be downloaded from %s\n",
                 intel_uc_fw_type_repr(uc_fw->type), INTEL_UC_FIRMWARE_URL);
 
 
 fail:
        i915_probe_error(gt->i915, "Failed to load %s firmware %s (%d)\n",
-                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->path,
+                        intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
                         err);
        intel_uc_fw_change_status(uc_fw, INTEL_UC_FIRMWARE_LOAD_FAIL);
        return err;
 void intel_uc_fw_dump(const struct intel_uc_fw *uc_fw, struct drm_printer *p)
 {
        drm_printf(p, "%s firmware: %s\n",
-                  intel_uc_fw_type_repr(uc_fw->type), uc_fw->wanted_path);
-       if (uc_fw->fallback.path) {
-               drm_printf(p, "%s firmware fallback: %s\n",
-                          intel_uc_fw_type_repr(uc_fw->type), uc_fw->fallback.path);
-               drm_printf(p, "fallback selected: %s\n",
-                          str_yes_no(uc_fw->path == uc_fw->fallback.path));
-       }
+                  intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path);
+       if (uc_fw->file_selected.path != uc_fw->file_wanted.path)
+               drm_printf(p, "%s firmware wanted: %s\n",
+                          intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_wanted.path);
        drm_printf(p, "\tstatus: %s\n",
                   intel_uc_fw_status_repr(uc_fw->status));
-       drm_printf(p, "\tversion: wanted %u.%u, found %u.%u\n",
-                  uc_fw->major_ver_wanted, uc_fw->minor_ver_wanted,
-                  uc_fw->major_ver_found, uc_fw->minor_ver_found);
+       if (uc_fw->file_wanted.major_ver)
+               drm_printf(p, "\tversion: wanted %u.%u, found %u.%u\n",
+                          uc_fw->file_wanted.major_ver, uc_fw->file_wanted.minor_ver,
+                          uc_fw->file_selected.major_ver, uc_fw->file_selected.minor_ver);
+       else
+               drm_printf(p, "\tversion: found %u.%u\n",
+                          uc_fw->file_selected.major_ver, uc_fw->file_selected.minor_ver);
        drm_printf(p, "\tuCode: %u bytes\n", uc_fw->ucode_size);
        drm_printf(p, "\tRSA: %u bytes\n", uc_fw->rsa_size);
 }