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