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