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