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 | #include <nvif/push507c.h> |
10 | |
11 | #include <nvhw/class/cl907d.h> |
12 | |
13 | #define CRC907D_MAX_ENTRIES 255 |
14 | |
15 | struct crc907d_notifier { |
16 | u32 status; |
17 | u32 :32; /* reserved */ |
18 | struct crc907d_entry { |
19 | u32 status; |
20 | u32 compositor_crc; |
21 | u32 output_crc[2]; |
22 | } entries[CRC907D_MAX_ENTRIES]; |
23 | } __packed; |
24 | |
25 | static int |
26 | crc907d_set_src(struct nv50_head *head, int or, enum nv50_crc_source_type source, |
27 | struct nv50_crc_notifier_ctx *ctx) |
28 | { |
29 | struct nvif_push *push = nv50_disp(dev: head->base.base.dev)->core->chan.push; |
30 | const int i = head->base.index; |
31 | u32 crc_args = NVDEF(NV907D, HEAD_SET_CRC_CONTROL, CONTROLLING_CHANNEL, CORE) | |
32 | NVDEF(NV907D, HEAD_SET_CRC_CONTROL, EXPECT_BUFFER_COLLAPSE, FALSE) | |
33 | NVDEF(NV907D, HEAD_SET_CRC_CONTROL, TIMESTAMP_MODE, FALSE) | |
34 | NVDEF(NV907D, HEAD_SET_CRC_CONTROL, SECONDARY_OUTPUT, NONE) | |
35 | NVDEF(NV907D, HEAD_SET_CRC_CONTROL, CRC_DURING_SNOOZE, DISABLE) | |
36 | NVDEF(NV907D, HEAD_SET_CRC_CONTROL, WIDE_PIPE_CRC, ENABLE); |
37 | int ret; |
38 | |
39 | switch (source) { |
40 | case NV50_CRC_SOURCE_TYPE_SOR: |
41 | crc_args |= NVDEF(NV907D, HEAD_SET_CRC_CONTROL, PRIMARY_OUTPUT, SOR(or)); |
42 | break; |
43 | case NV50_CRC_SOURCE_TYPE_PIOR: |
44 | crc_args |= NVDEF(NV907D, HEAD_SET_CRC_CONTROL, PRIMARY_OUTPUT, PIOR(or)); |
45 | break; |
46 | case NV50_CRC_SOURCE_TYPE_DAC: |
47 | crc_args |= NVDEF(NV907D, HEAD_SET_CRC_CONTROL, PRIMARY_OUTPUT, DAC(or)); |
48 | break; |
49 | case NV50_CRC_SOURCE_TYPE_RG: |
50 | crc_args |= NVDEF(NV907D, HEAD_SET_CRC_CONTROL, PRIMARY_OUTPUT, RG(i)); |
51 | break; |
52 | case NV50_CRC_SOURCE_TYPE_SF: |
53 | crc_args |= NVDEF(NV907D, HEAD_SET_CRC_CONTROL, PRIMARY_OUTPUT, SF(i)); |
54 | break; |
55 | case NV50_CRC_SOURCE_NONE: |
56 | crc_args |= NVDEF(NV907D, HEAD_SET_CRC_CONTROL, PRIMARY_OUTPUT, NONE); |
57 | break; |
58 | } |
59 | |
60 | if ((ret = PUSH_WAIT(push, 4))) |
61 | return ret; |
62 | |
63 | if (source) { |
64 | PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_CRC(i), ctx->ntfy.handle); |
65 | PUSH_MTHD(push, NV907D, HEAD_SET_CRC_CONTROL(i), crc_args); |
66 | } else { |
67 | PUSH_MTHD(push, NV907D, HEAD_SET_CRC_CONTROL(i), crc_args); |
68 | PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_CRC(i), 0); |
69 | } |
70 | |
71 | return 0; |
72 | } |
73 | |
74 | static int |
75 | crc907d_set_ctx(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx) |
76 | { |
77 | struct nvif_push *push = nv50_disp(dev: head->base.base.dev)->core->chan.push; |
78 | const int i = head->base.index; |
79 | int ret; |
80 | |
81 | if ((ret = PUSH_WAIT(push, 2))) |
82 | return ret; |
83 | |
84 | PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_CRC(i), ctx ? ctx->ntfy.handle : 0); |
85 | return 0; |
86 | } |
87 | |
88 | static u32 crc907d_get_entry(struct nv50_head *head, |
89 | struct nv50_crc_notifier_ctx *ctx, |
90 | enum nv50_crc_source source, int idx) |
91 | { |
92 | struct crc907d_notifier __iomem *notifier = ctx->mem.object.map.ptr; |
93 | |
94 | return ioread32_native(¬ifier->entries[idx].output_crc[0]); |
95 | } |
96 | |
97 | static bool crc907d_ctx_finished(struct nv50_head *head, |
98 | struct nv50_crc_notifier_ctx *ctx) |
99 | { |
100 | struct nouveau_drm *drm = nouveau_drm(head->base.base.dev); |
101 | struct crc907d_notifier __iomem *notifier = ctx->mem.object.map.ptr; |
102 | const u32 status = ioread32_native(¬ifier->status); |
103 | const u32 overflow = status & 0x0000003e; |
104 | |
105 | if (!(status & 0x00000001)) |
106 | return false; |
107 | |
108 | if (overflow) { |
109 | const char *engine = NULL; |
110 | |
111 | switch (overflow) { |
112 | case 0x00000004: engine = "DSI" ; break; |
113 | case 0x00000008: engine = "Compositor" ; break; |
114 | case 0x00000010: engine = "CRC output 1" ; break; |
115 | case 0x00000020: engine = "CRC output 2" ; break; |
116 | } |
117 | |
118 | if (engine) |
119 | NV_ERROR(drm, |
120 | "CRC notifier context for head %d overflowed on %s: %x\n" , |
121 | head->base.index, engine, status); |
122 | else |
123 | NV_ERROR(drm, |
124 | "CRC notifier context for head %d overflowed: %x\n" , |
125 | head->base.index, status); |
126 | } |
127 | |
128 | NV_DEBUG(drm, "Head %d CRC context status: %x\n" , |
129 | head->base.index, status); |
130 | |
131 | return true; |
132 | } |
133 | |
134 | const struct nv50_crc_func crc907d = { |
135 | .set_src = crc907d_set_src, |
136 | .set_ctx = crc907d_set_ctx, |
137 | .get_entry = crc907d_get_entry, |
138 | .ctx_finished = crc907d_ctx_finished, |
139 | .flip_threshold = CRC907D_MAX_ENTRIES - 10, |
140 | .num_entries = CRC907D_MAX_ENTRIES, |
141 | .notifier_len = sizeof(struct crc907d_notifier), |
142 | }; |
143 | |