xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/i915/display/intel_dsb.c (revision fca35a155675ec4f811854d5ba44c373b18272f9)
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