xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/i915/display/intel_dsb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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