1 // SPDX-License-Identifier: MIT 2 #include <drm/drm_crtc.h> 3 4 #include "crc.h" 5 #include "crcc37d.h" 6 #include "core.h" 7 #include "disp.h" 8 #include "head.h" 9 10 #include <nvif/pushc37b.h> 11 12 #include <nvhw/class/clc37d.h> 13 14 static int 15 crcc37d_set_src(struct nv50_head *head, int or, enum nv50_crc_source_type source, 16 struct nv50_crc_notifier_ctx *ctx) 17 { 18 struct nvif_push *push = &nv50_disp(head->base.base.dev)->core->chan.push; 19 const int i = head->base.index; 20 u32 crc_args = NVVAL(NVC37D, HEAD_SET_CRC_CONTROL, CONTROLLING_CHANNEL, i * 4) | 21 NVDEF(NVC37D, HEAD_SET_CRC_CONTROL, EXPECT_BUFFER_COLLAPSE, FALSE) | 22 NVDEF(NVC37D, HEAD_SET_CRC_CONTROL, SECONDARY_CRC, NONE) | 23 NVDEF(NVC37D, HEAD_SET_CRC_CONTROL, CRC_DURING_SNOOZE, DISABLE); 24 int ret; 25 26 switch (source) { 27 case NV50_CRC_SOURCE_TYPE_SOR: 28 crc_args |= NVDEF(NVC37D, HEAD_SET_CRC_CONTROL, PRIMARY_CRC, SOR(or)); 29 break; 30 case NV50_CRC_SOURCE_TYPE_PIOR: 31 crc_args |= NVDEF(NVC37D, HEAD_SET_CRC_CONTROL, PRIMARY_CRC, PIOR(or)); 32 break; 33 case NV50_CRC_SOURCE_TYPE_SF: 34 crc_args |= NVDEF(NVC37D, HEAD_SET_CRC_CONTROL, PRIMARY_CRC, SF); 35 break; 36 default: 37 break; 38 } 39 40 if ((ret = PUSH_WAIT(push, 4))) 41 return ret; 42 43 if (source) { 44 PUSH_MTHD(push, NVC37D, HEAD_SET_CONTEXT_DMA_CRC(i), ctx->ntfy.handle); 45 PUSH_MTHD(push, NVC37D, HEAD_SET_CRC_CONTROL(i), crc_args); 46 } else { 47 PUSH_MTHD(push, NVC37D, HEAD_SET_CRC_CONTROL(i), 0); 48 PUSH_MTHD(push, NVC37D, HEAD_SET_CONTEXT_DMA_CRC(i), 0); 49 } 50 51 return 0; 52 } 53 54 int crcc37d_set_ctx(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx) 55 { 56 struct nvif_push *push = &nv50_disp(head->base.base.dev)->core->chan.push; 57 const int i = head->base.index; 58 int ret; 59 60 if ((ret = PUSH_WAIT(push, 2))) 61 return ret; 62 63 PUSH_MTHD(push, NVC37D, HEAD_SET_CONTEXT_DMA_CRC(i), ctx ? ctx->ntfy.handle : 0); 64 return 0; 65 } 66 67 u32 crcc37d_get_entry(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx, 68 enum nv50_crc_source source, int idx) 69 { 70 struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr; 71 struct crcc37d_entry __iomem *entry = ¬ifier->entries[idx]; 72 u32 __iomem *crc_addr; 73 74 if (source == NV50_CRC_SOURCE_RG) 75 crc_addr = &entry->rg_crc; 76 else 77 crc_addr = &entry->output_crc[0]; 78 79 return ioread32_native(crc_addr); 80 } 81 82 bool crcc37d_ctx_finished(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx) 83 { 84 struct nouveau_drm *drm = nouveau_drm(head->base.base.dev); 85 struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr; 86 const u32 status = ioread32_native(¬ifier->status); 87 const u32 overflow = status & 0x0000007e; 88 89 if (!(status & 0x00000001)) 90 return false; 91 92 if (overflow) { 93 const char *engine = NULL; 94 95 switch (overflow) { 96 case 0x00000004: engine = "Front End"; break; 97 case 0x00000008: engine = "Compositor"; break; 98 case 0x00000010: engine = "RG"; break; 99 case 0x00000020: engine = "CRC output 1"; break; 100 case 0x00000040: engine = "CRC output 2"; break; 101 } 102 103 if (engine) 104 NV_ERROR(drm, 105 "CRC notifier context for head %d overflowed on %s: %x\n", 106 head->base.index, engine, status); 107 else 108 NV_ERROR(drm, 109 "CRC notifier context for head %d overflowed: %x\n", 110 head->base.index, status); 111 } 112 113 NV_DEBUG(drm, "Head %d CRC context status: %x\n", 114 head->base.index, status); 115 116 return true; 117 } 118 119 const struct nv50_crc_func crcc37d = { 120 .set_src = crcc37d_set_src, 121 .set_ctx = crcc37d_set_ctx, 122 .get_entry = crcc37d_get_entry, 123 .ctx_finished = crcc37d_ctx_finished, 124 .flip_threshold = CRCC37D_FLIP_THRESHOLD, 125 .num_entries = CRCC37D_MAX_ENTRIES, 126 .notifier_len = sizeof(struct crcc37d_notifier), 127 }; 128