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