drm/nv50/pm: mostly nailed down fan pwm frequency selection

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 432be65..251eaf8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -524,6 +524,7 @@
 struct nouveau_pm_fan {
 	u32 min_duty;
 	u32 max_duty;
+	u32 pwm_freq;
 };
 
 struct nouveau_pm_engine {
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c
index 3d20dca0..6ea57c9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_perf.c
+++ b/drivers/gpu/drm/nouveau/nouveau_perf.c
@@ -206,13 +206,12 @@
 		if (version < 0x40) {
 			recordlen = perf[3] + (perf[4] * perf[5]);
 			entries   = perf[2];
+
+			pm->pwm_divisor = ROM16(perf[6]);
 		} else {
 			recordlen = perf[2] + (perf[3] * perf[4]);
 			entries   = perf[5];
 		}
-
-		if (version < 0x30)
-			pm->pwm_divisor = ROM16(perf[6]);
 	} else {
 		if (bios->data[bios->offset + 6] < 0x25) {
 			legacy_perf_init(dev);
diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c
index 97c172c..6364e4c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_temp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_temp.c
@@ -164,6 +164,9 @@
 			pm->fan.min_duty = value & 0xff;
 			pm->fan.max_duty = (value & 0xff00) >> 8;
 			break;
+		case 0x26:
+			pm->fan.pwm_freq = value;
+			break;
 		}
 		temp += recordlen;
 	}
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index 713c718..0cbf538 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -201,6 +201,8 @@
 int
 nv50_pm_fanspeed_set(struct drm_device *dev, int percent)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
 	struct pwm_info pwm;
 	u32 divs, duty;
 	int ret;
@@ -209,12 +211,20 @@
 	if (ret)
 		return ret;
 
-	divs = nv_rd32(dev, 0x00e114 + (pwm.id * 8));
+	divs = pm->pwm_divisor;
+	if (pm->fan.pwm_freq) {
+		/*XXX: PNVIO clock more than likely... */
+		divs = 1350000 / pm->fan.pwm_freq;
+		if (dev_priv->chipset < 0xa3)
+			divs /= 4;
+	}
+
 	duty = ((divs * percent) + 99) / 100;
 	if (pwm.invert)
 		duty = divs - duty;
 
 	nv_mask(dev, pwm.ctrl, 0x00010001 << pwm.line, 0x00000001 << pwm.line);
+	nv_wr32(dev, 0x00e114 + (pwm.id * 8), divs);
 	nv_wr32(dev, 0x00e118 + (pwm.id * 8), 0x80000000 | duty);
 	return 0;
 }