drm/gk104-/fb/ram: parse ramcfg data for all frequencies up-front
authorBen Skeggs <bskeggs@redhat.com>
Mon, 8 Sep 2014 03:29:04 +0000 (13:29 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Mon, 15 Sep 2014 12:25:09 +0000 (22:25 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/include/subdev/fb.h
drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c

index 871e73914b24eb7b75ca6d2d952a16ed4cc72ecc..a0bd0ead90ab3d96e58769619b1c973d07a18326 100644 (file)
@@ -111,6 +111,7 @@ extern struct nouveau_oclass *gm107_fb_oclass;
 #include <subdev/bios/ramcfg.h>
 
 struct nouveau_ram_data {
+       struct list_head head;
        struct nvbios_ramcfg bios;
        u32 freq;
 };
index 1fa45c5e50d75b83c59505b1b0911b13aa957104..6af51ac3f0b4cbecbc2724d5e402d8e40ef25614 100644 (file)
@@ -136,6 +136,7 @@ struct nve0_ram {
        struct nouveau_ram base;
        struct nve0_ramfuc fuc;
 
+       struct list_head cfg;
        u32 parts;
        u32 pmask;
        u32 pnuts;
@@ -934,58 +935,24 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq)
  ******************************************************************************/
 
 static int
-nve0_ram_calc_data(struct nouveau_fb *pfb, u32 freq,
+nve0_ram_calc_data(struct nouveau_fb *pfb, u32 khz,
                   struct nouveau_ram_data *data)
 {
-       struct nouveau_bios *bios = nouveau_bios(pfb);
        struct nve0_ram *ram = (void *)pfb->ram;
-       u8 strap, cnt, len;
-
-       /* lookup memory config data relevant to the target frequency */
-       ram->base.rammap.data = nvbios_rammapEm(bios, freq / 1000,
-                                              &ram->base.rammap.version,
-                                              &ram->base.rammap.size,
-                                              &cnt, &len, &data->bios);
-       if (!ram->base.rammap.data || ram->base.rammap.version != 0x11 ||
-            ram->base.rammap.size < 0x09) {
-               nv_error(pfb, "invalid/missing rammap entry\n");
-               return -EINVAL;
-       }
-
-       /* locate specific data set for the attached memory */
-       strap = nvbios_ramcfg_index(nv_subdev(pfb));
-       ram->base.ramcfg.data = nvbios_rammapSp(bios, ram->base.rammap.data,
-                                               ram->base.rammap.version,
-                                               ram->base.rammap.size,
-                                               cnt, len, strap,
-                                               &ram->base.ramcfg.version,
-                                               &ram->base.ramcfg.size,
-                                               &data->bios);
-       if (!ram->base.ramcfg.data || ram->base.ramcfg.version != 0x11 ||
-            ram->base.ramcfg.size < 0x08) {
-               nv_error(pfb, "invalid/missing ramcfg entry\n");
-               return -EINVAL;
-       }
-
-       /* lookup memory timings, if bios says they're present */
-       if (data->bios.ramcfg_timing != 0xff) {
-               ram->base.timing.data =
-                       nvbios_timingEp(bios, data->bios.ramcfg_timing,
-                                       &ram->base.timing.version,
-                                       &ram->base.timing.size, &cnt, &len,
-                                       &data->bios);
-               if (!ram->base.timing.data ||
-                    ram->base.timing.version != 0x20 ||
-                    ram->base.timing.size < 0x33) {
-                       nv_error(pfb, "invalid/missing timing entry\n");
-                       return -EINVAL;
+       struct nouveau_ram_data *cfg;
+       u32 mhz = khz / 1000;
+
+       list_for_each_entry(cfg, &ram->cfg, head) {
+               if (mhz >= cfg->bios.rammap_min &&
+                   mhz <= cfg->bios.rammap_max) {
+                       *data = *cfg;
+                       data->freq = khz;
+                       return 0;
                }
-       } else {
-               ram->base.timing.data = 0;
        }
 
-       data->freq = freq;
-       return 0;
+       nv_error(ram, "ramcfg data for %dMHz not found\n", mhz);
+       return -EINVAL;
 }
 
 static int
@@ -1318,6 +1285,66 @@ nve0_ram_init(struct nouveau_object *object)
        return nve0_ram_train_init(pfb);
 }
 
+static int
+nve0_ram_ctor_data(struct nve0_ram *ram, u8 ramcfg, int i)
+{
+       struct nouveau_fb *pfb = (void *)nv_object(ram)->parent;
+       struct nouveau_bios *bios = nouveau_bios(pfb);
+       struct nouveau_ram_data *cfg;
+       u8  ver, hdr, cnt, len;
+       u32 data;
+       int ret;
+
+       if (!(cfg = kmalloc(sizeof(*cfg), GFP_KERNEL)))
+               return -ENOMEM;
+
+       /* memory config data for a range of target frequencies */
+       data = nvbios_rammapEp(bios, i, &ver, &hdr, &cnt, &len, &cfg->bios);
+       if (ret = -ENOENT, !data)
+               goto done;
+       if (ret = -ENOSYS, ver != 0x11 || hdr < 0x12)
+               goto done;
+
+       /* ... and a portion specific to the attached memory */
+       data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, ramcfg,
+                              &ver, &hdr, &cfg->bios);
+       if (ret = -EINVAL, !data)
+               goto done;
+       if (ret = -ENOSYS, ver != 0x11 || hdr < 0x0a)
+               goto done;
+
+       /* lookup memory timings, if bios says they're present */
+       if (cfg->bios.ramcfg_timing != 0xff) {
+               data = nvbios_timingEp(bios, cfg->bios.ramcfg_timing,
+                                      &ver, &hdr, &cnt, &len,
+                                      &cfg->bios);
+               if (ret = -EINVAL, !data)
+                       goto done;
+               if (ret = -ENOSYS, ver != 0x20 || hdr < 0x33)
+                       goto done;
+       }
+
+       list_add_tail(&cfg->head, &ram->cfg);
+       ret = 0;
+done:
+       if (ret)
+               kfree(cfg);
+       return ret;
+}
+
+static void
+nve0_ram_dtor(struct nouveau_object *object)
+{
+       struct nve0_ram *ram = (void *)object;
+       struct nouveau_ram_data *cfg, *tmp;
+
+       list_for_each_entry_safe(cfg, tmp, &ram->cfg, head) {
+               kfree(cfg);
+       }
+
+       nouveau_ram_destroy(&ram->base);
+}
+
 static int
 nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
              struct nouveau_oclass *oclass, void *data, u32 size,
@@ -1329,6 +1356,7 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct dcb_gpio_func func;
        struct nve0_ram *ram;
        int ret, i;
+       u8  ramcfg = nvbios_ramcfg_index(nv_subdev(pfb));
        u32 tmp;
 
        ret = nvc0_ram_create(parent, engine, oclass, 0x022554, &ram);
@@ -1336,6 +1364,8 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       INIT_LIST_HEAD(&ram->cfg);
+
        switch (ram->base.type) {
        case NV_MEM_TYPE_DDR3:
        case NV_MEM_TYPE_GDDR5:
@@ -1367,7 +1397,16 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                }
        }
 
-       // parse bios data for both pll's
+       /* parse ramcfg data for all possible target frequencies */
+       for (i = 0; !ret; i++) {
+               ret = nve0_ram_ctor_data(ram, ramcfg, i);
+               if (ret && ret != -ENOENT) {
+                       nv_error(pfb, "failed to parse ramcfg data\n");
+                       return ret;
+               }
+       }
+
+       /* parse bios data for both pll's */
        ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll);
        if (ret) {
                nv_error(pfb, "mclk refpll data not found\n");
@@ -1380,6 +1419,7 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
        }
 
+       /* lookup memory voltage gpios */
        ret = gpio->find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func);
        if (ret == 0) {
                ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04));
@@ -1488,7 +1528,7 @@ nve0_ram_oclass = {
        .handle = 0,
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nve0_ram_ctor,
-               .dtor = _nouveau_ram_dtor,
+               .dtor = nve0_ram_dtor,
                .init = nve0_ram_init,
                .fini = _nouveau_ram_fini,
        }