diff --git a/drivers/gpu/drm/nouveau/dispnv04/arb.c b/drivers/gpu/drm/nouveau/dispnv04/arb.c
index c79160c37f847784f2a35b451fc05c8441f1b0e2..362495535e693050f169e0061b70dfea8e349422 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/arb.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/arb.c
@@ -21,8 +21,6 @@
  * SOFTWARE.
  */
 
-#include <drm/drmP.h>
-
 #include "nouveau_drv.h"
 #include "nouveau_reg.h"
 #include "hw.h"
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index f22f0102062597dda5582c742cd19387431757ca..37c50ea8f84753c35176c311b87daa9b63cd24c3 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
@@ -22,11 +22,10 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
-#include <linux/pm_runtime.h>
-
-#include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_plane_helper.h>
+#include <drm/drm_vblank.h>
 
 #include "nouveau_drv.h"
 #include "nouveau_reg.h"
@@ -1031,53 +1030,6 @@ nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 	return 0;
 }
 
-static int
-nouveau_crtc_set_config(struct drm_mode_set *set,
-			struct drm_modeset_acquire_ctx *ctx)
-{
-	struct drm_device *dev;
-	struct nouveau_drm *drm;
-	int ret;
-	struct drm_crtc *crtc;
-	bool active = false;
-	if (!set || !set->crtc)
-		return -EINVAL;
-
-	dev = set->crtc->dev;
-
-	/* get a pm reference here */
-	ret = pm_runtime_get_sync(dev->dev);
-	if (ret < 0 && ret != -EACCES)
-		return ret;
-
-	ret = drm_crtc_helper_set_config(set, ctx);
-
-	drm = nouveau_drm(dev);
-
-	/* if we get here with no crtcs active then we can drop a reference */
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		if (crtc->enabled)
-			active = true;
-	}
-
-	pm_runtime_mark_last_busy(dev->dev);
-	/* if we have active crtcs and we don't have a power ref,
-	   take the current one */
-	if (active && !drm->have_disp_power_ref) {
-		drm->have_disp_power_ref = true;
-		return ret;
-	}
-	/* if we have no active crtcs, then drop the power ref
-	   we got before */
-	if (!active && drm->have_disp_power_ref) {
-		pm_runtime_put_autosuspend(dev->dev);
-		drm->have_disp_power_ref = false;
-	}
-	/* drop the power reference we got coming in here */
-	pm_runtime_put_autosuspend(dev->dev);
-	return ret;
-}
-
 struct nv04_page_flip_state {
 	struct list_head head;
 	struct drm_pending_vblank_event *event;
@@ -1293,7 +1245,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = {
 	.cursor_set = nv04_crtc_cursor_set,
 	.cursor_move = nv04_crtc_cursor_move,
 	.gamma_set = nv_crtc_gamma_set,
-	.set_config = nouveau_crtc_set_config,
+	.set_config = drm_crtc_helper_set_config,
 	.page_flip = nv04_crtc_page_flip,
 	.destroy = nv_crtc_destroy,
 };
diff --git a/drivers/gpu/drm/nouveau/dispnv04/cursor.c b/drivers/gpu/drm/nouveau/dispnv04/cursor.c
index 16e09f6b91139205717317099cfd5160d7ecf6ac..4c6440d29c3f3b00b55bd41fbbf9469151d859ce 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/cursor.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/cursor.c
@@ -1,5 +1,4 @@
 // SPDX-License-Identifier: MIT
-#include <drm/drmP.h>
 #include <drm/drm_mode.h>
 #include "nouveau_drv.h"
 #include "nouveau_reg.h"
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c
index e7af95d37ddb54488e31f92d2498066a745fc5c4..e8eef88a8382e95f3fd9ec1d80ef5ddedf6e94ac 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dac.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dac.c
@@ -24,7 +24,6 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-#include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 
 #include "nouveau_drv.h"
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
index 73d41abbb5103e2b6a4d01fc14a0a8e9df2f93f4..f9f4482c79b5488532ba11aed3cca877a3283421 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dfp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
@@ -24,8 +24,8 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-#include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_fourcc.h>
 
 #include "nouveau_drv.h"
 #include "nouveau_reg.h"
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c
index 5713bacaee80ba94798baad570cf28f2089e283f..dc64863b5fd88a8bcf30aea2c6212e87ac1d3cf0 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c
@@ -22,7 +22,6 @@
  * Author: Ben Skeggs
  */
 
-#include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 
 #include "nouveau_drv.h"
@@ -210,7 +209,7 @@ nv04_display_create(struct drm_device *dev)
 	nouveau_display(dev)->fini = nv04_display_fini;
 
 	/* Pre-nv50 doesn't support atomic, so don't expose the ioctls */
-	dev->driver->driver_features &= ~DRIVER_ATOMIC;
+	dev->driver_features &= ~DRIVER_ATOMIC;
 
 	/* Request page flip completion event. */
 	if (drm->nvsw.client) {
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h
index 6ccfc09bcf0f7c10583abfe835d0d10ab4a0fec5..495d3284e876695642727fb73ce908842315f144 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/disp.h
+++ b/drivers/gpu/drm/nouveau/dispnv04/disp.h
@@ -161,7 +161,6 @@ nv_match_device(struct drm_device *dev, unsigned device,
 		dev->pdev->subsystem_device == sub_device;
 }
 
-#include <subdev/bios.h>
 #include <subdev/bios/init.h>
 
 static inline void
diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.c b/drivers/gpu/drm/nouveau/dispnv04/hw.c
index 0c9bdf023f5b559059479232075d612172971905..3fdfafa8b0add6d05add3e2bac1af48b3f665e02 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/hw.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/hw.c
@@ -22,7 +22,6 @@
  * SOFTWARE.
  */
 
-#include <drm/drmP.h>
 #include "nouveau_drv.h"
 #include "hw.h"
 
diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.h b/drivers/gpu/drm/nouveau/dispnv04/hw.h
index 3a2be47fb4f12fe9f389e8f61546fa25a8f1d91d..6987e1766cd232fc7b97047ea6ecdf018d87670f 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/hw.h
+++ b/drivers/gpu/drm/nouveau/dispnv04/hw.h
@@ -23,7 +23,6 @@
 #ifndef __NOUVEAU_HW_H__
 #define __NOUVEAU_HW_H__
 
-#include <drm/drmP.h>
 #include "disp.h"
 #include "nvreg.h"
 
diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
index df4358e31075da8fdad2f78392af928932ccac81..a3a0a73ae8abd08396f2bbaa21e1f1166f027c7e 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
@@ -23,7 +23,6 @@
  * written by Arthur Huillet.
  */
 
-#include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fourcc.h>
 
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c
index 2b83b2c39d1ddfb838fa90652917f6b7e11ce079..2f6d2b6711ab25c2fcd999732f21f9c413d038e7 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c
@@ -24,7 +24,6 @@
  *
  */
 
-#include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include "nouveau_drv.h"
 #include "nouveau_encoder.h"
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
index de4490b4ed30c80d3e26996ba6d9731869c07302..b701a4d8fe7608b86b71b374a20df93551c018c3 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
@@ -24,7 +24,6 @@
  *
  */
 
-#include <drm/drmP.h>
 #include "nouveau_drv.h"
 #include "nouveau_reg.h"
 #include "nouveau_encoder.h"
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
index 26fd71c06626f5046d2c24fe8dc6754336c28ecb..03466f04c7419162f91659f91d12cada10458a39 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
@@ -24,7 +24,6 @@
  *
  */
 
-#include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_probe_helper.h>
 #include "nouveau_drv.h"
diff --git a/drivers/gpu/drm/nouveau/dispnv50/atom.h b/drivers/gpu/drm/nouveau/dispnv50/atom.h
index b5fae5ab3fa8cf44b301941b4d921e04fb193ca2..43df86c38f58bcf061491cb718f66e215db40a7c 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/atom.h
+++ b/drivers/gpu/drm/nouveau/dispnv50/atom.h
@@ -184,6 +184,11 @@ struct nv50_wndw_atom {
 		} i;
 	} xlut;
 
+	struct {
+		u32 matrix[12];
+		bool valid;
+	} csc;
+
 	struct {
 		u8  mode:2;
 		u8  interval:4;
@@ -216,14 +221,23 @@ struct nv50_wndw_atom {
 		u16 y;
 	} point;
 
+	struct {
+		u8 depth;
+		u8 k1;
+		u8 src_color:4;
+		u8 dst_color:4;
+	} blend;
+
 	union nv50_wndw_atom_mask {
 		struct {
 			bool ntfy:1;
 			bool sema:1;
 			bool xlut:1;
+			bool csc:1;
 			bool image:1;
 			bool scale:1;
 			bool point:1;
+			bool blend:1;
 		};
 		u8 mask;
 	} set, clr;
diff --git a/drivers/gpu/drm/nouveau/dispnv50/base507c.c b/drivers/gpu/drm/nouveau/dispnv50/base507c.c
index d5e295ca2caa54530d0bd29e47cc0c3bccde3915..00a85f1e1a4a7e1d32c661100bb4f41c0a3f60e1 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/base507c.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/base507c.c
@@ -25,7 +25,9 @@
 #include <nvif/event.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_plane_helper.h>
+
 #include "nouveau_bo.h"
 
 void
@@ -56,12 +58,21 @@ static void
 base507c_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
 {
 	u32 *push;
-	if ((push = evo_wait(&wndw->wndw, 10))) {
+	if ((push = evo_wait(&wndw->wndw, 13))) {
 		evo_mthd(push, 0x0084, 1);
 		evo_data(push, asyw->image.mode << 8 |
 			       asyw->image.interval << 4);
 		evo_mthd(push, 0x00c0, 1);
 		evo_data(push, asyw->image.handle[0]);
+		if (asyw->image.format == 0xca) {
+			evo_mthd(push, 0x0110, 2);
+			evo_data(push, 1);
+			evo_data(push, 0x6400);
+		} else {
+			evo_mthd(push, 0x0110, 2);
+			evo_data(push, 0);
+			evo_data(push, 0);
+		}
 		evo_mthd(push, 0x0800, 5);
 		evo_data(push, asyw->image.offset[0] >> 8);
 		evo_data(push, 0x00000000);
@@ -179,9 +190,6 @@ base507c_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw,
 	const struct drm_framebuffer *fb = asyw->state.fb;
 	int ret;
 
-	if (!fb->format->depth)
-		return -EINVAL;
-
 	ret = drm_atomic_helper_check_plane_state(&asyw->state, &asyh->state,
 						  DRM_PLANE_HELPER_NO_SCALING,
 						  DRM_PLANE_HELPER_NO_SCALING,
@@ -200,6 +208,14 @@ base507c_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw,
 	asyh->base.y = asyw->state.src.y1 >> 16;
 	asyh->base.w = asyw->state.fb->width;
 	asyh->base.h = asyw->state.fb->height;
+
+	/* Some newer formats, esp FP16 ones, don't have a
+	 * "depth". There's nothing that really makes sense there
+	 * either, so just set it to the implicit bit count.
+	 */
+	if (!asyh->base.depth)
+		asyh->base.depth = asyh->base.cpp * 8;
+
 	return 0;
 }
 
@@ -215,6 +231,8 @@ base507c_format[] = {
 	DRM_FORMAT_ABGR2101010,
 	DRM_FORMAT_XBGR8888,
 	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_XBGR16161616F,
+	DRM_FORMAT_ABGR16161616F,
 	0
 };
 
diff --git a/drivers/gpu/drm/nouveau/dispnv50/base827c.c b/drivers/gpu/drm/nouveau/dispnv50/base827c.c
index 73646819a0d686c0270b5084f90c3d89ed08bcf3..f4c05949dd6258ca0ba84130097ac7250f8e06a1 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/base827c.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/base827c.c
@@ -25,12 +25,21 @@ static void
 base827c_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
 {
 	u32 *push;
-	if ((push = evo_wait(&wndw->wndw, 10))) {
+	if ((push = evo_wait(&wndw->wndw, 13))) {
 		evo_mthd(push, 0x0084, 1);
 		evo_data(push, asyw->image.mode << 8 |
 			       asyw->image.interval << 4);
 		evo_mthd(push, 0x00c0, 1);
 		evo_data(push, asyw->image.handle[0]);
+		if (asyw->image.format == 0xca) {
+			evo_mthd(push, 0x0110, 2);
+			evo_data(push, 1);
+			evo_data(push, 0x6400);
+		} else {
+			evo_mthd(push, 0x0110, 2);
+			evo_data(push, 0);
+			evo_data(push, 0);
+		}
 		evo_mthd(push, 0x0800, 5);
 		evo_data(push, asyw->image.offset[0] >> 8);
 		evo_data(push, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/dispnv50/base907c.c b/drivers/gpu/drm/nouveau/dispnv50/base907c.c
index 049ce6da321c3fa3e102b1b1773122ea02b9566d..5f2de77e0f32e1bcce6a48a41c1546f4ba9b09a8 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/base907c.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/base907c.c
@@ -83,6 +83,68 @@ base907c_ilut(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
 	asyw->xlut.i.load = head907d_olut_load;
 }
 
+static inline u32
+csc_drm_to_base(u64 in)
+{
+	/* base takes a 19-bit 2's complement value in S3.16 format */
+	bool sign = in & BIT_ULL(63);
+	u32 integer = (in >> 32) & 0x7fffffff;
+	u32 fraction = in & 0xffffffff;
+
+	if (integer >= 4) {
+		return (1 << 18) - (sign ? 0 : 1);
+	} else {
+		u32 ret = (integer << 16) | (fraction >> 16);
+		if (sign)
+			ret = -ret;
+		return ret & GENMASK(18, 0);
+	}
+}
+
+void
+base907c_csc(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw,
+	     const struct drm_color_ctm *ctm)
+{
+	int i, j;
+
+	for (j = 0; j < 3; j++) {
+		for (i = 0; i < 4; i++) {
+			u32 *val = &asyw->csc.matrix[j * 4 + i];
+			/* DRM does not support constant offset, while
+			 * HW CSC does. Skip it. */
+			if (i == 3) {
+				*val = 0;
+			} else {
+				*val = csc_drm_to_base(ctm->matrix[j * 3 + i]);
+			}
+		}
+	}
+}
+
+static void
+base907c_csc_clr(struct nv50_wndw *wndw)
+{
+	u32 *push;
+	if ((push = evo_wait(&wndw->wndw, 2))) {
+		evo_mthd(push, 0x0140, 1);
+		evo_data(push, 0x00000000);
+		evo_kick(push, &wndw->wndw);
+	}
+}
+
+static void
+base907c_csc_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+	u32 *push, i;
+	if ((push = evo_wait(&wndw->wndw, 13))) {
+		evo_mthd(push, 0x0140, 12);
+		evo_data(push, asyw->csc.matrix[0] | 0x80000000);
+		for (i = 1; i < 12; i++)
+			evo_data(push, asyw->csc.matrix[i]);
+		evo_kick(push, &wndw->wndw);
+	}
+}
+
 const struct nv50_wndw_func
 base907c = {
 	.acquire = base507c_acquire,
@@ -94,6 +156,9 @@ base907c = {
 	.ntfy_clr = base507c_ntfy_clr,
 	.ntfy_wait_begun = base507c_ntfy_wait_begun,
 	.ilut = base907c_ilut,
+	.csc = base907c_csc,
+	.csc_set = base907c_csc_set,
+	.csc_clr = base907c_csc_clr,
 	.olut_core = true,
 	.xlut_set = base907c_xlut_set,
 	.xlut_clr = base907c_xlut_clr,
diff --git a/drivers/gpu/drm/nouveau/dispnv50/base917c.c b/drivers/gpu/drm/nouveau/dispnv50/base917c.c
index 54d705bb81a5dfa3e95fec5d4d7322ae5a44f9df..a1baed4fe0e9f5d434e1f2f3f992ffbf60000379 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/base917c.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/base917c.c
@@ -36,6 +36,8 @@ base917c_format[] = {
 	DRM_FORMAT_ABGR8888,
 	DRM_FORMAT_XRGB2101010,
 	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_XBGR16161616F,
+	DRM_FORMAT_ABGR16161616F,
 	0
 };
 
diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c
index 7860774b65bc0322aab7e0d69450738ff7c720c4..40d9b654ab8c12286b552796cbb5d40390961741 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c
@@ -82,7 +82,7 @@ corec37d_init(struct nv50_core *core)
 		for (i = 0; i < windows; i++) {
 			evo_mthd(push, 0x1000 + (i * 0x080), 3);
 			evo_data(push, i >> 1);
-			evo_data(push, 0x00000017);
+			evo_data(push, 0x0000001f);
 			evo_data(push, 0x00000000);
 			evo_mthd(push, 0x1010 + (i * 0x080), 1);
 			evo_data(push, 0x00127fff);
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 126703816794e77a521437271e09a34a09015f4b..f1dbc7852414a0a88a1ca0d09fd8841bbc8650fa 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -30,14 +30,14 @@
 #include <linux/dma-mapping.h>
 #include <linux/hdmi.h>
 
-#include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_scdc_helper.h>
-#include <drm/drm_edid.h>
+#include <drm/drm_vblank.h>
 
 #include <nvif/class.h>
 #include <nvif/cl0002.h>
@@ -1826,8 +1826,11 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
 
 		NV_ATOMIC(drm, "%s: clr %04x (set %04x)\n", crtc->name,
 			  asyh->clr.mask, asyh->set.mask);
-		if (old_crtc_state->active && !new_crtc_state->active)
+
+		if (old_crtc_state->active && !new_crtc_state->active) {
+			pm_runtime_put_noidle(dev->dev);
 			drm_crtc_vblank_off(crtc);
+		}
 
 		if (asyh->clr.mask) {
 			nv50_head_flush_clr(head, asyh, atom->flush_disable);
@@ -1913,8 +1916,10 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
 		}
 
 		if (new_crtc_state->active) {
-			if (!old_crtc_state->active)
+			if (!old_crtc_state->active) {
 				drm_crtc_vblank_on(crtc);
+				pm_runtime_get_noresume(dev->dev);
+			}
 			if (new_crtc_state->event)
 				drm_crtc_vblank_get(crtc);
 		}
@@ -1979,6 +1984,10 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
 	drm_atomic_helper_cleanup_planes(dev, state);
 	drm_atomic_helper_commit_cleanup_done(state);
 	drm_atomic_state_put(state);
+
+	/* Drop the RPM ref we got from nv50_disp_atomic_commit() */
+	pm_runtime_mark_last_busy(dev->dev);
+	pm_runtime_put_autosuspend(dev->dev);
 }
 
 static void
@@ -1993,11 +2002,8 @@ static int
 nv50_disp_atomic_commit(struct drm_device *dev,
 			struct drm_atomic_state *state, bool nonblock)
 {
-	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct drm_plane_state *new_plane_state;
 	struct drm_plane *plane;
-	struct drm_crtc *crtc;
-	bool active = false;
 	int ret, i;
 
 	ret = pm_runtime_get_sync(dev->dev);
@@ -2034,27 +2040,17 @@ nv50_disp_atomic_commit(struct drm_device *dev,
 
 	drm_atomic_state_get(state);
 
+	/*
+	 * Grab another RPM ref for the commit tail, which will release the
+	 * ref when it's finished
+	 */
+	pm_runtime_get_noresume(dev->dev);
+
 	if (nonblock)
 		queue_work(system_unbound_wq, &state->commit_work);
 	else
 		nv50_disp_atomic_commit_tail(state);
 
-	drm_for_each_crtc(crtc, dev) {
-		if (crtc->state->active) {
-			if (!drm->have_disp_power_ref) {
-				drm->have_disp_power_ref = true;
-				return 0;
-			}
-			active = true;
-			break;
-		}
-	}
-
-	if (!active && drm->have_disp_power_ref) {
-		pm_runtime_put_autosuspend(dev->dev);
-		drm->have_disp_power_ref = false;
-	}
-
 err_cleanup:
 	if (ret)
 		drm_atomic_helper_cleanup_planes(dev, state);
@@ -2316,6 +2312,7 @@ nv50_display_create(struct drm_device *dev)
 	disp->disp = &nouveau_display(dev)->disp;
 	dev->mode_config.funcs = &nv50_disp_func;
 	dev->mode_config.quirk_addfb_prefer_xbgr_30bpp = true;
+	dev->mode_config.normalize_zpos = true;
 
 	/* small shared memory area we use for notifiers and semaphores */
 	ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM,
diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c
index 929d93b1677e73c3d94501c4f236201b2c556f53..71c23bf1fe25915534fc0b61c46a760f500ce921 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/head.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/head.c
@@ -480,7 +480,7 @@ nv50_head_create(struct drm_device *dev, int index)
 	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct nv50_disp *disp = nv50_disp(dev);
 	struct nv50_head *head;
-	struct nv50_wndw *curs, *wndw;
+	struct nv50_wndw *base, *ovly, *curs;
 	struct drm_crtc *crtc;
 	int ret;
 
@@ -492,13 +492,13 @@ nv50_head_create(struct drm_device *dev, int index)
 	head->base.index = index;
 
 	if (disp->disp->object.oclass < GV100_DISP) {
-		ret = nv50_ovly_new(drm, head->base.index, &wndw);
-		ret = nv50_base_new(drm, head->base.index, &wndw);
+		ret = nv50_base_new(drm, head->base.index, &base);
+		ret = nv50_ovly_new(drm, head->base.index, &ovly);
 	} else {
-		ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY,
-				    head->base.index * 2 + 1, &wndw);
 		ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_PRIMARY,
-				    head->base.index * 2 + 0, &wndw);
+				    head->base.index * 2 + 0, &base);
+		ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY,
+				    head->base.index * 2 + 1, &ovly);
 	}
 	if (ret == 0)
 		ret = nv50_curs_new(drm, head->base.index, &curs);
@@ -508,10 +508,14 @@ nv50_head_create(struct drm_device *dev, int index)
 	}
 
 	crtc = &head->base.base;
-	drm_crtc_init_with_planes(dev, crtc, &wndw->plane, &curs->plane,
+	drm_crtc_init_with_planes(dev, crtc, &base->plane, &curs->plane,
 				  &nv50_head_func, "head-%d", head->base.index);
 	drm_crtc_helper_add(crtc, &nv50_head_help);
 	drm_mode_crtc_set_gamma_size(crtc, 256);
+	if (disp->disp->object.oclass >= GF110_DISP)
+		drm_crtc_enable_color_mgmt(crtc, 256, true, 256);
+	else
+		drm_crtc_enable_color_mgmt(crtc, 0, false, 256);
 
 	if (head->func->olut_set) {
 		ret = nv50_lut_init(disp, &drm->client.mmu, &head->olut);
diff --git a/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c b/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c
index cc417664f8231dba2def603a0e838dfed9c921ef..8ccd96113badcf0560bf3c134e3574b165fc551f 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/ovly507e.c
@@ -23,6 +23,7 @@
 #include "atom.h"
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_plane_helper.h>
 
 #include <nvif/cl507e.h>
@@ -160,9 +161,7 @@ ovly507e_format[] = {
 	DRM_FORMAT_YUYV,
 	DRM_FORMAT_UYVY,
 	DRM_FORMAT_XRGB8888,
-	DRM_FORMAT_ARGB8888,
 	DRM_FORMAT_XRGB1555,
-	DRM_FORMAT_ARGB1555,
 	0
 };
 
diff --git a/drivers/gpu/drm/nouveau/dispnv50/ovly827e.c b/drivers/gpu/drm/nouveau/dispnv50/ovly827e.c
index aaa9fe5a4fc8acab3f5559d20147e187485abb28..2e68fc736fe1b7a62aec1c89b0238f3b9a47329e 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/ovly827e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/ovly827e.c
@@ -90,11 +90,8 @@ ovly827e_format[] = {
 	DRM_FORMAT_YUYV,
 	DRM_FORMAT_UYVY,
 	DRM_FORMAT_XRGB8888,
-	DRM_FORMAT_ARGB8888,
 	DRM_FORMAT_XRGB1555,
-	DRM_FORMAT_ARGB1555,
 	DRM_FORMAT_XBGR2101010,
-	DRM_FORMAT_ABGR2101010,
 	0
 };
 
diff --git a/drivers/gpu/drm/nouveau/dispnv50/ovly907e.c b/drivers/gpu/drm/nouveau/dispnv50/ovly907e.c
index a3ce53046015c2f789013cd6456796d24b3dd70c..9efe5e9d5ce48c0b7402feb318833dee66dacda2 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/ovly907e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/ovly907e.c
@@ -61,10 +61,21 @@ ovly907e = {
 	.update = ovly507e_update,
 };
 
+static const u32
+ovly907e_format[] = {
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_XBGR2101010,
+	DRM_FORMAT_XBGR16161616F,
+	0
+};
+
 int
 ovly907e_new(struct nouveau_drm *drm, int head, s32 oclass,
 	     struct nv50_wndw **pwndw)
 {
-	return ovly507e_new_(&ovly907e, ovly827e_format, drm, head, oclass,
+	return ovly507e_new_(&ovly907e, ovly907e_format, drm, head, oclass,
 			     0x00000004 << (head * 4), pwndw);
 }
diff --git a/drivers/gpu/drm/nouveau/dispnv50/ovly917e.c b/drivers/gpu/drm/nouveau/dispnv50/ovly917e.c
index 505fa7e7852309b0446b6268771f87080c274510..e24d6fd234502da50295bfb0944cce454de7d8a7 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/ovly917e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/ovly917e.c
@@ -26,13 +26,10 @@ ovly917e_format[] = {
 	DRM_FORMAT_YUYV,
 	DRM_FORMAT_UYVY,
 	DRM_FORMAT_XRGB8888,
-	DRM_FORMAT_ARGB8888,
 	DRM_FORMAT_XRGB1555,
-	DRM_FORMAT_ARGB1555,
 	DRM_FORMAT_XBGR2101010,
-	DRM_FORMAT_ABGR2101010,
 	DRM_FORMAT_XRGB2101010,
-	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_XBGR16161616F,
 	0
 };
 
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
index 027a01b97d1c7e0f1b83781c84777bb5bc79ae10..2db029371c9192c5b3740e93bb370df657e05c56 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
@@ -26,6 +26,8 @@
 #include <nvif/cl0002.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_fourcc.h>
+
 #include "nouveau_bo.h"
 
 static void
@@ -118,6 +120,7 @@ nv50_wndw_flush_clr(struct nv50_wndw *wndw, u32 *interlock, bool flush,
 	if (clr.sema ) wndw->func-> sema_clr(wndw);
 	if (clr.ntfy ) wndw->func-> ntfy_clr(wndw);
 	if (clr.xlut ) wndw->func-> xlut_clr(wndw);
+	if (clr.csc  ) wndw->func->  csc_clr(wndw);
 	if (clr.image) wndw->func->image_clr(wndw);
 
 	interlock[wndw->interlock.type] |= wndw->interlock.data;
@@ -145,7 +148,9 @@ nv50_wndw_flush_set(struct nv50_wndw *wndw, u32 *interlock,
 		wndw->func->xlut_set(wndw, asyw);
 	}
 
+	if (asyw->set.csc  ) wndw->func->csc_set  (wndw, asyw);
 	if (asyw->set.scale) wndw->func->scale_set(wndw, asyw);
+	if (asyw->set.blend) wndw->func->blend_set(wndw, asyw);
 	if (asyw->set.point) {
 		if (asyw->set.point = false, asyw->set.mask)
 			interlock[wndw->interlock.type] |= wndw->interlock.data;
@@ -202,18 +207,20 @@ static int
 nv50_wndw_atomic_check_acquire_rgb(struct nv50_wndw_atom *asyw)
 {
 	switch (asyw->state.fb->format->format) {
-	case DRM_FORMAT_C8         : asyw->image.format = 0x1e; break;
-	case DRM_FORMAT_XRGB8888   :
-	case DRM_FORMAT_ARGB8888   : asyw->image.format = 0xcf; break;
-	case DRM_FORMAT_RGB565     : asyw->image.format = 0xe8; break;
-	case DRM_FORMAT_XRGB1555   :
-	case DRM_FORMAT_ARGB1555   : asyw->image.format = 0xe9; break;
-	case DRM_FORMAT_XBGR2101010:
-	case DRM_FORMAT_ABGR2101010: asyw->image.format = 0xd1; break;
-	case DRM_FORMAT_XBGR8888   :
-	case DRM_FORMAT_ABGR8888   : asyw->image.format = 0xd5; break;
-	case DRM_FORMAT_XRGB2101010:
-	case DRM_FORMAT_ARGB2101010: asyw->image.format = 0xdf; break;
+	case DRM_FORMAT_C8           : asyw->image.format = 0x1e; break;
+	case DRM_FORMAT_XRGB8888     :
+	case DRM_FORMAT_ARGB8888     : asyw->image.format = 0xcf; break;
+	case DRM_FORMAT_RGB565       : asyw->image.format = 0xe8; break;
+	case DRM_FORMAT_XRGB1555     :
+	case DRM_FORMAT_ARGB1555     : asyw->image.format = 0xe9; break;
+	case DRM_FORMAT_XBGR2101010  :
+	case DRM_FORMAT_ABGR2101010  : asyw->image.format = 0xd1; break;
+	case DRM_FORMAT_XBGR8888     :
+	case DRM_FORMAT_ABGR8888     : asyw->image.format = 0xd5; break;
+	case DRM_FORMAT_XRGB2101010  :
+	case DRM_FORMAT_ARGB2101010  : asyw->image.format = 0xdf; break;
+	case DRM_FORMAT_XBGR16161616F:
+	case DRM_FORMAT_ABGR16161616F: asyw->image.format = 0xca; break;
 	default:
 		return -EINVAL;
 	}
@@ -279,6 +286,28 @@ nv50_wndw_atomic_check_acquire(struct nv50_wndw *wndw, bool modeset,
 			asyw->set.scale = true;
 	}
 
+	if (wndw->func->blend_set) {
+		asyw->blend.depth = 255 - asyw->state.normalized_zpos;
+		asyw->blend.k1 = asyw->state.alpha >> 8;
+		switch (asyw->state.pixel_blend_mode) {
+		case DRM_MODE_BLEND_PREMULTI:
+			asyw->blend.src_color = 2; /* K1 */
+			asyw->blend.dst_color = 7; /* NEG_K1_TIMES_SRC */
+			break;
+		case DRM_MODE_BLEND_COVERAGE:
+			asyw->blend.src_color = 5; /* K1_TIMES_SRC */
+			asyw->blend.dst_color = 7; /* NEG_K1_TIMES_SRC */
+			break;
+		case DRM_MODE_BLEND_PIXEL_NONE:
+		default:
+			asyw->blend.src_color = 2; /* K1 */
+			asyw->blend.dst_color = 4; /* NEG_K1 */
+			break;
+		}
+		if (memcmp(&armw->blend, &asyw->blend, sizeof(asyw->blend)))
+			asyw->set.blend = true;
+	}
+
 	if (wndw->immd) {
 		asyw->point.x = asyw->state.crtc_x;
 		asyw->point.y = asyw->state.crtc_y;
@@ -320,7 +349,9 @@ nv50_wndw_atomic_check_lut(struct nv50_wndw *wndw,
 		asyh->wndw.olut &= ~BIT(wndw->id);
 	}
 
-	if (!ilut && wndw->func->ilut_identity) {
+	if (!ilut && wndw->func->ilut_identity &&
+	    asyw->state.fb->format->format != DRM_FORMAT_XBGR16161616F &&
+	    asyw->state.fb->format->format != DRM_FORMAT_ABGR16161616F) {
 		static struct drm_property_blob dummy = {};
 		ilut = &dummy;
 	}
@@ -332,6 +363,8 @@ nv50_wndw_atomic_check_lut(struct nv50_wndw *wndw,
 		asyw->xlut.handle = wndw->wndw.vram.handle;
 		asyw->xlut.i.buffer = !asyw->xlut.i.buffer;
 		asyw->set.xlut = true;
+	} else {
+		asyw->clr.xlut = armw->xlut.handle != 0;
 	}
 
 	/* Handle setting base SET_OUTPUT_LUT_LO_ENABLE_USE_CORE_LUT. */
@@ -339,6 +372,16 @@ nv50_wndw_atomic_check_lut(struct nv50_wndw *wndw,
 	    (!armw->visible || (armw->xlut.handle && !asyw->xlut.handle)))
 		asyw->set.xlut = true;
 
+	if (wndw->func->csc && asyh->state.ctm) {
+		const struct drm_color_ctm *ctm = asyh->state.ctm->data;
+		wndw->func->csc(wndw, asyw, ctm);
+		asyw->csc.valid = true;
+		asyw->set.csc = true;
+	} else {
+		asyw->csc.valid = false;
+		asyw->clr.csc = armw->csc.valid;
+	}
+
 	/* Can't do an immediate flip while changing the LUT. */
 	asyh->state.pageflip_flags &= ~DRM_MODE_PAGE_FLIP_ASYNC;
 }
@@ -408,6 +451,7 @@ nv50_wndw_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
 		asyw->clr.ntfy = armw->ntfy.handle != 0;
 		asyw->clr.sema = armw->sema.handle != 0;
 		asyw->clr.xlut = armw->xlut.handle != 0;
+		asyw->clr.csc  = armw->csc.valid;
 		if (wndw->func->image_clr)
 			asyw->clr.image = armw->image.handle[0] != 0;
 	}
@@ -499,6 +543,7 @@ nv50_wndw_atomic_duplicate_state(struct drm_plane *plane)
 	asyw->ntfy = armw->ntfy;
 	asyw->ilut = NULL;
 	asyw->xlut = armw->xlut;
+	asyw->csc  = armw->csc;
 	asyw->image = armw->image;
 	asyw->point = armw->point;
 	asyw->clr.mask = 0;
@@ -506,6 +551,13 @@ nv50_wndw_atomic_duplicate_state(struct drm_plane *plane)
 	return &asyw->state;
 }
 
+static int
+nv50_wndw_zpos_default(struct drm_plane *plane)
+{
+	return (plane->type == DRM_PLANE_TYPE_PRIMARY) ? 0 :
+	       (plane->type == DRM_PLANE_TYPE_OVERLAY) ? 1 : 255;
+}
+
 static void
 nv50_wndw_reset(struct drm_plane *plane)
 {
@@ -516,9 +568,10 @@ nv50_wndw_reset(struct drm_plane *plane)
 
 	if (plane->state)
 		plane->funcs->atomic_destroy_state(plane, plane->state);
-	plane->state = &asyw->state;
-	plane->state->plane = plane;
-	plane->state->rotation = DRM_MODE_ROTATE_0;
+
+	__drm_atomic_helper_plane_reset(plane, &asyw->state);
+	plane->state->zpos = nv50_wndw_zpos_default(plane);
+	plane->state->normalized_zpos = nv50_wndw_zpos_default(plane);
 }
 
 static void
@@ -613,6 +666,30 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
 	}
 
 	wndw->notify.func = nv50_wndw_notify;
+
+	if (wndw->func->blend_set) {
+		ret = drm_plane_create_zpos_property(&wndw->plane,
+				nv50_wndw_zpos_default(&wndw->plane), 0, 254);
+		if (ret)
+			return ret;
+
+		ret = drm_plane_create_alpha_property(&wndw->plane);
+		if (ret)
+			return ret;
+
+		ret = drm_plane_create_blend_mode_property(&wndw->plane,
+				BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+				BIT(DRM_MODE_BLEND_PREMULTI) |
+				BIT(DRM_MODE_BLEND_COVERAGE));
+		if (ret)
+			return ret;
+	} else {
+		ret = drm_plane_create_zpos_immutable_property(&wndw->plane,
+				nv50_wndw_zpos_default(&wndw->plane));
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.h b/drivers/gpu/drm/nouveau/dispnv50/wndw.h
index 03f3d8dc235a70cb5d15670c687b427c5c665319..c63bd3bdaf0690ea8de8acc5ba1a51bf69b384b1 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndw.h
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.h
@@ -65,6 +65,10 @@ struct nv50_wndw_func {
 	int (*ntfy_wait_begun)(struct nouveau_bo *, u32 offset,
 			       struct nvif_device *);
 	void (*ilut)(struct nv50_wndw *, struct nv50_wndw_atom *);
+	void (*csc)(struct nv50_wndw *, struct nv50_wndw_atom *,
+		    const struct drm_color_ctm *);
+	void (*csc_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
+	void (*csc_clr)(struct nv50_wndw *);
 	bool ilut_identity;
 	bool olut_core;
 	void (*xlut_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
@@ -72,6 +76,7 @@ struct nv50_wndw_func {
 	void (*image_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
 	void (*image_clr)(struct nv50_wndw *);
 	void (*scale_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
+	void (*blend_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
 
 	void (*update)(struct nv50_wndw *, u32 *interlock);
 };
@@ -81,6 +86,9 @@ extern const struct drm_plane_funcs nv50_wndw;
 void base507c_ntfy_reset(struct nouveau_bo *, u32);
 int base507c_ntfy_wait_begun(struct nouveau_bo *, u32, struct nvif_device *);
 
+void base907c_csc(struct nv50_wndw *, struct nv50_wndw_atom *,
+		  const struct drm_color_ctm *);
+
 struct nv50_wimm_func {
 	void (*point)(struct nv50_wndw *, struct nv50_wndw_atom *);
 
@@ -102,8 +110,8 @@ void wndwc37e_sema_set(struct nv50_wndw *, struct nv50_wndw_atom *);
 void wndwc37e_sema_clr(struct nv50_wndw *);
 void wndwc37e_ntfy_set(struct nv50_wndw *, struct nv50_wndw_atom *);
 void wndwc37e_ntfy_clr(struct nv50_wndw *);
-void wndwc37e_image_set(struct nv50_wndw *, struct nv50_wndw_atom *);
 void wndwc37e_image_clr(struct nv50_wndw *);
+void wndwc37e_blend_set(struct nv50_wndw *, struct nv50_wndw_atom *);
 void wndwc37e_update(struct nv50_wndw *, u32 *);
 
 int wndwc57e_new(struct nouveau_drm *, enum drm_plane_type, int, s32,
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c
index e52a85c83f7a81358981536b419db9e5a475d82e..0f9402162bde92eab82c0b86e0b554606dddd2e3 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c
@@ -28,6 +28,23 @@
 
 #include <nvif/clc37e.h>
 
+static void
+wndwc37e_csc_clr(struct nv50_wndw *wndw)
+{
+}
+
+static void
+wndwc37e_csc_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+	u32 *push, i;
+	if ((push = evo_wait(&wndw->wndw, 13))) {
+		 evo_mthd(push, 0x02bc, 12);
+		 for (i = 0; i < 12; i++)
+			  evo_data(push, asyw->csc.matrix[i]);
+		 evo_kick(push, &wndw->wndw);
+	}
+}
+
 static void
 wndwc37e_ilut_clr(struct nv50_wndw *wndw)
 {
@@ -64,6 +81,26 @@ wndwc37e_ilut(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
 	asyw->xlut.i.load = head907d_olut_load;
 }
 
+void
+wndwc37e_blend_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+	u32 *push;
+	if ((push = evo_wait(&wndw->wndw, 8))) {
+		evo_mthd(push, 0x02ec, 7);
+		evo_data(push, asyw->blend.depth << 4);
+		evo_data(push, asyw->blend.k1);
+		evo_data(push, asyw->blend.dst_color << 12 |
+			       asyw->blend.dst_color << 8 |
+			       asyw->blend.src_color << 4 |
+			       asyw->blend.src_color);
+		evo_data(push, 0xffff0000);
+		evo_data(push, 0xffff0000);
+		evo_data(push, 0xffff0000);
+		evo_data(push, 0xffff0000);
+		evo_kick(push, &wndw->wndw);
+	}
+}
+
 void
 wndwc37e_image_clr(struct nv50_wndw *wndw)
 {
@@ -77,12 +114,12 @@ wndwc37e_image_clr(struct nv50_wndw *wndw)
 	}
 }
 
-void
+static void
 wndwc37e_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
 {
 	u32 *push;
 
-	if (!(push = evo_wait(&wndw->wndw, 25)))
+	if (!(push = evo_wait(&wndw->wndw, 17)))
 		return;
 
 	evo_mthd(push, 0x0308, 1);
@@ -90,7 +127,9 @@ wndwc37e_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
 	evo_mthd(push, 0x0224, 4);
 	evo_data(push, asyw->image.h << 16 | asyw->image.w);
 	evo_data(push, asyw->image.layout << 4 | asyw->image.blockh);
-	evo_data(push, asyw->image.colorspace << 8 | asyw->image.format);
+	evo_data(push, asyw->csc.valid << 17 |
+		       asyw->image.colorspace << 8 |
+		       asyw->image.format);
 	evo_data(push, asyw->image.blocks[0] | (asyw->image.pitch[0] >> 6));
 	evo_mthd(push, 0x0240, 1);
 	evo_data(push, asyw->image.handle[0]);
@@ -105,16 +144,6 @@ wndwc37e_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
 	evo_mthd(push, 0x02a4, 1);
 	evo_data(push, asyw->state.crtc_h << 16 |
 		       asyw->state.crtc_w);
-
-	/*XXX: Composition-related stuff.  Need to implement properly. */
-	evo_mthd(push, 0x02ec, 1);
-	evo_data(push, (2 - (wndw->id & 1)) << 4);
-	evo_mthd(push, 0x02f4, 5);
-	evo_data(push, 0x00000011);
-	evo_data(push, 0xffff0000);
-	evo_data(push, 0xffff0000);
-	evo_data(push, 0xffff0000);
-	evo_data(push, 0xffff0000);
 	evo_kick(push, &wndw->wndw);
 }
 
@@ -216,6 +245,8 @@ wndwc37e_format[] = {
 	DRM_FORMAT_ABGR8888,
 	DRM_FORMAT_XRGB2101010,
 	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_XBGR16161616F,
+	DRM_FORMAT_ABGR16161616F,
 	0
 };
 
@@ -232,8 +263,12 @@ wndwc37e = {
 	.ilut = wndwc37e_ilut,
 	.xlut_set = wndwc37e_ilut_set,
 	.xlut_clr = wndwc37e_ilut_clr,
+	.csc = base907c_csc,
+	.csc_set = wndwc37e_csc_set,
+	.csc_clr = wndwc37e_csc_clr,
 	.image_set = wndwc37e_image_set,
 	.image_clr = wndwc37e_image_clr,
+	.blend_set = wndwc37e_blend_set,
 	.update = wndwc37e_update,
 };
 
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c
index ba89f1a5fcfaf7795b4ccbc3883bd6dcbc71f4fc..a311c79e52953fc243f09c0924c3dee833381b22 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c
@@ -28,6 +28,72 @@
 
 #include <nvif/clc37e.h>
 
+static void
+wndwc57e_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+	u32 *push;
+
+	if (!(push = evo_wait(&wndw->wndw, 17)))
+		return;
+
+	evo_mthd(push, 0x0308, 1);
+	evo_data(push, asyw->image.mode << 4 | asyw->image.interval);
+	evo_mthd(push, 0x0224, 4);
+	evo_data(push, asyw->image.h << 16 | asyw->image.w);
+	evo_data(push, asyw->image.layout << 4 | asyw->image.blockh);
+	evo_data(push, asyw->image.colorspace << 8 |
+		       asyw->image.format);
+	evo_data(push, asyw->image.blocks[0] | (asyw->image.pitch[0] >> 6));
+	evo_mthd(push, 0x0240, 1);
+	evo_data(push, asyw->image.handle[0]);
+	evo_mthd(push, 0x0260, 1);
+	evo_data(push, asyw->image.offset[0] >> 8);
+	evo_mthd(push, 0x0290, 1);
+	evo_data(push, (asyw->state.src_y >> 16) << 16 |
+		       (asyw->state.src_x >> 16));
+	evo_mthd(push, 0x0298, 1);
+	evo_data(push, (asyw->state.src_h >> 16) << 16 |
+		       (asyw->state.src_w >> 16));
+	evo_mthd(push, 0x02a4, 1);
+	evo_data(push, asyw->state.crtc_h << 16 |
+		       asyw->state.crtc_w);
+	evo_kick(push, &wndw->wndw);
+}
+
+static void
+wndwc57e_csc_clr(struct nv50_wndw *wndw)
+{
+	u32 *push;
+	if ((push = evo_wait(&wndw->wndw, 13))) {
+		 evo_mthd(push, 0x0400, 12);
+		 evo_data(push, 0x00010000);
+		 evo_data(push, 0x00000000);
+		 evo_data(push, 0x00000000);
+		 evo_data(push, 0x00000000);
+		 evo_data(push, 0x00000000);
+		 evo_data(push, 0x00010000);
+		 evo_data(push, 0x00000000);
+		 evo_data(push, 0x00000000);
+		 evo_data(push, 0x00000000);
+		 evo_data(push, 0x00000000);
+		 evo_data(push, 0x00010000);
+		 evo_data(push, 0x00000000);
+		 evo_kick(push, &wndw->wndw);
+	}
+}
+
+static void
+wndwc57e_csc_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+	u32 *push, i;
+	if ((push = evo_wait(&wndw->wndw, 13))) {
+		 evo_mthd(push, 0x0400, 12);
+		 for (i = 0; i < 12; i++)
+			  evo_data(push, asyw->csc.matrix[i]);
+		 evo_kick(push, &wndw->wndw);
+	}
+}
+
 static void
 wndwc57e_ilut_clr(struct nv50_wndw *wndw)
 {
@@ -119,8 +185,12 @@ wndwc57e = {
 	.ilut_identity = true,
 	.xlut_set = wndwc57e_ilut_set,
 	.xlut_clr = wndwc57e_ilut_clr,
-	.image_set = wndwc37e_image_set,
+	.csc = base907c_csc,
+	.csc_set = wndwc57e_csc_set,
+	.csc_clr = wndwc57e_csc_clr,
+	.image_set = wndwc57e_image_set,
 	.image_clr = wndwc37e_image_clr,
+	.blend_set = wndwc37e_blend_set,
 	.update = wndwc37e_update,
 };
 
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h
index f29f2d8da142bf0b4a5917018b69f7ef8b10760f..9ac3dda4b44f079462933be03fbcd5c92574b101 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/extdev.h
@@ -26,4 +26,6 @@ nvbios_extdev_parse(struct nvkm_bios *, int, struct nvbios_extdev_func *);
 int
 nvbios_extdev_find(struct nvkm_bios *, enum nvbios_extdev_type,
 		   struct nvbios_extdev_func *);
+
+bool nvbios_extdev_skip_probe(struct nvkm_bios *);
 #endif
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h
index 7c4f00366e7140f10f003f92bb6e5998fa14e84e..3f785f29dfac2f012aac17866265ed2a1ce137cf 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h
@@ -3,10 +3,13 @@
 #define __NVBIOS_GPIO_H__
 enum dcb_gpio_func_name {
 	DCB_GPIO_PANEL_POWER = 0x01,
+	DCB_GPIO_FAN = 0x09,
 	DCB_GPIO_TVDAC0 = 0x0c,
+	DCB_GPIO_THERM_EXT_POWER_EVENT = 0x10,
 	DCB_GPIO_TVDAC1 = 0x2d,
-	DCB_GPIO_FAN = 0x09,
 	DCB_GPIO_FAN_SENSE = 0x3d,
+	DCB_GPIO_POWER_ALERT = 0x4c,
+	DCB_GPIO_EXT_POWER_LOW = 0x79,
 	DCB_GPIO_LOGO_LED_PWM = 0x84,
 	DCB_GPIO_UNUSED = 0xff,
 	DCB_GPIO_VID0 = 0x04,
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h
index 24fbcccd93eba821e2ae37179e10038c9f82327f..4752006880f39469648551b60f207a28796fc355 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h
@@ -30,6 +30,7 @@ struct nvkm_pmu {
 int nvkm_pmu_send(struct nvkm_pmu *, u32 reply[2], u32 process,
 		  u32 message, u32 data0, u32 data1);
 void nvkm_pmu_pgob(struct nvkm_pmu *, bool enable);
+bool nvkm_pmu_fan_controlled(struct nvkm_device *);
 
 int gt215_pmu_new(struct nvkm_device *, int, struct nvkm_pmu **);
 int gf100_pmu_new(struct nvkm_device *, int, struct nvkm_pmu **);
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 66bf2aff4a3ed17b46e7d9cbb3dab34f6f66745b..d204ea8a5618edf2d42a63cad34696d7667dfdc7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -22,8 +22,6 @@
  * SOFTWARE.
  */
 
-#include <drm/drmP.h>
-
 #include "nouveau_drv.h"
 #include "nouveau_reg.h"
 #include "dispnv04/hw.h"
@@ -935,7 +933,7 @@ static int parse_bit_tmds_tbl_entry(struct drm_device *dev, struct nvbios *bios,
 
 	tmdstableptr = ROM16(bios->data[bitentry->offset]);
 	if (!tmdstableptr) {
-		NV_ERROR(drm, "Pointer to TMDS table invalid\n");
+		NV_INFO(drm, "Pointer to TMDS table not found\n");
 		return -EINVAL;
 	}
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 330d7d29a6e34244d6a1c4a0dab809cecc17edd9..94dfa2e5a9abed6c8e4a16410c22b11c44b40447 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -29,7 +29,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/vga_switcheroo.h>
 
-#include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_crtc_helper.h>
diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h
index 366acb928f5771682df777eddb721f4934ffc70b..7f63be2ec35db53377cdb8b0bcbd791d040502e3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_crtc.h
+++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h
@@ -27,6 +27,8 @@
 #ifndef __NOUVEAU_CRTC_H__
 #define __NOUVEAU_CRTC_H__
 
+#include <drm/drm_crtc.h>
+
 #include <nvif/notify.h>
 
 struct nouveau_crtc {
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.h b/drivers/gpu/drm/nouveau/nouveau_debugfs.h
index 9420a6aca1385cc2b52a3dc64d1068cfb8695f97..8909c010e8eaebfe4fe4eb08737a61e01e98be11 100644
--- a/drivers/gpu/drm/nouveau/nouveau_debugfs.h
+++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.h
@@ -2,7 +2,7 @@
 #ifndef __NOUVEAU_DEBUGFS_H__
 #define __NOUVEAU_DEBUGFS_H__
 
-#include <drm/drmP.h>
+#include <drm/drm_debugfs.h>
 
 #if defined(CONFIG_DEBUG_FS)
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 98afc50162e93e01a5683e45b27fd68a13e9ec84..6f038511a03a9a1ba2051e5994de671c641315d7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -25,12 +25,14 @@
  */
 
 #include <acpi/video.h>
-#include <drm/drmP.h>
+
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
 
 #include "nouveau_fbcon.h"
 #include "nouveau_crtc.h"
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h
index 9185f01e2d9b94b350cf846fd7316181de9789d1..6e8e66882e4547084c67f7fd190b3be6b856fc10 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.h
+++ b/drivers/gpu/drm/nouveau/nouveau_display.h
@@ -1,9 +1,13 @@
 /* SPDX-License-Identifier: MIT */
 #ifndef __NOUVEAU_DISPLAY_H__
 #define __NOUVEAU_DISPLAY_H__
+
 #include "nouveau_drv.h"
+
 #include <nvif/disp.h>
 
+#include <drm/drm_framebuffer.h>
+
 struct nouveau_framebuffer {
 	struct drm_framebuffer base;
 	struct nouveau_bo *nvbo;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
index 078f65d849ce4403839a9d857021612c7ce6e7c8..3c430a550a514c3310630f7be78a90fe2006eea3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -118,7 +118,7 @@ nv50_dma_push_wait(struct nouveau_channel *chan, int count)
 		}
 
 		if ((++cnt & 0xff) == 0) {
-			DRM_UDELAY(1);
+			udelay(1);
 			if (cnt > 100000)
 				return -EBUSY;
 		}
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 0d052e1660f839b4dee6b68b13590982fc33c00c..2674f1587457a0fa8c053fc9cb139ef8e7453a15 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -22,7 +22,6 @@
  * Authors: Ben Skeggs
  */
 
-#include <drm/drmP.h>
 #include <drm/drm_dp_helper.h>
 
 #include "nouveau_drv.h"
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 7e045580a3a4d3f3b135f38b5c5f7a039d7c7e3a..bdc948352467a97fa702b522992f9027b8c8e714 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -29,8 +29,9 @@
 #include <linux/pm_runtime.h>
 #include <linux/vga_switcheroo.h>
 
-#include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_vblank.h>
 
 #include <core/gpuobj.h>
 #include <core/option.h>
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index aae03581638377dd33f3381034326aeb347a4501..70f34cacc552cbd3617abe2a22cb9111a0171eea 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -46,7 +46,10 @@
 #include <nvif/mmu.h>
 #include <nvif/vmm.h>
 
-#include <drm/drmP.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_device.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
 
 #include <drm/ttm/ttm_bo_api.h>
 #include <drm/ttm/ttm_bo_driver.h>
@@ -127,7 +130,6 @@ nouveau_cli(struct drm_file *fpriv)
 }
 
 #include <nvif/object.h>
-#include <nvif/device.h>
 
 struct nouveau_drm {
 	struct nouveau_cli master;
@@ -204,9 +206,6 @@ struct nouveau_drm {
 	/* led management */
 	struct nouveau_led *led;
 
-	/* display power reference */
-	bool have_disp_power_ref;
-
 	struct dev_pm_domain vga_pm_domain;
 
 	struct nouveau_svm *svm;
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 73cc3217068a5560cdd3360840c2ce17a4f29047..f439f0a5b43a5a619550fa6977efd340c05bafba 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -37,10 +37,10 @@
 #include <linux/vga_switcheroo.h>
 #include <linux/console.h>
 
-#include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_atomic.h>
 
 #include "nouveau_drv.h"
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 8df390078c855b2937cbdc2f45be5c28b1bd5b87..9118df035b28da6fb00e5c51b12fc86d5f4d2e85 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -24,10 +24,9 @@
  *
  */
 
-#include <drm/drmP.h>
-
 #include <linux/ktime.h>
 #include <linux/hrtimer.h>
+#include <linux/sched/signal.h>
 #include <trace/events/dma_fence.h>
 
 #include <nvif/cl826e.h>
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.h b/drivers/gpu/drm/nouveau/nouveau_gem.h
index 40ba0f1ba5aac8c762512213ab5e0422360cec30..978e07591990bd4a17f12cfdd0a28498aee2c5ea 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.h
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.h
@@ -2,8 +2,6 @@
 #ifndef __NOUVEAU_GEM_H__
 #define __NOUVEAU_GEM_H__
 
-#include <drm/drmP.h>
-
 #include "nouveau_drv.h"
 #include "nouveau_bo.h"
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
index 6af2d299c3f9ce78e41bfbb883d37b3f8a3f6bc6..d445c6f3fecebb4c6775b413349972ccfc532917 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hwmon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
@@ -29,8 +29,6 @@
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
 
-#include <drm/drmP.h>
-
 #include "nouveau_drv.h"
 #include "nouveau_hwmon.h"
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_ioc32.c b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
index 462679a8fec5783735a5cabd61058a79e212999c..adf01ca9e035d50cea5537dac985dbdd42324224 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ioc32.c
+++ b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
@@ -33,7 +33,8 @@
 
 #include <linux/compat.h>
 
-#include <drm/drmP.h>
+#include <drm/drm.h>
+#include <drm/drm_ioctl.h>
 
 #include "nouveau_ioctl.h"
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c
index 7262ced9688a7b7503df2bb400cb4db540c172a4..6a222cdf293561feb4717e8e7f9f27f363d68166 100644
--- a/drivers/gpu/drm/nouveau/nouveau_prime.c
+++ b/drivers/gpu/drm/nouveau/nouveau_prime.c
@@ -22,7 +22,6 @@
  * Authors: Dave Airlie
  */
 
-#include <drm/drmP.h>
 #include <linux/dma-buf.h>
 
 #include "nouveau_drv.h"
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 8f4b12a8092caaea52c166bd1fba5850ae082d13..d865d8aeac3c7153eac0d6e60aed8df421953f72 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -2,7 +2,6 @@
 #include <linux/vgaarb.h>
 #include <linux/vga_switcheroo.h>
 
-#include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
 
diff --git a/drivers/gpu/drm/nouveau/nvif/mmu.c b/drivers/gpu/drm/nouveau/nvif/mmu.c
index ae08a1ca804484b6329a5712474b158ea6b6d7ea..5641bda2046da6efb3d73cca0f5adde90ef13ae2 100644
--- a/drivers/gpu/drm/nouveau/nvif/mmu.c
+++ b/drivers/gpu/drm/nouveau/nvif/mmu.c
@@ -110,7 +110,7 @@ nvif_mmu_init(struct nvif_object *parent, s32 oclass, struct nvif_mmu *mmu)
 
 	if (mmu->kind_nr) {
 		struct nvif_mmu_kind_v0 *kind;
-		u32 argc = sizeof(*kind) + sizeof(*kind->data) * mmu->kind_nr;
+		size_t argc = struct_size(kind, data, mmu->kind_nr);
 
 		if (ret = -ENOMEM, !(kind = kmalloc(argc, GFP_KERNEL)))
 			goto done;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c
index 10a2e7039a7522a51b1d05326e8fe93db61bb1e5..5a39e51d42d738fe5800fc2a97feb8255c901b8a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c
@@ -28,6 +28,7 @@
 #include <core/enum.h>
 #include <core/gpuobj.h>
 #include <subdev/bar.h>
+#include <subdev/fault.h>
 #include <engine/sw.h>
 
 #include <nvif/class.h>
@@ -193,68 +194,6 @@ gf100_fifo_recover(struct gf100_fifo *fifo, struct nvkm_engine *engine,
 	nvkm_fifo_kevent(&fifo->base, chid);
 }
 
-static const struct nvkm_enum
-gf100_fifo_sched_reason[] = {
-	{ 0x0a, "CTXSW_TIMEOUT" },
-	{}
-};
-
-static void
-gf100_fifo_intr_sched_ctxsw(struct gf100_fifo *fifo)
-{
-	struct nvkm_device *device = fifo->base.engine.subdev.device;
-	struct nvkm_engine *engine;
-	struct gf100_fifo_chan *chan;
-	unsigned long flags;
-	u32 engn;
-
-	spin_lock_irqsave(&fifo->base.lock, flags);
-	for (engn = 0; engn < 6; engn++) {
-		u32 stat = nvkm_rd32(device, 0x002640 + (engn * 0x04));
-		u32 busy = (stat & 0x80000000);
-		u32 save = (stat & 0x00100000); /* maybe? */
-		u32 unk0 = (stat & 0x00040000);
-		u32 unk1 = (stat & 0x00001000);
-		u32 chid = (stat & 0x0000007f);
-		(void)save;
-
-		if (busy && unk0 && unk1) {
-			list_for_each_entry(chan, &fifo->chan, head) {
-				if (chan->base.chid == chid) {
-					engine = gf100_fifo_engine(fifo, engn);
-					if (!engine)
-						break;
-					gf100_fifo_recover(fifo, engine, chan);
-					break;
-				}
-			}
-		}
-	}
-	spin_unlock_irqrestore(&fifo->base.lock, flags);
-}
-
-static void
-gf100_fifo_intr_sched(struct gf100_fifo *fifo)
-{
-	struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
-	struct nvkm_device *device = subdev->device;
-	u32 intr = nvkm_rd32(device, 0x00254c);
-	u32 code = intr & 0x000000ff;
-	const struct nvkm_enum *en;
-
-	en = nvkm_enum_find(gf100_fifo_sched_reason, code);
-
-	nvkm_error(subdev, "SCHED_ERROR %02x [%s]\n", code, en ? en->name : "");
-
-	switch (code) {
-	case 0x0a:
-		gf100_fifo_intr_sched_ctxsw(fifo);
-		break;
-	default:
-		break;
-	}
-}
-
 static const struct nvkm_enum
 gf100_fifo_fault_engine[] = {
 	{ 0x00, "PGRAPH", NULL, NVKM_ENGINE_GR },
@@ -315,32 +254,24 @@ gf100_fifo_fault_gpcclient[] = {
 };
 
 static void
-gf100_fifo_intr_fault(struct gf100_fifo *fifo, int unit)
+gf100_fifo_fault(struct nvkm_fifo *base, struct nvkm_fault_data *info)
 {
+	struct gf100_fifo *fifo = gf100_fifo(base);
 	struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
 	struct nvkm_device *device = subdev->device;
-	u32 inst = nvkm_rd32(device, 0x002800 + (unit * 0x10));
-	u32 valo = nvkm_rd32(device, 0x002804 + (unit * 0x10));
-	u32 vahi = nvkm_rd32(device, 0x002808 + (unit * 0x10));
-	u32 stat = nvkm_rd32(device, 0x00280c + (unit * 0x10));
-	u32 gpc    = (stat & 0x1f000000) >> 24;
-	u32 client = (stat & 0x00001f00) >> 8;
-	u32 write  = (stat & 0x00000080);
-	u32 hub    = (stat & 0x00000040);
-	u32 reason = (stat & 0x0000000f);
 	const struct nvkm_enum *er, *eu, *ec;
 	struct nvkm_engine *engine = NULL;
 	struct nvkm_fifo_chan *chan;
 	unsigned long flags;
 	char gpcid[8] = "";
 
-	er = nvkm_enum_find(gf100_fifo_fault_reason, reason);
-	eu = nvkm_enum_find(gf100_fifo_fault_engine, unit);
-	if (hub) {
-		ec = nvkm_enum_find(gf100_fifo_fault_hubclient, client);
+	er = nvkm_enum_find(gf100_fifo_fault_reason, info->reason);
+	eu = nvkm_enum_find(gf100_fifo_fault_engine, info->engine);
+	if (info->hub) {
+		ec = nvkm_enum_find(gf100_fifo_fault_hubclient, info->client);
 	} else {
-		ec = nvkm_enum_find(gf100_fifo_fault_gpcclient, client);
-		snprintf(gpcid, sizeof(gpcid), "GPC%d/", gpc);
+		ec = nvkm_enum_find(gf100_fifo_fault_gpcclient, info->client);
+		snprintf(gpcid, sizeof(gpcid), "GPC%d/", info->gpc);
 	}
 
 	if (eu && eu->data2) {
@@ -360,22 +291,108 @@ gf100_fifo_intr_fault(struct gf100_fifo *fifo, int unit)
 		}
 	}
 
-	chan = nvkm_fifo_chan_inst(&fifo->base, (u64)inst << 12, &flags);
+	chan = nvkm_fifo_chan_inst(&fifo->base, info->inst, &flags);
 
 	nvkm_error(subdev,
 		   "%s fault at %010llx engine %02x [%s] client %02x [%s%s] "
 		   "reason %02x [%s] on channel %d [%010llx %s]\n",
-		   write ? "write" : "read", (u64)vahi << 32 | valo,
-		   unit, eu ? eu->name : "", client, gpcid, ec ? ec->name : "",
-		   reason, er ? er->name : "", chan ? chan->chid : -1,
-		   (u64)inst << 12,
-		   chan ? chan->object.client->name : "unknown");
+		   info->access ? "write" : "read", info->addr,
+		   info->engine, eu ? eu->name : "",
+		   info->client, gpcid, ec ? ec->name : "",
+		   info->reason, er ? er->name : "", chan ? chan->chid : -1,
+		   info->inst, chan ? chan->object.client->name : "unknown");
 
 	if (engine && chan)
 		gf100_fifo_recover(fifo, engine, (void *)chan);
 	nvkm_fifo_chan_put(&fifo->base, flags, &chan);
 }
 
+static const struct nvkm_enum
+gf100_fifo_sched_reason[] = {
+	{ 0x0a, "CTXSW_TIMEOUT" },
+	{}
+};
+
+static void
+gf100_fifo_intr_sched_ctxsw(struct gf100_fifo *fifo)
+{
+	struct nvkm_device *device = fifo->base.engine.subdev.device;
+	struct nvkm_engine *engine;
+	struct gf100_fifo_chan *chan;
+	unsigned long flags;
+	u32 engn;
+
+	spin_lock_irqsave(&fifo->base.lock, flags);
+	for (engn = 0; engn < 6; engn++) {
+		u32 stat = nvkm_rd32(device, 0x002640 + (engn * 0x04));
+		u32 busy = (stat & 0x80000000);
+		u32 save = (stat & 0x00100000); /* maybe? */
+		u32 unk0 = (stat & 0x00040000);
+		u32 unk1 = (stat & 0x00001000);
+		u32 chid = (stat & 0x0000007f);
+		(void)save;
+
+		if (busy && unk0 && unk1) {
+			list_for_each_entry(chan, &fifo->chan, head) {
+				if (chan->base.chid == chid) {
+					engine = gf100_fifo_engine(fifo, engn);
+					if (!engine)
+						break;
+					gf100_fifo_recover(fifo, engine, chan);
+					break;
+				}
+			}
+		}
+	}
+	spin_unlock_irqrestore(&fifo->base.lock, flags);
+}
+
+static void
+gf100_fifo_intr_sched(struct gf100_fifo *fifo)
+{
+	struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
+	struct nvkm_device *device = subdev->device;
+	u32 intr = nvkm_rd32(device, 0x00254c);
+	u32 code = intr & 0x000000ff;
+	const struct nvkm_enum *en;
+
+	en = nvkm_enum_find(gf100_fifo_sched_reason, code);
+
+	nvkm_error(subdev, "SCHED_ERROR %02x [%s]\n", code, en ? en->name : "");
+
+	switch (code) {
+	case 0x0a:
+		gf100_fifo_intr_sched_ctxsw(fifo);
+		break;
+	default:
+		break;
+	}
+}
+
+void
+gf100_fifo_intr_fault(struct nvkm_fifo *fifo, int unit)
+{
+	struct nvkm_device *device = fifo->engine.subdev.device;
+	u32 inst = nvkm_rd32(device, 0x002800 + (unit * 0x10));
+	u32 valo = nvkm_rd32(device, 0x002804 + (unit * 0x10));
+	u32 vahi = nvkm_rd32(device, 0x002808 + (unit * 0x10));
+	u32 type = nvkm_rd32(device, 0x00280c + (unit * 0x10));
+	struct nvkm_fault_data info;
+
+	info.inst   =  (u64)inst << 12;
+	info.addr   = ((u64)vahi << 32) | valo;
+	info.time   = 0;
+	info.engine = unit;
+	info.valid  = 1;
+	info.gpc    = (type & 0x1f000000) >> 24;
+	info.client = (type & 0x00001f00) >> 8;
+	info.access = (type & 0x00000080) >> 7;
+	info.hub    = (type & 0x00000040) >> 6;
+	info.reason = (type & 0x0000000f);
+
+	nvkm_fifo_fault(fifo, &info);
+}
+
 static const struct nvkm_bitfield
 gf100_fifo_pbdma_intr[] = {
 /*	{ 0x00008000, "" }	seen with null ib push */
@@ -518,7 +535,7 @@ gf100_fifo_intr(struct nvkm_fifo *base)
 		u32 mask = nvkm_rd32(device, 0x00259c);
 		while (mask) {
 			u32 unit = __ffs(mask);
-			gf100_fifo_intr_fault(fifo, unit);
+			gf100_fifo_intr_fault(&fifo->base, unit);
 			nvkm_wr32(device, 0x00259c, (1 << unit));
 			mask &= ~(1 << unit);
 		}
@@ -655,6 +672,7 @@ gf100_fifo = {
 	.init = gf100_fifo_init,
 	.fini = gf100_fifo_fini,
 	.intr = gf100_fifo_intr,
+	.fault = gf100_fifo_fault,
 	.uevent_init = gf100_fifo_uevent_init,
 	.uevent_fini = gf100_fifo_uevent_fini,
 	.chan = {
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c
index 1053fe7964661ce809b4ab78e87ec04049d9180f..5d4b695cab8e5840e3e81d4890b72ab2457ddefc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c
@@ -646,31 +646,6 @@ gk104_fifo_intr_dropped_fault(struct gk104_fifo *fifo)
 	nvkm_error(subdev, "DROPPED_MMU_FAULT %08x\n", stat);
 }
 
-static void
-gk104_fifo_intr_fault(struct gk104_fifo *fifo, int unit)
-{
-	struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
-	struct nvkm_device *device = subdev->device;
-	u32 inst = nvkm_rd32(device, 0x002800 + (unit * 0x10));
-	u32 valo = nvkm_rd32(device, 0x002804 + (unit * 0x10));
-	u32 vahi = nvkm_rd32(device, 0x002808 + (unit * 0x10));
-	u32 type = nvkm_rd32(device, 0x00280c + (unit * 0x10));
-	struct nvkm_fault_data info;
-
-	info.inst   =  (u64)inst << 12;
-	info.addr   = ((u64)vahi << 32) | valo;
-	info.time   = 0;
-	info.engine = unit;
-	info.valid  = 1;
-	info.gpc    = (type & 0x1f000000) >> 24;
-	info.client = (type & 0x00001f00) >> 8;
-	info.access = (type & 0x00000080) >> 7;
-	info.hub    = (type & 0x00000040) >> 6;
-	info.reason = (type & 0x000000ff);
-
-	nvkm_fifo_fault(&fifo->base, &info);
-}
-
 static const struct nvkm_bitfield gk104_fifo_pbdma_intr_0[] = {
 	{ 0x00000001, "MEMREQ" },
 	{ 0x00000002, "MEMACK_TIMEOUT" },
@@ -849,7 +824,7 @@ gk104_fifo_intr(struct nvkm_fifo *base)
 		u32 mask = nvkm_rd32(device, 0x00259c);
 		while (mask) {
 			u32 unit = __ffs(mask);
-			gk104_fifo_intr_fault(fifo, unit);
+			fifo->func->intr.fault(&fifo->base, unit);
 			nvkm_wr32(device, 0x00259c, (1 << unit));
 			mask &= ~(1 << unit);
 		}
@@ -1204,6 +1179,7 @@ gk104_fifo_fault_gpcclient[] = {
 
 static const struct gk104_fifo_func
 gk104_fifo = {
+	.intr.fault = gf100_fifo_intr_fault,
 	.pbdma = &gk104_fifo_pbdma,
 	.fault.access = gk104_fifo_fault_access,
 	.fault.engine = gk104_fifo_fault_engine,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h
index c33f4593cbc688c73e4ad79751795f6281cffdc7..6407a4a174cf7d6f52b8d20b4393fc01b8fa686a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.h
@@ -45,6 +45,10 @@ struct gk104_fifo {
 };
 
 struct gk104_fifo_func {
+	struct {
+		void (*fault)(struct nvkm_fifo *, int unit);
+	} intr;
+
 	const struct gk104_fifo_pbdma_func {
 		int (*nr)(struct gk104_fifo *);
 		void (*init)(struct gk104_fifo *);
@@ -110,12 +114,14 @@ void gk110_fifo_runlist_cgrp(struct nvkm_fifo_cgrp *,
 extern const struct gk104_fifo_pbdma_func gk208_fifo_pbdma;
 void gk208_fifo_pbdma_init_timeout(struct gk104_fifo *);
 
+void gm107_fifo_intr_fault(struct nvkm_fifo *, int);
 extern const struct nvkm_enum gm107_fifo_fault_engine[];
 extern const struct gk104_fifo_runlist_func gm107_fifo_runlist;
 
 extern const struct gk104_fifo_pbdma_func gm200_fifo_pbdma;
 int gm200_fifo_pbdma_nr(struct gk104_fifo *);
 
+void gp100_fifo_intr_fault(struct nvkm_fifo *, int);
 extern const struct nvkm_enum gp100_fifo_fault_engine[];
 
 extern const struct nvkm_enum gv100_fifo_fault_access[];
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c
index 8adfa6b182cbab9b703bee09a9bdb89014fa7656..f820969e4405df8908298bcef62d2a928f74b1c5 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk110.c
@@ -48,6 +48,7 @@ gk110_fifo_runlist = {
 
 static const struct gk104_fifo_func
 gk110_fifo = {
+	.intr.fault = gf100_fifo_intr_fault,
 	.pbdma = &gk104_fifo_pbdma,
 	.fault.access = gk104_fifo_fault_access,
 	.fault.engine = gk104_fifo_fault_engine,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c
index 9553fb4af601f033e37b6bdc8878bf9fa6e9f727..2f54787b5fd0b4285d021514f41ab0541b5e2225 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk208.c
@@ -45,6 +45,7 @@ gk208_fifo_pbdma = {
 
 static const struct gk104_fifo_func
 gk208_fifo = {
+	.intr.fault = gf100_fifo_intr_fault,
 	.pbdma = &gk208_fifo_pbdma,
 	.fault.access = gk104_fifo_fault_access,
 	.fault.engine = gk104_fifo_fault_engine,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c
index a4c6ac3cd6c70a1e8d5cef24e01ea33f5c30b871..a814c4e0ed3e5ebff69518833d54dc6a92407394 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk20a.c
@@ -26,6 +26,7 @@
 
 static const struct gk104_fifo_func
 gk20a_fifo = {
+	.intr.fault = gf100_fifo_intr_fault,
 	.pbdma = &gk208_fifo_pbdma,
 	.fault.access = gk104_fifo_fault_access,
 	.fault.engine = gk104_fifo_fault_engine,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c
index acf230764cb0bb966e915d6826e8ddeb4561f2e0..c2a2e4572f6cb1a54d6f8ccd673f08dc1e813ce3 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c
@@ -25,6 +25,7 @@
 #include "changk104.h"
 
 #include <core/gpuobj.h>
+#include <subdev/fault.h>
 
 #include <nvif/class.h>
 
@@ -67,8 +68,33 @@ gm107_fifo_fault_engine[] = {
 	{}
 };
 
+void
+gm107_fifo_intr_fault(struct nvkm_fifo *fifo, int unit)
+{
+	struct nvkm_device *device = fifo->engine.subdev.device;
+	u32 inst = nvkm_rd32(device, 0x002800 + (unit * 0x10));
+	u32 valo = nvkm_rd32(device, 0x002804 + (unit * 0x10));
+	u32 vahi = nvkm_rd32(device, 0x002808 + (unit * 0x10));
+	u32 type = nvkm_rd32(device, 0x00280c + (unit * 0x10));
+	struct nvkm_fault_data info;
+
+	info.inst   =  (u64)inst << 12;
+	info.addr   = ((u64)vahi << 32) | valo;
+	info.time   = 0;
+	info.engine = unit;
+	info.valid  = 1;
+	info.gpc    = (type & 0x1f000000) >> 24;
+	info.client = (type & 0x00003f00) >> 8;
+	info.access = (type & 0x00000080) >> 7;
+	info.hub    = (type & 0x00000040) >> 6;
+	info.reason = (type & 0x0000000f);
+
+	nvkm_fifo_fault(fifo, &info);
+}
+
 static const struct gk104_fifo_func
 gm107_fifo = {
+	.intr.fault = gm107_fifo_intr_fault,
 	.pbdma = &gk208_fifo_pbdma,
 	.fault.access = gk104_fifo_fault_access,
 	.fault.engine = gm107_fifo_fault_engine,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c
index b96c1c5d6577f0a12a1494077fef43d76ba23cfe..b8cfe3b28c4fd5302a682b41bd6314667d39df93 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm200.c
@@ -42,6 +42,7 @@ gm200_fifo_pbdma = {
 
 static const struct gk104_fifo_func
 gm200_fifo = {
+	.intr.fault = gm107_fifo_intr_fault,
 	.pbdma = &gm200_fifo_pbdma,
 	.fault.access = gk104_fifo_fault_access,
 	.fault.engine = gm107_fifo_fault_engine,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c
index a49539b9e4ec328f24d7d3b1e4aa798b19ae92c2..70b4feebc1faca571b0db72e3bcc4844439a16cc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm20b.c
@@ -26,6 +26,7 @@
 
 static const struct gk104_fifo_func
 gm20b_fifo = {
+	.intr.fault = gm107_fifo_intr_fault,
 	.pbdma = &gm200_fifo_pbdma,
 	.fault.access = gk104_fifo_fault_access,
 	.fault.engine = gm107_fifo_fault_engine,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c
index 54377e0f6a88fb1957beca80c28e062dab08009c..2c7a0176b3c840a13a3e50742cb460d1f0bd58f8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c
@@ -24,6 +24,8 @@
 #include "gk104.h"
 #include "changk104.h"
 
+#include <subdev/fault.h>
+
 #include <nvif/class.h>
 
 const struct nvkm_enum
@@ -50,8 +52,33 @@ gp100_fifo_fault_engine[] = {
 	{}
 };
 
+void
+gp100_fifo_intr_fault(struct nvkm_fifo *fifo, int unit)
+{
+	struct nvkm_device *device = fifo->engine.subdev.device;
+	u32 inst = nvkm_rd32(device, 0x002800 + (unit * 0x10));
+	u32 valo = nvkm_rd32(device, 0x002804 + (unit * 0x10));
+	u32 vahi = nvkm_rd32(device, 0x002808 + (unit * 0x10));
+	u32 type = nvkm_rd32(device, 0x00280c + (unit * 0x10));
+	struct nvkm_fault_data info;
+
+	info.inst   =  (u64)inst << 12;
+	info.addr   = ((u64)vahi << 32) | valo;
+	info.time   = 0;
+	info.engine = unit;
+	info.valid  = 1;
+	info.gpc    = (type & 0x1f000000) >> 24;
+	info.hub    = (type & 0x00100000) >> 20;
+	info.access = (type & 0x00070000) >> 16;
+	info.client = (type & 0x00007f00) >> 8;
+	info.reason = (type & 0x0000001f);
+
+	nvkm_fifo_fault(fifo, &info);
+}
+
 static const struct gk104_fifo_func
 gp100_fifo = {
+	.intr.fault = gp100_fifo_intr_fault,
 	.pbdma = &gm200_fifo_pbdma,
 	.fault.access = gk104_fifo_fault_access,
 	.fault.engine = gp100_fifo_fault_engine,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c
index 778ba7e46fb36f1d10ea48d9c651df2a2bd7520a..8c65ad4feedbeb0bbf1bb5c44369a415672c5267 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp10b.c
@@ -26,6 +26,7 @@
 
 static const struct gk104_fifo_func
 gp10b_fifo = {
+	.intr.fault = gp100_fifo_intr_fault,
 	.pbdma = &gm200_fifo_pbdma,
 	.fault.access = gk104_fifo_fault_access,
 	.fault.engine = gp100_fifo_fault_engine,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h
index c66f5370b21f163ac68378461a02093c04520ed8..0ef8baab513e5ff4e8411da035d72b8994e53454 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/priv.h
@@ -37,4 +37,6 @@ struct nvkm_fifo_func {
 void nv04_fifo_intr(struct nvkm_fifo *);
 void nv04_fifo_pause(struct nvkm_fifo *, unsigned long *);
 void nv04_fifo_start(struct nvkm_fifo *, unsigned long *);
+
+void gf100_fifo_intr_fault(struct nvkm_fifo *, int);
 #endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c
index b8578359e61bc4c17d79a4c0f4b96404ff365823..118e33174cbed165cc1ddb7fef96715c715ae9d7 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/extdev.c
@@ -46,6 +46,19 @@ extdev_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt)
 	return extdev + *hdr;
 }
 
+bool
+nvbios_extdev_skip_probe(struct nvkm_bios *bios)
+{
+	u8  ver, hdr, len, cnt;
+	u16 data = extdev_table(bios, &ver, &hdr, &len, &cnt);
+	if (data && ver == 0x40 && hdr >= 5) {
+		u8 flags = nvbios_rd08(bios, data - hdr + 4);
+		if (flags & 1)
+			return true;
+	}
+	return false;
+}
+
 static u16
 nvbios_extdev_entry(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len)
 {
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
index ec0e9f7224b5a95bf2841058a151d8e25c273293..9de74f41dcd2a89087c6a38b32d8121e4606295b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
@@ -834,7 +834,7 @@ init_generic_condition(struct nvbios_init *init)
 		init_exec_set(init, false);
 		break;
 	default:
-		warn("INIT_GENERIC_CONDITON: unknown 0x%02x\n", cond);
+		warn("INIT_GENERIC_CONDITION: unknown 0x%02x\n", cond);
 		init->offset += size;
 		break;
 	}
@@ -1934,6 +1934,28 @@ init_ram_restrict_pll(struct nvbios_init *init)
 	}
 }
 
+/**
+ * INIT_RESET_BEGUN - opcode 0x8c
+ *
+ */
+static void
+init_reset_begun(struct nvbios_init *init)
+{
+	trace("RESET_BEGUN\n");
+	init->offset += 1;
+}
+
+/**
+ * INIT_RESET_END - opcode 0x8d
+ *
+ */
+static void
+init_reset_end(struct nvbios_init *init)
+{
+	trace("RESET_END\n");
+	init->offset += 1;
+}
+
 /**
  * INIT_GPIO - opcode 0x8e
  *
@@ -2260,8 +2282,8 @@ static struct nvbios_init_opcode {
 	[0x79] = { init_pll },
 	[0x7a] = { init_zm_reg },
 	[0x87] = { init_ram_restrict_pll },
-	[0x8c] = { init_reserved },
-	[0x8d] = { init_reserved },
+	[0x8c] = { init_reset_begun },
+	[0x8d] = { init_reset_end },
 	[0x8e] = { init_gpio },
 	[0x8f] = { init_ram_restrict_zm_reg_group },
 	[0x90] = { init_copy_zm_reg },
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c
index 7143ea4611aa3ef7d809d1d1b90148a97b1c7c67..33a9fb5ac558577fe7879c2b760d5b5d1beef3aa 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c
@@ -96,6 +96,8 @@ nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
 		info->min     = min(info->base,
 				    info->base + info->step * info->vidmask);
 		info->max     = nvbios_rd32(bios, volt + 0x0e);
+		if (!info->max)
+			info->max = max(info->base, info->base + info->step * info->vidmask);
 		break;
 	case 0x50:
 		info->min     = nvbios_rd32(bios, volt + 0x0a);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c
index 1399d923d44623940c000dc0a54be40865e75624..914276410ef853a8b080e31eb1f09ef4bd4060e1 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c
@@ -23,6 +23,7 @@
  */
 #include "priv.h"
 
+#include <core/option.h>
 #include <core/notify.h>
 
 static int
@@ -182,12 +183,43 @@ static const struct dmi_system_id gpio_reset_ids[] = {
 	{ }
 };
 
+static enum dcb_gpio_func_name power_checks[] = {
+	DCB_GPIO_THERM_EXT_POWER_EVENT,
+	DCB_GPIO_POWER_ALERT,
+	DCB_GPIO_EXT_POWER_LOW,
+};
+
 static int
 nvkm_gpio_init(struct nvkm_subdev *subdev)
 {
 	struct nvkm_gpio *gpio = nvkm_gpio(subdev);
+	struct dcb_gpio_func func;
+	int ret;
+	int i;
+
 	if (dmi_check_system(gpio_reset_ids))
 		nvkm_gpio_reset(gpio, DCB_GPIO_UNUSED);
+
+	if (nvkm_boolopt(subdev->device->cfgopt, "NvPowerChecks", true)) {
+		for (i = 0; i < ARRAY_SIZE(power_checks); ++i) {
+			ret = nvkm_gpio_find(gpio, 0, power_checks[i],
+					     DCB_GPIO_UNUSED, &func);
+			if (ret)
+				continue;
+
+			ret = nvkm_gpio_get(gpio, 0, func.func, func.line);
+			if (!ret)
+				continue;
+
+			nvkm_error(&gpio->subdev,
+				   "GPU is missing power, check its power "
+				   "cables.  Boot with "
+				   "nouveau.config=NvPowerChecks=0 to "
+				   "disable.\n");
+			return -EINVAL;
+		}
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c
index ce70a193caa7f4db194e2cb68e683b93121bc820..ea2e11771bca522e820151b204c098b230a56b22 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c
@@ -26,6 +26,24 @@
 #include <core/msgqueue.h>
 #include <subdev/timer.h>
 
+bool
+nvkm_pmu_fan_controlled(struct nvkm_device *device)
+{
+	struct nvkm_pmu *pmu = device->pmu;
+
+	/* Internal PMU FW does not currently control fans in any way,
+	 * allow SW control of fans instead.
+	 */
+	if (pmu && pmu->func->code.size)
+		return false;
+
+	/* Default (board-loaded, or VBIOS PMU/PREOS) PMU FW on Fermi
+	 * and newer automatically control the fan speed, which would
+	 * interfere with SW control.
+	 */
+	return (device->chipset >= 0xc0);
+}
+
 void
 nvkm_pmu_pgob(struct nvkm_pmu *pmu, bool enable)
 {
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c
index 4fd4cfe459b8ac5541eaf0eb8d1f70c3a2ab71e1..7af971db91bce2121495872cff5188fe9a6ff6ce 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c
@@ -1088,7 +1088,7 @@ acr_r352_ls_gpccs_func_0 = {
 	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
 };
 
-const struct acr_r352_ls_func
+static const struct acr_r352_ls_func
 acr_r352_ls_gpccs_func = {
 	.load = acr_ls_ucode_load_gpccs,
 	.version_max = 0,
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
index 07914e36939e3d80b557ee1267ab3f9f0f648ca2..4a4d1e224126464fd1881a062cbddcc1b34ad8b4 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
@@ -21,9 +21,11 @@
  *
  * Authors: Martin Peres
  */
-#include <nvkm/core/option.h>
 #include "priv.h"
 
+#include <core/option.h>
+#include <subdev/pmu.h>
+
 int
 nvkm_therm_temp_get(struct nvkm_therm *therm)
 {
@@ -192,8 +194,7 @@ nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode)
 
 	/* The default PPWR ucode on fermi interferes with fan management */
 	if ((mode >= ARRAY_SIZE(name)) ||
-	    (mode != NVKM_THERM_CTRL_NONE && device->card_type >= NV_C0 &&
-	     !device->pmu))
+	    (mode != NVKM_THERM_CTRL_NONE && nvkm_pmu_fan_controlled(device)))
 		return -EINVAL;
 
 	/* do not allow automatic fan management if the thermal sensor is
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
index 6e0ddc1bb583541af27d90e3029805cb231c7ad1..03b355dabab3dc64507e3102259b1fd6451972ba 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
@@ -116,6 +116,9 @@ nvkm_therm_ic_ctor(struct nvkm_therm *therm)
 			return;
 	}
 
+	if (nvbios_extdev_skip_probe(bios))
+		return;
+
 	/* The vbios doesn't provide the address of an exisiting monitoring
 	   device. Let's try our static list.
 	 */