18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT 28c2ecf20Sopenharmony_ci#include <linux/string.h> 38c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 48c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 58c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 68c2ecf20Sopenharmony_ci#include <drm/drm_vblank_work.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <nvif/class.h> 98c2ecf20Sopenharmony_ci#include <nvif/cl0002.h> 108c2ecf20Sopenharmony_ci#include <nvif/timer.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <nvhw/class/cl907d.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "nouveau_drv.h" 158c2ecf20Sopenharmony_ci#include "core.h" 168c2ecf20Sopenharmony_ci#include "head.h" 178c2ecf20Sopenharmony_ci#include "wndw.h" 188c2ecf20Sopenharmony_ci#include "handles.h" 198c2ecf20Sopenharmony_ci#include "crc.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic const char * const nv50_crc_sources[] = { 228c2ecf20Sopenharmony_ci [NV50_CRC_SOURCE_NONE] = "none", 238c2ecf20Sopenharmony_ci [NV50_CRC_SOURCE_AUTO] = "auto", 248c2ecf20Sopenharmony_ci [NV50_CRC_SOURCE_RG] = "rg", 258c2ecf20Sopenharmony_ci [NV50_CRC_SOURCE_OUTP_ACTIVE] = "outp-active", 268c2ecf20Sopenharmony_ci [NV50_CRC_SOURCE_OUTP_COMPLETE] = "outp-complete", 278c2ecf20Sopenharmony_ci [NV50_CRC_SOURCE_OUTP_INACTIVE] = "outp-inactive", 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int nv50_crc_parse_source(const char *buf, enum nv50_crc_source *s) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int i; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (!buf) { 358c2ecf20Sopenharmony_ci *s = NV50_CRC_SOURCE_NONE; 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci i = match_string(nv50_crc_sources, ARRAY_SIZE(nv50_crc_sources), buf); 408c2ecf20Sopenharmony_ci if (i < 0) 418c2ecf20Sopenharmony_ci return i; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci *s = i; 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ciint 488c2ecf20Sopenharmony_cinv50_crc_verify_source(struct drm_crtc *crtc, const char *source_name, 498c2ecf20Sopenharmony_ci size_t *values_cnt) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(crtc->dev); 528c2ecf20Sopenharmony_ci enum nv50_crc_source source; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (nv50_crc_parse_source(source_name, &source) < 0) { 558c2ecf20Sopenharmony_ci NV_DEBUG(drm, "unknown source %s\n", source_name); 568c2ecf20Sopenharmony_ci return -EINVAL; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci *values_cnt = 1; 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciconst char *const *nv50_crc_get_sources(struct drm_crtc *crtc, size_t *count) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci *count = ARRAY_SIZE(nv50_crc_sources); 668c2ecf20Sopenharmony_ci return nv50_crc_sources; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void 708c2ecf20Sopenharmony_cinv50_crc_program_ctx(struct nv50_head *head, 718c2ecf20Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(head->base.base.dev); 748c2ecf20Sopenharmony_ci struct nv50_core *core = disp->core; 758c2ecf20Sopenharmony_ci u32 interlock[NV50_DISP_INTERLOCK__SIZE] = { 0 }; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci core->func->crc->set_ctx(head, ctx); 788c2ecf20Sopenharmony_ci core->func->update(core, interlock, false); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void nv50_crc_ctx_flip_work(struct kthread_work *base) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct drm_vblank_work *work = to_drm_vblank_work(base); 848c2ecf20Sopenharmony_ci struct nv50_crc *crc = container_of(work, struct nv50_crc, flip_work); 858c2ecf20Sopenharmony_ci struct nv50_head *head = container_of(crc, struct nv50_head, crc); 868c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 878c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(crtc->dev); 888c2ecf20Sopenharmony_ci u8 new_idx = crc->ctx_idx ^ 1; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * We don't want to accidentally wait for longer then the vblank, so 928c2ecf20Sopenharmony_ci * try again for the next vblank if we don't grab the lock 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci if (!mutex_trylock(&disp->mutex)) { 958c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(crtc->dev->dev, 968c2ecf20Sopenharmony_ci "Lock contended, delaying CRC ctx flip for head-%d\n", 978c2ecf20Sopenharmony_ci head->base.index); 988c2ecf20Sopenharmony_ci drm_vblank_work_schedule(work, 998c2ecf20Sopenharmony_ci drm_crtc_vblank_count(crtc) + 1, 1008c2ecf20Sopenharmony_ci true); 1018c2ecf20Sopenharmony_ci return; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(crtc->dev->dev, 1058c2ecf20Sopenharmony_ci "Flipping notifier ctx for head %d (%d -> %d)\n", 1068c2ecf20Sopenharmony_ci drm_crtc_index(crtc), crc->ctx_idx, new_idx); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci nv50_crc_program_ctx(head, NULL); 1098c2ecf20Sopenharmony_ci nv50_crc_program_ctx(head, &crc->ctx[new_idx]); 1108c2ecf20Sopenharmony_ci mutex_unlock(&disp->mutex); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci spin_lock_irq(&crc->lock); 1138c2ecf20Sopenharmony_ci crc->ctx_changed = true; 1148c2ecf20Sopenharmony_ci spin_unlock_irq(&crc->lock); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic inline void nv50_crc_reset_ctx(struct nv50_crc_notifier_ctx *ctx) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci memset_io(ctx->mem.object.map.ptr, 0, ctx->mem.object.map.size); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void 1238c2ecf20Sopenharmony_cinv50_crc_get_entries(struct nv50_head *head, 1248c2ecf20Sopenharmony_ci const struct nv50_crc_func *func, 1258c2ecf20Sopenharmony_ci enum nv50_crc_source source) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 1288c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 1298c2ecf20Sopenharmony_ci u32 output_crc; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci while (crc->entry_idx < func->num_entries) { 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * While Nvidia's documentation says CRCs are written on each 1348c2ecf20Sopenharmony_ci * subsequent vblank after being enabled, in practice they 1358c2ecf20Sopenharmony_ci * aren't written immediately. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci output_crc = func->get_entry(head, &crc->ctx[crc->ctx_idx], 1388c2ecf20Sopenharmony_ci source, crc->entry_idx); 1398c2ecf20Sopenharmony_ci if (!output_crc) 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci drm_crtc_add_crc_entry(crtc, true, crc->frame, &output_crc); 1438c2ecf20Sopenharmony_ci crc->frame++; 1448c2ecf20Sopenharmony_ci crc->entry_idx++; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_civoid nv50_crc_handle_vblank(struct nv50_head *head) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 1518c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 1528c2ecf20Sopenharmony_ci const struct nv50_crc_func *func = 1538c2ecf20Sopenharmony_ci nv50_disp(head->base.base.dev)->core->func->crc; 1548c2ecf20Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx; 1558c2ecf20Sopenharmony_ci bool need_reschedule = false; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!func) 1588c2ecf20Sopenharmony_ci return; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* 1618c2ecf20Sopenharmony_ci * We don't lose events if we aren't able to report CRCs until the 1628c2ecf20Sopenharmony_ci * next vblank, so only report CRCs if the locks we need aren't 1638c2ecf20Sopenharmony_ci * contended to prevent missing an actual vblank event 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci if (!spin_trylock(&crc->lock)) 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!crc->src) 1698c2ecf20Sopenharmony_ci goto out; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ctx = &crc->ctx[crc->ctx_idx]; 1728c2ecf20Sopenharmony_ci if (crc->ctx_changed && func->ctx_finished(head, ctx)) { 1738c2ecf20Sopenharmony_ci nv50_crc_get_entries(head, func, crc->src); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci crc->ctx_idx ^= 1; 1768c2ecf20Sopenharmony_ci crc->entry_idx = 0; 1778c2ecf20Sopenharmony_ci crc->ctx_changed = false; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * Unfortunately when notifier contexts are changed during CRC 1818c2ecf20Sopenharmony_ci * capture, we will inevitably lose the CRC entry for the 1828c2ecf20Sopenharmony_ci * frame where the hardware actually latched onto the first 1838c2ecf20Sopenharmony_ci * UPDATE. According to Nvidia's hardware engineers, there's 1848c2ecf20Sopenharmony_ci * no workaround for this. 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * Now, we could try to be smart here and calculate the number 1878c2ecf20Sopenharmony_ci * of missed CRCs based on audit timestamps, but those were 1888c2ecf20Sopenharmony_ci * removed starting with volta. Since we always flush our 1898c2ecf20Sopenharmony_ci * updates back-to-back without waiting, we'll just be 1908c2ecf20Sopenharmony_ci * optimistic and assume we always miss exactly one frame. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(head->base.base.dev->dev, 1938c2ecf20Sopenharmony_ci "Notifier ctx flip for head-%d finished, lost CRC for frame %llu\n", 1948c2ecf20Sopenharmony_ci head->base.index, crc->frame); 1958c2ecf20Sopenharmony_ci crc->frame++; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci nv50_crc_reset_ctx(ctx); 1988c2ecf20Sopenharmony_ci need_reschedule = true; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci nv50_crc_get_entries(head, func, crc->src); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (need_reschedule) 2048c2ecf20Sopenharmony_ci drm_vblank_work_schedule(&crc->flip_work, 2058c2ecf20Sopenharmony_ci drm_crtc_vblank_count(crtc) 2068c2ecf20Sopenharmony_ci + crc->flip_threshold 2078c2ecf20Sopenharmony_ci - crc->entry_idx, 2088c2ecf20Sopenharmony_ci true); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ciout: 2118c2ecf20Sopenharmony_ci spin_unlock(&crc->lock); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void nv50_crc_wait_ctx_finished(struct nv50_head *head, 2158c2ecf20Sopenharmony_ci const struct nv50_crc_func *func, 2168c2ecf20Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct drm_device *dev = head->base.base.dev; 2198c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 2208c2ecf20Sopenharmony_ci s64 ret; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci ret = nvif_msec(&drm->client.device, 50, 2238c2ecf20Sopenharmony_ci if (func->ctx_finished(head, ctx)) break;); 2248c2ecf20Sopenharmony_ci if (ret == -ETIMEDOUT) 2258c2ecf20Sopenharmony_ci NV_ERROR(drm, 2268c2ecf20Sopenharmony_ci "CRC notifier ctx for head %d not finished after 50ms\n", 2278c2ecf20Sopenharmony_ci head->base.index); 2288c2ecf20Sopenharmony_ci else if (ret) 2298c2ecf20Sopenharmony_ci NV_ATOMIC(drm, 2308c2ecf20Sopenharmony_ci "CRC notifier ctx for head-%d finished after %lldns\n", 2318c2ecf20Sopenharmony_ci head->base.index, ret); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_civoid nv50_crc_atomic_stop_reporting(struct drm_atomic_state *state) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 2378c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 2388c2ecf20Sopenharmony_ci int i; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 2418c2ecf20Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 2428c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); 2438c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (!asyh->clr.crc) 2468c2ecf20Sopenharmony_ci continue; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci spin_lock_irq(&crc->lock); 2498c2ecf20Sopenharmony_ci crc->src = NV50_CRC_SOURCE_NONE; 2508c2ecf20Sopenharmony_ci spin_unlock_irq(&crc->lock); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci drm_crtc_vblank_put(crtc); 2538c2ecf20Sopenharmony_ci drm_vblank_work_cancel_sync(&crc->flip_work); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci NV_ATOMIC(nouveau_drm(crtc->dev), 2568c2ecf20Sopenharmony_ci "CRC reporting on vblank for head-%d disabled\n", 2578c2ecf20Sopenharmony_ci head->base.index); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* CRC generation is still enabled in hw, we'll just report 2608c2ecf20Sopenharmony_ci * any remaining CRC entries ourselves after it gets disabled 2618c2ecf20Sopenharmony_ci * in hardware 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_civoid nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *state) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct drm_crtc_state *new_crtc_state; 2698c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 2708c2ecf20Sopenharmony_ci int i; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { 2738c2ecf20Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 2748c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); 2758c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 2768c2ecf20Sopenharmony_ci int i; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (!asyh->set.crc) 2798c2ecf20Sopenharmony_ci continue; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci crc->entry_idx = 0; 2828c2ecf20Sopenharmony_ci crc->ctx_changed = false; 2838c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) 2848c2ecf20Sopenharmony_ci nv50_crc_reset_ctx(&crc->ctx[i]); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_civoid nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *state) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci const struct nv50_crc_func *func = 2918c2ecf20Sopenharmony_ci nv50_disp(state->dev)->core->func->crc; 2928c2ecf20Sopenharmony_ci struct drm_crtc_state *new_crtc_state; 2938c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 2948c2ecf20Sopenharmony_ci int i; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { 2978c2ecf20Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 2988c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); 2998c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 3008c2ecf20Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx = &crc->ctx[crc->ctx_idx]; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (!asyh->clr.crc) 3038c2ecf20Sopenharmony_ci continue; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (crc->ctx_changed) { 3068c2ecf20Sopenharmony_ci nv50_crc_wait_ctx_finished(head, func, ctx); 3078c2ecf20Sopenharmony_ci ctx = &crc->ctx[crc->ctx_idx ^ 1]; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci nv50_crc_wait_ctx_finished(head, func, ctx); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_civoid nv50_crc_atomic_start_reporting(struct drm_atomic_state *state) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 3168c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 3178c2ecf20Sopenharmony_ci int i; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 3208c2ecf20Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 3218c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); 3228c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 3238c2ecf20Sopenharmony_ci u64 vbl_count; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (!asyh->set.crc) 3268c2ecf20Sopenharmony_ci continue; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci drm_crtc_vblank_get(crtc); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci spin_lock_irq(&crc->lock); 3318c2ecf20Sopenharmony_ci vbl_count = drm_crtc_vblank_count(crtc); 3328c2ecf20Sopenharmony_ci crc->frame = vbl_count; 3338c2ecf20Sopenharmony_ci crc->src = asyh->crc.src; 3348c2ecf20Sopenharmony_ci drm_vblank_work_schedule(&crc->flip_work, 3358c2ecf20Sopenharmony_ci vbl_count + crc->flip_threshold, 3368c2ecf20Sopenharmony_ci true); 3378c2ecf20Sopenharmony_ci spin_unlock_irq(&crc->lock); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci NV_ATOMIC(nouveau_drm(crtc->dev), 3408c2ecf20Sopenharmony_ci "CRC reporting on vblank for head-%d enabled\n", 3418c2ecf20Sopenharmony_ci head->base.index); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ciint nv50_crc_atomic_check_head(struct nv50_head *head, 3468c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh, 3478c2ecf20Sopenharmony_ci struct nv50_head_atom *armh) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct nv50_atom *atom = nv50_atom(asyh->state.state); 3508c2ecf20Sopenharmony_ci struct drm_device *dev = head->base.base.dev; 3518c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(dev); 3528c2ecf20Sopenharmony_ci bool changed = armh->crc.src != asyh->crc.src; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (!armh->crc.src && !asyh->crc.src) { 3558c2ecf20Sopenharmony_ci asyh->set.crc = false; 3568c2ecf20Sopenharmony_ci asyh->clr.crc = false; 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* While we don't care about entry tags, Volta+ hw always needs the 3618c2ecf20Sopenharmony_ci * controlling wndw channel programmed to a wndw that's owned by our 3628c2ecf20Sopenharmony_ci * head 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ci if (asyh->crc.src && disp->disp->object.oclass >= GV100_DISP && 3658c2ecf20Sopenharmony_ci !(BIT(asyh->crc.wndw) & asyh->wndw.owned)) { 3668c2ecf20Sopenharmony_ci if (!asyh->wndw.owned) { 3678c2ecf20Sopenharmony_ci /* TODO: once we support flexible channel ownership, 3688c2ecf20Sopenharmony_ci * we should write some code here to handle attempting 3698c2ecf20Sopenharmony_ci * to "steal" a plane: e.g. take a plane that is 3708c2ecf20Sopenharmony_ci * currently not-visible and owned by another head, 3718c2ecf20Sopenharmony_ci * and reassign it to this head. If we fail to do so, 3728c2ecf20Sopenharmony_ci * we shuld reject the mode outright as CRC capture 3738c2ecf20Sopenharmony_ci * then becomes impossible. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ci NV_ATOMIC(nouveau_drm(dev), 3768c2ecf20Sopenharmony_ci "No available wndws for CRC readback\n"); 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci asyh->crc.wndw = ffs(asyh->wndw.owned) - 1; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (drm_atomic_crtc_needs_modeset(&asyh->state) || changed || 3838c2ecf20Sopenharmony_ci armh->crc.wndw != asyh->crc.wndw) { 3848c2ecf20Sopenharmony_ci asyh->clr.crc = armh->crc.src && armh->state.active; 3858c2ecf20Sopenharmony_ci asyh->set.crc = asyh->crc.src && asyh->state.active; 3868c2ecf20Sopenharmony_ci if (changed) 3878c2ecf20Sopenharmony_ci asyh->set.or |= armh->or.crc_raster != 3888c2ecf20Sopenharmony_ci asyh->or.crc_raster; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (asyh->clr.crc && asyh->set.crc) 3918c2ecf20Sopenharmony_ci atom->flush_disable = true; 3928c2ecf20Sopenharmony_ci } else { 3938c2ecf20Sopenharmony_ci asyh->set.crc = false; 3948c2ecf20Sopenharmony_ci asyh->clr.crc = false; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_civoid nv50_crc_atomic_check_outp(struct nv50_atom *atom) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 4038c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state, *new_crtc_state; 4048c2ecf20Sopenharmony_ci int i; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (atom->flush_disable) 4078c2ecf20Sopenharmony_ci return; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci for_each_oldnew_crtc_in_state(&atom->state, crtc, old_crtc_state, 4108c2ecf20Sopenharmony_ci new_crtc_state, i) { 4118c2ecf20Sopenharmony_ci struct nv50_head_atom *armh = nv50_head_atom(old_crtc_state); 4128c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); 4138c2ecf20Sopenharmony_ci struct nv50_outp_atom *outp_atom; 4148c2ecf20Sopenharmony_ci struct nouveau_encoder *outp; 4158c2ecf20Sopenharmony_ci struct drm_encoder *encoder, *enc; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci enc = nv50_head_atom_get_encoder(armh); 4188c2ecf20Sopenharmony_ci if (!enc) 4198c2ecf20Sopenharmony_ci continue; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci outp = nv50_real_outp(enc); 4228c2ecf20Sopenharmony_ci if (!outp) 4238c2ecf20Sopenharmony_ci continue; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci encoder = &outp->base.base; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (!asyh->clr.crc) 4288c2ecf20Sopenharmony_ci continue; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * Re-programming ORs can't be done in the same flush as 4328c2ecf20Sopenharmony_ci * disabling CRCs 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ci list_for_each_entry(outp_atom, &atom->outp, head) { 4358c2ecf20Sopenharmony_ci if (outp_atom->encoder == encoder) { 4368c2ecf20Sopenharmony_ci if (outp_atom->set.mask) { 4378c2ecf20Sopenharmony_ci atom->flush_disable = true; 4388c2ecf20Sopenharmony_ci return; 4398c2ecf20Sopenharmony_ci } else { 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic enum nv50_crc_source_type 4488c2ecf20Sopenharmony_cinv50_crc_source_type(struct nouveau_encoder *outp, 4498c2ecf20Sopenharmony_ci enum nv50_crc_source source) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct dcb_output *dcbe = outp->dcb; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci switch (source) { 4548c2ecf20Sopenharmony_ci case NV50_CRC_SOURCE_NONE: return NV50_CRC_SOURCE_TYPE_NONE; 4558c2ecf20Sopenharmony_ci case NV50_CRC_SOURCE_RG: return NV50_CRC_SOURCE_TYPE_RG; 4568c2ecf20Sopenharmony_ci default: break; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (dcbe->location != DCB_LOC_ON_CHIP) 4608c2ecf20Sopenharmony_ci return NV50_CRC_SOURCE_TYPE_PIOR; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci switch (dcbe->type) { 4638c2ecf20Sopenharmony_ci case DCB_OUTPUT_DP: return NV50_CRC_SOURCE_TYPE_SF; 4648c2ecf20Sopenharmony_ci case DCB_OUTPUT_ANALOG: return NV50_CRC_SOURCE_TYPE_DAC; 4658c2ecf20Sopenharmony_ci default: return NV50_CRC_SOURCE_TYPE_SOR; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_civoid nv50_crc_atomic_set(struct nv50_head *head, 4708c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 4738c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 4748c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 4758c2ecf20Sopenharmony_ci const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; 4768c2ecf20Sopenharmony_ci struct nouveau_encoder *outp; 4778c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci encoder = nv50_head_atom_get_encoder(asyh); 4808c2ecf20Sopenharmony_ci if (!encoder) 4818c2ecf20Sopenharmony_ci return; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci outp = nv50_real_outp(encoder); 4848c2ecf20Sopenharmony_ci if (!outp) 4858c2ecf20Sopenharmony_ci return; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci func->set_src(head, outp->or, 4888c2ecf20Sopenharmony_ci nv50_crc_source_type(outp, asyh->crc.src), 4898c2ecf20Sopenharmony_ci &crc->ctx[crc->ctx_idx], asyh->crc.wndw); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_civoid nv50_crc_atomic_clr(struct nv50_head *head) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci const struct nv50_crc_func *func = 4958c2ecf20Sopenharmony_ci nv50_disp(head->base.base.dev)->core->func->crc; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE, NULL, 0); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic inline int 5018c2ecf20Sopenharmony_cinv50_crc_raster_type(enum nv50_crc_source source) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci switch (source) { 5048c2ecf20Sopenharmony_ci case NV50_CRC_SOURCE_NONE: 5058c2ecf20Sopenharmony_ci case NV50_CRC_SOURCE_AUTO: 5068c2ecf20Sopenharmony_ci case NV50_CRC_SOURCE_RG: 5078c2ecf20Sopenharmony_ci case NV50_CRC_SOURCE_OUTP_ACTIVE: 5088c2ecf20Sopenharmony_ci return NV907D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_CRC_MODE_ACTIVE_RASTER; 5098c2ecf20Sopenharmony_ci case NV50_CRC_SOURCE_OUTP_COMPLETE: 5108c2ecf20Sopenharmony_ci return NV907D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_CRC_MODE_COMPLETE_RASTER; 5118c2ecf20Sopenharmony_ci case NV50_CRC_SOURCE_OUTP_INACTIVE: 5128c2ecf20Sopenharmony_ci return NV907D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_CRC_MODE_NON_ACTIVE_RASTER; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci/* We handle mapping the memory for CRC notifiers ourselves, since each 5198c2ecf20Sopenharmony_ci * notifier needs it's own handle 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_cistatic inline int 5228c2ecf20Sopenharmony_cinv50_crc_ctx_init(struct nv50_head *head, struct nvif_mmu *mmu, 5238c2ecf20Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx, size_t len, int idx) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct nv50_core *core = nv50_disp(head->base.base.dev)->core; 5268c2ecf20Sopenharmony_ci int ret; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci ret = nvif_mem_ctor_map(mmu, "kmsCrcNtfy", NVIF_MEM_VRAM, len, &ctx->mem); 5298c2ecf20Sopenharmony_ci if (ret) 5308c2ecf20Sopenharmony_ci return ret; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci ret = nvif_object_ctor(&core->chan.base.user, "kmsCrcNtfyCtxDma", 5338c2ecf20Sopenharmony_ci NV50_DISP_HANDLE_CRC_CTX(head, idx), 5348c2ecf20Sopenharmony_ci NV_DMA_IN_MEMORY, 5358c2ecf20Sopenharmony_ci &(struct nv_dma_v0) { 5368c2ecf20Sopenharmony_ci .target = NV_DMA_V0_TARGET_VRAM, 5378c2ecf20Sopenharmony_ci .access = NV_DMA_V0_ACCESS_RDWR, 5388c2ecf20Sopenharmony_ci .start = ctx->mem.addr, 5398c2ecf20Sopenharmony_ci .limit = ctx->mem.addr 5408c2ecf20Sopenharmony_ci + ctx->mem.size - 1, 5418c2ecf20Sopenharmony_ci }, sizeof(struct nv_dma_v0), 5428c2ecf20Sopenharmony_ci &ctx->ntfy); 5438c2ecf20Sopenharmony_ci if (ret) 5448c2ecf20Sopenharmony_ci goto fail_fini; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cifail_fini: 5498c2ecf20Sopenharmony_ci nvif_mem_dtor(&ctx->mem); 5508c2ecf20Sopenharmony_ci return ret; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic inline void 5548c2ecf20Sopenharmony_cinv50_crc_ctx_fini(struct nv50_crc_notifier_ctx *ctx) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci nvif_object_dtor(&ctx->ntfy); 5578c2ecf20Sopenharmony_ci nvif_mem_dtor(&ctx->mem); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ciint nv50_crc_set_source(struct drm_crtc *crtc, const char *source_str) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 5638c2ecf20Sopenharmony_ci struct drm_atomic_state *state; 5648c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx ctx; 5658c2ecf20Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 5668c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 5678c2ecf20Sopenharmony_ci const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; 5688c2ecf20Sopenharmony_ci struct nvif_mmu *mmu = &nouveau_drm(dev)->client.mmu; 5698c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh; 5708c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 5718c2ecf20Sopenharmony_ci enum nv50_crc_source source; 5728c2ecf20Sopenharmony_ci int ret = 0, ctx_flags = 0, i; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci ret = nv50_crc_parse_source(source_str, &source); 5758c2ecf20Sopenharmony_ci if (ret) 5768c2ecf20Sopenharmony_ci return ret; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* 5798c2ecf20Sopenharmony_ci * Since we don't want the user to accidentally interrupt us as we're 5808c2ecf20Sopenharmony_ci * disabling CRCs 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ci if (source) 5838c2ecf20Sopenharmony_ci ctx_flags |= DRM_MODESET_ACQUIRE_INTERRUPTIBLE; 5848c2ecf20Sopenharmony_ci drm_modeset_acquire_init(&ctx, ctx_flags); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci state = drm_atomic_state_alloc(dev); 5878c2ecf20Sopenharmony_ci if (!state) { 5888c2ecf20Sopenharmony_ci ret = -ENOMEM; 5898c2ecf20Sopenharmony_ci goto out_acquire_fini; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci state->acquire_ctx = &ctx; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (source) { 5948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(head->crc.ctx); i++) { 5958c2ecf20Sopenharmony_ci ret = nv50_crc_ctx_init(head, mmu, &crc->ctx[i], 5968c2ecf20Sopenharmony_ci func->notifier_len, i); 5978c2ecf20Sopenharmony_ci if (ret) 5988c2ecf20Sopenharmony_ci goto out_ctx_fini; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ciretry: 6038c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_crtc_state(state, &head->base.base); 6048c2ecf20Sopenharmony_ci if (IS_ERR(crtc_state)) { 6058c2ecf20Sopenharmony_ci ret = PTR_ERR(crtc_state); 6068c2ecf20Sopenharmony_ci if (ret == -EDEADLK) 6078c2ecf20Sopenharmony_ci goto deadlock; 6088c2ecf20Sopenharmony_ci else if (ret) 6098c2ecf20Sopenharmony_ci goto out_drop_locks; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci asyh = nv50_head_atom(crtc_state); 6128c2ecf20Sopenharmony_ci asyh->crc.src = source; 6138c2ecf20Sopenharmony_ci asyh->or.crc_raster = nv50_crc_raster_type(source); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci ret = drm_atomic_commit(state); 6168c2ecf20Sopenharmony_ci if (ret == -EDEADLK) 6178c2ecf20Sopenharmony_ci goto deadlock; 6188c2ecf20Sopenharmony_ci else if (ret) 6198c2ecf20Sopenharmony_ci goto out_drop_locks; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (!source) { 6228c2ecf20Sopenharmony_ci /* 6238c2ecf20Sopenharmony_ci * If the user specified a custom flip threshold through 6248c2ecf20Sopenharmony_ci * debugfs, reset it 6258c2ecf20Sopenharmony_ci */ 6268c2ecf20Sopenharmony_ci crc->flip_threshold = func->flip_threshold; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ciout_drop_locks: 6308c2ecf20Sopenharmony_ci drm_modeset_drop_locks(&ctx); 6318c2ecf20Sopenharmony_ciout_ctx_fini: 6328c2ecf20Sopenharmony_ci if (!source || ret) { 6338c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) 6348c2ecf20Sopenharmony_ci nv50_crc_ctx_fini(&crc->ctx[i]); 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci drm_atomic_state_put(state); 6378c2ecf20Sopenharmony_ciout_acquire_fini: 6388c2ecf20Sopenharmony_ci drm_modeset_acquire_fini(&ctx); 6398c2ecf20Sopenharmony_ci return ret; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cideadlock: 6428c2ecf20Sopenharmony_ci drm_atomic_state_clear(state); 6438c2ecf20Sopenharmony_ci drm_modeset_backoff(&ctx); 6448c2ecf20Sopenharmony_ci goto retry; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int 6488c2ecf20Sopenharmony_cinv50_crc_debugfs_flip_threshold_get(struct seq_file *m, void *data) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct nv50_head *head = m->private; 6518c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 6528c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 6538c2ecf20Sopenharmony_ci int ret; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci ret = drm_modeset_lock_single_interruptible(&crtc->mutex); 6568c2ecf20Sopenharmony_ci if (ret) 6578c2ecf20Sopenharmony_ci return ret; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci seq_printf(m, "%d\n", crc->flip_threshold); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci drm_modeset_unlock(&crtc->mutex); 6628c2ecf20Sopenharmony_ci return ret; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic int 6668c2ecf20Sopenharmony_cinv50_crc_debugfs_flip_threshold_open(struct inode *inode, struct file *file) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci return single_open(file, nv50_crc_debugfs_flip_threshold_get, 6698c2ecf20Sopenharmony_ci inode->i_private); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic ssize_t 6738c2ecf20Sopenharmony_cinv50_crc_debugfs_flip_threshold_set(struct file *file, 6748c2ecf20Sopenharmony_ci const char __user *ubuf, size_t len, 6758c2ecf20Sopenharmony_ci loff_t *offp) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct seq_file *m = file->private_data; 6788c2ecf20Sopenharmony_ci struct nv50_head *head = m->private; 6798c2ecf20Sopenharmony_ci struct nv50_head_atom *armh; 6808c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 6818c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(crtc->dev); 6828c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 6838c2ecf20Sopenharmony_ci const struct nv50_crc_func *func = 6848c2ecf20Sopenharmony_ci nv50_disp(crtc->dev)->core->func->crc; 6858c2ecf20Sopenharmony_ci int value, ret; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ret = kstrtoint_from_user(ubuf, len, 10, &value); 6888c2ecf20Sopenharmony_ci if (ret) 6898c2ecf20Sopenharmony_ci return ret; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (value > func->flip_threshold) 6928c2ecf20Sopenharmony_ci return -EINVAL; 6938c2ecf20Sopenharmony_ci else if (value == -1) 6948c2ecf20Sopenharmony_ci value = func->flip_threshold; 6958c2ecf20Sopenharmony_ci else if (value < -1) 6968c2ecf20Sopenharmony_ci return -EINVAL; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci ret = drm_modeset_lock_single_interruptible(&crtc->mutex); 6998c2ecf20Sopenharmony_ci if (ret) 7008c2ecf20Sopenharmony_ci return ret; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci armh = nv50_head_atom(crtc->state); 7038c2ecf20Sopenharmony_ci if (armh->crc.src) { 7048c2ecf20Sopenharmony_ci ret = -EBUSY; 7058c2ecf20Sopenharmony_ci goto out; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci NV_DEBUG(drm, 7098c2ecf20Sopenharmony_ci "Changing CRC flip threshold for next capture on head-%d to %d\n", 7108c2ecf20Sopenharmony_ci head->base.index, value); 7118c2ecf20Sopenharmony_ci crc->flip_threshold = value; 7128c2ecf20Sopenharmony_ci ret = len; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ciout: 7158c2ecf20Sopenharmony_ci drm_modeset_unlock(&crtc->mutex); 7168c2ecf20Sopenharmony_ci return ret; 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic const struct file_operations nv50_crc_flip_threshold_fops = { 7208c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7218c2ecf20Sopenharmony_ci .open = nv50_crc_debugfs_flip_threshold_open, 7228c2ecf20Sopenharmony_ci .read = seq_read, 7238c2ecf20Sopenharmony_ci .write = nv50_crc_debugfs_flip_threshold_set, 7248c2ecf20Sopenharmony_ci .release = single_release, 7258c2ecf20Sopenharmony_ci}; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ciint nv50_head_crc_late_register(struct nv50_head *head) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 7308c2ecf20Sopenharmony_ci const struct nv50_crc_func *func = 7318c2ecf20Sopenharmony_ci nv50_disp(crtc->dev)->core->func->crc; 7328c2ecf20Sopenharmony_ci struct dentry *root; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (!func || !crtc->debugfs_entry) 7358c2ecf20Sopenharmony_ci return 0; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci root = debugfs_create_dir("nv_crc", crtc->debugfs_entry); 7388c2ecf20Sopenharmony_ci debugfs_create_file("flip_threshold", 0644, root, head, 7398c2ecf20Sopenharmony_ci &nv50_crc_flip_threshold_fops); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic inline void 7458c2ecf20Sopenharmony_cinv50_crc_init_head(struct nv50_disp *disp, const struct nv50_crc_func *func, 7468c2ecf20Sopenharmony_ci struct nv50_head *head) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct nv50_crc *crc = &head->crc; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci crc->flip_threshold = func->flip_threshold; 7518c2ecf20Sopenharmony_ci spin_lock_init(&crc->lock); 7528c2ecf20Sopenharmony_ci drm_vblank_work_init(&crc->flip_work, &head->base.base, 7538c2ecf20Sopenharmony_ci nv50_crc_ctx_flip_work); 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_civoid nv50_crc_init(struct drm_device *dev) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(dev); 7598c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 7608c2ecf20Sopenharmony_ci const struct nv50_crc_func *func = disp->core->func->crc; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (!func) 7638c2ecf20Sopenharmony_ci return; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, dev) 7668c2ecf20Sopenharmony_ci nv50_crc_init_head(disp, func, nv50_head(crtc)); 7678c2ecf20Sopenharmony_ci} 768