Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3b5565dd authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/pm: add support for parsing perflvl voltage on fermi chips



Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent a31214ef
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -451,7 +451,8 @@ struct nouveau_pm_level {
	u32 unk05;
	u32 unk0a;

	u32 voltage; /* microvolts */
	u32 volt_min; /* microvolts */
	u32 volt_max;
	u8  fanspeed;

	u16 memscript;
+51 −7
Original line number Diff line number Diff line
@@ -134,6 +134,49 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P,
	return &pm->memtimings.timing[entry[1]];
}

static void
nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P,
		     struct nouveau_pm_level *perflvl)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nvbios *bios = &dev_priv->vbios;
	u8 *vmap;
	int id;

	id = perflvl->volt_min;
	perflvl->volt_min = 0;

	/* pre-fermi vbios stores the voltage level directly in the
	 * perflvl entry as a multiple of 10mV
	 */
	if (dev_priv->card_type < NV_C0) {
		perflvl->volt_min = id * 10000;
		perflvl->volt_max = perflvl->volt_min;
		return;
	}

	/* from fermi onwards, the perflvl stores an index into yet another
	 * vbios table containing a min/max voltage value for the perflvl
	 */
	if (P->version != 2 || P->length < 34) {
		NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n",
			 P->version, P->length);
		return;
	}

	vmap = ROMPTR(bios, P->data[32]);
	if (!vmap) {
		NV_DEBUG(dev, "volt map table pointer invalid\n");
		return;
	}

	if (id < vmap[3]) {
		vmap += vmap[1] + (vmap[2] * id);
		perflvl->volt_min = ROM32(vmap[0]);
		perflvl->volt_max = ROM32(vmap[4]);
	}
}

void
nouveau_perf_init(struct drm_device *dev)
{
@@ -204,7 +247,7 @@ nouveau_perf_init(struct drm_device *dev)
		case 0x15:
			perflvl->fanspeed = entry[55];
			if (recordlen > 56)
				perflvl->voltage = entry[56] * 10000;
				perflvl->volt_min = entry[56];
			perflvl->core = ROM32(entry[1]) * 10;
			perflvl->memory = ROM32(entry[5]) * 20;
			break;
@@ -212,7 +255,7 @@ nouveau_perf_init(struct drm_device *dev)
		case 0x23:
		case 0x24:
			perflvl->fanspeed = entry[4];
			perflvl->voltage = entry[5] * 10000;
			perflvl->volt_min = entry[5];
			perflvl->core = ROM16(entry[6]) * 1000;

			if (dev_priv->chipset == 0x49 ||
@@ -224,7 +267,7 @@ nouveau_perf_init(struct drm_device *dev)
			break;
		case 0x25:
			perflvl->fanspeed = entry[4];
			perflvl->voltage = entry[5] * 10000;
			perflvl->volt_min = entry[5];
			perflvl->core = ROM16(entry[6]) * 1000;
			perflvl->shader = ROM16(entry[10]) * 1000;
			perflvl->memory = ROM16(entry[12]) * 1000;
@@ -233,7 +276,7 @@ nouveau_perf_init(struct drm_device *dev)
			perflvl->memscript = ROM16(entry[2]);
		case 0x35:
			perflvl->fanspeed = entry[6];
			perflvl->voltage = entry[7] * 10000;
			perflvl->volt_min = entry[7];
			perflvl->core = ROM16(entry[8]) * 1000;
			perflvl->shader = ROM16(entry[10]) * 1000;
			perflvl->memory = ROM16(entry[12]) * 1000;
@@ -243,7 +286,7 @@ nouveau_perf_init(struct drm_device *dev)
		case 0x40:
#define subent(n) entry[perf[2] + ((n) * perf[3])]
			perflvl->fanspeed = 0; /*XXX*/
			perflvl->voltage = entry[2] * 10000;
			perflvl->volt_min = entry[2];
			if (dev_priv->card_type == NV_50) {
				perflvl->core = ROM16(subent(0)) & 0xfff;
				perflvl->shader = ROM16(subent(1)) & 0xfff;
@@ -263,8 +306,9 @@ nouveau_perf_init(struct drm_device *dev)
		}

		/* make sure vid is valid */
		if (pm->voltage.supported && perflvl->voltage) {
			vid = nouveau_volt_vid_lookup(dev, perflvl->voltage);
		nouveau_perf_voltage(dev, &P, perflvl);
		if (pm->voltage.supported && perflvl->volt_min) {
			vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min);
			if (vid < 0) {
				NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i);
				entry += recordlen;
+16 −8
Original line number Diff line number Diff line
@@ -64,11 +64,11 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
	if (perflvl == pm->cur)
		return 0;

	if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) {
		ret = pm->voltage_set(dev, perflvl->voltage);
	if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) {
		ret = pm->voltage_set(dev, perflvl->volt_min);
		if (ret) {
			NV_ERROR(dev, "voltage_set %d failed: %d\n",
				 perflvl->voltage, ret);
				 perflvl->volt_min, ret);
		}
	}

@@ -146,8 +146,10 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)

	if (pm->voltage.supported && pm->voltage_get) {
		ret = pm->voltage_get(dev);
		if (ret > 0)
			perflvl->voltage = ret;
		if (ret > 0) {
			perflvl->volt_min = ret;
			perflvl->volt_max = ret;
		}
	}

	return 0;
@@ -156,7 +158,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
static void
nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
{
	char c[16], s[16], v[16], f[16], t[16];
	char c[16], s[16], v[32], f[16], t[16];

	c[0] = '\0';
	if (perflvl->core)
@@ -167,8 +169,14 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
		snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);

	v[0] = '\0';
	if (perflvl->voltage)
		snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage / 1000);
	if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
		snprintf(v, sizeof(v), " voltage %dmV-%dmV",
			 perflvl->volt_min / 1000, perflvl->volt_max / 1000);
	} else
	if (perflvl->volt_min) {
		snprintf(v, sizeof(v), " voltage %dmV",
			 perflvl->volt_min / 1000);
	}

	f[0] = '\0';
	if (perflvl->fanspeed)