162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci#include <linux/string.h> 362306a36Sopenharmony_ci#include <drm/drm_crtc.h> 462306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 562306a36Sopenharmony_ci#include <drm/drm_vblank.h> 662306a36Sopenharmony_ci#include <drm/drm_vblank_work.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <nvif/class.h> 962306a36Sopenharmony_ci#include <nvif/cl0002.h> 1062306a36Sopenharmony_ci#include <nvif/timer.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <nvhw/class/cl907d.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "nouveau_drv.h" 1562306a36Sopenharmony_ci#include "core.h" 1662306a36Sopenharmony_ci#include "head.h" 1762306a36Sopenharmony_ci#include "wndw.h" 1862306a36Sopenharmony_ci#include "handles.h" 1962306a36Sopenharmony_ci#include "crc.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic const char * const nv50_crc_sources[] = { 2262306a36Sopenharmony_ci [NV50_CRC_SOURCE_NONE] = "none", 2362306a36Sopenharmony_ci [NV50_CRC_SOURCE_AUTO] = "auto", 2462306a36Sopenharmony_ci [NV50_CRC_SOURCE_RG] = "rg", 2562306a36Sopenharmony_ci [NV50_CRC_SOURCE_OUTP_ACTIVE] = "outp-active", 2662306a36Sopenharmony_ci [NV50_CRC_SOURCE_OUTP_COMPLETE] = "outp-complete", 2762306a36Sopenharmony_ci [NV50_CRC_SOURCE_OUTP_INACTIVE] = "outp-inactive", 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int nv50_crc_parse_source(const char *buf, enum nv50_crc_source *s) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int i; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (!buf) { 3562306a36Sopenharmony_ci *s = NV50_CRC_SOURCE_NONE; 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci i = match_string(nv50_crc_sources, ARRAY_SIZE(nv50_crc_sources), buf); 4062306a36Sopenharmony_ci if (i < 0) 4162306a36Sopenharmony_ci return i; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci *s = i; 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciint 4862306a36Sopenharmony_cinv50_crc_verify_source(struct drm_crtc *crtc, const char *source_name, 4962306a36Sopenharmony_ci size_t *values_cnt) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(crtc->dev); 5262306a36Sopenharmony_ci enum nv50_crc_source source; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (nv50_crc_parse_source(source_name, &source) < 0) { 5562306a36Sopenharmony_ci NV_DEBUG(drm, "unknown source %s\n", source_name); 5662306a36Sopenharmony_ci return -EINVAL; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci *values_cnt = 1; 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ciconst char *const *nv50_crc_get_sources(struct drm_crtc *crtc, size_t *count) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci *count = ARRAY_SIZE(nv50_crc_sources); 6662306a36Sopenharmony_ci return nv50_crc_sources; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void 7062306a36Sopenharmony_cinv50_crc_program_ctx(struct nv50_head *head, 7162306a36Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct nv50_disp *disp = nv50_disp(head->base.base.dev); 7462306a36Sopenharmony_ci struct nv50_core *core = disp->core; 7562306a36Sopenharmony_ci u32 interlock[NV50_DISP_INTERLOCK__SIZE] = { 0 }; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci core->func->crc->set_ctx(head, ctx); 7862306a36Sopenharmony_ci core->func->update(core, interlock, false); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void nv50_crc_ctx_flip_work(struct kthread_work *base) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct drm_vblank_work *work = to_drm_vblank_work(base); 8462306a36Sopenharmony_ci struct nv50_crc *crc = container_of(work, struct nv50_crc, flip_work); 8562306a36Sopenharmony_ci struct nv50_head *head = container_of(crc, struct nv50_head, crc); 8662306a36Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 8762306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 8862306a36Sopenharmony_ci struct nv50_disp *disp = nv50_disp(dev); 8962306a36Sopenharmony_ci const uint64_t start_vbl = drm_crtc_vblank_count(crtc); 9062306a36Sopenharmony_ci uint64_t end_vbl; 9162306a36Sopenharmony_ci u8 new_idx = crc->ctx_idx ^ 1; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * We don't want to accidentally wait for longer then the vblank, so 9562306a36Sopenharmony_ci * try again for the next vblank if we don't grab the lock 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci if (!mutex_trylock(&disp->mutex)) { 9862306a36Sopenharmony_ci drm_dbg_kms(dev, "Lock contended, delaying CRC ctx flip for %s\n", crtc->name); 9962306a36Sopenharmony_ci drm_vblank_work_schedule(work, start_vbl + 1, true); 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci drm_dbg_kms(dev, "Flipping notifier ctx for %s (%d -> %d)\n", 10462306a36Sopenharmony_ci crtc->name, crc->ctx_idx, new_idx); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci nv50_crc_program_ctx(head, NULL); 10762306a36Sopenharmony_ci nv50_crc_program_ctx(head, &crc->ctx[new_idx]); 10862306a36Sopenharmony_ci mutex_unlock(&disp->mutex); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci end_vbl = drm_crtc_vblank_count(crtc); 11162306a36Sopenharmony_ci if (unlikely(end_vbl != start_vbl)) 11262306a36Sopenharmony_ci NV_ERROR(nouveau_drm(dev), 11362306a36Sopenharmony_ci "Failed to flip CRC context on %s on time (%llu > %llu)\n", 11462306a36Sopenharmony_ci crtc->name, end_vbl, start_vbl); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci spin_lock_irq(&crc->lock); 11762306a36Sopenharmony_ci crc->ctx_changed = true; 11862306a36Sopenharmony_ci spin_unlock_irq(&crc->lock); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic inline void nv50_crc_reset_ctx(struct nv50_crc_notifier_ctx *ctx) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci memset_io(ctx->mem.object.map.ptr, 0, ctx->mem.object.map.size); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void 12762306a36Sopenharmony_cinv50_crc_get_entries(struct nv50_head *head, 12862306a36Sopenharmony_ci const struct nv50_crc_func *func, 12962306a36Sopenharmony_ci enum nv50_crc_source source) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 13262306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 13362306a36Sopenharmony_ci u32 output_crc; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci while (crc->entry_idx < func->num_entries) { 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * While Nvidia's documentation says CRCs are written on each 13862306a36Sopenharmony_ci * subsequent vblank after being enabled, in practice they 13962306a36Sopenharmony_ci * aren't written immediately. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci output_crc = func->get_entry(head, &crc->ctx[crc->ctx_idx], 14262306a36Sopenharmony_ci source, crc->entry_idx); 14362306a36Sopenharmony_ci if (!output_crc) 14462306a36Sopenharmony_ci return; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci drm_crtc_add_crc_entry(crtc, true, crc->frame, &output_crc); 14762306a36Sopenharmony_ci crc->frame++; 14862306a36Sopenharmony_ci crc->entry_idx++; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_civoid nv50_crc_handle_vblank(struct nv50_head *head) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 15562306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 15662306a36Sopenharmony_ci const struct nv50_crc_func *func = 15762306a36Sopenharmony_ci nv50_disp(head->base.base.dev)->core->func->crc; 15862306a36Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx; 15962306a36Sopenharmony_ci bool need_reschedule = false; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (!func) 16262306a36Sopenharmony_ci return; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * We don't lose events if we aren't able to report CRCs until the 16662306a36Sopenharmony_ci * next vblank, so only report CRCs if the locks we need aren't 16762306a36Sopenharmony_ci * contended to prevent missing an actual vblank event 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci if (!spin_trylock(&crc->lock)) 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (!crc->src) 17362306a36Sopenharmony_ci goto out; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci ctx = &crc->ctx[crc->ctx_idx]; 17662306a36Sopenharmony_ci if (crc->ctx_changed && func->ctx_finished(head, ctx)) { 17762306a36Sopenharmony_ci nv50_crc_get_entries(head, func, crc->src); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci crc->ctx_idx ^= 1; 18062306a36Sopenharmony_ci crc->entry_idx = 0; 18162306a36Sopenharmony_ci crc->ctx_changed = false; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * Unfortunately when notifier contexts are changed during CRC 18562306a36Sopenharmony_ci * capture, we will inevitably lose the CRC entry for the 18662306a36Sopenharmony_ci * frame where the hardware actually latched onto the first 18762306a36Sopenharmony_ci * UPDATE. According to Nvidia's hardware engineers, there's 18862306a36Sopenharmony_ci * no workaround for this. 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * Now, we could try to be smart here and calculate the number 19162306a36Sopenharmony_ci * of missed CRCs based on audit timestamps, but those were 19262306a36Sopenharmony_ci * removed starting with volta. Since we always flush our 19362306a36Sopenharmony_ci * updates back-to-back without waiting, we'll just be 19462306a36Sopenharmony_ci * optimistic and assume we always miss exactly one frame. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci drm_dbg_kms(head->base.base.dev, 19762306a36Sopenharmony_ci "Notifier ctx flip for head-%d finished, lost CRC for frame %llu\n", 19862306a36Sopenharmony_ci head->base.index, crc->frame); 19962306a36Sopenharmony_ci crc->frame++; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci nv50_crc_reset_ctx(ctx); 20262306a36Sopenharmony_ci need_reschedule = true; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci nv50_crc_get_entries(head, func, crc->src); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (need_reschedule) 20862306a36Sopenharmony_ci drm_vblank_work_schedule(&crc->flip_work, 20962306a36Sopenharmony_ci drm_crtc_vblank_count(crtc) 21062306a36Sopenharmony_ci + crc->flip_threshold 21162306a36Sopenharmony_ci - crc->entry_idx, 21262306a36Sopenharmony_ci true); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ciout: 21562306a36Sopenharmony_ci spin_unlock(&crc->lock); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void nv50_crc_wait_ctx_finished(struct nv50_head *head, 21962306a36Sopenharmony_ci const struct nv50_crc_func *func, 22062306a36Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct drm_device *dev = head->base.base.dev; 22362306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 22462306a36Sopenharmony_ci s64 ret; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci ret = nvif_msec(&drm->client.device, 50, 22762306a36Sopenharmony_ci if (func->ctx_finished(head, ctx)) break;); 22862306a36Sopenharmony_ci if (ret == -ETIMEDOUT) 22962306a36Sopenharmony_ci NV_ERROR(drm, 23062306a36Sopenharmony_ci "CRC notifier ctx for head %d not finished after 50ms\n", 23162306a36Sopenharmony_ci head->base.index); 23262306a36Sopenharmony_ci else if (ret) 23362306a36Sopenharmony_ci NV_ATOMIC(drm, 23462306a36Sopenharmony_ci "CRC notifier ctx for head-%d finished after %lldns\n", 23562306a36Sopenharmony_ci head->base.index, ret); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_civoid nv50_crc_atomic_stop_reporting(struct drm_atomic_state *state) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 24162306a36Sopenharmony_ci struct drm_crtc *crtc; 24262306a36Sopenharmony_ci int i; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 24562306a36Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 24662306a36Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); 24762306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (!asyh->clr.crc) 25062306a36Sopenharmony_ci continue; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci spin_lock_irq(&crc->lock); 25362306a36Sopenharmony_ci crc->src = NV50_CRC_SOURCE_NONE; 25462306a36Sopenharmony_ci spin_unlock_irq(&crc->lock); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci drm_crtc_vblank_put(crtc); 25762306a36Sopenharmony_ci drm_vblank_work_cancel_sync(&crc->flip_work); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci NV_ATOMIC(nouveau_drm(crtc->dev), 26062306a36Sopenharmony_ci "CRC reporting on vblank for head-%d disabled\n", 26162306a36Sopenharmony_ci head->base.index); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* CRC generation is still enabled in hw, we'll just report 26462306a36Sopenharmony_ci * any remaining CRC entries ourselves after it gets disabled 26562306a36Sopenharmony_ci * in hardware 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_civoid nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *state) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct drm_crtc_state *new_crtc_state; 27362306a36Sopenharmony_ci struct drm_crtc *crtc; 27462306a36Sopenharmony_ci int i; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { 27762306a36Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 27862306a36Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); 27962306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 28062306a36Sopenharmony_ci int i; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (!asyh->set.crc) 28362306a36Sopenharmony_ci continue; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci crc->entry_idx = 0; 28662306a36Sopenharmony_ci crc->ctx_changed = false; 28762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) 28862306a36Sopenharmony_ci nv50_crc_reset_ctx(&crc->ctx[i]); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_civoid nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *state) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci const struct nv50_crc_func *func = 29562306a36Sopenharmony_ci nv50_disp(state->dev)->core->func->crc; 29662306a36Sopenharmony_ci struct drm_crtc_state *new_crtc_state; 29762306a36Sopenharmony_ci struct drm_crtc *crtc; 29862306a36Sopenharmony_ci int i; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { 30162306a36Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 30262306a36Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); 30362306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 30462306a36Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx = &crc->ctx[crc->ctx_idx]; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!asyh->clr.crc) 30762306a36Sopenharmony_ci continue; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (crc->ctx_changed) { 31062306a36Sopenharmony_ci nv50_crc_wait_ctx_finished(head, func, ctx); 31162306a36Sopenharmony_ci ctx = &crc->ctx[crc->ctx_idx ^ 1]; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci nv50_crc_wait_ctx_finished(head, func, ctx); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_civoid nv50_crc_atomic_start_reporting(struct drm_atomic_state *state) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 32062306a36Sopenharmony_ci struct drm_crtc *crtc; 32162306a36Sopenharmony_ci int i; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 32462306a36Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 32562306a36Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); 32662306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 32762306a36Sopenharmony_ci u64 vbl_count; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (!asyh->set.crc) 33062306a36Sopenharmony_ci continue; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci drm_crtc_vblank_get(crtc); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci spin_lock_irq(&crc->lock); 33562306a36Sopenharmony_ci vbl_count = drm_crtc_vblank_count(crtc); 33662306a36Sopenharmony_ci crc->frame = vbl_count; 33762306a36Sopenharmony_ci crc->src = asyh->crc.src; 33862306a36Sopenharmony_ci drm_vblank_work_schedule(&crc->flip_work, 33962306a36Sopenharmony_ci vbl_count + crc->flip_threshold, 34062306a36Sopenharmony_ci true); 34162306a36Sopenharmony_ci spin_unlock_irq(&crc->lock); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci NV_ATOMIC(nouveau_drm(crtc->dev), 34462306a36Sopenharmony_ci "CRC reporting on vblank for head-%d enabled\n", 34562306a36Sopenharmony_ci head->base.index); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ciint nv50_crc_atomic_check_head(struct nv50_head *head, 35062306a36Sopenharmony_ci struct nv50_head_atom *asyh, 35162306a36Sopenharmony_ci struct nv50_head_atom *armh) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct nv50_atom *atom = nv50_atom(asyh->state.state); 35462306a36Sopenharmony_ci bool changed = armh->crc.src != asyh->crc.src; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!armh->crc.src && !asyh->crc.src) { 35762306a36Sopenharmony_ci asyh->set.crc = false; 35862306a36Sopenharmony_ci asyh->clr.crc = false; 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (drm_atomic_crtc_needs_modeset(&asyh->state) || changed) { 36362306a36Sopenharmony_ci asyh->clr.crc = armh->crc.src && armh->state.active; 36462306a36Sopenharmony_ci asyh->set.crc = asyh->crc.src && asyh->state.active; 36562306a36Sopenharmony_ci if (changed) 36662306a36Sopenharmony_ci asyh->set.or |= armh->or.crc_raster != 36762306a36Sopenharmony_ci asyh->or.crc_raster; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (asyh->clr.crc && asyh->set.crc) 37062306a36Sopenharmony_ci atom->flush_disable = true; 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci asyh->set.crc = false; 37362306a36Sopenharmony_ci asyh->clr.crc = false; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_civoid nv50_crc_atomic_check_outp(struct nv50_atom *atom) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct drm_crtc *crtc; 38262306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state, *new_crtc_state; 38362306a36Sopenharmony_ci int i; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (atom->flush_disable) 38662306a36Sopenharmony_ci return; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci for_each_oldnew_crtc_in_state(&atom->state, crtc, old_crtc_state, 38962306a36Sopenharmony_ci new_crtc_state, i) { 39062306a36Sopenharmony_ci struct nv50_head_atom *armh = nv50_head_atom(old_crtc_state); 39162306a36Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); 39262306a36Sopenharmony_ci struct nv50_outp_atom *outp_atom; 39362306a36Sopenharmony_ci struct nouveau_encoder *outp; 39462306a36Sopenharmony_ci struct drm_encoder *encoder, *enc; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci enc = nv50_head_atom_get_encoder(armh); 39762306a36Sopenharmony_ci if (!enc) 39862306a36Sopenharmony_ci continue; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci outp = nv50_real_outp(enc); 40162306a36Sopenharmony_ci if (!outp) 40262306a36Sopenharmony_ci continue; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci encoder = &outp->base.base; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (!asyh->clr.crc) 40762306a36Sopenharmony_ci continue; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* 41062306a36Sopenharmony_ci * Re-programming ORs can't be done in the same flush as 41162306a36Sopenharmony_ci * disabling CRCs 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_ci list_for_each_entry(outp_atom, &atom->outp, head) { 41462306a36Sopenharmony_ci if (outp_atom->encoder == encoder) { 41562306a36Sopenharmony_ci if (outp_atom->set.mask) { 41662306a36Sopenharmony_ci atom->flush_disable = true; 41762306a36Sopenharmony_ci return; 41862306a36Sopenharmony_ci } else { 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic enum nv50_crc_source_type 42762306a36Sopenharmony_cinv50_crc_source_type(struct nouveau_encoder *outp, 42862306a36Sopenharmony_ci enum nv50_crc_source source) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct dcb_output *dcbe = outp->dcb; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci switch (source) { 43362306a36Sopenharmony_ci case NV50_CRC_SOURCE_NONE: return NV50_CRC_SOURCE_TYPE_NONE; 43462306a36Sopenharmony_ci case NV50_CRC_SOURCE_RG: return NV50_CRC_SOURCE_TYPE_RG; 43562306a36Sopenharmony_ci default: break; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (dcbe->location != DCB_LOC_ON_CHIP) 43962306a36Sopenharmony_ci return NV50_CRC_SOURCE_TYPE_PIOR; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci switch (dcbe->type) { 44262306a36Sopenharmony_ci case DCB_OUTPUT_DP: return NV50_CRC_SOURCE_TYPE_SF; 44362306a36Sopenharmony_ci case DCB_OUTPUT_ANALOG: return NV50_CRC_SOURCE_TYPE_DAC; 44462306a36Sopenharmony_ci default: return NV50_CRC_SOURCE_TYPE_SOR; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_civoid nv50_crc_atomic_set(struct nv50_head *head, 44962306a36Sopenharmony_ci struct nv50_head_atom *asyh) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 45262306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 45362306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 45462306a36Sopenharmony_ci const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; 45562306a36Sopenharmony_ci struct nouveau_encoder *outp; 45662306a36Sopenharmony_ci struct drm_encoder *encoder; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci encoder = nv50_head_atom_get_encoder(asyh); 45962306a36Sopenharmony_ci if (!encoder) 46062306a36Sopenharmony_ci return; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci outp = nv50_real_outp(encoder); 46362306a36Sopenharmony_ci if (!outp) 46462306a36Sopenharmony_ci return; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci func->set_src(head, outp->outp.or.id, nv50_crc_source_type(outp, asyh->crc.src), 46762306a36Sopenharmony_ci &crc->ctx[crc->ctx_idx]); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_civoid nv50_crc_atomic_clr(struct nv50_head *head) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci const struct nv50_crc_func *func = 47362306a36Sopenharmony_ci nv50_disp(head->base.base.dev)->core->func->crc; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE, NULL); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic inline int 47962306a36Sopenharmony_cinv50_crc_raster_type(enum nv50_crc_source source) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci switch (source) { 48262306a36Sopenharmony_ci case NV50_CRC_SOURCE_NONE: 48362306a36Sopenharmony_ci case NV50_CRC_SOURCE_AUTO: 48462306a36Sopenharmony_ci case NV50_CRC_SOURCE_RG: 48562306a36Sopenharmony_ci case NV50_CRC_SOURCE_OUTP_ACTIVE: 48662306a36Sopenharmony_ci return NV907D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_CRC_MODE_ACTIVE_RASTER; 48762306a36Sopenharmony_ci case NV50_CRC_SOURCE_OUTP_COMPLETE: 48862306a36Sopenharmony_ci return NV907D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_CRC_MODE_COMPLETE_RASTER; 48962306a36Sopenharmony_ci case NV50_CRC_SOURCE_OUTP_INACTIVE: 49062306a36Sopenharmony_ci return NV907D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_CRC_MODE_NON_ACTIVE_RASTER; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/* We handle mapping the memory for CRC notifiers ourselves, since each 49762306a36Sopenharmony_ci * notifier needs it's own handle 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_cistatic inline int 50062306a36Sopenharmony_cinv50_crc_ctx_init(struct nv50_head *head, struct nvif_mmu *mmu, 50162306a36Sopenharmony_ci struct nv50_crc_notifier_ctx *ctx, size_t len, int idx) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct nv50_core *core = nv50_disp(head->base.base.dev)->core; 50462306a36Sopenharmony_ci int ret; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = nvif_mem_ctor_map(mmu, "kmsCrcNtfy", NVIF_MEM_VRAM, len, &ctx->mem); 50762306a36Sopenharmony_ci if (ret) 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ret = nvif_object_ctor(&core->chan.base.user, "kmsCrcNtfyCtxDma", 51162306a36Sopenharmony_ci NV50_DISP_HANDLE_CRC_CTX(head, idx), 51262306a36Sopenharmony_ci NV_DMA_IN_MEMORY, 51362306a36Sopenharmony_ci &(struct nv_dma_v0) { 51462306a36Sopenharmony_ci .target = NV_DMA_V0_TARGET_VRAM, 51562306a36Sopenharmony_ci .access = NV_DMA_V0_ACCESS_RDWR, 51662306a36Sopenharmony_ci .start = ctx->mem.addr, 51762306a36Sopenharmony_ci .limit = ctx->mem.addr 51862306a36Sopenharmony_ci + ctx->mem.size - 1, 51962306a36Sopenharmony_ci }, sizeof(struct nv_dma_v0), 52062306a36Sopenharmony_ci &ctx->ntfy); 52162306a36Sopenharmony_ci if (ret) 52262306a36Sopenharmony_ci goto fail_fini; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cifail_fini: 52762306a36Sopenharmony_ci nvif_mem_dtor(&ctx->mem); 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic inline void 53262306a36Sopenharmony_cinv50_crc_ctx_fini(struct nv50_crc_notifier_ctx *ctx) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci nvif_object_dtor(&ctx->ntfy); 53562306a36Sopenharmony_ci nvif_mem_dtor(&ctx->mem); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ciint nv50_crc_set_source(struct drm_crtc *crtc, const char *source_str) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 54162306a36Sopenharmony_ci struct drm_atomic_state *state; 54262306a36Sopenharmony_ci struct drm_modeset_acquire_ctx ctx; 54362306a36Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 54462306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 54562306a36Sopenharmony_ci const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; 54662306a36Sopenharmony_ci struct nvif_mmu *mmu = &nouveau_drm(dev)->client.mmu; 54762306a36Sopenharmony_ci struct nv50_head_atom *asyh; 54862306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 54962306a36Sopenharmony_ci enum nv50_crc_source source; 55062306a36Sopenharmony_ci int ret = 0, ctx_flags = 0, i; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ret = nv50_crc_parse_source(source_str, &source); 55362306a36Sopenharmony_ci if (ret) 55462306a36Sopenharmony_ci return ret; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* 55762306a36Sopenharmony_ci * Since we don't want the user to accidentally interrupt us as we're 55862306a36Sopenharmony_ci * disabling CRCs 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ci if (source) 56162306a36Sopenharmony_ci ctx_flags |= DRM_MODESET_ACQUIRE_INTERRUPTIBLE; 56262306a36Sopenharmony_ci drm_modeset_acquire_init(&ctx, ctx_flags); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci state = drm_atomic_state_alloc(dev); 56562306a36Sopenharmony_ci if (!state) { 56662306a36Sopenharmony_ci ret = -ENOMEM; 56762306a36Sopenharmony_ci goto out_acquire_fini; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci state->acquire_ctx = &ctx; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (source) { 57262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(head->crc.ctx); i++) { 57362306a36Sopenharmony_ci ret = nv50_crc_ctx_init(head, mmu, &crc->ctx[i], 57462306a36Sopenharmony_ci func->notifier_len, i); 57562306a36Sopenharmony_ci if (ret) 57662306a36Sopenharmony_ci goto out_ctx_fini; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ciretry: 58162306a36Sopenharmony_ci crtc_state = drm_atomic_get_crtc_state(state, &head->base.base); 58262306a36Sopenharmony_ci if (IS_ERR(crtc_state)) { 58362306a36Sopenharmony_ci ret = PTR_ERR(crtc_state); 58462306a36Sopenharmony_ci if (ret == -EDEADLK) 58562306a36Sopenharmony_ci goto deadlock; 58662306a36Sopenharmony_ci else if (ret) 58762306a36Sopenharmony_ci goto out_drop_locks; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci asyh = nv50_head_atom(crtc_state); 59062306a36Sopenharmony_ci asyh->crc.src = source; 59162306a36Sopenharmony_ci asyh->or.crc_raster = nv50_crc_raster_type(source); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci ret = drm_atomic_commit(state); 59462306a36Sopenharmony_ci if (ret == -EDEADLK) 59562306a36Sopenharmony_ci goto deadlock; 59662306a36Sopenharmony_ci else if (ret) 59762306a36Sopenharmony_ci goto out_drop_locks; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (!source) { 60062306a36Sopenharmony_ci /* 60162306a36Sopenharmony_ci * If the user specified a custom flip threshold through 60262306a36Sopenharmony_ci * debugfs, reset it 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci crc->flip_threshold = func->flip_threshold; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ciout_drop_locks: 60862306a36Sopenharmony_ci drm_modeset_drop_locks(&ctx); 60962306a36Sopenharmony_ciout_ctx_fini: 61062306a36Sopenharmony_ci if (!source || ret) { 61162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) 61262306a36Sopenharmony_ci nv50_crc_ctx_fini(&crc->ctx[i]); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci drm_atomic_state_put(state); 61562306a36Sopenharmony_ciout_acquire_fini: 61662306a36Sopenharmony_ci drm_modeset_acquire_fini(&ctx); 61762306a36Sopenharmony_ci return ret; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cideadlock: 62062306a36Sopenharmony_ci drm_atomic_state_clear(state); 62162306a36Sopenharmony_ci drm_modeset_backoff(&ctx); 62262306a36Sopenharmony_ci goto retry; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic int 62662306a36Sopenharmony_cinv50_crc_debugfs_flip_threshold_get(struct seq_file *m, void *data) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct nv50_head *head = m->private; 62962306a36Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 63062306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 63162306a36Sopenharmony_ci int ret; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ret = drm_modeset_lock_single_interruptible(&crtc->mutex); 63462306a36Sopenharmony_ci if (ret) 63562306a36Sopenharmony_ci return ret; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci seq_printf(m, "%d\n", crc->flip_threshold); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci drm_modeset_unlock(&crtc->mutex); 64062306a36Sopenharmony_ci return ret; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic int 64462306a36Sopenharmony_cinv50_crc_debugfs_flip_threshold_open(struct inode *inode, struct file *file) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci return single_open(file, nv50_crc_debugfs_flip_threshold_get, 64762306a36Sopenharmony_ci inode->i_private); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic ssize_t 65162306a36Sopenharmony_cinv50_crc_debugfs_flip_threshold_set(struct file *file, 65262306a36Sopenharmony_ci const char __user *ubuf, size_t len, 65362306a36Sopenharmony_ci loff_t *offp) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct seq_file *m = file->private_data; 65662306a36Sopenharmony_ci struct nv50_head *head = m->private; 65762306a36Sopenharmony_ci struct nv50_head_atom *armh; 65862306a36Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 65962306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(crtc->dev); 66062306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 66162306a36Sopenharmony_ci const struct nv50_crc_func *func = 66262306a36Sopenharmony_ci nv50_disp(crtc->dev)->core->func->crc; 66362306a36Sopenharmony_ci int value, ret; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ret = kstrtoint_from_user(ubuf, len, 10, &value); 66662306a36Sopenharmony_ci if (ret) 66762306a36Sopenharmony_ci return ret; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (value > func->flip_threshold) 67062306a36Sopenharmony_ci return -EINVAL; 67162306a36Sopenharmony_ci else if (value == -1) 67262306a36Sopenharmony_ci value = func->flip_threshold; 67362306a36Sopenharmony_ci else if (value < -1) 67462306a36Sopenharmony_ci return -EINVAL; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = drm_modeset_lock_single_interruptible(&crtc->mutex); 67762306a36Sopenharmony_ci if (ret) 67862306a36Sopenharmony_ci return ret; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci armh = nv50_head_atom(crtc->state); 68162306a36Sopenharmony_ci if (armh->crc.src) { 68262306a36Sopenharmony_ci ret = -EBUSY; 68362306a36Sopenharmony_ci goto out; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci NV_DEBUG(drm, 68762306a36Sopenharmony_ci "Changing CRC flip threshold for next capture on head-%d to %d\n", 68862306a36Sopenharmony_ci head->base.index, value); 68962306a36Sopenharmony_ci crc->flip_threshold = value; 69062306a36Sopenharmony_ci ret = len; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ciout: 69362306a36Sopenharmony_ci drm_modeset_unlock(&crtc->mutex); 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic const struct file_operations nv50_crc_flip_threshold_fops = { 69862306a36Sopenharmony_ci .owner = THIS_MODULE, 69962306a36Sopenharmony_ci .open = nv50_crc_debugfs_flip_threshold_open, 70062306a36Sopenharmony_ci .read = seq_read, 70162306a36Sopenharmony_ci .write = nv50_crc_debugfs_flip_threshold_set, 70262306a36Sopenharmony_ci .release = single_release, 70362306a36Sopenharmony_ci}; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ciint nv50_head_crc_late_register(struct nv50_head *head) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct drm_crtc *crtc = &head->base.base; 70862306a36Sopenharmony_ci const struct nv50_crc_func *func = 70962306a36Sopenharmony_ci nv50_disp(crtc->dev)->core->func->crc; 71062306a36Sopenharmony_ci struct dentry *root; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (!func || !crtc->debugfs_entry) 71362306a36Sopenharmony_ci return 0; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci root = debugfs_create_dir("nv_crc", crtc->debugfs_entry); 71662306a36Sopenharmony_ci debugfs_create_file("flip_threshold", 0644, root, head, 71762306a36Sopenharmony_ci &nv50_crc_flip_threshold_fops); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return 0; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic inline void 72362306a36Sopenharmony_cinv50_crc_init_head(struct nv50_disp *disp, const struct nv50_crc_func *func, 72462306a36Sopenharmony_ci struct nv50_head *head) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct nv50_crc *crc = &head->crc; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci crc->flip_threshold = func->flip_threshold; 72962306a36Sopenharmony_ci spin_lock_init(&crc->lock); 73062306a36Sopenharmony_ci drm_vblank_work_init(&crc->flip_work, &head->base.base, 73162306a36Sopenharmony_ci nv50_crc_ctx_flip_work); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_civoid nv50_crc_init(struct drm_device *dev) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct nv50_disp *disp = nv50_disp(dev); 73762306a36Sopenharmony_ci struct drm_crtc *crtc; 73862306a36Sopenharmony_ci const struct nv50_crc_func *func = disp->core->func->crc; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (!func) 74162306a36Sopenharmony_ci return; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) 74462306a36Sopenharmony_ci nv50_crc_init_head(disp, func, nv50_head(crtc)); 74562306a36Sopenharmony_ci} 746