diff options
Diffstat (limited to 'drm/nouveau/nvkm/engine/gr/gf100.c')
-rw-r--r-- | drm/nouveau/nvkm/engine/gr/gf100.c | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/drm/nouveau/nvkm/engine/gr/gf100.c b/drm/nouveau/nvkm/engine/gr/gf100.c index 1dd482e9d..b751d0dc4 100644 --- a/drm/nouveau/nvkm/engine/gr/gf100.c +++ b/drm/nouveau/nvkm/engine/gr/gf100.c @@ -24,6 +24,11 @@ #include "gf100.h" #include "ctxgf100.h" #include "fuc/os.h" +#include "netlist.h" + +#ifdef __KERNEL__ +#include <nouveau_platform.h> +#endif #include <core/client.h> #include <core/device.h> @@ -1519,6 +1524,89 @@ gf100_gr_ctor_fw(struct gf100_gr_priv *priv, const char *fwname, return (fuc->data != NULL) ? 0 : -ENOMEM; } +static int +duplicate_netlist_region(const struct firmware *fw, + struct netlist_region *region, + struct gf100_gr_fuc *fuc) +{ + fuc->size = region->data_size; + fuc->data = kmemdup(fw->data + region->data_offset, fuc->size, + GFP_KERNEL); + if (!fuc->data) + return -ENOMEM; + + return 0; +} + +static int +gf100_graph_ctor_fw_netlist(struct gf100_gr_priv *priv) +{ + struct nvkm_device *device = nv_device(priv); + struct nouveau_platform_device *platdev; + struct netlist_image *netimage; + const struct firmware *fw; + const u32 required_fw = BIT(NETLIST_REGIONID_FECS_UCODE_DATA) | + BIT(NETLIST_REGIONID_FECS_UCODE_INST) | + BIT(NETLIST_REGIONID_GPCCS_UCODE_DATA) | + BIT(NETLIST_REGIONID_GPCCS_UCODE_INST); + u32 loaded_fw = 0; + int i; + + /* Are we a platform device? */ + if (!device->platformdev) + return 0; + + platdev = nv_device_to_platform(device); + fw = platdev->gpu->ctxsw_fw; + /* No firmware loaded? */ + if (!fw) + return 0; + + netimage = (struct netlist_image *)fw->data; + for (i = 0; i < netimage->header.regions; i++) { + struct netlist_region region = netimage->regions[i]; + int err = 0; + + if (loaded_fw & BIT(region.region_id)) { + nv_warn(priv, "skipping already loaded fw region\n"); + continue; + } + + switch (region.region_id) { + case NETLIST_REGIONID_FECS_UCODE_DATA: + err = duplicate_netlist_region(fw, ®ion, + &priv->fuc409d); + break; + case NETLIST_REGIONID_FECS_UCODE_INST: + err = duplicate_netlist_region(fw, ®ion, + &priv->fuc409c); + break; + case NETLIST_REGIONID_GPCCS_UCODE_DATA: + err = duplicate_netlist_region(fw, ®ion, + &priv->fuc41ad); + break; + case NETLIST_REGIONID_GPCCS_UCODE_INST: + err = duplicate_netlist_region(fw, ®ion, + &priv->fuc41ac); + break; + } + + if (err) + return err; + + loaded_fw |= BIT(region.region_id); + } + + /* Check that all FWs have been loaded */ + if ((loaded_fw & required_fw) != required_fw) { + nv_error(priv, "firmwares missing from the netlist image!\n"); + return -EINVAL; + } + + priv->firmware = true; + return 0; +} + void gf100_gr_dtor(struct nvkm_object *object) { @@ -1563,6 +1651,14 @@ gf100_gr_ctor(struct nvkm_object *parent, struct nvkm_object *engine, priv->base.units = gf100_gr_units; if (use_ext_fw) { + /* First look for a netlist blob */ + ret = gf100_graph_ctor_fw_netlist(priv); + if (ret) + return ret; + } + + /* Legacy firmware loading if no netlist was present */ + if (use_ext_fw && !priv->firmware) { nv_info(priv, "using external firmware\n"); if (gf100_gr_ctor_fw(priv, "fuc409c", &priv->fuc409c) || gf100_gr_ctor_fw(priv, "fuc409d", &priv->fuc409d) || |