From fdd239ac99a0cc298b382c5ab5e7bcd09e8933d7 Mon Sep 17 00:00:00 2001
From: Ben Skeggs <bskeggs@redhat.com>
Date: Tue, 14 Jan 2014 14:56:22 +1000
Subject: [PATCH] drm/nouveau: fix null ptr dereferences on some boards

Regression from "device: populate master subdev pointer only when fully
constructed"

Reported-by: Bob Gleitsmann <rjgleits@bellsouth.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
---
 drivers/gpu/drm/nouveau/core/include/subdev/i2c.h     |  2 +-
 drivers/gpu/drm/nouveau/core/include/subdev/instmem.h |  7 +++++++
 drivers/gpu/drm/nouveau/core/subdev/i2c/base.c        |  4 ++--
 drivers/gpu/drm/nouveau/core/subdev/therm/ic.c        | 10 +++++-----
 drivers/gpu/drm/nouveau/dispnv04/dfp.c                |  2 +-
 drivers/gpu/drm/nouveau/dispnv04/tvnv04.c             |  2 +-
 6 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
index 9fa5da723871..7f50a858b16f 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
@@ -73,7 +73,7 @@ struct nouveau_i2c {
 	int (*identify)(struct nouveau_i2c *, int index,
 			const char *what, struct nouveau_i2c_board_info *,
 			bool (*match)(struct nouveau_i2c_port *,
-				      struct i2c_board_info *));
+				      struct i2c_board_info *, void *), void *);
 	struct list_head ports;
 };
 
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/instmem.h b/drivers/gpu/drm/nouveau/core/include/subdev/instmem.h
index ec7a54e91a08..4aca33887aaa 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/instmem.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/instmem.h
@@ -50,6 +50,13 @@ struct nouveau_instmem {
 static inline struct nouveau_instmem *
 nouveau_instmem(void *obj)
 {
+	/* nv04/nv40 impls need to create objects in their constructor,
+	 * which is before the subdev pointer is valid
+	 */
+	if (nv_iclass(obj, NV_SUBDEV_CLASS) &&
+	    nv_subidx(obj) == NVDEV_SUBDEV_INSTMEM)
+		return obj;
+
 	return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_INSTMEM];
 }
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
index 041fd5edaebf..c33c03d2f4af 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
@@ -197,7 +197,7 @@ static int
 nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
 		     struct nouveau_i2c_board_info *info,
 		     bool (*match)(struct nouveau_i2c_port *,
-				   struct i2c_board_info *))
+				   struct i2c_board_info *, void *), void *data)
 {
 	struct nouveau_i2c_port *port = nouveau_i2c_find(i2c, index);
 	int i;
@@ -221,7 +221,7 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
 		}
 
 		if (nv_probe_i2c(port, info[i].dev.addr) &&
-		    (!match || match(port, &info[i].dev))) {
+		    (!match || match(port, &info[i].dev, data))) {
 			nv_info(i2c, "detected %s: %s\n", what,
 				info[i].dev.type);
 			return i;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
index 13b850076443..30c384e5ec22 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
@@ -29,9 +29,9 @@
 
 static bool
 probe_monitoring_device(struct nouveau_i2c_port *i2c,
-			struct i2c_board_info *info)
+			struct i2c_board_info *info, void *data)
 {
-	struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c);
+	struct nouveau_therm_priv *priv = data;
 	struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
 	struct i2c_client *client;
 
@@ -95,7 +95,7 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm)
 		};
 
 		i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
-				  board, probe_monitoring_device);
+			      board, probe_monitoring_device, therm);
 		if (priv->ic)
 			return;
 	}
@@ -107,7 +107,7 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm)
 		};
 
 		i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
-				  board, probe_monitoring_device);
+			      board, probe_monitoring_device, therm);
 		if (priv->ic)
 			return;
 	}
@@ -116,5 +116,5 @@ nouveau_therm_ic_ctor(struct nouveau_therm *therm)
 	   device. Let's try our static list.
 	 */
 	i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
-		      nv_board_infos, probe_monitoring_device);
+		      nv_board_infos, probe_monitoring_device, therm);
 }
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
index 936a71c59080..7fdc51e2a571 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dfp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
@@ -643,7 +643,7 @@ static void nv04_tmds_slave_init(struct drm_encoder *encoder)
 	    get_tmds_slave(encoder))
 		return;
 
-	type = i2c->identify(i2c, 2, "TMDS transmitter", info, NULL);
+	type = i2c->identify(i2c, 2, "TMDS transmitter", info, NULL, NULL);
 	if (type < 0)
 		return;
 
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
index cc4b208ce546..244822df8ffc 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
@@ -59,7 +59,7 @@ int nv04_tv_identify(struct drm_device *dev, int i2c_index)
 	struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
 
 	return i2c->identify(i2c, i2c_index, "TV encoder",
-			     nv04_tv_encoder_info, NULL);
+			     nv04_tv_encoder_info, NULL, NULL);
 }
 
 
-- 
2.49.0