1 /* $NetBSD: intel_dsb.c,v 1.3 2021/12/19 11:38:26 riastradh Exp $ */
2
3 // SPDX-License-Identifier: MIT
4 /*
5 * Copyright © 2019 Intel Corporation
6 *
7 */
8
9 #include <sys/cdefs.h>
10 __KERNEL_RCSID(0, "$NetBSD: intel_dsb.c,v 1.3 2021/12/19 11:38:26 riastradh Exp $");
11
12 #include "i915_drv.h"
13 #include "intel_display_types.h"
14
15 #include <linux/nbsd-namespace.h>
16
17 #define DSB_BUF_SIZE (2 * PAGE_SIZE)
18
19 /**
20 * DOC: DSB
21 *
22 * A DSB (Display State Buffer) is a queue of MMIO instructions in the memory
23 * which can be offloaded to DSB HW in Display Controller. DSB HW is a DMA
24 * engine that can be programmed to download the DSB from memory.
25 * It allows driver to batch submit display HW programming. This helps to
26 * reduce loading time and CPU activity, thereby making the context switch
27 * faster. DSB Support added from Gen12 Intel graphics based platform.
28 *
29 * DSB's can access only the pipe, plane, and transcoder Data Island Packet
30 * registers.
31 *
32 * DSB HW can support only register writes (both indexed and direct MMIO
33 * writes). There are no registers reads possible with DSB HW engine.
34 */
35
36 /* DSB opcodes. */
37 #define DSB_OPCODE_SHIFT 24
38 #define DSB_OPCODE_MMIO_WRITE 0x1
39 #define DSB_OPCODE_INDEXED_WRITE 0x9
40 #define DSB_BYTE_EN 0xF
41 #define DSB_BYTE_EN_SHIFT 20
42 #define DSB_REG_VALUE_MASK 0xfffff
43
is_dsb_busy(struct intel_dsb * dsb)44 static inline bool is_dsb_busy(struct intel_dsb *dsb)
45 {
46 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb);
47 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
48 enum pipe pipe = crtc->pipe;
49
50 return DSB_STATUS & I915_READ(DSB_CTRL(pipe, dsb->id));
51 }
52
intel_dsb_enable_engine(struct intel_dsb * dsb)53 static inline bool intel_dsb_enable_engine(struct intel_dsb *dsb)
54 {
55 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb);
56 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
57 enum pipe pipe = crtc->pipe;
58 u32 dsb_ctrl;
59
60 dsb_ctrl = I915_READ(DSB_CTRL(pipe, dsb->id));
61 if (DSB_STATUS & dsb_ctrl) {
62 DRM_DEBUG_KMS("DSB engine is busy.\n");
63 return false;
64 }
65
66 dsb_ctrl |= DSB_ENABLE;
67 I915_WRITE(DSB_CTRL(pipe, dsb->id), dsb_ctrl);
68
69 POSTING_READ(DSB_CTRL(pipe, dsb->id));
70 return true;
71 }
72
intel_dsb_disable_engine(struct intel_dsb * dsb)73 static inline bool intel_dsb_disable_engine(struct intel_dsb *dsb)
74 {
75 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb);
76 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
77 enum pipe pipe = crtc->pipe;
78 u32 dsb_ctrl;
79
80 dsb_ctrl = I915_READ(DSB_CTRL(pipe, dsb->id));
81 if (DSB_STATUS & dsb_ctrl) {
82 DRM_DEBUG_KMS("DSB engine is busy.\n");
83 return false;
84 }
85
86 dsb_ctrl &= ~DSB_ENABLE;
87 I915_WRITE(DSB_CTRL(pipe, dsb->id), dsb_ctrl);
88
89 POSTING_READ(DSB_CTRL(pipe, dsb->id));
90 return true;
91 }
92
93 /**
94 * intel_dsb_get() - Allocate DSB context and return a DSB instance.
95 * @crtc: intel_crtc structure to get pipe info.
96 *
97 * This function provides handle of a DSB instance, for the further DSB
98 * operations.
99 *
100 * Returns: address of Intel_dsb instance requested for.
101 * Failure: Returns the same DSB instance, but without a command buffer.
102 */
103
104 struct intel_dsb *
intel_dsb_get(struct intel_crtc * crtc)105 intel_dsb_get(struct intel_crtc *crtc)
106 {
107 struct drm_device *dev = crtc->base.dev;
108 struct drm_i915_private *i915 = to_i915(dev);
109 struct intel_dsb *dsb = &crtc->dsb;
110 struct drm_i915_gem_object *obj;
111 struct i915_vma *vma;
112 u32 *buf;
113 intel_wakeref_t wakeref;
114
115 if (!HAS_DSB(i915))
116 return dsb;
117
118 if (dsb->refcount++ != 0)
119 return dsb;
120
121 wakeref = intel_runtime_pm_get(&i915->runtime_pm);
122
123 obj = i915_gem_object_create_internal(i915, DSB_BUF_SIZE);
124 if (IS_ERR(obj)) {
125 DRM_ERROR("Gem object creation failed\n");
126 goto out;
127 }
128
129 vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0);
130 if (IS_ERR(vma)) {
131 DRM_ERROR("Vma creation failed\n");
132 i915_gem_object_put(obj);
133 goto out;
134 }
135
136 buf = i915_gem_object_pin_map(vma->obj, I915_MAP_WC);
137 if (IS_ERR(buf)) {
138 DRM_ERROR("Command buffer creation failed\n");
139 goto out;
140 }
141
142 dsb->id = DSB1;
143 dsb->vma = vma;
144 dsb->cmd_buf = buf;
145
146 out:
147 /*
148 * On error dsb->cmd_buf will continue to be NULL, making the writes
149 * pass-through. Leave the dangling ref to be removed later by the
150 * corresponding intel_dsb_put(): the important error message will
151 * already be logged above.
152 */
153
154 intel_runtime_pm_put(&i915->runtime_pm, wakeref);
155
156 return dsb;
157 }
158
159 /**
160 * intel_dsb_put() - To destroy DSB context.
161 * @dsb: intel_dsb structure.
162 *
163 * This function destroys the DSB context allocated by a dsb_get(), by
164 * unpinning and releasing the VMA object associated with it.
165 */
166
intel_dsb_put(struct intel_dsb * dsb)167 void intel_dsb_put(struct intel_dsb *dsb)
168 {
169 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb);
170 struct drm_i915_private *i915 = to_i915(crtc->base.dev);
171
172 if (!HAS_DSB(i915))
173 return;
174
175 if (WARN_ON(dsb->refcount == 0))
176 return;
177
178 if (--dsb->refcount == 0) {
179 i915_vma_unpin_and_release(&dsb->vma, I915_VMA_RELEASE_MAP);
180 dsb->cmd_buf = NULL;
181 dsb->free_pos = 0;
182 dsb->ins_start_offset = 0;
183 }
184 }
185
186 /**
187 * intel_dsb_indexed_reg_write() -Write to the DSB context for auto
188 * increment register.
189 * @dsb: intel_dsb structure.
190 * @reg: register address.
191 * @val: value.
192 *
193 * This function is used for writing register-value pair in command
194 * buffer of DSB for auto-increment register. During command buffer overflow,
195 * a warning is thrown and rest all erroneous condition register programming
196 * is done through mmio write.
197 */
198
intel_dsb_indexed_reg_write(struct intel_dsb * dsb,i915_reg_t reg,u32 val)199 void intel_dsb_indexed_reg_write(struct intel_dsb *dsb, i915_reg_t reg,
200 u32 val)
201 {
202 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb);
203 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
204 u32 *buf = dsb->cmd_buf;
205 u32 reg_val;
206
207 if (!buf) {
208 I915_WRITE(reg, val);
209 return;
210 }
211
212 if (WARN_ON(dsb->free_pos >= DSB_BUF_SIZE)) {
213 DRM_DEBUG_KMS("DSB buffer overflow\n");
214 return;
215 }
216
217 /*
218 * For example the buffer will look like below for 3 dwords for auto
219 * increment register:
220 * +--------------------------------------------------------+
221 * | size = 3 | offset &| value1 | value2 | value3 | zero |
222 * | | opcode | | | | |
223 * +--------------------------------------------------------+
224 * + + + + + + +
225 * 0 4 8 12 16 20 24
226 * Byte
227 *
228 * As every instruction is 8 byte aligned the index of dsb instruction
229 * will start always from even number while dealing with u32 array. If
230 * we are writing odd no of dwords, Zeros will be added in the end for
231 * padding.
232 */
233 reg_val = buf[dsb->ins_start_offset + 1] & DSB_REG_VALUE_MASK;
234 if (reg_val != i915_mmio_reg_offset(reg)) {
235 /* Every instruction should be 8 byte aligned. */
236 dsb->free_pos = ALIGN(dsb->free_pos, 2);
237
238 dsb->ins_start_offset = dsb->free_pos;
239
240 /* Update the size. */
241 buf[dsb->free_pos++] = 1;
242
243 /* Update the opcode and reg. */
244 buf[dsb->free_pos++] = (DSB_OPCODE_INDEXED_WRITE <<
245 DSB_OPCODE_SHIFT) |
246 i915_mmio_reg_offset(reg);
247
248 /* Update the value. */
249 buf[dsb->free_pos++] = val;
250 } else {
251 /* Update the new value. */
252 buf[dsb->free_pos++] = val;
253
254 /* Update the size. */
255 buf[dsb->ins_start_offset]++;
256 }
257
258 /* if number of data words is odd, then the last dword should be 0.*/
259 if (dsb->free_pos & 0x1)
260 buf[dsb->free_pos] = 0;
261 }
262
263 /**
264 * intel_dsb_reg_write() -Write to the DSB context for normal
265 * register.
266 * @dsb: intel_dsb structure.
267 * @reg: register address.
268 * @val: value.
269 *
270 * This function is used for writing register-value pair in command
271 * buffer of DSB. During command buffer overflow, a warning is thrown
272 * and rest all erroneous condition register programming is done
273 * through mmio write.
274 */
intel_dsb_reg_write(struct intel_dsb * dsb,i915_reg_t reg,u32 val)275 void intel_dsb_reg_write(struct intel_dsb *dsb, i915_reg_t reg, u32 val)
276 {
277 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb);
278 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
279 u32 *buf = dsb->cmd_buf;
280
281 if (!buf) {
282 I915_WRITE(reg, val);
283 return;
284 }
285
286 if (WARN_ON(dsb->free_pos >= DSB_BUF_SIZE)) {
287 DRM_DEBUG_KMS("DSB buffer overflow\n");
288 return;
289 }
290
291 dsb->ins_start_offset = dsb->free_pos;
292 buf[dsb->free_pos++] = val;
293 buf[dsb->free_pos++] = (DSB_OPCODE_MMIO_WRITE << DSB_OPCODE_SHIFT) |
294 (DSB_BYTE_EN << DSB_BYTE_EN_SHIFT) |
295 i915_mmio_reg_offset(reg);
296 }
297
298 /**
299 * intel_dsb_commit() - Trigger workload execution of DSB.
300 * @dsb: intel_dsb structure.
301 *
302 * This function is used to do actual write to hardware using DSB.
303 * On errors, fall back to MMIO. Also this function help to reset the context.
304 */
intel_dsb_commit(struct intel_dsb * dsb)305 void intel_dsb_commit(struct intel_dsb *dsb)
306 {
307 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb);
308 struct drm_device *dev = crtc->base.dev;
309 struct drm_i915_private *dev_priv = to_i915(dev);
310 enum pipe pipe = crtc->pipe;
311 u32 tail;
312
313 if (!dsb->free_pos)
314 return;
315
316 if (!intel_dsb_enable_engine(dsb))
317 goto reset;
318
319 if (is_dsb_busy(dsb)) {
320 DRM_ERROR("HEAD_PTR write failed - dsb engine is busy.\n");
321 goto reset;
322 }
323 I915_WRITE(DSB_HEAD(pipe, dsb->id), i915_ggtt_offset(dsb->vma));
324
325 tail = ALIGN(dsb->free_pos * 4, CACHELINE_BYTES);
326 if (tail > dsb->free_pos * 4)
327 memset(&dsb->cmd_buf[dsb->free_pos], 0,
328 (tail - dsb->free_pos * 4));
329
330 if (is_dsb_busy(dsb)) {
331 DRM_ERROR("TAIL_PTR write failed - dsb engine is busy.\n");
332 goto reset;
333 }
334 DRM_DEBUG_KMS("DSB execution started - head 0x%x, tail 0x%x\n",
335 i915_ggtt_offset(dsb->vma), tail);
336 I915_WRITE(DSB_TAIL(pipe, dsb->id), i915_ggtt_offset(dsb->vma) + tail);
337 if (wait_for(!is_dsb_busy(dsb), 1)) {
338 DRM_ERROR("Timed out waiting for DSB workload completion.\n");
339 goto reset;
340 }
341
342 reset:
343 dsb->free_pos = 0;
344 dsb->ins_start_offset = 0;
345 intel_dsb_disable_engine(dsb);
346 }
347