1 | // SPDX-License-Identifier: MIT |
2 | #include <linux/string.h> |
3 | #include <drm/drm_crtc.h> |
4 | #include <drm/drm_atomic_helper.h> |
5 | #include <drm/drm_vblank.h> |
6 | #include <drm/drm_vblank_work.h> |
7 | |
8 | #include <nvif/class.h> |
9 | #include <nvif/cl0002.h> |
10 | #include <nvif/timer.h> |
11 | |
12 | #include <nvhw/class/cl907d.h> |
13 | |
14 | #include "nouveau_drv.h" |
15 | #include "core.h" |
16 | #include "head.h" |
17 | #include "wndw.h" |
18 | #include "handles.h" |
19 | #include "crc.h" |
20 | |
21 | static const char * const nv50_crc_sources[] = { |
22 | [NV50_CRC_SOURCE_NONE] = "none" , |
23 | [NV50_CRC_SOURCE_AUTO] = "auto" , |
24 | [NV50_CRC_SOURCE_RG] = "rg" , |
25 | [NV50_CRC_SOURCE_OUTP_ACTIVE] = "outp-active" , |
26 | [NV50_CRC_SOURCE_OUTP_COMPLETE] = "outp-complete" , |
27 | [NV50_CRC_SOURCE_OUTP_INACTIVE] = "outp-inactive" , |
28 | }; |
29 | |
30 | static int nv50_crc_parse_source(const char *buf, enum nv50_crc_source *s) |
31 | { |
32 | int i; |
33 | |
34 | if (!buf) { |
35 | *s = NV50_CRC_SOURCE_NONE; |
36 | return 0; |
37 | } |
38 | |
39 | i = match_string(array: nv50_crc_sources, ARRAY_SIZE(nv50_crc_sources), string: buf); |
40 | if (i < 0) |
41 | return i; |
42 | |
43 | *s = i; |
44 | return 0; |
45 | } |
46 | |
47 | int |
48 | nv50_crc_verify_source(struct drm_crtc *crtc, const char *source_name, |
49 | size_t *values_cnt) |
50 | { |
51 | struct nouveau_drm *drm = nouveau_drm(crtc->dev); |
52 | enum nv50_crc_source source; |
53 | |
54 | if (nv50_crc_parse_source(buf: source_name, s: &source) < 0) { |
55 | NV_DEBUG(drm, "unknown source %s\n" , source_name); |
56 | return -EINVAL; |
57 | } |
58 | |
59 | *values_cnt = 1; |
60 | return 0; |
61 | } |
62 | |
63 | const char *const *nv50_crc_get_sources(struct drm_crtc *crtc, size_t *count) |
64 | { |
65 | *count = ARRAY_SIZE(nv50_crc_sources); |
66 | return nv50_crc_sources; |
67 | } |
68 | |
69 | static void |
70 | nv50_crc_program_ctx(struct nv50_head *head, |
71 | struct nv50_crc_notifier_ctx *ctx) |
72 | { |
73 | struct nv50_disp *disp = nv50_disp(dev: head->base.base.dev); |
74 | struct nv50_core *core = disp->core; |
75 | u32 interlock[NV50_DISP_INTERLOCK__SIZE] = { 0 }; |
76 | |
77 | core->func->crc->set_ctx(head, ctx); |
78 | core->func->update(core, interlock, false); |
79 | } |
80 | |
81 | static void nv50_crc_ctx_flip_work(struct kthread_work *base) |
82 | { |
83 | struct drm_vblank_work *work = to_drm_vblank_work(base); |
84 | struct nv50_crc *crc = container_of(work, struct nv50_crc, flip_work); |
85 | struct nv50_head *head = container_of(crc, struct nv50_head, crc); |
86 | struct drm_crtc *crtc = &head->base.base; |
87 | struct drm_device *dev = crtc->dev; |
88 | struct nv50_disp *disp = nv50_disp(dev); |
89 | const uint64_t start_vbl = drm_crtc_vblank_count(crtc); |
90 | uint64_t end_vbl; |
91 | u8 new_idx = crc->ctx_idx ^ 1; |
92 | |
93 | /* |
94 | * We don't want to accidentally wait for longer then the vblank, so |
95 | * try again for the next vblank if we don't grab the lock |
96 | */ |
97 | if (!mutex_trylock(lock: &disp->mutex)) { |
98 | drm_dbg_kms(dev, "Lock contended, delaying CRC ctx flip for %s\n" , crtc->name); |
99 | drm_vblank_work_schedule(work, count: start_vbl + 1, nextonmiss: true); |
100 | return; |
101 | } |
102 | |
103 | drm_dbg_kms(dev, "Flipping notifier ctx for %s (%d -> %d)\n" , |
104 | crtc->name, crc->ctx_idx, new_idx); |
105 | |
106 | nv50_crc_program_ctx(head, NULL); |
107 | nv50_crc_program_ctx(head, ctx: &crc->ctx[new_idx]); |
108 | mutex_unlock(lock: &disp->mutex); |
109 | |
110 | end_vbl = drm_crtc_vblank_count(crtc); |
111 | if (unlikely(end_vbl != start_vbl)) |
112 | NV_ERROR(nouveau_drm(dev), |
113 | "Failed to flip CRC context on %s on time (%llu > %llu)\n" , |
114 | crtc->name, end_vbl, start_vbl); |
115 | |
116 | spin_lock_irq(lock: &crc->lock); |
117 | crc->ctx_changed = true; |
118 | spin_unlock_irq(lock: &crc->lock); |
119 | } |
120 | |
121 | static inline void nv50_crc_reset_ctx(struct nv50_crc_notifier_ctx *ctx) |
122 | { |
123 | memset_io(ctx->mem.object.map.ptr, 0, ctx->mem.object.map.size); |
124 | } |
125 | |
126 | static void |
127 | nv50_crc_get_entries(struct nv50_head *head, |
128 | const struct nv50_crc_func *func, |
129 | enum nv50_crc_source source) |
130 | { |
131 | struct drm_crtc *crtc = &head->base.base; |
132 | struct nv50_crc *crc = &head->crc; |
133 | u32 output_crc; |
134 | |
135 | while (crc->entry_idx < func->num_entries) { |
136 | /* |
137 | * While Nvidia's documentation says CRCs are written on each |
138 | * subsequent vblank after being enabled, in practice they |
139 | * aren't written immediately. |
140 | */ |
141 | output_crc = func->get_entry(head, &crc->ctx[crc->ctx_idx], |
142 | source, crc->entry_idx); |
143 | if (!output_crc) |
144 | return; |
145 | |
146 | drm_crtc_add_crc_entry(crtc, has_frame: true, frame: crc->frame, crcs: &output_crc); |
147 | crc->frame++; |
148 | crc->entry_idx++; |
149 | } |
150 | } |
151 | |
152 | void nv50_crc_handle_vblank(struct nv50_head *head) |
153 | { |
154 | struct drm_crtc *crtc = &head->base.base; |
155 | struct nv50_crc *crc = &head->crc; |
156 | const struct nv50_crc_func *func = |
157 | nv50_disp(dev: head->base.base.dev)->core->func->crc; |
158 | struct nv50_crc_notifier_ctx *ctx; |
159 | bool need_reschedule = false; |
160 | |
161 | if (!func) |
162 | return; |
163 | |
164 | /* |
165 | * We don't lose events if we aren't able to report CRCs until the |
166 | * next vblank, so only report CRCs if the locks we need aren't |
167 | * contended to prevent missing an actual vblank event |
168 | */ |
169 | if (!spin_trylock(lock: &crc->lock)) |
170 | return; |
171 | |
172 | if (!crc->src) |
173 | goto out; |
174 | |
175 | ctx = &crc->ctx[crc->ctx_idx]; |
176 | if (crc->ctx_changed && func->ctx_finished(head, ctx)) { |
177 | nv50_crc_get_entries(head, func, source: crc->src); |
178 | |
179 | crc->ctx_idx ^= 1; |
180 | crc->entry_idx = 0; |
181 | crc->ctx_changed = false; |
182 | |
183 | /* |
184 | * Unfortunately when notifier contexts are changed during CRC |
185 | * capture, we will inevitably lose the CRC entry for the |
186 | * frame where the hardware actually latched onto the first |
187 | * UPDATE. According to Nvidia's hardware engineers, there's |
188 | * no workaround for this. |
189 | * |
190 | * Now, we could try to be smart here and calculate the number |
191 | * of missed CRCs based on audit timestamps, but those were |
192 | * removed starting with volta. Since we always flush our |
193 | * updates back-to-back without waiting, we'll just be |
194 | * optimistic and assume we always miss exactly one frame. |
195 | */ |
196 | drm_dbg_kms(head->base.base.dev, |
197 | "Notifier ctx flip for head-%d finished, lost CRC for frame %llu\n" , |
198 | head->base.index, crc->frame); |
199 | crc->frame++; |
200 | |
201 | nv50_crc_reset_ctx(ctx); |
202 | need_reschedule = true; |
203 | } |
204 | |
205 | nv50_crc_get_entries(head, func, source: crc->src); |
206 | |
207 | if (need_reschedule) |
208 | drm_vblank_work_schedule(work: &crc->flip_work, |
209 | count: drm_crtc_vblank_count(crtc) |
210 | + crc->flip_threshold |
211 | - crc->entry_idx, |
212 | nextonmiss: true); |
213 | |
214 | out: |
215 | spin_unlock(lock: &crc->lock); |
216 | } |
217 | |
218 | static void nv50_crc_wait_ctx_finished(struct nv50_head *head, |
219 | const struct nv50_crc_func *func, |
220 | struct nv50_crc_notifier_ctx *ctx) |
221 | { |
222 | struct drm_device *dev = head->base.base.dev; |
223 | struct nouveau_drm *drm = nouveau_drm(dev); |
224 | s64 ret; |
225 | |
226 | ret = nvif_msec(&drm->client.device, 50, |
227 | if (func->ctx_finished(head, ctx)) break;); |
228 | if (ret == -ETIMEDOUT) |
229 | NV_ERROR(drm, |
230 | "CRC notifier ctx for head %d not finished after 50ms\n" , |
231 | head->base.index); |
232 | else if (ret) |
233 | NV_ATOMIC(drm, |
234 | "CRC notifier ctx for head-%d finished after %lldns\n" , |
235 | head->base.index, ret); |
236 | } |
237 | |
238 | void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *state) |
239 | { |
240 | struct drm_crtc_state *crtc_state; |
241 | struct drm_crtc *crtc; |
242 | int i; |
243 | |
244 | for_each_new_crtc_in_state(state, crtc, crtc_state, i) { |
245 | struct nv50_head *head = nv50_head(crtc); |
246 | struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); |
247 | struct nv50_crc *crc = &head->crc; |
248 | |
249 | if (!asyh->clr.crc) |
250 | continue; |
251 | |
252 | spin_lock_irq(lock: &crc->lock); |
253 | crc->src = NV50_CRC_SOURCE_NONE; |
254 | spin_unlock_irq(lock: &crc->lock); |
255 | |
256 | drm_crtc_vblank_put(crtc); |
257 | drm_vblank_work_cancel_sync(work: &crc->flip_work); |
258 | |
259 | NV_ATOMIC(nouveau_drm(crtc->dev), |
260 | "CRC reporting on vblank for head-%d disabled\n" , |
261 | head->base.index); |
262 | |
263 | /* CRC generation is still enabled in hw, we'll just report |
264 | * any remaining CRC entries ourselves after it gets disabled |
265 | * in hardware |
266 | */ |
267 | } |
268 | } |
269 | |
270 | void nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *state) |
271 | { |
272 | struct drm_crtc_state *new_crtc_state; |
273 | struct drm_crtc *crtc; |
274 | int i; |
275 | |
276 | for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { |
277 | struct nv50_head *head = nv50_head(crtc); |
278 | struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); |
279 | struct nv50_crc *crc = &head->crc; |
280 | int i; |
281 | |
282 | if (!asyh->set.crc) |
283 | continue; |
284 | |
285 | crc->entry_idx = 0; |
286 | crc->ctx_changed = false; |
287 | for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) |
288 | nv50_crc_reset_ctx(ctx: &crc->ctx[i]); |
289 | } |
290 | } |
291 | |
292 | void nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *state) |
293 | { |
294 | const struct nv50_crc_func *func = |
295 | nv50_disp(dev: state->dev)->core->func->crc; |
296 | struct drm_crtc_state *new_crtc_state; |
297 | struct drm_crtc *crtc; |
298 | int i; |
299 | |
300 | for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { |
301 | struct nv50_head *head = nv50_head(crtc); |
302 | struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); |
303 | struct nv50_crc *crc = &head->crc; |
304 | struct nv50_crc_notifier_ctx *ctx = &crc->ctx[crc->ctx_idx]; |
305 | |
306 | if (!asyh->clr.crc) |
307 | continue; |
308 | |
309 | if (crc->ctx_changed) { |
310 | nv50_crc_wait_ctx_finished(head, func, ctx); |
311 | ctx = &crc->ctx[crc->ctx_idx ^ 1]; |
312 | } |
313 | nv50_crc_wait_ctx_finished(head, func, ctx); |
314 | } |
315 | } |
316 | |
317 | void nv50_crc_atomic_start_reporting(struct drm_atomic_state *state) |
318 | { |
319 | struct drm_crtc_state *crtc_state; |
320 | struct drm_crtc *crtc; |
321 | int i; |
322 | |
323 | for_each_new_crtc_in_state(state, crtc, crtc_state, i) { |
324 | struct nv50_head *head = nv50_head(crtc); |
325 | struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); |
326 | struct nv50_crc *crc = &head->crc; |
327 | u64 vbl_count; |
328 | |
329 | if (!asyh->set.crc) |
330 | continue; |
331 | |
332 | drm_crtc_vblank_get(crtc); |
333 | |
334 | spin_lock_irq(lock: &crc->lock); |
335 | vbl_count = drm_crtc_vblank_count(crtc); |
336 | crc->frame = vbl_count; |
337 | crc->src = asyh->crc.src; |
338 | drm_vblank_work_schedule(work: &crc->flip_work, |
339 | count: vbl_count + crc->flip_threshold, |
340 | nextonmiss: true); |
341 | spin_unlock_irq(lock: &crc->lock); |
342 | |
343 | NV_ATOMIC(nouveau_drm(crtc->dev), |
344 | "CRC reporting on vblank for head-%d enabled\n" , |
345 | head->base.index); |
346 | } |
347 | } |
348 | |
349 | int nv50_crc_atomic_check_head(struct nv50_head *head, |
350 | struct nv50_head_atom *asyh, |
351 | struct nv50_head_atom *armh) |
352 | { |
353 | struct nv50_atom *atom = nv50_atom(asyh->state.state); |
354 | bool changed = armh->crc.src != asyh->crc.src; |
355 | |
356 | if (!armh->crc.src && !asyh->crc.src) { |
357 | asyh->set.crc = false; |
358 | asyh->clr.crc = false; |
359 | return 0; |
360 | } |
361 | |
362 | if (drm_atomic_crtc_needs_modeset(state: &asyh->state) || changed) { |
363 | asyh->clr.crc = armh->crc.src && armh->state.active; |
364 | asyh->set.crc = asyh->crc.src && asyh->state.active; |
365 | if (changed) |
366 | asyh->set.or |= armh->or.crc_raster != |
367 | asyh->or.crc_raster; |
368 | |
369 | if (asyh->clr.crc && asyh->set.crc) |
370 | atom->flush_disable = true; |
371 | } else { |
372 | asyh->set.crc = false; |
373 | asyh->clr.crc = false; |
374 | } |
375 | |
376 | return 0; |
377 | } |
378 | |
379 | void nv50_crc_atomic_check_outp(struct nv50_atom *atom) |
380 | { |
381 | struct drm_crtc *crtc; |
382 | struct drm_crtc_state *old_crtc_state, *new_crtc_state; |
383 | int i; |
384 | |
385 | if (atom->flush_disable) |
386 | return; |
387 | |
388 | for_each_oldnew_crtc_in_state(&atom->state, crtc, old_crtc_state, |
389 | new_crtc_state, i) { |
390 | struct nv50_head_atom *armh = nv50_head_atom(old_crtc_state); |
391 | struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); |
392 | struct nv50_outp_atom *outp_atom; |
393 | struct nouveau_encoder *outp; |
394 | struct drm_encoder *encoder, *enc; |
395 | |
396 | enc = nv50_head_atom_get_encoder(atom: armh); |
397 | if (!enc) |
398 | continue; |
399 | |
400 | outp = nv50_real_outp(encoder: enc); |
401 | if (!outp) |
402 | continue; |
403 | |
404 | encoder = &outp->base.base; |
405 | |
406 | if (!asyh->clr.crc) |
407 | continue; |
408 | |
409 | /* |
410 | * Re-programming ORs can't be done in the same flush as |
411 | * disabling CRCs |
412 | */ |
413 | list_for_each_entry(outp_atom, &atom->outp, head) { |
414 | if (outp_atom->encoder == encoder) { |
415 | if (outp_atom->set.mask) { |
416 | atom->flush_disable = true; |
417 | return; |
418 | } else { |
419 | break; |
420 | } |
421 | } |
422 | } |
423 | } |
424 | } |
425 | |
426 | static enum nv50_crc_source_type |
427 | nv50_crc_source_type(struct nouveau_encoder *outp, |
428 | enum nv50_crc_source source) |
429 | { |
430 | struct dcb_output *dcbe = outp->dcb; |
431 | |
432 | switch (source) { |
433 | case NV50_CRC_SOURCE_NONE: return NV50_CRC_SOURCE_TYPE_NONE; |
434 | case NV50_CRC_SOURCE_RG: return NV50_CRC_SOURCE_TYPE_RG; |
435 | default: break; |
436 | } |
437 | |
438 | if (dcbe->location != DCB_LOC_ON_CHIP) |
439 | return NV50_CRC_SOURCE_TYPE_PIOR; |
440 | |
441 | switch (dcbe->type) { |
442 | case DCB_OUTPUT_DP: return NV50_CRC_SOURCE_TYPE_SF; |
443 | case DCB_OUTPUT_ANALOG: return NV50_CRC_SOURCE_TYPE_DAC; |
444 | default: return NV50_CRC_SOURCE_TYPE_SOR; |
445 | } |
446 | } |
447 | |
448 | void nv50_crc_atomic_set(struct nv50_head *head, |
449 | struct nv50_head_atom *asyh) |
450 | { |
451 | struct drm_crtc *crtc = &head->base.base; |
452 | struct drm_device *dev = crtc->dev; |
453 | struct nv50_crc *crc = &head->crc; |
454 | const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; |
455 | struct nouveau_encoder *outp; |
456 | struct drm_encoder *encoder; |
457 | |
458 | encoder = nv50_head_atom_get_encoder(atom: asyh); |
459 | if (!encoder) |
460 | return; |
461 | |
462 | outp = nv50_real_outp(encoder); |
463 | if (!outp) |
464 | return; |
465 | |
466 | func->set_src(head, outp->outp.or.id, nv50_crc_source_type(outp, source: asyh->crc.src), |
467 | &crc->ctx[crc->ctx_idx]); |
468 | } |
469 | |
470 | void nv50_crc_atomic_clr(struct nv50_head *head) |
471 | { |
472 | const struct nv50_crc_func *func = |
473 | nv50_disp(dev: head->base.base.dev)->core->func->crc; |
474 | |
475 | func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE, NULL); |
476 | } |
477 | |
478 | static inline int |
479 | nv50_crc_raster_type(enum nv50_crc_source source) |
480 | { |
481 | switch (source) { |
482 | case NV50_CRC_SOURCE_NONE: |
483 | case NV50_CRC_SOURCE_AUTO: |
484 | case NV50_CRC_SOURCE_RG: |
485 | case NV50_CRC_SOURCE_OUTP_ACTIVE: |
486 | return NV907D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_CRC_MODE_ACTIVE_RASTER; |
487 | case NV50_CRC_SOURCE_OUTP_COMPLETE: |
488 | return NV907D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_CRC_MODE_COMPLETE_RASTER; |
489 | case NV50_CRC_SOURCE_OUTP_INACTIVE: |
490 | return NV907D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_CRC_MODE_NON_ACTIVE_RASTER; |
491 | } |
492 | |
493 | return 0; |
494 | } |
495 | |
496 | /* We handle mapping the memory for CRC notifiers ourselves, since each |
497 | * notifier needs it's own handle |
498 | */ |
499 | static inline int |
500 | nv50_crc_ctx_init(struct nv50_head *head, struct nvif_mmu *mmu, |
501 | struct nv50_crc_notifier_ctx *ctx, size_t len, int idx) |
502 | { |
503 | struct nv50_core *core = nv50_disp(dev: head->base.base.dev)->core; |
504 | int ret; |
505 | |
506 | ret = nvif_mem_ctor_map(mmu, "kmsCrcNtfy" , NVIF_MEM_VRAM, len, &ctx->mem); |
507 | if (ret) |
508 | return ret; |
509 | |
510 | ret = nvif_object_ctor(&core->chan.base.user, "kmsCrcNtfyCtxDma" , |
511 | NV50_DISP_HANDLE_CRC_CTX(head, idx), |
512 | NV_DMA_IN_MEMORY, |
513 | &(struct nv_dma_v0) { |
514 | .target = NV_DMA_V0_TARGET_VRAM, |
515 | .access = NV_DMA_V0_ACCESS_RDWR, |
516 | .start = ctx->mem.addr, |
517 | .limit = ctx->mem.addr |
518 | + ctx->mem.size - 1, |
519 | }, sizeof(struct nv_dma_v0), |
520 | &ctx->ntfy); |
521 | if (ret) |
522 | goto fail_fini; |
523 | |
524 | return 0; |
525 | |
526 | fail_fini: |
527 | nvif_mem_dtor(&ctx->mem); |
528 | return ret; |
529 | } |
530 | |
531 | static inline void |
532 | nv50_crc_ctx_fini(struct nv50_crc_notifier_ctx *ctx) |
533 | { |
534 | nvif_object_dtor(&ctx->ntfy); |
535 | nvif_mem_dtor(&ctx->mem); |
536 | } |
537 | |
538 | int nv50_crc_set_source(struct drm_crtc *crtc, const char *source_str) |
539 | { |
540 | struct drm_device *dev = crtc->dev; |
541 | struct drm_atomic_state *state; |
542 | struct drm_modeset_acquire_ctx ctx; |
543 | struct nv50_head *head = nv50_head(crtc); |
544 | struct nv50_crc *crc = &head->crc; |
545 | const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; |
546 | struct nvif_mmu *mmu = &nouveau_drm(dev)->client.mmu; |
547 | struct nv50_head_atom *asyh; |
548 | struct drm_crtc_state *crtc_state; |
549 | enum nv50_crc_source source; |
550 | int ret = 0, ctx_flags = 0, i; |
551 | |
552 | ret = nv50_crc_parse_source(buf: source_str, s: &source); |
553 | if (ret) |
554 | return ret; |
555 | |
556 | /* |
557 | * Since we don't want the user to accidentally interrupt us as we're |
558 | * disabling CRCs |
559 | */ |
560 | if (source) |
561 | ctx_flags |= DRM_MODESET_ACQUIRE_INTERRUPTIBLE; |
562 | drm_modeset_acquire_init(ctx: &ctx, flags: ctx_flags); |
563 | |
564 | state = drm_atomic_state_alloc(dev); |
565 | if (!state) { |
566 | ret = -ENOMEM; |
567 | goto out_acquire_fini; |
568 | } |
569 | state->acquire_ctx = &ctx; |
570 | |
571 | if (source) { |
572 | for (i = 0; i < ARRAY_SIZE(head->crc.ctx); i++) { |
573 | ret = nv50_crc_ctx_init(head, mmu, ctx: &crc->ctx[i], |
574 | len: func->notifier_len, idx: i); |
575 | if (ret) |
576 | goto out_ctx_fini; |
577 | } |
578 | } |
579 | |
580 | retry: |
581 | crtc_state = drm_atomic_get_crtc_state(state, crtc: &head->base.base); |
582 | if (IS_ERR(ptr: crtc_state)) { |
583 | ret = PTR_ERR(ptr: crtc_state); |
584 | if (ret == -EDEADLK) |
585 | goto deadlock; |
586 | else if (ret) |
587 | goto out_drop_locks; |
588 | } |
589 | asyh = nv50_head_atom(crtc_state); |
590 | asyh->crc.src = source; |
591 | asyh->or.crc_raster = nv50_crc_raster_type(source); |
592 | |
593 | ret = drm_atomic_commit(state); |
594 | if (ret == -EDEADLK) |
595 | goto deadlock; |
596 | else if (ret) |
597 | goto out_drop_locks; |
598 | |
599 | if (!source) { |
600 | /* |
601 | * If the user specified a custom flip threshold through |
602 | * debugfs, reset it |
603 | */ |
604 | crc->flip_threshold = func->flip_threshold; |
605 | } |
606 | |
607 | out_drop_locks: |
608 | drm_modeset_drop_locks(ctx: &ctx); |
609 | out_ctx_fini: |
610 | if (!source || ret) { |
611 | for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) |
612 | nv50_crc_ctx_fini(ctx: &crc->ctx[i]); |
613 | } |
614 | drm_atomic_state_put(state); |
615 | out_acquire_fini: |
616 | drm_modeset_acquire_fini(ctx: &ctx); |
617 | return ret; |
618 | |
619 | deadlock: |
620 | drm_atomic_state_clear(state); |
621 | drm_modeset_backoff(ctx: &ctx); |
622 | goto retry; |
623 | } |
624 | |
625 | static int |
626 | nv50_crc_debugfs_flip_threshold_get(struct seq_file *m, void *data) |
627 | { |
628 | struct nv50_head *head = m->private; |
629 | struct drm_crtc *crtc = &head->base.base; |
630 | struct nv50_crc *crc = &head->crc; |
631 | int ret; |
632 | |
633 | ret = drm_modeset_lock_single_interruptible(lock: &crtc->mutex); |
634 | if (ret) |
635 | return ret; |
636 | |
637 | seq_printf(m, "%d\n" , crc->flip_threshold); |
638 | |
639 | drm_modeset_unlock(lock: &crtc->mutex); |
640 | return ret; |
641 | } |
642 | |
643 | static int |
644 | nv50_crc_debugfs_flip_threshold_open(struct inode *inode, struct file *file) |
645 | { |
646 | return single_open(file, nv50_crc_debugfs_flip_threshold_get, |
647 | inode->i_private); |
648 | } |
649 | |
650 | static ssize_t |
651 | nv50_crc_debugfs_flip_threshold_set(struct file *file, |
652 | const char __user *ubuf, size_t len, |
653 | loff_t *offp) |
654 | { |
655 | struct seq_file *m = file->private_data; |
656 | struct nv50_head *head = m->private; |
657 | struct nv50_head_atom *armh; |
658 | struct drm_crtc *crtc = &head->base.base; |
659 | struct nouveau_drm *drm = nouveau_drm(crtc->dev); |
660 | struct nv50_crc *crc = &head->crc; |
661 | const struct nv50_crc_func *func = |
662 | nv50_disp(dev: crtc->dev)->core->func->crc; |
663 | int value, ret; |
664 | |
665 | ret = kstrtoint_from_user(s: ubuf, count: len, base: 10, res: &value); |
666 | if (ret) |
667 | return ret; |
668 | |
669 | if (value > func->flip_threshold) |
670 | return -EINVAL; |
671 | else if (value == -1) |
672 | value = func->flip_threshold; |
673 | else if (value < -1) |
674 | return -EINVAL; |
675 | |
676 | ret = drm_modeset_lock_single_interruptible(lock: &crtc->mutex); |
677 | if (ret) |
678 | return ret; |
679 | |
680 | armh = nv50_head_atom(crtc->state); |
681 | if (armh->crc.src) { |
682 | ret = -EBUSY; |
683 | goto out; |
684 | } |
685 | |
686 | NV_DEBUG(drm, |
687 | "Changing CRC flip threshold for next capture on head-%d to %d\n" , |
688 | head->base.index, value); |
689 | crc->flip_threshold = value; |
690 | ret = len; |
691 | |
692 | out: |
693 | drm_modeset_unlock(lock: &crtc->mutex); |
694 | return ret; |
695 | } |
696 | |
697 | static const struct file_operations nv50_crc_flip_threshold_fops = { |
698 | .owner = THIS_MODULE, |
699 | .open = nv50_crc_debugfs_flip_threshold_open, |
700 | .read = seq_read, |
701 | .write = nv50_crc_debugfs_flip_threshold_set, |
702 | .release = single_release, |
703 | }; |
704 | |
705 | int nv50_head_crc_late_register(struct nv50_head *head) |
706 | { |
707 | struct drm_crtc *crtc = &head->base.base; |
708 | const struct nv50_crc_func *func = |
709 | nv50_disp(dev: crtc->dev)->core->func->crc; |
710 | struct dentry *root; |
711 | |
712 | if (!func || !crtc->debugfs_entry) |
713 | return 0; |
714 | |
715 | root = debugfs_create_dir("nv_crc" , crtc->debugfs_entry); |
716 | debugfs_create_file("flip_threshold" , 0644, root, head, |
717 | &nv50_crc_flip_threshold_fops); |
718 | |
719 | return 0; |
720 | } |
721 | |
722 | static inline void |
723 | nv50_crc_init_head(struct nv50_disp *disp, const struct nv50_crc_func *func, |
724 | struct nv50_head *head) |
725 | { |
726 | struct nv50_crc *crc = &head->crc; |
727 | |
728 | crc->flip_threshold = func->flip_threshold; |
729 | spin_lock_init(&crc->lock); |
730 | drm_vblank_work_init(work: &crc->flip_work, crtc: &head->base.base, |
731 | func: nv50_crc_ctx_flip_work); |
732 | } |
733 | |
734 | void nv50_crc_init(struct drm_device *dev) |
735 | { |
736 | struct nv50_disp *disp = nv50_disp(dev); |
737 | struct drm_crtc *crtc; |
738 | const struct nv50_crc_func *func = disp->core->func->crc; |
739 | |
740 | if (!func) |
741 | return; |
742 | |
743 | drm_for_each_crtc(crtc, dev) |
744 | nv50_crc_init_head(disp, func, nv50_head(crtc)); |
745 | } |
746 | |