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