]>
Commit | Line | Data |
---|---|---|
67f3b58f AM |
1 | // SPDX-License-Identifier: MIT |
2 | /* | |
3 | * Copyright © 2019 Intel Corporation | |
4 | * | |
5 | */ | |
6 | ||
7 | #include "i915_drv.h" | |
7785ae0b | 8 | #include "intel_de.h" |
67f3b58f AM |
9 | #include "intel_display_types.h" |
10 | ||
11 | #define DSB_BUF_SIZE (2 * PAGE_SIZE) | |
12 | ||
5dd85e72 AM |
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 | ||
061489c6 JN |
30 | /* DSB opcodes. */ |
31 | #define DSB_OPCODE_SHIFT 24 | |
32 | #define DSB_OPCODE_MMIO_WRITE 0x1 | |
b27a96ad | 33 | #define DSB_OPCODE_INDEXED_WRITE 0x9 |
061489c6 JN |
34 | #define DSB_BYTE_EN 0xF |
35 | #define DSB_BYTE_EN_SHIFT 20 | |
b27a96ad | 36 | #define DSB_REG_VALUE_MASK 0xfffff |
061489c6 | 37 | |
afeda4f3 AM |
38 | static bool is_dsb_busy(struct drm_i915_private *i915, enum pipe pipe, |
39 | enum dsb_id id) | |
a6e58d9a | 40 | { |
afeda4f3 | 41 | return DSB_STATUS & intel_de_read(i915, DSB_CTRL(pipe, id)); |
a6e58d9a AM |
42 | } |
43 | ||
afeda4f3 AM |
44 | static bool intel_dsb_enable_engine(struct drm_i915_private *i915, |
45 | enum pipe pipe, enum dsb_id id) | |
f7619c47 | 46 | { |
f7619c47 AM |
47 | u32 dsb_ctrl; |
48 | ||
afeda4f3 | 49 | dsb_ctrl = intel_de_read(i915, DSB_CTRL(pipe, id)); |
f7619c47 | 50 | if (DSB_STATUS & dsb_ctrl) { |
afeda4f3 | 51 | drm_dbg_kms(&i915->drm, "DSB engine is busy.\n"); |
f7619c47 AM |
52 | return false; |
53 | } | |
54 | ||
55 | dsb_ctrl |= DSB_ENABLE; | |
afeda4f3 | 56 | intel_de_write(i915, DSB_CTRL(pipe, id), dsb_ctrl); |
f7619c47 | 57 | |
afeda4f3 | 58 | intel_de_posting_read(i915, DSB_CTRL(pipe, id)); |
f7619c47 AM |
59 | return true; |
60 | } | |
61 | ||
afeda4f3 AM |
62 | static bool intel_dsb_disable_engine(struct drm_i915_private *i915, |
63 | enum pipe pipe, enum dsb_id id) | |
f7619c47 | 64 | { |
f7619c47 AM |
65 | u32 dsb_ctrl; |
66 | ||
afeda4f3 | 67 | dsb_ctrl = intel_de_read(i915, DSB_CTRL(pipe, id)); |
f7619c47 | 68 | if (DSB_STATUS & dsb_ctrl) { |
afeda4f3 | 69 | drm_dbg_kms(&i915->drm, "DSB engine is busy.\n"); |
f7619c47 AM |
70 | return false; |
71 | } | |
72 | ||
73 | dsb_ctrl &= ~DSB_ENABLE; | |
afeda4f3 | 74 | intel_de_write(i915, DSB_CTRL(pipe, id), dsb_ctrl); |
f7619c47 | 75 | |
afeda4f3 | 76 | intel_de_posting_read(i915, DSB_CTRL(pipe, id)); |
f7619c47 AM |
77 | return true; |
78 | } | |
79 | ||
5dd85e72 AM |
80 | /** |
81 | * intel_dsb_indexed_reg_write() -Write to the DSB context for auto | |
82 | * increment register. | |
afeda4f3 | 83 | * @crtc_state: intel_crtc_state structure |
5dd85e72 AM |
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 | ||
afeda4f3 AM |
93 | void intel_dsb_indexed_reg_write(const struct intel_crtc_state *crtc_state, |
94 | i915_reg_t reg, u32 val) | |
b27a96ad | 95 | { |
afeda4f3 AM |
96 | struct intel_dsb *dsb = crtc_state->dsb; |
97 | struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); | |
b27a96ad | 98 | struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); |
afeda4f3 | 99 | u32 *buf; |
b27a96ad AM |
100 | u32 reg_val; |
101 | ||
afeda4f3 | 102 | if (!dsb) { |
a98eaa8f | 103 | intel_de_write_fw(dev_priv, reg, val); |
b27a96ad AM |
104 | return; |
105 | } | |
afeda4f3 | 106 | buf = dsb->cmd_buf; |
f4224a4c | 107 | if (drm_WARN_ON(&dev_priv->drm, dsb->free_pos >= DSB_BUF_SIZE)) { |
32fc2849 | 108 | drm_dbg_kms(&dev_priv->drm, "DSB buffer overflow\n"); |
b27a96ad AM |
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 | ||
5dd85e72 AM |
158 | /** |
159 | * intel_dsb_reg_write() -Write to the DSB context for normal | |
160 | * register. | |
afeda4f3 | 161 | * @crtc_state: intel_crtc_state structure |
5dd85e72 AM |
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 | */ | |
afeda4f3 AM |
170 | void intel_dsb_reg_write(const struct intel_crtc_state *crtc_state, |
171 | i915_reg_t reg, u32 val) | |
061489c6 | 172 | { |
afeda4f3 | 173 | struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); |
061489c6 | 174 | struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); |
6f081dbf CW |
175 | struct intel_dsb *dsb; |
176 | u32 *buf; | |
061489c6 | 177 | |
6f081dbf | 178 | dsb = crtc_state->dsb; |
afeda4f3 | 179 | if (!dsb) { |
a98eaa8f | 180 | intel_de_write_fw(dev_priv, reg, val); |
061489c6 JN |
181 | return; |
182 | } | |
6f081dbf | 183 | |
afeda4f3 | 184 | buf = dsb->cmd_buf; |
f4224a4c | 185 | if (drm_WARN_ON(&dev_priv->drm, dsb->free_pos >= DSB_BUF_SIZE)) { |
32fc2849 | 186 | drm_dbg_kms(&dev_priv->drm, "DSB buffer overflow\n"); |
061489c6 | 187 | return; |
67f3b58f | 188 | } |
061489c6 | 189 | |
b27a96ad | 190 | dsb->ins_start_offset = dsb->free_pos; |
061489c6 JN |
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); | |
67f3b58f | 195 | } |
1abf329a | 196 | |
5dd85e72 AM |
197 | /** |
198 | * intel_dsb_commit() - Trigger workload execution of DSB. | |
afeda4f3 | 199 | * @crtc_state: intel_crtc_state structure |
5dd85e72 AM |
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 | */ | |
afeda4f3 | 204 | void intel_dsb_commit(const struct intel_crtc_state *crtc_state) |
1abf329a | 205 | { |
afeda4f3 AM |
206 | struct intel_dsb *dsb = crtc_state->dsb; |
207 | struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); | |
1abf329a AM |
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 | ||
afeda4f3 | 213 | if (!(dsb && dsb->free_pos)) |
1abf329a AM |
214 | return; |
215 | ||
afeda4f3 | 216 | if (!intel_dsb_enable_engine(dev_priv, pipe, dsb->id)) |
1abf329a AM |
217 | goto reset; |
218 | ||
afeda4f3 | 219 | if (is_dsb_busy(dev_priv, pipe, dsb->id)) { |
32fc2849 WK |
220 | drm_err(&dev_priv->drm, |
221 | "HEAD_PTR write failed - dsb engine is busy.\n"); | |
1abf329a AM |
222 | goto reset; |
223 | } | |
7cdccb4c JN |
224 | intel_de_write(dev_priv, DSB_HEAD(pipe, dsb->id), |
225 | i915_ggtt_offset(dsb->vma)); | |
1abf329a AM |
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 | ||
afeda4f3 | 232 | if (is_dsb_busy(dev_priv, pipe, dsb->id)) { |
32fc2849 WK |
233 | drm_err(&dev_priv->drm, |
234 | "TAIL_PTR write failed - dsb engine is busy.\n"); | |
1abf329a AM |
235 | goto reset; |
236 | } | |
32fc2849 WK |
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); | |
7cdccb4c JN |
240 | intel_de_write(dev_priv, DSB_TAIL(pipe, dsb->id), |
241 | i915_ggtt_offset(dsb->vma) + tail); | |
afeda4f3 | 242 | if (wait_for(!is_dsb_busy(dev_priv, pipe, dsb->id), 1)) { |
32fc2849 WK |
243 | drm_err(&dev_priv->drm, |
244 | "Timed out waiting for DSB workload completion.\n"); | |
1abf329a AM |
245 | goto reset; |
246 | } | |
247 | ||
248 | reset: | |
249 | dsb->free_pos = 0; | |
250 | dsb->ins_start_offset = 0; | |
afeda4f3 AM |
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); | |
607856a8 CIK |
275 | if (!dsb) { |
276 | drm_err(&i915->drm, "DSB object creation failed\n"); | |
277 | return; | |
278 | } | |
afeda4f3 AM |
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 | ||
1d5ab1ca | 297 | buf = i915_gem_object_pin_map_unlocked(vma->obj, I915_MAP_WC); |
afeda4f3 AM |
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; | |
1abf329a | 330 | } |