1 /*
2  * Copyright 2018 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  */
22 #include "wndw.h"
23 #include "wimm.h"
24 #include "handles.h"
25 
26 #include <nvif/class.h>
27 #include <nvif/cl0002.h>
28 
29 #include <drm/drm_atomic_helper.h>
30 #include <drm/drm_fourcc.h>
31 
32 #include "nouveau_bo.h"
33 #include "nouveau_gem.h"
34 
35 static void
36 nv50_wndw_ctxdma_del(struct nv50_wndw_ctxdma *ctxdma)
37 {
38 	nvif_object_fini(&ctxdma->object);
39 	list_del(&ctxdma->head);
40 	kfree(ctxdma);
41 }
42 
43 static struct nv50_wndw_ctxdma *
44 nv50_wndw_ctxdma_new(struct nv50_wndw *wndw, struct drm_framebuffer *fb)
45 {
46 	struct nouveau_drm *drm = nouveau_drm(fb->dev);
47 	struct nv50_wndw_ctxdma *ctxdma;
48 	u32 handle;
49 	u32 unused;
50 	u8  kind;
51 	struct {
52 		struct nv_dma_v0 base;
53 		union {
54 			struct nv50_dma_v0 nv50;
55 			struct gf100_dma_v0 gf100;
56 			struct gf119_dma_v0 gf119;
57 		};
58 	} args = {};
59 	u32 argc = sizeof(args.base);
60 	int ret;
61 
62 	nouveau_framebuffer_get_layout(fb, &unused, &kind);
63 	handle = NV50_DISP_HANDLE_WNDW_CTX(kind);
64 
65 	list_for_each_entry(ctxdma, &wndw->ctxdma.list, head) {
66 		if (ctxdma->object.handle == handle)
67 			return ctxdma;
68 	}
69 
70 	if (!(ctxdma = kzalloc(sizeof(*ctxdma), GFP_KERNEL)))
71 		return ERR_PTR(-ENOMEM);
72 	list_add(&ctxdma->head, &wndw->ctxdma.list);
73 
74 	args.base.target = NV_DMA_V0_TARGET_VRAM;
75 	args.base.access = NV_DMA_V0_ACCESS_RDWR;
76 	args.base.start  = 0;
77 	args.base.limit  = drm->client.device.info.ram_user - 1;
78 
79 	if (drm->client.device.info.chipset < 0x80) {
80 		args.nv50.part = NV50_DMA_V0_PART_256;
81 		argc += sizeof(args.nv50);
82 	} else
83 	if (drm->client.device.info.chipset < 0xc0) {
84 		args.nv50.part = NV50_DMA_V0_PART_256;
85 		args.nv50.kind = kind;
86 		argc += sizeof(args.nv50);
87 	} else
88 	if (drm->client.device.info.chipset < 0xd0) {
89 		args.gf100.kind = kind;
90 		argc += sizeof(args.gf100);
91 	} else {
92 		args.gf119.page = GF119_DMA_V0_PAGE_LP;
93 		args.gf119.kind = kind;
94 		argc += sizeof(args.gf119);
95 	}
96 
97 	ret = nvif_object_init(wndw->ctxdma.parent, handle, NV_DMA_IN_MEMORY,
98 			       &args, argc, &ctxdma->object);
99 	if (ret) {
100 		nv50_wndw_ctxdma_del(ctxdma);
101 		return ERR_PTR(ret);
102 	}
103 
104 	return ctxdma;
105 }
106 
107 int
108 nv50_wndw_wait_armed(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
109 {
110 	struct nv50_disp *disp = nv50_disp(wndw->plane.dev);
111 	if (asyw->set.ntfy) {
112 		return wndw->func->ntfy_wait_begun(disp->sync,
113 						   asyw->ntfy.offset,
114 						   wndw->wndw.base.device);
115 	}
116 	return 0;
117 }
118 
119 void
120 nv50_wndw_flush_clr(struct nv50_wndw *wndw, u32 *interlock, bool flush,
121 		    struct nv50_wndw_atom *asyw)
122 {
123 	union nv50_wndw_atom_mask clr = {
124 		.mask = asyw->clr.mask & ~(flush ? 0 : asyw->set.mask),
125 	};
126 	if (clr.sema ) wndw->func-> sema_clr(wndw);
127 	if (clr.ntfy ) wndw->func-> ntfy_clr(wndw);
128 	if (clr.xlut ) wndw->func-> xlut_clr(wndw);
129 	if (clr.csc  ) wndw->func->  csc_clr(wndw);
130 	if (clr.image) wndw->func->image_clr(wndw);
131 
132 	interlock[wndw->interlock.type] |= wndw->interlock.data;
133 }
134 
135 void
136 nv50_wndw_flush_set(struct nv50_wndw *wndw, u32 *interlock,
137 		    struct nv50_wndw_atom *asyw)
138 {
139 	if (interlock[NV50_DISP_INTERLOCK_CORE]) {
140 		asyw->image.mode = 0;
141 		asyw->image.interval = 1;
142 	}
143 
144 	if (asyw->set.sema ) wndw->func->sema_set (wndw, asyw);
145 	if (asyw->set.ntfy ) wndw->func->ntfy_set (wndw, asyw);
146 	if (asyw->set.image) wndw->func->image_set(wndw, asyw);
147 
148 	if (asyw->set.xlut ) {
149 		if (asyw->ilut) {
150 			asyw->xlut.i.offset =
151 				nv50_lut_load(&wndw->ilut, asyw->xlut.i.buffer,
152 					      asyw->ilut, asyw->xlut.i.load);
153 		}
154 		wndw->func->xlut_set(wndw, asyw);
155 	}
156 
157 	if (asyw->set.csc  ) wndw->func->csc_set  (wndw, asyw);
158 	if (asyw->set.scale) wndw->func->scale_set(wndw, asyw);
159 	if (asyw->set.blend) wndw->func->blend_set(wndw, asyw);
160 	if (asyw->set.point) {
161 		if (asyw->set.point = false, asyw->set.mask)
162 			interlock[wndw->interlock.type] |= wndw->interlock.data;
163 		interlock[NV50_DISP_INTERLOCK_WIMM] |= wndw->interlock.wimm;
164 
165 		wndw->immd->point(wndw, asyw);
166 		wndw->immd->update(wndw, interlock);
167 	} else {
168 		interlock[wndw->interlock.type] |= wndw->interlock.data;
169 	}
170 }
171 
172 void
173 nv50_wndw_ntfy_enable(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
174 {
175 	struct nv50_disp *disp = nv50_disp(wndw->plane.dev);
176 
177 	asyw->ntfy.handle = wndw->wndw.sync.handle;
178 	asyw->ntfy.offset = wndw->ntfy;
179 	asyw->ntfy.awaken = false;
180 	asyw->set.ntfy = true;
181 
182 	wndw->func->ntfy_reset(disp->sync, wndw->ntfy);
183 	wndw->ntfy ^= 0x10;
184 }
185 
186 static void
187 nv50_wndw_atomic_check_release(struct nv50_wndw *wndw,
188 			       struct nv50_wndw_atom *asyw,
189 			       struct nv50_head_atom *asyh)
190 {
191 	struct nouveau_drm *drm = nouveau_drm(wndw->plane.dev);
192 	NV_ATOMIC(drm, "%s release\n", wndw->plane.name);
193 	wndw->func->release(wndw, asyw, asyh);
194 	asyw->ntfy.handle = 0;
195 	asyw->sema.handle = 0;
196 	asyw->xlut.handle = 0;
197 	memset(asyw->image.handle, 0x00, sizeof(asyw->image.handle));
198 }
199 
200 static int
201 nv50_wndw_atomic_check_acquire_yuv(struct nv50_wndw_atom *asyw)
202 {
203 	switch (asyw->state.fb->format->format) {
204 	case DRM_FORMAT_YUYV: asyw->image.format = 0x28; break;
205 	case DRM_FORMAT_UYVY: asyw->image.format = 0x29; break;
206 	default:
207 		WARN_ON(1);
208 		return -EINVAL;
209 	}
210 	asyw->image.colorspace = 1;
211 	return 0;
212 }
213 
214 static int
215 nv50_wndw_atomic_check_acquire_rgb(struct nv50_wndw_atom *asyw)
216 {
217 	switch (asyw->state.fb->format->format) {
218 	case DRM_FORMAT_C8           : asyw->image.format = 0x1e; break;
219 	case DRM_FORMAT_XRGB8888     :
220 	case DRM_FORMAT_ARGB8888     : asyw->image.format = 0xcf; break;
221 	case DRM_FORMAT_RGB565       : asyw->image.format = 0xe8; break;
222 	case DRM_FORMAT_XRGB1555     :
223 	case DRM_FORMAT_ARGB1555     : asyw->image.format = 0xe9; break;
224 	case DRM_FORMAT_XBGR2101010  :
225 	case DRM_FORMAT_ABGR2101010  : asyw->image.format = 0xd1; break;
226 	case DRM_FORMAT_XBGR8888     :
227 	case DRM_FORMAT_ABGR8888     : asyw->image.format = 0xd5; break;
228 	case DRM_FORMAT_XRGB2101010  :
229 	case DRM_FORMAT_ARGB2101010  : asyw->image.format = 0xdf; break;
230 	case DRM_FORMAT_XBGR16161616F:
231 	case DRM_FORMAT_ABGR16161616F: asyw->image.format = 0xca; break;
232 	default:
233 		return -EINVAL;
234 	}
235 	asyw->image.colorspace = 0;
236 	return 0;
237 }
238 
239 static int
240 nv50_wndw_atomic_check_acquire(struct nv50_wndw *wndw, bool modeset,
241 			       struct nv50_wndw_atom *armw,
242 			       struct nv50_wndw_atom *asyw,
243 			       struct nv50_head_atom *asyh)
244 {
245 	struct drm_framebuffer *fb = asyw->state.fb;
246 	struct nouveau_drm *drm = nouveau_drm(wndw->plane.dev);
247 	uint8_t kind;
248 	uint32_t tile_mode;
249 	int ret;
250 
251 	NV_ATOMIC(drm, "%s acquire\n", wndw->plane.name);
252 
253 	if (fb != armw->state.fb || !armw->visible || modeset) {
254 		nouveau_framebuffer_get_layout(fb, &tile_mode, &kind);
255 
256 		asyw->image.w = fb->width;
257 		asyw->image.h = fb->height;
258 		asyw->image.kind = kind;
259 
260 		ret = nv50_wndw_atomic_check_acquire_rgb(asyw);
261 		if (ret) {
262 			ret = nv50_wndw_atomic_check_acquire_yuv(asyw);
263 			if (ret)
264 				return ret;
265 		}
266 
267 		if (asyw->image.kind) {
268 			asyw->image.layout = 0;
269 			if (drm->client.device.info.chipset >= 0xc0)
270 				asyw->image.blockh = tile_mode >> 4;
271 			else
272 				asyw->image.blockh = tile_mode;
273 			asyw->image.blocks[0] = fb->pitches[0] / 64;
274 			asyw->image.pitch[0] = 0;
275 		} else {
276 			asyw->image.layout = 1;
277 			asyw->image.blockh = 0;
278 			asyw->image.blocks[0] = 0;
279 			asyw->image.pitch[0] = fb->pitches[0];
280 		}
281 
282 		if (!asyh->state.async_flip)
283 			asyw->image.interval = 1;
284 		else
285 			asyw->image.interval = 0;
286 		asyw->image.mode = asyw->image.interval ? 0 : 1;
287 		asyw->set.image = wndw->func->image_set != NULL;
288 	}
289 
290 	if (wndw->func->scale_set) {
291 		asyw->scale.sx = asyw->state.src_x >> 16;
292 		asyw->scale.sy = asyw->state.src_y >> 16;
293 		asyw->scale.sw = asyw->state.src_w >> 16;
294 		asyw->scale.sh = asyw->state.src_h >> 16;
295 		asyw->scale.dw = asyw->state.crtc_w;
296 		asyw->scale.dh = asyw->state.crtc_h;
297 		if (memcmp(&armw->scale, &asyw->scale, sizeof(asyw->scale)))
298 			asyw->set.scale = true;
299 	}
300 
301 	if (wndw->func->blend_set) {
302 		asyw->blend.depth = 255 - asyw->state.normalized_zpos;
303 		asyw->blend.k1 = asyw->state.alpha >> 8;
304 		switch (asyw->state.pixel_blend_mode) {
305 		case DRM_MODE_BLEND_PREMULTI:
306 			asyw->blend.src_color = 2; /* K1 */
307 			asyw->blend.dst_color = 7; /* NEG_K1_TIMES_SRC */
308 			break;
309 		case DRM_MODE_BLEND_COVERAGE:
310 			asyw->blend.src_color = 5; /* K1_TIMES_SRC */
311 			asyw->blend.dst_color = 7; /* NEG_K1_TIMES_SRC */
312 			break;
313 		case DRM_MODE_BLEND_PIXEL_NONE:
314 		default:
315 			asyw->blend.src_color = 2; /* K1 */
316 			asyw->blend.dst_color = 4; /* NEG_K1 */
317 			break;
318 		}
319 		if (memcmp(&armw->blend, &asyw->blend, sizeof(asyw->blend)))
320 			asyw->set.blend = true;
321 	}
322 
323 	if (wndw->immd) {
324 		asyw->point.x = asyw->state.crtc_x;
325 		asyw->point.y = asyw->state.crtc_y;
326 		if (memcmp(&armw->point, &asyw->point, sizeof(asyw->point)))
327 			asyw->set.point = true;
328 	}
329 
330 	return wndw->func->acquire(wndw, asyw, asyh);
331 }
332 
333 static int
334 nv50_wndw_atomic_check_lut(struct nv50_wndw *wndw,
335 			   struct nv50_wndw_atom *armw,
336 			   struct nv50_wndw_atom *asyw,
337 			   struct nv50_head_atom *asyh)
338 {
339 	struct drm_property_blob *ilut = asyh->state.degamma_lut;
340 
341 	/* I8 format without an input LUT makes no sense, and the
342 	 * HW error-checks for this.
343 	 *
344 	 * In order to handle legacy gamma, when there's no input
345 	 * LUT we need to steal the output LUT and use it instead.
346 	 */
347 	if (!ilut && asyw->state.fb->format->format == DRM_FORMAT_C8) {
348 		/* This should be an error, but there's legacy clients
349 		 * that do a modeset before providing a gamma table.
350 		 *
351 		 * We keep the window disabled to avoid angering HW.
352 		 */
353 		if (!(ilut = asyh->state.gamma_lut)) {
354 			asyw->visible = false;
355 			return 0;
356 		}
357 
358 		if (wndw->func->ilut)
359 			asyh->wndw.olut |= BIT(wndw->id);
360 	} else {
361 		asyh->wndw.olut &= ~BIT(wndw->id);
362 	}
363 
364 	if (!ilut && wndw->func->ilut_identity &&
365 	    asyw->state.fb->format->format != DRM_FORMAT_XBGR16161616F &&
366 	    asyw->state.fb->format->format != DRM_FORMAT_ABGR16161616F) {
367 		static struct drm_property_blob dummy = {};
368 		ilut = &dummy;
369 	}
370 
371 	/* Recalculate LUT state. */
372 	memset(&asyw->xlut, 0x00, sizeof(asyw->xlut));
373 	if ((asyw->ilut = wndw->func->ilut ? ilut : NULL)) {
374 		if (!wndw->func->ilut(wndw, asyw, drm_color_lut_size(ilut))) {
375 			DRM_DEBUG_KMS("Invalid ilut\n");
376 			return -EINVAL;
377 		}
378 		asyw->xlut.handle = wndw->wndw.vram.handle;
379 		asyw->xlut.i.buffer = !asyw->xlut.i.buffer;
380 		asyw->set.xlut = true;
381 	} else {
382 		asyw->clr.xlut = armw->xlut.handle != 0;
383 	}
384 
385 	/* Handle setting base SET_OUTPUT_LUT_LO_ENABLE_USE_CORE_LUT. */
386 	if (wndw->func->olut_core &&
387 	    (!armw->visible || (armw->xlut.handle && !asyw->xlut.handle)))
388 		asyw->set.xlut = true;
389 
390 	if (wndw->func->csc && asyh->state.ctm) {
391 		const struct drm_color_ctm *ctm = asyh->state.ctm->data;
392 		wndw->func->csc(wndw, asyw, ctm);
393 		asyw->csc.valid = true;
394 		asyw->set.csc = true;
395 	} else {
396 		asyw->csc.valid = false;
397 		asyw->clr.csc = armw->csc.valid;
398 	}
399 
400 	/* Can't do an immediate flip while changing the LUT. */
401 	asyh->state.async_flip = false;
402 	return 0;
403 }
404 
405 static int
406 nv50_wndw_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
407 {
408 	struct nouveau_drm *drm = nouveau_drm(plane->dev);
409 	struct nv50_wndw *wndw = nv50_wndw(plane);
410 	struct nv50_wndw_atom *armw = nv50_wndw_atom(wndw->plane.state);
411 	struct nv50_wndw_atom *asyw = nv50_wndw_atom(state);
412 	struct nv50_head_atom *harm = NULL, *asyh = NULL;
413 	bool modeset = false;
414 	int ret;
415 
416 	NV_ATOMIC(drm, "%s atomic_check\n", plane->name);
417 
418 	/* Fetch the assembly state for the head the window will belong to,
419 	 * and determine whether the window will be visible.
420 	 */
421 	if (asyw->state.crtc) {
422 		asyh = nv50_head_atom_get(asyw->state.state, asyw->state.crtc);
423 		if (IS_ERR(asyh))
424 			return PTR_ERR(asyh);
425 		modeset = drm_atomic_crtc_needs_modeset(&asyh->state);
426 		asyw->visible = asyh->state.active;
427 	} else {
428 		asyw->visible = false;
429 	}
430 
431 	/* Fetch assembly state for the head the window used to belong to. */
432 	if (armw->state.crtc) {
433 		harm = nv50_head_atom_get(asyw->state.state, armw->state.crtc);
434 		if (IS_ERR(harm))
435 			return PTR_ERR(harm);
436 	}
437 
438 	/* LUT configuration can potentially cause the window to be disabled. */
439 	if (asyw->visible && wndw->func->xlut_set &&
440 	    (!armw->visible ||
441 	     asyh->state.color_mgmt_changed ||
442 	     asyw->state.fb->format->format !=
443 	     armw->state.fb->format->format)) {
444 		ret = nv50_wndw_atomic_check_lut(wndw, armw, asyw, asyh);
445 		if (ret)
446 			return ret;
447 	}
448 
449 	/* Calculate new window state. */
450 	if (asyw->visible) {
451 		ret = nv50_wndw_atomic_check_acquire(wndw, modeset,
452 						     armw, asyw, asyh);
453 		if (ret)
454 			return ret;
455 
456 		asyh->wndw.mask |= BIT(wndw->id);
457 	} else
458 	if (armw->visible) {
459 		nv50_wndw_atomic_check_release(wndw, asyw, harm);
460 		harm->wndw.mask &= ~BIT(wndw->id);
461 	} else {
462 		return 0;
463 	}
464 
465 	/* Aside from the obvious case where the window is actively being
466 	 * disabled, we might also need to temporarily disable the window
467 	 * when performing certain modeset operations.
468 	 */
469 	if (!asyw->visible || modeset) {
470 		asyw->clr.ntfy = armw->ntfy.handle != 0;
471 		asyw->clr.sema = armw->sema.handle != 0;
472 		asyw->clr.xlut = armw->xlut.handle != 0;
473 		if (asyw->clr.xlut && asyw->visible)
474 			asyw->set.xlut = asyw->xlut.handle != 0;
475 		asyw->clr.csc  = armw->csc.valid;
476 		if (wndw->func->image_clr)
477 			asyw->clr.image = armw->image.handle[0] != 0;
478 	}
479 
480 	return 0;
481 }
482 
483 static void
484 nv50_wndw_cleanup_fb(struct drm_plane *plane, struct drm_plane_state *old_state)
485 {
486 	struct nouveau_drm *drm = nouveau_drm(plane->dev);
487 	struct nouveau_bo *nvbo;
488 
489 	NV_ATOMIC(drm, "%s cleanup: %p\n", plane->name, old_state->fb);
490 	if (!old_state->fb)
491 		return;
492 
493 	nvbo = nouveau_gem_object(old_state->fb->obj[0]);
494 	nouveau_bo_unpin(nvbo);
495 }
496 
497 static int
498 nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state)
499 {
500 	struct drm_framebuffer *fb = state->fb;
501 	struct nouveau_drm *drm = nouveau_drm(plane->dev);
502 	struct nv50_wndw *wndw = nv50_wndw(plane);
503 	struct nv50_wndw_atom *asyw = nv50_wndw_atom(state);
504 	struct nouveau_bo *nvbo;
505 	struct nv50_head_atom *asyh;
506 	struct nv50_wndw_ctxdma *ctxdma;
507 	int ret;
508 
509 	NV_ATOMIC(drm, "%s prepare: %p\n", plane->name, fb);
510 	if (!asyw->state.fb)
511 		return 0;
512 
513 	nvbo = nouveau_gem_object(fb->obj[0]);
514 	ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM, true);
515 	if (ret)
516 		return ret;
517 
518 	if (wndw->ctxdma.parent) {
519 		ctxdma = nv50_wndw_ctxdma_new(wndw, fb);
520 		if (IS_ERR(ctxdma)) {
521 			nouveau_bo_unpin(nvbo);
522 			return PTR_ERR(ctxdma);
523 		}
524 
525 		if (asyw->visible)
526 			asyw->image.handle[0] = ctxdma->object.handle;
527 	}
528 
529 	asyw->state.fence = dma_resv_get_excl_rcu(nvbo->bo.base.resv);
530 	asyw->image.offset[0] = nvbo->offset;
531 
532 	if (wndw->func->prepare) {
533 		asyh = nv50_head_atom_get(asyw->state.state, asyw->state.crtc);
534 		if (IS_ERR(asyh))
535 			return PTR_ERR(asyh);
536 
537 		wndw->func->prepare(wndw, asyh, asyw);
538 	}
539 
540 	return 0;
541 }
542 
543 static const struct drm_plane_helper_funcs
544 nv50_wndw_helper = {
545 	.prepare_fb = nv50_wndw_prepare_fb,
546 	.cleanup_fb = nv50_wndw_cleanup_fb,
547 	.atomic_check = nv50_wndw_atomic_check,
548 };
549 
550 static void
551 nv50_wndw_atomic_destroy_state(struct drm_plane *plane,
552 			       struct drm_plane_state *state)
553 {
554 	struct nv50_wndw_atom *asyw = nv50_wndw_atom(state);
555 	__drm_atomic_helper_plane_destroy_state(&asyw->state);
556 	kfree(asyw);
557 }
558 
559 static struct drm_plane_state *
560 nv50_wndw_atomic_duplicate_state(struct drm_plane *plane)
561 {
562 	struct nv50_wndw_atom *armw = nv50_wndw_atom(plane->state);
563 	struct nv50_wndw_atom *asyw;
564 	if (!(asyw = kmalloc(sizeof(*asyw), GFP_KERNEL)))
565 		return NULL;
566 	__drm_atomic_helper_plane_duplicate_state(plane, &asyw->state);
567 	asyw->sema = armw->sema;
568 	asyw->ntfy = armw->ntfy;
569 	asyw->ilut = NULL;
570 	asyw->xlut = armw->xlut;
571 	asyw->csc  = armw->csc;
572 	asyw->image = armw->image;
573 	asyw->point = armw->point;
574 	asyw->clr.mask = 0;
575 	asyw->set.mask = 0;
576 	return &asyw->state;
577 }
578 
579 static int
580 nv50_wndw_zpos_default(struct drm_plane *plane)
581 {
582 	return (plane->type == DRM_PLANE_TYPE_PRIMARY) ? 0 :
583 	       (plane->type == DRM_PLANE_TYPE_OVERLAY) ? 1 : 255;
584 }
585 
586 static void
587 nv50_wndw_reset(struct drm_plane *plane)
588 {
589 	struct nv50_wndw_atom *asyw;
590 
591 	if (WARN_ON(!(asyw = kzalloc(sizeof(*asyw), GFP_KERNEL))))
592 		return;
593 
594 	if (plane->state)
595 		plane->funcs->atomic_destroy_state(plane, plane->state);
596 
597 	__drm_atomic_helper_plane_reset(plane, &asyw->state);
598 	plane->state->zpos = nv50_wndw_zpos_default(plane);
599 	plane->state->normalized_zpos = nv50_wndw_zpos_default(plane);
600 }
601 
602 static void
603 nv50_wndw_destroy(struct drm_plane *plane)
604 {
605 	struct nv50_wndw *wndw = nv50_wndw(plane);
606 	struct nv50_wndw_ctxdma *ctxdma, *ctxtmp;
607 
608 	list_for_each_entry_safe(ctxdma, ctxtmp, &wndw->ctxdma.list, head) {
609 		nv50_wndw_ctxdma_del(ctxdma);
610 	}
611 
612 	nvif_notify_fini(&wndw->notify);
613 	nv50_dmac_destroy(&wndw->wimm);
614 	nv50_dmac_destroy(&wndw->wndw);
615 
616 	nv50_lut_fini(&wndw->ilut);
617 
618 	drm_plane_cleanup(&wndw->plane);
619 	kfree(wndw);
620 }
621 
622 /* This function assumes the format has already been validated against the plane
623  * and the modifier was validated against the device-wides modifier list at FB
624  * creation time.
625  */
626 static bool nv50_plane_format_mod_supported(struct drm_plane *plane,
627 					    u32 format, u64 modifier)
628 {
629 	struct nouveau_drm *drm = nouveau_drm(plane->dev);
630 	uint8_t i;
631 
632 	if (drm->client.device.info.chipset < 0xc0) {
633 		const struct drm_format_info *info = drm_format_info(format);
634 		const uint8_t kind = (modifier >> 12) & 0xff;
635 
636 		if (!format) return false;
637 
638 		for (i = 0; i < info->num_planes; i++)
639 			if ((info->cpp[i] != 4) && kind != 0x70) return false;
640 	}
641 
642 	return true;
643 }
644 
645 const struct drm_plane_funcs
646 nv50_wndw = {
647 	.update_plane = drm_atomic_helper_update_plane,
648 	.disable_plane = drm_atomic_helper_disable_plane,
649 	.destroy = nv50_wndw_destroy,
650 	.reset = nv50_wndw_reset,
651 	.atomic_duplicate_state = nv50_wndw_atomic_duplicate_state,
652 	.atomic_destroy_state = nv50_wndw_atomic_destroy_state,
653 	.format_mod_supported = nv50_plane_format_mod_supported,
654 };
655 
656 static int
657 nv50_wndw_notify(struct nvif_notify *notify)
658 {
659 	return NVIF_NOTIFY_KEEP;
660 }
661 
662 void
663 nv50_wndw_fini(struct nv50_wndw *wndw)
664 {
665 	nvif_notify_put(&wndw->notify);
666 }
667 
668 void
669 nv50_wndw_init(struct nv50_wndw *wndw)
670 {
671 	nvif_notify_get(&wndw->notify);
672 }
673 
674 int
675 nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
676 	       enum drm_plane_type type, const char *name, int index,
677 	       const u32 *format, u32 heads,
678 	       enum nv50_disp_interlock_type interlock_type, u32 interlock_data,
679 	       struct nv50_wndw **pwndw)
680 {
681 	struct nouveau_drm *drm = nouveau_drm(dev);
682 	struct nvif_mmu *mmu = &drm->client.mmu;
683 	struct nv50_disp *disp = nv50_disp(dev);
684 	struct nv50_wndw *wndw;
685 	int nformat;
686 	int ret;
687 
688 	if (!(wndw = *pwndw = kzalloc(sizeof(*wndw), GFP_KERNEL)))
689 		return -ENOMEM;
690 	wndw->func = func;
691 	wndw->id = index;
692 	wndw->interlock.type = interlock_type;
693 	wndw->interlock.data = interlock_data;
694 
695 	wndw->ctxdma.parent = &wndw->wndw.base.user;
696 	INIT_LIST_HEAD(&wndw->ctxdma.list);
697 
698 	for (nformat = 0; format[nformat]; nformat++);
699 
700 	ret = drm_universal_plane_init(dev, &wndw->plane, heads, &nv50_wndw,
701 				       format, nformat,
702 				       nouveau_display(dev)->format_modifiers,
703 				       type, "%s-%d", name, index);
704 	if (ret) {
705 		kfree(*pwndw);
706 		*pwndw = NULL;
707 		return ret;
708 	}
709 
710 	drm_plane_helper_add(&wndw->plane, &nv50_wndw_helper);
711 
712 	if (wndw->func->ilut) {
713 		ret = nv50_lut_init(disp, mmu, &wndw->ilut);
714 		if (ret)
715 			return ret;
716 	}
717 
718 	wndw->notify.func = nv50_wndw_notify;
719 
720 	if (wndw->func->blend_set) {
721 		ret = drm_plane_create_zpos_property(&wndw->plane,
722 				nv50_wndw_zpos_default(&wndw->plane), 0, 254);
723 		if (ret)
724 			return ret;
725 
726 		ret = drm_plane_create_alpha_property(&wndw->plane);
727 		if (ret)
728 			return ret;
729 
730 		ret = drm_plane_create_blend_mode_property(&wndw->plane,
731 				BIT(DRM_MODE_BLEND_PIXEL_NONE) |
732 				BIT(DRM_MODE_BLEND_PREMULTI) |
733 				BIT(DRM_MODE_BLEND_COVERAGE));
734 		if (ret)
735 			return ret;
736 	} else {
737 		ret = drm_plane_create_zpos_immutable_property(&wndw->plane,
738 				nv50_wndw_zpos_default(&wndw->plane));
739 		if (ret)
740 			return ret;
741 	}
742 
743 	return 0;
744 }
745 
746 int
747 nv50_wndw_new(struct nouveau_drm *drm, enum drm_plane_type type, int index,
748 	      struct nv50_wndw **pwndw)
749 {
750 	struct {
751 		s32 oclass;
752 		int version;
753 		int (*new)(struct nouveau_drm *, enum drm_plane_type,
754 			   int, s32, struct nv50_wndw **);
755 	} wndws[] = {
756 		{ TU102_DISP_WINDOW_CHANNEL_DMA, 0, wndwc57e_new },
757 		{ GV100_DISP_WINDOW_CHANNEL_DMA, 0, wndwc37e_new },
758 		{}
759 	};
760 	struct nv50_disp *disp = nv50_disp(drm->dev);
761 	int cid, ret;
762 
763 	cid = nvif_mclass(&disp->disp->object, wndws);
764 	if (cid < 0) {
765 		NV_ERROR(drm, "No supported window class\n");
766 		return cid;
767 	}
768 
769 	ret = wndws[cid].new(drm, type, index, wndws[cid].oclass, pwndw);
770 	if (ret)
771 		return ret;
772 
773 	return nv50_wimm_init(drm, *pwndw);
774 }
775