Skip to content

Commit

Permalink
drm/nvc0/gr: better handling of fuc firmware
Browse files Browse the repository at this point in the history
Allows per-chipset firmware to be installed, and keeps a copy in memory
for suspend/resume purposes.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
  • Loading branch information
Ben Skeggs committed May 16, 2011
1 parent 9548258 commit fe79911
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 35 deletions.
111 changes: 76 additions & 35 deletions drivers/gpu/drm/nouveau/nvc0_graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,61 +398,36 @@ nvc0_graph_init_rop(struct drm_device *dev)
}
}

static int
nvc0_fuc_load_fw(struct drm_device *dev, u32 fuc_base,
const char *code_fw, const char *data_fw)
static void
nvc0_graph_init_fuc(struct drm_device *dev, u32 fuc_base,
struct nvc0_graph_fuc *code, struct nvc0_graph_fuc *data)
{
const struct firmware *fw;
char name[32];
int ret, i;

snprintf(name, sizeof(name), "nouveau/%s", data_fw);
ret = request_firmware(&fw, name, &dev->pdev->dev);
if (ret) {
NV_ERROR(dev, "failed to load %s\n", data_fw);
return ret;
}
int i;

nv_wr32(dev, fuc_base + 0x01c0, 0x01000000);
for (i = 0; i < fw->size / 4; i++)
nv_wr32(dev, fuc_base + 0x01c4, ((u32 *)fw->data)[i]);
release_firmware(fw);

snprintf(name, sizeof(name), "nouveau/%s", code_fw);
ret = request_firmware(&fw, name, &dev->pdev->dev);
if (ret) {
NV_ERROR(dev, "failed to load %s\n", code_fw);
return ret;
}
for (i = 0; i < data->size / 4; i++)
nv_wr32(dev, fuc_base + 0x01c4, data->data[i]);

nv_wr32(dev, fuc_base + 0x0180, 0x01000000);
for (i = 0; i < fw->size / 4; i++) {
for (i = 0; i < code->size / 4; i++) {
if ((i & 0x3f) == 0)
nv_wr32(dev, fuc_base + 0x0188, i >> 6);
nv_wr32(dev, fuc_base + 0x0184, ((u32 *)fw->data)[i]);
nv_wr32(dev, fuc_base + 0x0184, code->data[i]);
}
release_firmware(fw);

return 0;
}

static int
nvc0_graph_init_ctxctl(struct drm_device *dev)
{
struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
u32 r000260;
int ret;

/* load fuc microcode */
r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000);
ret = nvc0_fuc_load_fw(dev, 0x409000, "fuc409c", "fuc409d");
if (ret == 0)
ret = nvc0_fuc_load_fw(dev, 0x41a000, "fuc41ac", "fuc41ad");
nvc0_graph_init_fuc(dev, 0x409000, &priv->fuc409c, &priv->fuc409d);
nvc0_graph_init_fuc(dev, 0x41a000, &priv->fuc41ac, &priv->fuc41ad);
nv_wr32(dev, 0x000260, r000260);

if (ret)
return ret;

/* start both of them running */
nv_wr32(dev, 0x409840, 0xffffffff);
nv_wr32(dev, 0x41a10c, 0x00000000);
Expand Down Expand Up @@ -636,11 +611,51 @@ nvc0_runk140_isr(struct drm_device *dev)
}
}

static int
nvc0_graph_create_fw(struct drm_device *dev, const char *fwname,
struct nvc0_graph_fuc *fuc)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
const struct firmware *fw;
char f[32];
int ret;

snprintf(f, sizeof(f), "nouveau/nv%02x_%s", dev_priv->chipset, fwname);
ret = request_firmware(&fw, f, &dev->pdev->dev);
if (ret) {
snprintf(f, sizeof(f), "nouveau/%s", fwname);
ret = request_firmware(&fw, f, &dev->pdev->dev);
if (ret) {
NV_ERROR(dev, "failed to load %s\n", fwname);
return ret;
}
}

fuc->size = fw->size;
fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL);
release_firmware(fw);
return (fuc->data != NULL) ? 0 : -ENOMEM;
}

static void
nvc0_graph_destroy_fw(struct nvc0_graph_fuc *fuc)
{
if (fuc->data) {
kfree(fuc->data);
fuc->data = NULL;
}
}

static void
nvc0_graph_destroy(struct drm_device *dev, int engine)
{
struct nvc0_graph_priv *priv = nv_engine(dev, engine);

nvc0_graph_destroy_fw(&priv->fuc409c);
nvc0_graph_destroy_fw(&priv->fuc409d);
nvc0_graph_destroy_fw(&priv->fuc41ac);
nvc0_graph_destroy_fw(&priv->fuc41ad);

nouveau_irq_unregister(dev, 12);
nouveau_irq_unregister(dev, 25);

Expand Down Expand Up @@ -686,6 +701,15 @@ nvc0_graph_create(struct drm_device *dev)
nouveau_irq_register(dev, 12, nvc0_graph_isr);
nouveau_irq_register(dev, 25, nvc0_runk140_isr);

if (nvc0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) ||
nvc0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) ||
nvc0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) ||
nvc0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) {
ret = 0;
goto error;
}


ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b4);
if (ret)
goto error;
Expand Down Expand Up @@ -777,3 +801,20 @@ nvc0_graph_create(struct drm_device *dev)
nvc0_graph_destroy(dev, NVOBJ_ENGINE_GR);
return ret;
}

MODULE_FIRMWARE("nouveau/nvc0_fuc409c");
MODULE_FIRMWARE("nouveau/nvc0_fuc409d");
MODULE_FIRMWARE("nouveau/nvc0_fuc41ac");
MODULE_FIRMWARE("nouveau/nvc0_fuc41ad");
MODULE_FIRMWARE("nouveau/nvc3_fuc409c");
MODULE_FIRMWARE("nouveau/nvc3_fuc409d");
MODULE_FIRMWARE("nouveau/nvc3_fuc41ac");
MODULE_FIRMWARE("nouveau/nvc3_fuc41ad");
MODULE_FIRMWARE("nouveau/nvc4_fuc409c");
MODULE_FIRMWARE("nouveau/nvc4_fuc409d");
MODULE_FIRMWARE("nouveau/nvc4_fuc41ac");
MODULE_FIRMWARE("nouveau/nvc4_fuc41ad");
MODULE_FIRMWARE("nouveau/fuc409c");
MODULE_FIRMWARE("nouveau/fuc409d");
MODULE_FIRMWARE("nouveau/fuc41ac");
MODULE_FIRMWARE("nouveau/fuc41ad");
10 changes: 10 additions & 0 deletions drivers/gpu/drm/nouveau/nvc0_graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,19 @@
#define GPC_UNIT(t, r) (0x500000 + (t) * 0x8000 + (r))
#define TP_UNIT(t, m, r) (0x504000 + (t) * 0x8000 + (m) * 0x800 + (r))

struct nvc0_graph_fuc {
u32 *data;
u32 size;
};

struct nvc0_graph_priv {
struct nouveau_exec_engine base;

struct nvc0_graph_fuc fuc409c;
struct nvc0_graph_fuc fuc409d;
struct nvc0_graph_fuc fuc41ac;
struct nvc0_graph_fuc fuc41ad;

u8 gpc_nr;
u8 rop_nr;
u8 tp_nr[GPC_MAX];
Expand Down

0 comments on commit fe79911

Please sign in to comment.