mirror of https://gitee.com/openkylin/linux.git
drm/nouveau: Import initial memory timing work
This isn't correct everywhere yet, but since we don't use the data yet it's perfectly safe to push in, and the information we gain from logs will help to fix the remaining issues. v2 (Ben Skeggs <bskeggs@redhat.com>): - fixed up formatting - free parsed timing info on takedown - switched timing table printout to debug loglevel Signed-off-by: Roy Spliet <r.spliet@student.tudelft.nl> Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
5b32165b04
commit
7760fcb020
|
@ -401,10 +401,28 @@ struct nouveau_pm_threshold_temp {
|
|||
s16 fan_boost;
|
||||
};
|
||||
|
||||
struct nouveau_pm_memtiming {
|
||||
u32 reg_100220;
|
||||
u32 reg_100224;
|
||||
u32 reg_100228;
|
||||
u32 reg_10022c;
|
||||
u32 reg_100230;
|
||||
u32 reg_100234;
|
||||
u32 reg_100238;
|
||||
u32 reg_10023c;
|
||||
};
|
||||
|
||||
struct nouveau_pm_memtimings {
|
||||
bool supported;
|
||||
struct nouveau_pm_memtiming *timing;
|
||||
int nr_timing;
|
||||
};
|
||||
|
||||
struct nouveau_pm_engine {
|
||||
struct nouveau_pm_voltage voltage;
|
||||
struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL];
|
||||
int nr_perflvl;
|
||||
struct nouveau_pm_memtimings memtimings;
|
||||
struct nouveau_pm_temp_sensor_constants sensor_constants;
|
||||
struct nouveau_pm_threshold_temp threshold_temp;
|
||||
|
||||
|
|
|
@ -648,3 +648,147 @@ nouveau_mem_gart_init(struct drm_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_mem_timing_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
|
||||
struct nouveau_pm_memtimings *memtimings = &pm->memtimings;
|
||||
struct nvbios *bios = &dev_priv->vbios;
|
||||
struct bit_entry P;
|
||||
u8 tUNK_0, tUNK_1, tUNK_2;
|
||||
u8 tRP; /* Byte 3 */
|
||||
u8 tRAS; /* Byte 5 */
|
||||
u8 tRFC; /* Byte 7 */
|
||||
u8 tRC; /* Byte 9 */
|
||||
u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
|
||||
u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
|
||||
u8 *mem = NULL, *entry;
|
||||
int i, recordlen, entries;
|
||||
|
||||
if (bios->type == NVBIOS_BIT) {
|
||||
if (bit_table(dev, 'P', &P))
|
||||
return;
|
||||
|
||||
if (P.version == 1)
|
||||
mem = ROMPTR(bios, P.data[4]);
|
||||
else
|
||||
if (P.version == 2)
|
||||
mem = ROMPTR(bios, P.data[8]);
|
||||
else {
|
||||
NV_WARN(dev, "unknown mem for BIT P %d\n", P.version);
|
||||
}
|
||||
} else {
|
||||
NV_DEBUG(dev, "BMP version too old for memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mem) {
|
||||
NV_DEBUG(dev, "memory timing table pointer invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem[0] != 0x10) {
|
||||
NV_WARN(dev, "memory timing table 0x%02x unknown\n", mem[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* validate record length */
|
||||
entries = mem[2];
|
||||
recordlen = mem[3];
|
||||
if (recordlen < 15) {
|
||||
NV_ERROR(dev, "mem timing table length unknown: %d\n", mem[3]);
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse vbios entries into common format */
|
||||
memtimings->timing =
|
||||
kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL);
|
||||
if (!memtimings->timing)
|
||||
return;
|
||||
|
||||
entry = mem + mem[1];
|
||||
for (i = 0; i < entries; i++, entry += recordlen) {
|
||||
struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i];
|
||||
if (entry[0] == 0)
|
||||
continue;
|
||||
|
||||
tUNK_18 = 1;
|
||||
tUNK_19 = 1;
|
||||
tUNK_20 = 0;
|
||||
tUNK_21 = 0;
|
||||
switch (recordlen) {
|
||||
case 0x21:
|
||||
tUNK_21 = entry[21];
|
||||
case 0x20:
|
||||
tUNK_20 = entry[20];
|
||||
case 0x19:
|
||||
tUNK_19 = entry[19];
|
||||
case 0x18:
|
||||
tUNK_18 = entry[18];
|
||||
default:
|
||||
tUNK_0 = entry[0];
|
||||
tUNK_1 = entry[1];
|
||||
tUNK_2 = entry[2];
|
||||
tRP = entry[3];
|
||||
tRAS = entry[5];
|
||||
tRFC = entry[7];
|
||||
tRC = entry[9];
|
||||
tUNK_10 = entry[10];
|
||||
tUNK_11 = entry[11];
|
||||
tUNK_12 = entry[12];
|
||||
tUNK_13 = entry[13];
|
||||
tUNK_14 = entry[14];
|
||||
break;
|
||||
}
|
||||
|
||||
timing->reg_100220 = (tRC << 24 | tRFC << 16 | tRAS << 8 | tRP);
|
||||
|
||||
/* XXX: I don't trust the -1's and +1's... they must come
|
||||
* from somewhere! */
|
||||
timing->reg_100224 = ((tUNK_0 + tUNK_19 + 1) << 24 |
|
||||
tUNK_18 << 16 |
|
||||
(tUNK_1 + tUNK_19 + 1) << 8 |
|
||||
(tUNK_2 - 1));
|
||||
|
||||
timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10);
|
||||
if(recordlen > 19) {
|
||||
timing->reg_100228 += (tUNK_19 - 1) << 24;
|
||||
} else {
|
||||
timing->reg_100228 += tUNK_12 << 24;
|
||||
}
|
||||
|
||||
/* XXX: reg_10022c */
|
||||
|
||||
timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
|
||||
tUNK_13 << 8 | tUNK_13);
|
||||
|
||||
/* XXX: +6? */
|
||||
timing->reg_100234 = (tRAS << 24 | (tUNK_19 + 6) << 8 | tRC);
|
||||
if(tUNK_10 > tUNK_11) {
|
||||
timing->reg_100234 += tUNK_10 << 16;
|
||||
} else {
|
||||
timing->reg_100234 += tUNK_11 << 16;
|
||||
}
|
||||
|
||||
/* XXX; reg_100238, reg_10023c */
|
||||
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i,
|
||||
timing->reg_100220, timing->reg_100224,
|
||||
timing->reg_100228, timing->reg_10022c);
|
||||
NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
|
||||
timing->reg_100230, timing->reg_100234,
|
||||
timing->reg_100238, timing->reg_10023c);
|
||||
}
|
||||
|
||||
memtimings->nr_timing = entries;
|
||||
memtimings->supported = true;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_mem_timing_fini(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings;
|
||||
|
||||
kfree(mem->timing);
|
||||
}
|
||||
|
|
|
@ -451,6 +451,7 @@ nouveau_pm_init(struct drm_device *dev)
|
|||
nouveau_volt_init(dev);
|
||||
nouveau_perf_init(dev);
|
||||
nouveau_temp_init(dev);
|
||||
nouveau_mem_timing_init(dev);
|
||||
|
||||
NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
|
||||
for (i = 0; i < pm->nr_perflvl; i++) {
|
||||
|
@ -491,9 +492,10 @@ nouveau_pm_fini(struct drm_device *dev)
|
|||
if (pm->cur != &pm->boot)
|
||||
nouveau_pm_perflvl_set(dev, &pm->boot);
|
||||
|
||||
nouveau_mem_timing_fini(dev);
|
||||
nouveau_temp_fini(dev);
|
||||
nouveau_perf_fini(dev);
|
||||
nouveau_volt_fini(dev);
|
||||
nouveau_temp_fini(dev);
|
||||
|
||||
nouveau_hwmon_fini(dev);
|
||||
nouveau_sysfs_fini(dev);
|
||||
|
|
|
@ -42,6 +42,10 @@ int nouveau_voltage_gpio_set(struct drm_device *, int voltage);
|
|||
void nouveau_perf_init(struct drm_device *);
|
||||
void nouveau_perf_fini(struct drm_device *);
|
||||
|
||||
/* nouveau_mem.c */
|
||||
void nouveau_mem_timing_init(struct drm_device *);
|
||||
void nouveau_mem_timing_fini(struct drm_device *);
|
||||
|
||||
/* nv04_pm.c */
|
||||
int nv04_pm_clock_get(struct drm_device *, u32 id);
|
||||
void *nv04_pm_clock_pre(struct drm_device *, u32 id, int khz);
|
||||
|
|
Loading…
Reference in New Issue