1 // SPDX-License-Identifier: MIT
2 #include <drm/drm_crtc.h>
3 
4 #include "crc.h"
5 #include "core.h"
6 #include "disp.h"
7 #include "head.h"
8 
9 #define CRCC37D_MAX_ENTRIES 2047
10 
11 struct crcc37d_notifier {
12 	u32 status;
13 
14 	/* reserved */
15 	u32 :32;
16 	u32 :32;
17 	u32 :32;
18 	u32 :32;
19 	u32 :32;
20 	u32 :32;
21 	u32 :32;
22 
23 	struct crcc37d_entry {
24 		u32 status[2];
25 		u32 :32; /* reserved */
26 		u32 compositor_crc;
27 		u32 rg_crc;
28 		u32 output_crc[2];
29 		u32 :32; /* reserved */
30 	} entries[CRCC37D_MAX_ENTRIES];
31 } __packed;
32 
33 static void
34 crcc37d_set_src(struct nv50_head *head, int or,
35 		enum nv50_crc_source_type source,
36 		struct nv50_crc_notifier_ctx *ctx, u32 wndw)
37 {
38 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
39 	const u32 hoff = head->base.index * 0x400;
40 	u32 *push;
41 	u32 crc_args;
42 
43 	switch (source) {
44 	case NV50_CRC_SOURCE_TYPE_SOR:
45 		crc_args = (0x00000050 + or) << 12;
46 		break;
47 	case NV50_CRC_SOURCE_TYPE_PIOR:
48 		crc_args = (0x00000060 + or) << 12;
49 		break;
50 	case NV50_CRC_SOURCE_TYPE_SF:
51 		crc_args = 0x00000030 << 12;
52 		break;
53 	default:
54 		crc_args = 0;
55 		break;
56 	}
57 
58 	push = evo_wait(core, 4);
59 	if (!push)
60 		return;
61 
62 	if (source) {
63 		evo_mthd(push, 0x2180 + hoff, 1);
64 		evo_data(push, ctx->ntfy.handle);
65 		evo_mthd(push, 0x2184 + hoff, 1);
66 		evo_data(push, crc_args | wndw);
67 	} else {
68 		evo_mthd(push, 0x2184 + hoff, 1);
69 		evo_data(push, 0);
70 		evo_mthd(push, 0x2180 + hoff, 1);
71 		evo_data(push, 0);
72 	}
73 
74 	evo_kick(push, core);
75 }
76 
77 static void crcc37d_set_ctx(struct nv50_head *head,
78 			    struct nv50_crc_notifier_ctx *ctx)
79 {
80 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
81 	u32 *push = evo_wait(core, 2);
82 
83 	if (!push)
84 		return;
85 
86 	evo_mthd(push, 0x2180 + (head->base.index * 0x400), 1);
87 	evo_data(push, ctx ? ctx->ntfy.handle : 0);
88 	evo_kick(push, core);
89 }
90 
91 static u32 crcc37d_get_entry(struct nv50_head *head,
92 			     struct nv50_crc_notifier_ctx *ctx,
93 			     enum nv50_crc_source source, int idx)
94 {
95 	struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr;
96 	struct crcc37d_entry __iomem *entry = &notifier->entries[idx];
97 	u32 __iomem *crc_addr;
98 
99 	if (source == NV50_CRC_SOURCE_RG)
100 		crc_addr = &entry->rg_crc;
101 	else
102 		crc_addr = &entry->output_crc[0];
103 
104 	return ioread32_native(crc_addr);
105 }
106 
107 static bool crcc37d_ctx_finished(struct nv50_head *head,
108 				 struct nv50_crc_notifier_ctx *ctx)
109 {
110 	struct nouveau_drm *drm = nouveau_drm(head->base.base.dev);
111 	struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr;
112 	const u32 status = ioread32_native(&notifier->status);
113 	const u32 overflow = status & 0x0000007e;
114 
115 	if (!(status & 0x00000001))
116 		return false;
117 
118 	if (overflow) {
119 		const char *engine = NULL;
120 
121 		switch (overflow) {
122 		case 0x00000004: engine = "Front End"; break;
123 		case 0x00000008: engine = "Compositor"; break;
124 		case 0x00000010: engine = "RG"; break;
125 		case 0x00000020: engine = "CRC output 1"; break;
126 		case 0x00000040: engine = "CRC output 2"; break;
127 		}
128 
129 		if (engine)
130 			NV_ERROR(drm,
131 				 "CRC notifier context for head %d overflowed on %s: %x\n",
132 				 head->base.index, engine, status);
133 		else
134 			NV_ERROR(drm,
135 				 "CRC notifier context for head %d overflowed: %x\n",
136 				 head->base.index, status);
137 	}
138 
139 	NV_DEBUG(drm, "Head %d CRC context status: %x\n",
140 		 head->base.index, status);
141 
142 	return true;
143 }
144 
145 const struct nv50_crc_func crcc37d = {
146 	.set_src = crcc37d_set_src,
147 	.set_ctx = crcc37d_set_ctx,
148 	.get_entry = crcc37d_get_entry,
149 	.ctx_finished = crcc37d_ctx_finished,
150 	.flip_threshold = CRCC37D_MAX_ENTRIES - 30,
151 	.num_entries = CRCC37D_MAX_ENTRIES,
152 	.notifier_len = sizeof(struct crcc37d_notifier),
153 };
154