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