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