1352ff8bdSFrançois Tigeot /*
2352ff8bdSFrançois Tigeot  * Copyright © 2014 Intel Corporation
3352ff8bdSFrançois Tigeot  *
4352ff8bdSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
5352ff8bdSFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
6352ff8bdSFrançois Tigeot  * to deal in the Software without restriction, including without limitation
7352ff8bdSFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8352ff8bdSFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
9352ff8bdSFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
10352ff8bdSFrançois Tigeot  *
11352ff8bdSFrançois Tigeot  * The above copyright notice and this permission notice (including the next
12352ff8bdSFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
13352ff8bdSFrançois Tigeot  * Software.
14352ff8bdSFrançois Tigeot  *
15352ff8bdSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16352ff8bdSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17352ff8bdSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18352ff8bdSFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19352ff8bdSFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20352ff8bdSFrançois Tigeot  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21352ff8bdSFrançois Tigeot  * IN THE SOFTWARE.
22352ff8bdSFrançois Tigeot  *
23352ff8bdSFrançois Tigeot  */
24352ff8bdSFrançois Tigeot #include <linux/firmware.h>
258cd49a21SFrançois Tigeot #include <linux/circ_buf.h>
26352ff8bdSFrançois Tigeot #include "i915_drv.h"
27352ff8bdSFrançois Tigeot #include "intel_guc.h"
28352ff8bdSFrançois Tigeot 
29352ff8bdSFrançois Tigeot /**
30aee94f86SFrançois Tigeot  * DOC: GuC-based command submission
31352ff8bdSFrançois Tigeot  *
32352ff8bdSFrançois Tigeot  * i915_guc_client:
33352ff8bdSFrançois Tigeot  * We use the term client to avoid confusion with contexts. A i915_guc_client is
34352ff8bdSFrançois Tigeot  * equivalent to GuC object guc_context_desc. This context descriptor is
35352ff8bdSFrançois Tigeot  * allocated from a pool of 1024 entries. Kernel driver will allocate doorbell
36352ff8bdSFrançois Tigeot  * and workqueue for it. Also the process descriptor (guc_process_desc), which
37352ff8bdSFrançois Tigeot  * is mapped to client space. So the client can write Work Item then ring the
38352ff8bdSFrançois Tigeot  * doorbell.
39352ff8bdSFrançois Tigeot  *
40352ff8bdSFrançois Tigeot  * To simplify the implementation, we allocate one gem object that contains all
41352ff8bdSFrançois Tigeot  * pages for doorbell, process descriptor and workqueue.
42352ff8bdSFrançois Tigeot  *
43352ff8bdSFrançois Tigeot  * The Scratch registers:
44352ff8bdSFrançois Tigeot  * There are 16 MMIO-based registers start from 0xC180. The kernel driver writes
45352ff8bdSFrançois Tigeot  * a value to the action register (SOFT_SCRATCH_0) along with any data. It then
46352ff8bdSFrançois Tigeot  * triggers an interrupt on the GuC via another register write (0xC4C8).
47352ff8bdSFrançois Tigeot  * Firmware writes a success/fail code back to the action register after
48352ff8bdSFrançois Tigeot  * processes the request. The kernel driver polls waiting for this update and
49352ff8bdSFrançois Tigeot  * then proceeds.
50352ff8bdSFrançois Tigeot  * See host2guc_action()
51352ff8bdSFrançois Tigeot  *
52352ff8bdSFrançois Tigeot  * Doorbells:
53352ff8bdSFrançois Tigeot  * Doorbells are interrupts to uKernel. A doorbell is a single cache line (QW)
54352ff8bdSFrançois Tigeot  * mapped into process space.
55352ff8bdSFrançois Tigeot  *
56352ff8bdSFrançois Tigeot  * Work Items:
57352ff8bdSFrançois Tigeot  * There are several types of work items that the host may place into a
58352ff8bdSFrançois Tigeot  * workqueue, each with its own requirements and limitations. Currently only
59352ff8bdSFrançois Tigeot  * WQ_TYPE_INORDER is needed to support legacy submission via GuC, which
60352ff8bdSFrançois Tigeot  * represents in-order queue. The kernel driver packs ring tail pointer and an
61352ff8bdSFrançois Tigeot  * ELSP context descriptor dword into Work Item.
62352ff8bdSFrançois Tigeot  * See guc_add_workqueue_item()
63352ff8bdSFrançois Tigeot  *
64352ff8bdSFrançois Tigeot  */
65352ff8bdSFrançois Tigeot 
66352ff8bdSFrançois Tigeot /*
67352ff8bdSFrançois Tigeot  * Read GuC command/status register (SOFT_SCRATCH_0)
68352ff8bdSFrançois Tigeot  * Return true if it contains a response rather than a command
69352ff8bdSFrançois Tigeot  */
70352ff8bdSFrançois Tigeot static inline bool host2guc_action_response(struct drm_i915_private *dev_priv,
71352ff8bdSFrançois Tigeot 					    u32 *status)
72352ff8bdSFrançois Tigeot {
73352ff8bdSFrançois Tigeot 	u32 val = I915_READ(SOFT_SCRATCH(0));
74352ff8bdSFrançois Tigeot 	*status = val;
75352ff8bdSFrançois Tigeot 	return GUC2HOST_IS_RESPONSE(val);
76352ff8bdSFrançois Tigeot }
77352ff8bdSFrançois Tigeot 
78352ff8bdSFrançois Tigeot static int host2guc_action(struct intel_guc *guc, u32 *data, u32 len)
79352ff8bdSFrançois Tigeot {
80352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = guc_to_i915(guc);
81352ff8bdSFrançois Tigeot 	u32 status;
82352ff8bdSFrançois Tigeot 	int i;
83352ff8bdSFrançois Tigeot 	int ret;
84352ff8bdSFrançois Tigeot 
85352ff8bdSFrançois Tigeot 	if (WARN_ON(len < 1 || len > 15))
86352ff8bdSFrançois Tigeot 		return -EINVAL;
87352ff8bdSFrançois Tigeot 
88352ff8bdSFrançois Tigeot 	intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
89352ff8bdSFrançois Tigeot 
90352ff8bdSFrançois Tigeot 	dev_priv->guc.action_count += 1;
91352ff8bdSFrançois Tigeot 	dev_priv->guc.action_cmd = data[0];
92352ff8bdSFrançois Tigeot 
93352ff8bdSFrançois Tigeot 	for (i = 0; i < len; i++)
94352ff8bdSFrançois Tigeot 		I915_WRITE(SOFT_SCRATCH(i), data[i]);
95352ff8bdSFrançois Tigeot 
96352ff8bdSFrançois Tigeot 	POSTING_READ(SOFT_SCRATCH(i - 1));
97352ff8bdSFrançois Tigeot 
98352ff8bdSFrançois Tigeot 	I915_WRITE(HOST2GUC_INTERRUPT, HOST2GUC_TRIGGER);
99352ff8bdSFrançois Tigeot 
100352ff8bdSFrançois Tigeot 	/* No HOST2GUC command should take longer than 10ms */
101352ff8bdSFrançois Tigeot 	ret = wait_for_atomic(host2guc_action_response(dev_priv, &status), 10);
102352ff8bdSFrançois Tigeot 	if (status != GUC2HOST_STATUS_SUCCESS) {
103352ff8bdSFrançois Tigeot 		/*
104352ff8bdSFrançois Tigeot 		 * Either the GuC explicitly returned an error (which
105352ff8bdSFrançois Tigeot 		 * we convert to -EIO here) or no response at all was
106352ff8bdSFrançois Tigeot 		 * received within the timeout limit (-ETIMEDOUT)
107352ff8bdSFrançois Tigeot 		 */
108352ff8bdSFrançois Tigeot 		if (ret != -ETIMEDOUT)
109352ff8bdSFrançois Tigeot 			ret = -EIO;
110352ff8bdSFrançois Tigeot 
111352ff8bdSFrançois Tigeot 		DRM_ERROR("GUC: host2guc action 0x%X failed. ret=%d "
112352ff8bdSFrançois Tigeot 				"status=0x%08X response=0x%08X\n",
113352ff8bdSFrançois Tigeot 				data[0], ret, status,
114352ff8bdSFrançois Tigeot 				I915_READ(SOFT_SCRATCH(15)));
115352ff8bdSFrançois Tigeot 
116352ff8bdSFrançois Tigeot 		dev_priv->guc.action_fail += 1;
117352ff8bdSFrançois Tigeot 		dev_priv->guc.action_err = ret;
118352ff8bdSFrançois Tigeot 	}
119352ff8bdSFrançois Tigeot 	dev_priv->guc.action_status = status;
120352ff8bdSFrançois Tigeot 
121352ff8bdSFrançois Tigeot 	intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
122352ff8bdSFrançois Tigeot 
123352ff8bdSFrançois Tigeot 	return ret;
124352ff8bdSFrançois Tigeot }
125352ff8bdSFrançois Tigeot 
126352ff8bdSFrançois Tigeot /*
127352ff8bdSFrançois Tigeot  * Tell the GuC to allocate or deallocate a specific doorbell
128352ff8bdSFrançois Tigeot  */
129352ff8bdSFrançois Tigeot 
130352ff8bdSFrançois Tigeot static int host2guc_allocate_doorbell(struct intel_guc *guc,
131352ff8bdSFrançois Tigeot 				      struct i915_guc_client *client)
132352ff8bdSFrançois Tigeot {
133352ff8bdSFrançois Tigeot 	u32 data[2];
134352ff8bdSFrançois Tigeot 
135352ff8bdSFrançois Tigeot 	data[0] = HOST2GUC_ACTION_ALLOCATE_DOORBELL;
136352ff8bdSFrançois Tigeot 	data[1] = client->ctx_index;
137352ff8bdSFrançois Tigeot 
138352ff8bdSFrançois Tigeot 	return host2guc_action(guc, data, 2);
139352ff8bdSFrançois Tigeot }
140352ff8bdSFrançois Tigeot 
141352ff8bdSFrançois Tigeot static int host2guc_release_doorbell(struct intel_guc *guc,
142352ff8bdSFrançois Tigeot 				     struct i915_guc_client *client)
143352ff8bdSFrançois Tigeot {
144352ff8bdSFrançois Tigeot 	u32 data[2];
145352ff8bdSFrançois Tigeot 
146352ff8bdSFrançois Tigeot 	data[0] = HOST2GUC_ACTION_DEALLOCATE_DOORBELL;
147352ff8bdSFrançois Tigeot 	data[1] = client->ctx_index;
148352ff8bdSFrançois Tigeot 
149352ff8bdSFrançois Tigeot 	return host2guc_action(guc, data, 2);
150352ff8bdSFrançois Tigeot }
151352ff8bdSFrançois Tigeot 
152352ff8bdSFrançois Tigeot static int host2guc_sample_forcewake(struct intel_guc *guc,
153352ff8bdSFrançois Tigeot 				     struct i915_guc_client *client)
154352ff8bdSFrançois Tigeot {
155352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = guc_to_i915(guc);
156352ff8bdSFrançois Tigeot 	struct drm_device *dev = dev_priv->dev;
157352ff8bdSFrançois Tigeot 	u32 data[2];
158352ff8bdSFrançois Tigeot 
159352ff8bdSFrançois Tigeot 	data[0] = HOST2GUC_ACTION_SAMPLE_FORCEWAKE;
160352ff8bdSFrançois Tigeot 	/* WaRsDisableCoarsePowerGating:skl,bxt */
161c0e85e96SFrançois Tigeot 	if (!intel_enable_rc6(dev) ||
162c0e85e96SFrançois Tigeot 	    NEEDS_WaRsDisableCoarsePowerGating(dev))
163352ff8bdSFrançois Tigeot 		data[1] = 0;
164352ff8bdSFrançois Tigeot 	else
165352ff8bdSFrançois Tigeot 		/* bit 0 and 1 are for Render and Media domain separately */
166352ff8bdSFrançois Tigeot 		data[1] = GUC_FORCEWAKE_RENDER | GUC_FORCEWAKE_MEDIA;
167352ff8bdSFrançois Tigeot 
168352ff8bdSFrançois Tigeot 	return host2guc_action(guc, data, ARRAY_SIZE(data));
169352ff8bdSFrançois Tigeot }
170352ff8bdSFrançois Tigeot 
171352ff8bdSFrançois Tigeot /*
172352ff8bdSFrançois Tigeot  * Initialise, update, or clear doorbell data shared with the GuC
173352ff8bdSFrançois Tigeot  *
174352ff8bdSFrançois Tigeot  * These functions modify shared data and so need access to the mapped
175352ff8bdSFrançois Tigeot  * client object which contains the page being used for the doorbell
176352ff8bdSFrançois Tigeot  */
177352ff8bdSFrançois Tigeot 
178352ff8bdSFrançois Tigeot static void guc_init_doorbell(struct intel_guc *guc,
179352ff8bdSFrançois Tigeot 			      struct i915_guc_client *client)
180352ff8bdSFrançois Tigeot {
181352ff8bdSFrançois Tigeot 	struct guc_doorbell_info *doorbell;
182352ff8bdSFrançois Tigeot 
183*8621f407SFrançois Tigeot 	doorbell = (struct guc_doorbell_info *)((char *)client->client_base + client->doorbell_offset);
184352ff8bdSFrançois Tigeot 
185*8621f407SFrançois Tigeot 	doorbell->db_status = GUC_DOORBELL_ENABLED;
186352ff8bdSFrançois Tigeot 	doorbell->cookie = 0;
187352ff8bdSFrançois Tigeot }
188352ff8bdSFrançois Tigeot 
189352ff8bdSFrançois Tigeot static int guc_ring_doorbell(struct i915_guc_client *gc)
190352ff8bdSFrançois Tigeot {
191352ff8bdSFrançois Tigeot 	struct guc_process_desc *desc;
192352ff8bdSFrançois Tigeot 	union guc_doorbell_qw db_cmp, db_exc, db_ret;
193352ff8bdSFrançois Tigeot 	union guc_doorbell_qw *db;
194352ff8bdSFrançois Tigeot 	int attempt = 2, ret = -EAGAIN;
195352ff8bdSFrançois Tigeot 
196*8621f407SFrançois Tigeot 	desc = (struct guc_process_desc *)((char *)gc->client_base + gc->proc_desc_offset);
197352ff8bdSFrançois Tigeot 
198352ff8bdSFrançois Tigeot 	/* Update the tail so it is visible to GuC */
199352ff8bdSFrançois Tigeot 	desc->tail = gc->wq_tail;
200352ff8bdSFrançois Tigeot 
201352ff8bdSFrançois Tigeot 	/* current cookie */
202352ff8bdSFrançois Tigeot 	db_cmp.db_status = GUC_DOORBELL_ENABLED;
203352ff8bdSFrançois Tigeot 	db_cmp.cookie = gc->cookie;
204352ff8bdSFrançois Tigeot 
205352ff8bdSFrançois Tigeot 	/* cookie to be updated */
206352ff8bdSFrançois Tigeot 	db_exc.db_status = GUC_DOORBELL_ENABLED;
207352ff8bdSFrançois Tigeot 	db_exc.cookie = gc->cookie + 1;
208352ff8bdSFrançois Tigeot 	if (db_exc.cookie == 0)
209352ff8bdSFrançois Tigeot 		db_exc.cookie = 1;
210352ff8bdSFrançois Tigeot 
211352ff8bdSFrançois Tigeot 	/* pointer of current doorbell cacheline */
212*8621f407SFrançois Tigeot 	db = (union guc_doorbell_qw *)((char *)gc->client_base + gc->doorbell_offset);
213352ff8bdSFrançois Tigeot 
214352ff8bdSFrançois Tigeot 	while (attempt--) {
215352ff8bdSFrançois Tigeot 		/* lets ring the doorbell */
216352ff8bdSFrançois Tigeot 		db_ret.value_qw = atomic64_cmpxchg((atomic64_t *)db,
217352ff8bdSFrançois Tigeot 			db_cmp.value_qw, db_exc.value_qw);
218352ff8bdSFrançois Tigeot 
219352ff8bdSFrançois Tigeot 		/* if the exchange was successfully executed */
220352ff8bdSFrançois Tigeot 		if (db_ret.value_qw == db_cmp.value_qw) {
221352ff8bdSFrançois Tigeot 			/* db was successfully rung */
222352ff8bdSFrançois Tigeot 			gc->cookie = db_exc.cookie;
223352ff8bdSFrançois Tigeot 			ret = 0;
224352ff8bdSFrançois Tigeot 			break;
225352ff8bdSFrançois Tigeot 		}
226352ff8bdSFrançois Tigeot 
227352ff8bdSFrançois Tigeot 		/* XXX: doorbell was lost and need to acquire it again */
228352ff8bdSFrançois Tigeot 		if (db_ret.db_status == GUC_DOORBELL_DISABLED)
229352ff8bdSFrançois Tigeot 			break;
230352ff8bdSFrançois Tigeot 
231352ff8bdSFrançois Tigeot 		DRM_ERROR("Cookie mismatch. Expected %d, returned %d\n",
232352ff8bdSFrançois Tigeot 			  db_cmp.cookie, db_ret.cookie);
233352ff8bdSFrançois Tigeot 
234352ff8bdSFrançois Tigeot 		/* update the cookie to newly read cookie from GuC */
235352ff8bdSFrançois Tigeot 		db_cmp.cookie = db_ret.cookie;
236352ff8bdSFrançois Tigeot 		db_exc.cookie = db_ret.cookie + 1;
237352ff8bdSFrançois Tigeot 		if (db_exc.cookie == 0)
238352ff8bdSFrançois Tigeot 			db_exc.cookie = 1;
239352ff8bdSFrançois Tigeot 	}
240352ff8bdSFrançois Tigeot 
241352ff8bdSFrançois Tigeot 	return ret;
242352ff8bdSFrançois Tigeot }
243352ff8bdSFrançois Tigeot 
244352ff8bdSFrançois Tigeot static void guc_disable_doorbell(struct intel_guc *guc,
245352ff8bdSFrançois Tigeot 				 struct i915_guc_client *client)
246352ff8bdSFrançois Tigeot {
247352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = guc_to_i915(guc);
248352ff8bdSFrançois Tigeot 	struct guc_doorbell_info *doorbell;
249aee94f86SFrançois Tigeot 	i915_reg_t drbreg = GEN8_DRBREGL(client->doorbell_id);
250352ff8bdSFrançois Tigeot 	int value;
251352ff8bdSFrançois Tigeot 
252*8621f407SFrançois Tigeot 	doorbell = (struct guc_doorbell_info*)((char *)client->client_base + client->doorbell_offset);
253352ff8bdSFrançois Tigeot 
254*8621f407SFrançois Tigeot 	doorbell->db_status = GUC_DOORBELL_DISABLED;
255352ff8bdSFrançois Tigeot 
256352ff8bdSFrançois Tigeot 	I915_WRITE(drbreg, I915_READ(drbreg) & ~GEN8_DRB_VALID);
257352ff8bdSFrançois Tigeot 
258352ff8bdSFrançois Tigeot 	value = I915_READ(drbreg);
259352ff8bdSFrançois Tigeot 	WARN_ON((value & GEN8_DRB_VALID) != 0);
260352ff8bdSFrançois Tigeot 
261352ff8bdSFrançois Tigeot 	I915_WRITE(GEN8_DRBREGU(client->doorbell_id), 0);
262352ff8bdSFrançois Tigeot 	I915_WRITE(drbreg, 0);
263352ff8bdSFrançois Tigeot 
264352ff8bdSFrançois Tigeot 	/* XXX: wait for any interrupts */
265352ff8bdSFrançois Tigeot 	/* XXX: wait for workqueue to drain */
266352ff8bdSFrançois Tigeot }
267352ff8bdSFrançois Tigeot 
268352ff8bdSFrançois Tigeot /*
269352ff8bdSFrançois Tigeot  * Select, assign and relase doorbell cachelines
270352ff8bdSFrançois Tigeot  *
271352ff8bdSFrançois Tigeot  * These functions track which doorbell cachelines are in use.
272352ff8bdSFrançois Tigeot  * The data they manipulate is protected by the host2guc lock.
273352ff8bdSFrançois Tigeot  */
274352ff8bdSFrançois Tigeot 
275352ff8bdSFrançois Tigeot static uint32_t select_doorbell_cacheline(struct intel_guc *guc)
276352ff8bdSFrançois Tigeot {
277352ff8bdSFrançois Tigeot 	const uint32_t cacheline_size = cache_line_size();
278352ff8bdSFrançois Tigeot 	uint32_t offset;
279352ff8bdSFrançois Tigeot 
280352ff8bdSFrançois Tigeot 	/* Doorbell uses a single cache line within a page */
281352ff8bdSFrançois Tigeot 	offset = offset_in_page(guc->db_cacheline);
282352ff8bdSFrançois Tigeot 
283352ff8bdSFrançois Tigeot 	/* Moving to next cache line to reduce contention */
284352ff8bdSFrançois Tigeot 	guc->db_cacheline += cacheline_size;
285352ff8bdSFrançois Tigeot 
286352ff8bdSFrançois Tigeot 	DRM_DEBUG_DRIVER("selected doorbell cacheline 0x%x, next 0x%x, linesize %u\n",
287352ff8bdSFrançois Tigeot 			offset, guc->db_cacheline, cacheline_size);
288352ff8bdSFrançois Tigeot 
289352ff8bdSFrançois Tigeot 	return offset;
290352ff8bdSFrançois Tigeot }
291352ff8bdSFrançois Tigeot 
292352ff8bdSFrançois Tigeot static uint16_t assign_doorbell(struct intel_guc *guc, uint32_t priority)
293352ff8bdSFrançois Tigeot {
294352ff8bdSFrançois Tigeot 	/*
295352ff8bdSFrançois Tigeot 	 * The bitmap is split into two halves; the first half is used for
296352ff8bdSFrançois Tigeot 	 * normal priority contexts, the second half for high-priority ones.
297352ff8bdSFrançois Tigeot 	 * Note that logically higher priorities are numerically less than
298352ff8bdSFrançois Tigeot 	 * normal ones, so the test below means "is it high-priority?"
299352ff8bdSFrançois Tigeot 	 */
300352ff8bdSFrançois Tigeot 	const bool hi_pri = (priority <= GUC_CTX_PRIORITY_HIGH);
301352ff8bdSFrançois Tigeot 	const uint16_t half = GUC_MAX_DOORBELLS / 2;
302352ff8bdSFrançois Tigeot 	const uint16_t start = hi_pri ? half : 0;
303352ff8bdSFrançois Tigeot 	const uint16_t end = start + half;
304352ff8bdSFrançois Tigeot 	uint16_t id;
305352ff8bdSFrançois Tigeot 
306352ff8bdSFrançois Tigeot 	id = find_next_zero_bit(guc->doorbell_bitmap, end, start);
307352ff8bdSFrançois Tigeot 	if (id == end)
308352ff8bdSFrançois Tigeot 		id = GUC_INVALID_DOORBELL_ID;
309352ff8bdSFrançois Tigeot 	else
310352ff8bdSFrançois Tigeot 		bitmap_set(guc->doorbell_bitmap, id, 1);
311352ff8bdSFrançois Tigeot 
312352ff8bdSFrançois Tigeot 	DRM_DEBUG_DRIVER("assigned %s priority doorbell id 0x%x\n",
313352ff8bdSFrançois Tigeot 			hi_pri ? "high" : "normal", id);
314352ff8bdSFrançois Tigeot 
315352ff8bdSFrançois Tigeot 	return id;
316352ff8bdSFrançois Tigeot }
317352ff8bdSFrançois Tigeot 
318352ff8bdSFrançois Tigeot static void release_doorbell(struct intel_guc *guc, uint16_t id)
319352ff8bdSFrançois Tigeot {
320352ff8bdSFrançois Tigeot 	bitmap_clear(guc->doorbell_bitmap, id, 1);
321352ff8bdSFrançois Tigeot }
322352ff8bdSFrançois Tigeot 
323352ff8bdSFrançois Tigeot /*
324352ff8bdSFrançois Tigeot  * Initialise the process descriptor shared with the GuC firmware.
325352ff8bdSFrançois Tigeot  */
326352ff8bdSFrançois Tigeot static void guc_init_proc_desc(struct intel_guc *guc,
327352ff8bdSFrançois Tigeot 			       struct i915_guc_client *client)
328352ff8bdSFrançois Tigeot {
329352ff8bdSFrançois Tigeot 	struct guc_process_desc *desc;
330352ff8bdSFrançois Tigeot 
331*8621f407SFrançois Tigeot 	desc = (struct guc_process_desc *)((char *)client->client_base + client->proc_desc_offset);
332352ff8bdSFrançois Tigeot 
333352ff8bdSFrançois Tigeot 	memset(desc, 0, sizeof(*desc));
334352ff8bdSFrançois Tigeot 
335352ff8bdSFrançois Tigeot 	/*
336352ff8bdSFrançois Tigeot 	 * XXX: pDoorbell and WQVBaseAddress are pointers in process address
337352ff8bdSFrançois Tigeot 	 * space for ring3 clients (set them as in mmap_ioctl) or kernel
338352ff8bdSFrançois Tigeot 	 * space for kernel clients (map on demand instead? May make debug
339352ff8bdSFrançois Tigeot 	 * easier to have it mapped).
340352ff8bdSFrançois Tigeot 	 */
341352ff8bdSFrançois Tigeot 	desc->wq_base_addr = 0;
342352ff8bdSFrançois Tigeot 	desc->db_base_addr = 0;
343352ff8bdSFrançois Tigeot 
344352ff8bdSFrançois Tigeot 	desc->context_id = client->ctx_index;
345352ff8bdSFrançois Tigeot 	desc->wq_size_bytes = client->wq_size;
346352ff8bdSFrançois Tigeot 	desc->wq_status = WQ_STATUS_ACTIVE;
347352ff8bdSFrançois Tigeot 	desc->priority = client->priority;
348352ff8bdSFrançois Tigeot }
349352ff8bdSFrançois Tigeot 
350352ff8bdSFrançois Tigeot /*
351352ff8bdSFrançois Tigeot  * Initialise/clear the context descriptor shared with the GuC firmware.
352352ff8bdSFrançois Tigeot  *
353352ff8bdSFrançois Tigeot  * This descriptor tells the GuC where (in GGTT space) to find the important
354352ff8bdSFrançois Tigeot  * data structures relating to this client (doorbell, process descriptor,
355352ff8bdSFrançois Tigeot  * write queue, etc).
356352ff8bdSFrançois Tigeot  */
357352ff8bdSFrançois Tigeot 
358352ff8bdSFrançois Tigeot static void guc_init_ctx_desc(struct intel_guc *guc,
359352ff8bdSFrançois Tigeot 			      struct i915_guc_client *client)
360352ff8bdSFrançois Tigeot {
361*8621f407SFrançois Tigeot 	struct drm_i915_gem_object *client_obj = client->client_obj;
362c0e85e96SFrançois Tigeot 	struct drm_i915_private *dev_priv = guc_to_i915(guc);
363*8621f407SFrançois Tigeot 	struct intel_engine_cs *engine;
364352ff8bdSFrançois Tigeot 	struct intel_context *ctx = client->owner;
365352ff8bdSFrançois Tigeot 	struct guc_context_desc desc;
366352ff8bdSFrançois Tigeot 	struct sg_table *sg;
367*8621f407SFrançois Tigeot 	enum intel_engine_id id;
368*8621f407SFrançois Tigeot 	u32 gfx_addr;
369352ff8bdSFrançois Tigeot 
370352ff8bdSFrançois Tigeot 	memset(&desc, 0, sizeof(desc));
371352ff8bdSFrançois Tigeot 
372352ff8bdSFrançois Tigeot 	desc.attribute = GUC_CTX_DESC_ATTR_ACTIVE | GUC_CTX_DESC_ATTR_KERNEL;
373352ff8bdSFrançois Tigeot 	desc.context_id = client->ctx_index;
374352ff8bdSFrançois Tigeot 	desc.priority = client->priority;
375352ff8bdSFrançois Tigeot 	desc.db_id = client->doorbell_id;
376352ff8bdSFrançois Tigeot 
377*8621f407SFrançois Tigeot 	for_each_engine_id(engine, dev_priv, id) {
378*8621f407SFrançois Tigeot 		struct guc_execlist_context *lrc = &desc.lrc[engine->guc_id];
379352ff8bdSFrançois Tigeot 		struct drm_i915_gem_object *obj;
380352ff8bdSFrançois Tigeot 		uint64_t ctx_desc;
381352ff8bdSFrançois Tigeot 
382352ff8bdSFrançois Tigeot 		/* TODO: We have a design issue to be solved here. Only when we
383352ff8bdSFrançois Tigeot 		 * receive the first batch, we know which engine is used by the
384352ff8bdSFrançois Tigeot 		 * user. But here GuC expects the lrc and ring to be pinned. It
385352ff8bdSFrançois Tigeot 		 * is not an issue for default context, which is the only one
386352ff8bdSFrançois Tigeot 		 * for now who owns a GuC client. But for future owner of GuC
387352ff8bdSFrançois Tigeot 		 * client, need to make sure lrc is pinned prior to enter here.
388352ff8bdSFrançois Tigeot 		 */
389*8621f407SFrançois Tigeot 		obj = ctx->engine[id].state;
390352ff8bdSFrançois Tigeot 		if (!obj)
391352ff8bdSFrançois Tigeot 			break;	/* XXX: continue? */
392352ff8bdSFrançois Tigeot 
393*8621f407SFrançois Tigeot 		ctx_desc = intel_lr_context_descriptor(ctx, engine);
394352ff8bdSFrançois Tigeot 		lrc->context_desc = (u32)ctx_desc;
395352ff8bdSFrançois Tigeot 
396352ff8bdSFrançois Tigeot 		/* The state page is after PPHWSP */
397*8621f407SFrançois Tigeot 		gfx_addr = i915_gem_obj_ggtt_offset(obj);
398*8621f407SFrançois Tigeot 		lrc->ring_lcra = gfx_addr + LRC_STATE_PN * PAGE_SIZE;
399352ff8bdSFrançois Tigeot 		lrc->context_id = (client->ctx_index << GUC_ELC_CTXID_OFFSET) |
400*8621f407SFrançois Tigeot 				(engine->guc_id << GUC_ELC_ENGINE_OFFSET);
401352ff8bdSFrançois Tigeot 
402*8621f407SFrançois Tigeot 		obj = ctx->engine[id].ringbuf->obj;
403*8621f407SFrançois Tigeot 		gfx_addr = i915_gem_obj_ggtt_offset(obj);
404352ff8bdSFrançois Tigeot 
405*8621f407SFrançois Tigeot 		lrc->ring_begin = gfx_addr;
406*8621f407SFrançois Tigeot 		lrc->ring_end = gfx_addr + obj->base.size - 1;
407*8621f407SFrançois Tigeot 		lrc->ring_next_free_location = gfx_addr;
408352ff8bdSFrançois Tigeot 		lrc->ring_current_tail_pointer_value = 0;
409352ff8bdSFrançois Tigeot 
410*8621f407SFrançois Tigeot 		desc.engines_used |= (1 << engine->guc_id);
411352ff8bdSFrançois Tigeot 	}
412352ff8bdSFrançois Tigeot 
413352ff8bdSFrançois Tigeot 	WARN_ON(desc.engines_used == 0);
414352ff8bdSFrançois Tigeot 
415352ff8bdSFrançois Tigeot 	/*
416*8621f407SFrançois Tigeot 	 * The doorbell, process descriptor, and workqueue are all parts
417*8621f407SFrançois Tigeot 	 * of the client object, which the GuC will reference via the GGTT
418352ff8bdSFrançois Tigeot 	 */
419*8621f407SFrançois Tigeot 	gfx_addr = i915_gem_obj_ggtt_offset(client_obj);
420*8621f407SFrançois Tigeot 	desc.db_trigger_phy = sg_dma_address(client_obj->pages->sgl) +
421*8621f407SFrançois Tigeot 				client->doorbell_offset;
422*8621f407SFrançois Tigeot 	desc.db_trigger_cpu = (uintptr_t)client->client_base +
423*8621f407SFrançois Tigeot 				client->doorbell_offset;
424*8621f407SFrançois Tigeot 	desc.db_trigger_uk = gfx_addr + client->doorbell_offset;
425*8621f407SFrançois Tigeot 	desc.process_desc = gfx_addr + client->proc_desc_offset;
426*8621f407SFrançois Tigeot 	desc.wq_addr = gfx_addr + client->wq_offset;
427352ff8bdSFrançois Tigeot 	desc.wq_size = client->wq_size;
428352ff8bdSFrançois Tigeot 
429352ff8bdSFrançois Tigeot 	/*
430352ff8bdSFrançois Tigeot 	 * XXX: Take LRCs from an existing intel_context if this is not an
431352ff8bdSFrançois Tigeot 	 * IsKMDCreatedContext client
432352ff8bdSFrançois Tigeot 	 */
433352ff8bdSFrançois Tigeot 	desc.desc_private = (uintptr_t)client;
434352ff8bdSFrançois Tigeot 
435352ff8bdSFrançois Tigeot 	/* Pool context is pinned already */
436352ff8bdSFrançois Tigeot 	sg = guc->ctx_pool_obj->pages;
437352ff8bdSFrançois Tigeot 	sg_pcopy_from_buffer(sg->sgl, sg->nents, &desc, sizeof(desc),
438352ff8bdSFrançois Tigeot 			     sizeof(desc) * client->ctx_index);
439352ff8bdSFrançois Tigeot }
440352ff8bdSFrançois Tigeot 
441352ff8bdSFrançois Tigeot static void guc_fini_ctx_desc(struct intel_guc *guc,
442352ff8bdSFrançois Tigeot 			      struct i915_guc_client *client)
443352ff8bdSFrançois Tigeot {
444352ff8bdSFrançois Tigeot 	struct guc_context_desc desc;
445352ff8bdSFrançois Tigeot 	struct sg_table *sg;
446352ff8bdSFrançois Tigeot 
447352ff8bdSFrançois Tigeot 	memset(&desc, 0, sizeof(desc));
448352ff8bdSFrançois Tigeot 
449352ff8bdSFrançois Tigeot 	sg = guc->ctx_pool_obj->pages;
450352ff8bdSFrançois Tigeot 	sg_pcopy_from_buffer(sg->sgl, sg->nents, &desc, sizeof(desc),
451352ff8bdSFrançois Tigeot 			     sizeof(desc) * client->ctx_index);
452352ff8bdSFrançois Tigeot }
453352ff8bdSFrançois Tigeot 
454c0e85e96SFrançois Tigeot int i915_guc_wq_check_space(struct i915_guc_client *gc)
455352ff8bdSFrançois Tigeot {
456352ff8bdSFrançois Tigeot 	struct guc_process_desc *desc;
457352ff8bdSFrançois Tigeot 	u32 size = sizeof(struct guc_wq_item);
458aee94f86SFrançois Tigeot 	int ret = -ETIMEDOUT, timeout_counter = 200;
459352ff8bdSFrançois Tigeot 
460c0e85e96SFrançois Tigeot 	if (!gc)
461c0e85e96SFrançois Tigeot 		return 0;
462c0e85e96SFrançois Tigeot 
463*8621f407SFrançois Tigeot 	desc = (struct guc_process_desc *)((char *)gc->client_base + gc->proc_desc_offset);
464352ff8bdSFrançois Tigeot 
465352ff8bdSFrançois Tigeot 	while (timeout_counter-- > 0) {
466*8621f407SFrançois Tigeot 		if (CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size) >= size) {
467aee94f86SFrançois Tigeot 			ret = 0;
468c0e85e96SFrançois Tigeot 			break;
469352ff8bdSFrançois Tigeot 		}
470aee94f86SFrançois Tigeot 
471aee94f86SFrançois Tigeot 		if (timeout_counter)
472aee94f86SFrançois Tigeot 			usleep_range(1000, 2000);
473352ff8bdSFrançois Tigeot 	};
474352ff8bdSFrançois Tigeot 
475352ff8bdSFrançois Tigeot 	return ret;
476352ff8bdSFrançois Tigeot }
477352ff8bdSFrançois Tigeot 
478352ff8bdSFrançois Tigeot static int guc_add_workqueue_item(struct i915_guc_client *gc,
479352ff8bdSFrançois Tigeot 				  struct drm_i915_gem_request *rq)
480352ff8bdSFrançois Tigeot {
481*8621f407SFrançois Tigeot 	struct guc_process_desc *desc;
482352ff8bdSFrançois Tigeot 	struct guc_wq_item *wqi;
483352ff8bdSFrançois Tigeot 	void *base;
484c0e85e96SFrançois Tigeot 	u32 tail, wq_len, wq_off, space;
485352ff8bdSFrançois Tigeot 
486*8621f407SFrançois Tigeot 	desc = (struct guc_process_desc *)((char *)gc->client_base + gc->proc_desc_offset);
487*8621f407SFrançois Tigeot 	space = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size);
488c0e85e96SFrançois Tigeot 	if (WARN_ON(space < sizeof(struct guc_wq_item)))
489c0e85e96SFrançois Tigeot 		return -ENOSPC; /* shouldn't happen */
490c0e85e96SFrançois Tigeot 
491c0e85e96SFrançois Tigeot 	/* postincrement WQ tail for next time */
492c0e85e96SFrançois Tigeot 	wq_off = gc->wq_tail;
493c0e85e96SFrançois Tigeot 	gc->wq_tail += sizeof(struct guc_wq_item);
494c0e85e96SFrançois Tigeot 	gc->wq_tail &= gc->wq_size - 1;
495352ff8bdSFrançois Tigeot 
496352ff8bdSFrançois Tigeot 	/* For now workqueue item is 4 DWs; workqueue buffer is 2 pages. So we
497352ff8bdSFrançois Tigeot 	 * should not have the case where structure wqi is across page, neither
498352ff8bdSFrançois Tigeot 	 * wrapped to the beginning. This simplifies the implementation below.
499352ff8bdSFrançois Tigeot 	 *
500352ff8bdSFrançois Tigeot 	 * XXX: if not the case, we need save data to a temp wqi and copy it to
501352ff8bdSFrançois Tigeot 	 * workqueue buffer dw by dw.
502352ff8bdSFrançois Tigeot 	 */
503352ff8bdSFrançois Tigeot 	WARN_ON(sizeof(struct guc_wq_item) != 16);
504352ff8bdSFrançois Tigeot 	WARN_ON(wq_off & 3);
505352ff8bdSFrançois Tigeot 
506352ff8bdSFrançois Tigeot 	/* wq starts from the page after doorbell / process_desc */
507352ff8bdSFrançois Tigeot 	base = kmap_atomic(i915_gem_object_get_page(gc->client_obj,
508352ff8bdSFrançois Tigeot 			(wq_off + GUC_DB_SIZE) >> PAGE_SHIFT));
509352ff8bdSFrançois Tigeot 	wq_off &= PAGE_SIZE - 1;
510352ff8bdSFrançois Tigeot 	wqi = (struct guc_wq_item *)((char *)base + wq_off);
511352ff8bdSFrançois Tigeot 
512352ff8bdSFrançois Tigeot 	/* len does not include the header */
513352ff8bdSFrançois Tigeot 	wq_len = sizeof(struct guc_wq_item) / sizeof(u32) - 1;
514352ff8bdSFrançois Tigeot 	wqi->header = WQ_TYPE_INORDER |
515352ff8bdSFrançois Tigeot 			(wq_len << WQ_LEN_SHIFT) |
516*8621f407SFrançois Tigeot 			(rq->engine->guc_id << WQ_TARGET_SHIFT) |
517352ff8bdSFrançois Tigeot 			WQ_NO_WCFLUSH_WAIT;
518352ff8bdSFrançois Tigeot 
519352ff8bdSFrançois Tigeot 	/* The GuC wants only the low-order word of the context descriptor */
520*8621f407SFrançois Tigeot 	wqi->context_desc = (u32)intel_lr_context_descriptor(rq->ctx,
521*8621f407SFrançois Tigeot 							     rq->engine);
522352ff8bdSFrançois Tigeot 
523352ff8bdSFrançois Tigeot 	/* The GuC firmware wants the tail index in QWords, not bytes */
524352ff8bdSFrançois Tigeot 	tail = rq->ringbuf->tail >> 3;
525352ff8bdSFrançois Tigeot 	wqi->ring_tail = tail << WQ_RING_TAIL_SHIFT;
526352ff8bdSFrançois Tigeot 	wqi->fence_id = 0; /*XXX: what fence to be here */
527352ff8bdSFrançois Tigeot 
528352ff8bdSFrançois Tigeot 	kunmap_atomic(base);
529352ff8bdSFrançois Tigeot 
530352ff8bdSFrançois Tigeot 	return 0;
531352ff8bdSFrançois Tigeot }
532352ff8bdSFrançois Tigeot 
533352ff8bdSFrançois Tigeot /**
534352ff8bdSFrançois Tigeot  * i915_guc_submit() - Submit commands through GuC
535352ff8bdSFrançois Tigeot  * @client:	the guc client where commands will go through
536aee94f86SFrançois Tigeot  * @rq:		request associated with the commands
537352ff8bdSFrançois Tigeot  *
538352ff8bdSFrançois Tigeot  * Return:	0 if succeed
539352ff8bdSFrançois Tigeot  */
540352ff8bdSFrançois Tigeot int i915_guc_submit(struct i915_guc_client *client,
541352ff8bdSFrançois Tigeot 		    struct drm_i915_gem_request *rq)
542352ff8bdSFrançois Tigeot {
543352ff8bdSFrançois Tigeot 	struct intel_guc *guc = client->guc;
544*8621f407SFrançois Tigeot 	unsigned int engine_id = rq->engine->guc_id;
545352ff8bdSFrançois Tigeot 	int q_ret, b_ret;
546352ff8bdSFrançois Tigeot 
547352ff8bdSFrançois Tigeot 	q_ret = guc_add_workqueue_item(client, rq);
548352ff8bdSFrançois Tigeot 	if (q_ret == 0)
549352ff8bdSFrançois Tigeot 		b_ret = guc_ring_doorbell(client);
550352ff8bdSFrançois Tigeot 
551c0e85e96SFrançois Tigeot 	client->submissions[engine_id] += 1;
552352ff8bdSFrançois Tigeot 	if (q_ret) {
553352ff8bdSFrançois Tigeot 		client->q_fail += 1;
554352ff8bdSFrançois Tigeot 		client->retcode = q_ret;
555352ff8bdSFrançois Tigeot 	} else if (b_ret) {
556352ff8bdSFrançois Tigeot 		client->b_fail += 1;
557352ff8bdSFrançois Tigeot 		client->retcode = q_ret = b_ret;
558352ff8bdSFrançois Tigeot 	} else {
559352ff8bdSFrançois Tigeot 		client->retcode = 0;
560352ff8bdSFrançois Tigeot 	}
561c0e85e96SFrançois Tigeot 	guc->submissions[engine_id] += 1;
562c0e85e96SFrançois Tigeot 	guc->last_seqno[engine_id] = rq->seqno;
563352ff8bdSFrançois Tigeot 
564352ff8bdSFrançois Tigeot 	return q_ret;
565352ff8bdSFrançois Tigeot }
566352ff8bdSFrançois Tigeot 
567352ff8bdSFrançois Tigeot /*
568352ff8bdSFrançois Tigeot  * Everything below here is concerned with setup & teardown, and is
569352ff8bdSFrançois Tigeot  * therefore not part of the somewhat time-critical batch-submission
570352ff8bdSFrançois Tigeot  * path of i915_guc_submit() above.
571352ff8bdSFrançois Tigeot  */
572352ff8bdSFrançois Tigeot 
573352ff8bdSFrançois Tigeot /**
574352ff8bdSFrançois Tigeot  * gem_allocate_guc_obj() - Allocate gem object for GuC usage
575352ff8bdSFrançois Tigeot  * @dev:	drm device
576352ff8bdSFrançois Tigeot  * @size:	size of object
577352ff8bdSFrançois Tigeot  *
578352ff8bdSFrançois Tigeot  * This is a wrapper to create a gem obj. In order to use it inside GuC, the
579352ff8bdSFrançois Tigeot  * object needs to be pinned lifetime. Also we must pin it to gtt space other
580352ff8bdSFrançois Tigeot  * than [0, GUC_WOPCM_TOP) because this range is reserved inside GuC.
581352ff8bdSFrançois Tigeot  *
582352ff8bdSFrançois Tigeot  * Return:	A drm_i915_gem_object if successful, otherwise NULL.
583352ff8bdSFrançois Tigeot  */
584352ff8bdSFrançois Tigeot static struct drm_i915_gem_object *gem_allocate_guc_obj(struct drm_device *dev,
585352ff8bdSFrançois Tigeot 							u32 size)
586352ff8bdSFrançois Tigeot {
587352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
588352ff8bdSFrançois Tigeot 	struct drm_i915_gem_object *obj;
589352ff8bdSFrançois Tigeot 
590352ff8bdSFrançois Tigeot 	obj = i915_gem_alloc_object(dev, size);
591352ff8bdSFrançois Tigeot 	if (!obj)
592352ff8bdSFrançois Tigeot 		return NULL;
593352ff8bdSFrançois Tigeot 
594352ff8bdSFrançois Tigeot 	if (i915_gem_object_get_pages(obj)) {
595352ff8bdSFrançois Tigeot 		drm_gem_object_unreference(&obj->base);
596352ff8bdSFrançois Tigeot 		return NULL;
597352ff8bdSFrançois Tigeot 	}
598352ff8bdSFrançois Tigeot 
599352ff8bdSFrançois Tigeot 	if (i915_gem_obj_ggtt_pin(obj, PAGE_SIZE,
600352ff8bdSFrançois Tigeot 			PIN_OFFSET_BIAS | GUC_WOPCM_TOP)) {
601352ff8bdSFrançois Tigeot 		drm_gem_object_unreference(&obj->base);
602352ff8bdSFrançois Tigeot 		return NULL;
603352ff8bdSFrançois Tigeot 	}
604352ff8bdSFrançois Tigeot 
605352ff8bdSFrançois Tigeot 	/* Invalidate GuC TLB to let GuC take the latest updates to GTT. */
606352ff8bdSFrançois Tigeot 	I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE);
607352ff8bdSFrançois Tigeot 
608352ff8bdSFrançois Tigeot 	return obj;
609352ff8bdSFrançois Tigeot }
610352ff8bdSFrançois Tigeot 
611352ff8bdSFrançois Tigeot /**
612352ff8bdSFrançois Tigeot  * gem_release_guc_obj() - Release gem object allocated for GuC usage
613352ff8bdSFrançois Tigeot  * @obj:	gem obj to be released
614352ff8bdSFrançois Tigeot  */
615352ff8bdSFrançois Tigeot static void gem_release_guc_obj(struct drm_i915_gem_object *obj)
616352ff8bdSFrançois Tigeot {
617352ff8bdSFrançois Tigeot 	if (!obj)
618352ff8bdSFrançois Tigeot 		return;
619352ff8bdSFrançois Tigeot 
620352ff8bdSFrançois Tigeot 	if (i915_gem_obj_is_pinned(obj))
621352ff8bdSFrançois Tigeot 		i915_gem_object_ggtt_unpin(obj);
622352ff8bdSFrançois Tigeot 
623352ff8bdSFrançois Tigeot 	drm_gem_object_unreference(&obj->base);
624352ff8bdSFrançois Tigeot }
625352ff8bdSFrançois Tigeot 
626352ff8bdSFrançois Tigeot static void guc_client_free(struct drm_device *dev,
627352ff8bdSFrançois Tigeot 			    struct i915_guc_client *client)
628352ff8bdSFrançois Tigeot {
629352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
630352ff8bdSFrançois Tigeot 	struct intel_guc *guc = &dev_priv->guc;
631352ff8bdSFrançois Tigeot 
632352ff8bdSFrançois Tigeot 	if (!client)
633352ff8bdSFrançois Tigeot 		return;
634352ff8bdSFrançois Tigeot 
635352ff8bdSFrançois Tigeot 	/*
636*8621f407SFrançois Tigeot 	 * XXX: wait for any outstanding submissions before freeing memory.
637*8621f407SFrançois Tigeot 	 * Be sure to drop any locks
638352ff8bdSFrançois Tigeot 	 */
639*8621f407SFrançois Tigeot 
640*8621f407SFrançois Tigeot 	if (client->client_base) {
641*8621f407SFrançois Tigeot 		/*
642*8621f407SFrançois Tigeot 		 * If we got as far as setting up a doorbell, make sure
643*8621f407SFrançois Tigeot 		 * we shut it down before unmapping & deallocating the
644*8621f407SFrançois Tigeot 		 * memory. So first disable the doorbell, then tell the
645*8621f407SFrançois Tigeot 		 * GuC that we've finished with it, finally deallocate
646*8621f407SFrançois Tigeot 		 * it in our bitmap
647*8621f407SFrançois Tigeot 		 */
648*8621f407SFrançois Tigeot 		if (client->doorbell_id != GUC_INVALID_DOORBELL_ID) {
649352ff8bdSFrançois Tigeot 			guc_disable_doorbell(guc, client);
650352ff8bdSFrançois Tigeot 			host2guc_release_doorbell(guc, client);
651352ff8bdSFrançois Tigeot 			release_doorbell(guc, client->doorbell_id);
652352ff8bdSFrançois Tigeot 		}
653352ff8bdSFrançois Tigeot 
654*8621f407SFrançois Tigeot 		kunmap(kmap_to_page(client->client_base));
655*8621f407SFrançois Tigeot 	}
656352ff8bdSFrançois Tigeot 
657352ff8bdSFrançois Tigeot 	gem_release_guc_obj(client->client_obj);
658352ff8bdSFrançois Tigeot 
659352ff8bdSFrançois Tigeot 	if (client->ctx_index != GUC_INVALID_CTX_ID) {
660352ff8bdSFrançois Tigeot 		guc_fini_ctx_desc(guc, client);
661352ff8bdSFrançois Tigeot 		ida_simple_remove(&guc->ctx_ids, client->ctx_index);
662352ff8bdSFrançois Tigeot 	}
663352ff8bdSFrançois Tigeot 
664352ff8bdSFrançois Tigeot 	kfree(client);
665352ff8bdSFrançois Tigeot }
666352ff8bdSFrançois Tigeot 
667352ff8bdSFrançois Tigeot /**
668352ff8bdSFrançois Tigeot  * guc_client_alloc() - Allocate an i915_guc_client
669352ff8bdSFrançois Tigeot  * @dev:	drm device
670352ff8bdSFrançois Tigeot  * @priority:	four levels priority _CRITICAL, _HIGH, _NORMAL and _LOW
671352ff8bdSFrançois Tigeot  * 		The kernel client to replace ExecList submission is created with
672352ff8bdSFrançois Tigeot  * 		NORMAL priority. Priority of a client for scheduler can be HIGH,
673352ff8bdSFrançois Tigeot  * 		while a preemption context can use CRITICAL.
674aee94f86SFrançois Tigeot  * @ctx:	the context that owns the client (we use the default render
675aee94f86SFrançois Tigeot  * 		context)
676352ff8bdSFrançois Tigeot  *
677*8621f407SFrançois Tigeot  * Return:	An i915_guc_client object if success, else NULL.
678352ff8bdSFrançois Tigeot  */
679352ff8bdSFrançois Tigeot static struct i915_guc_client *guc_client_alloc(struct drm_device *dev,
680352ff8bdSFrançois Tigeot 						uint32_t priority,
681352ff8bdSFrançois Tigeot 						struct intel_context *ctx)
682352ff8bdSFrançois Tigeot {
683352ff8bdSFrançois Tigeot 	struct i915_guc_client *client;
684352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
685352ff8bdSFrançois Tigeot 	struct intel_guc *guc = &dev_priv->guc;
686352ff8bdSFrançois Tigeot 	struct drm_i915_gem_object *obj;
687352ff8bdSFrançois Tigeot 
688352ff8bdSFrançois Tigeot 	client = kzalloc(sizeof(*client), GFP_KERNEL);
689352ff8bdSFrançois Tigeot 	if (!client)
690352ff8bdSFrançois Tigeot 		return NULL;
691352ff8bdSFrançois Tigeot 
692352ff8bdSFrançois Tigeot 	client->doorbell_id = GUC_INVALID_DOORBELL_ID;
693352ff8bdSFrançois Tigeot 	client->priority = priority;
694352ff8bdSFrançois Tigeot 	client->owner = ctx;
695352ff8bdSFrançois Tigeot 	client->guc = guc;
696352ff8bdSFrançois Tigeot 
697352ff8bdSFrançois Tigeot 	client->ctx_index = (uint32_t)ida_simple_get(&guc->ctx_ids, 0,
698352ff8bdSFrançois Tigeot 			GUC_MAX_GPU_CONTEXTS, GFP_KERNEL);
699352ff8bdSFrançois Tigeot 	if (client->ctx_index >= GUC_MAX_GPU_CONTEXTS) {
700352ff8bdSFrançois Tigeot 		client->ctx_index = GUC_INVALID_CTX_ID;
701352ff8bdSFrançois Tigeot 		goto err;
702352ff8bdSFrançois Tigeot 	}
703352ff8bdSFrançois Tigeot 
704352ff8bdSFrançois Tigeot 	/* The first page is doorbell/proc_desc. Two followed pages are wq. */
705352ff8bdSFrançois Tigeot 	obj = gem_allocate_guc_obj(dev, GUC_DB_SIZE + GUC_WQ_SIZE);
706352ff8bdSFrançois Tigeot 	if (!obj)
707352ff8bdSFrançois Tigeot 		goto err;
708352ff8bdSFrançois Tigeot 
709*8621f407SFrançois Tigeot 	/* We'll keep just the first (doorbell/proc) page permanently kmap'd. */
710352ff8bdSFrançois Tigeot 	client->client_obj = obj;
711*8621f407SFrançois Tigeot 	client->client_base = kmap(i915_gem_object_get_page(obj, 0));
712352ff8bdSFrançois Tigeot 	client->wq_offset = GUC_DB_SIZE;
713352ff8bdSFrançois Tigeot 	client->wq_size = GUC_WQ_SIZE;
714352ff8bdSFrançois Tigeot 
715352ff8bdSFrançois Tigeot 	client->doorbell_offset = select_doorbell_cacheline(guc);
716352ff8bdSFrançois Tigeot 
717352ff8bdSFrançois Tigeot 	/*
718352ff8bdSFrançois Tigeot 	 * Since the doorbell only requires a single cacheline, we can save
719352ff8bdSFrançois Tigeot 	 * space by putting the application process descriptor in the same
720352ff8bdSFrançois Tigeot 	 * page. Use the half of the page that doesn't include the doorbell.
721352ff8bdSFrançois Tigeot 	 */
722352ff8bdSFrançois Tigeot 	if (client->doorbell_offset >= (GUC_DB_SIZE / 2))
723352ff8bdSFrançois Tigeot 		client->proc_desc_offset = 0;
724352ff8bdSFrançois Tigeot 	else
725352ff8bdSFrançois Tigeot 		client->proc_desc_offset = (GUC_DB_SIZE / 2);
726352ff8bdSFrançois Tigeot 
727352ff8bdSFrançois Tigeot 	client->doorbell_id = assign_doorbell(guc, client->priority);
728352ff8bdSFrançois Tigeot 	if (client->doorbell_id == GUC_INVALID_DOORBELL_ID)
729352ff8bdSFrançois Tigeot 		/* XXX: evict a doorbell instead */
730352ff8bdSFrançois Tigeot 		goto err;
731352ff8bdSFrançois Tigeot 
732352ff8bdSFrançois Tigeot 	guc_init_proc_desc(guc, client);
733352ff8bdSFrançois Tigeot 	guc_init_ctx_desc(guc, client);
734352ff8bdSFrançois Tigeot 	guc_init_doorbell(guc, client);
735352ff8bdSFrançois Tigeot 
736352ff8bdSFrançois Tigeot 	/* XXX: Any cache flushes needed? General domain mgmt calls? */
737352ff8bdSFrançois Tigeot 
738352ff8bdSFrançois Tigeot 	if (host2guc_allocate_doorbell(guc, client))
739352ff8bdSFrançois Tigeot 		goto err;
740352ff8bdSFrançois Tigeot 
741352ff8bdSFrançois Tigeot 	DRM_DEBUG_DRIVER("new priority %u client %p: ctx_index %u db_id %u\n",
742352ff8bdSFrançois Tigeot 		priority, client, client->ctx_index, client->doorbell_id);
743352ff8bdSFrançois Tigeot 
744352ff8bdSFrançois Tigeot 	return client;
745352ff8bdSFrançois Tigeot 
746352ff8bdSFrançois Tigeot err:
747352ff8bdSFrançois Tigeot 	DRM_ERROR("FAILED to create priority %u GuC client!\n", priority);
748352ff8bdSFrançois Tigeot 
749352ff8bdSFrançois Tigeot 	guc_client_free(dev, client);
750352ff8bdSFrançois Tigeot 	return NULL;
751352ff8bdSFrançois Tigeot }
752352ff8bdSFrançois Tigeot 
753352ff8bdSFrançois Tigeot static void guc_create_log(struct intel_guc *guc)
754352ff8bdSFrançois Tigeot {
755352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = guc_to_i915(guc);
756352ff8bdSFrançois Tigeot 	struct drm_i915_gem_object *obj;
757352ff8bdSFrançois Tigeot 	unsigned long offset;
758352ff8bdSFrançois Tigeot 	uint32_t size, flags;
759352ff8bdSFrançois Tigeot 
760352ff8bdSFrançois Tigeot 	if (i915.guc_log_level < GUC_LOG_VERBOSITY_MIN)
761352ff8bdSFrançois Tigeot 		return;
762352ff8bdSFrançois Tigeot 
763352ff8bdSFrançois Tigeot 	if (i915.guc_log_level > GUC_LOG_VERBOSITY_MAX)
764352ff8bdSFrançois Tigeot 		i915.guc_log_level = GUC_LOG_VERBOSITY_MAX;
765352ff8bdSFrançois Tigeot 
766352ff8bdSFrançois Tigeot 	/* The first page is to save log buffer state. Allocate one
767352ff8bdSFrançois Tigeot 	 * extra page for others in case for overlap */
768352ff8bdSFrançois Tigeot 	size = (1 + GUC_LOG_DPC_PAGES + 1 +
769352ff8bdSFrançois Tigeot 		GUC_LOG_ISR_PAGES + 1 +
770352ff8bdSFrançois Tigeot 		GUC_LOG_CRASH_PAGES + 1) << PAGE_SHIFT;
771352ff8bdSFrançois Tigeot 
772352ff8bdSFrançois Tigeot 	obj = guc->log_obj;
773352ff8bdSFrançois Tigeot 	if (!obj) {
774352ff8bdSFrançois Tigeot 		obj = gem_allocate_guc_obj(dev_priv->dev, size);
775352ff8bdSFrançois Tigeot 		if (!obj) {
776352ff8bdSFrançois Tigeot 			/* logging will be off */
777352ff8bdSFrançois Tigeot 			i915.guc_log_level = -1;
778352ff8bdSFrançois Tigeot 			return;
779352ff8bdSFrançois Tigeot 		}
780352ff8bdSFrançois Tigeot 
781352ff8bdSFrançois Tigeot 		guc->log_obj = obj;
782352ff8bdSFrançois Tigeot 	}
783352ff8bdSFrançois Tigeot 
784352ff8bdSFrançois Tigeot 	/* each allocated unit is a page */
785352ff8bdSFrançois Tigeot 	flags = GUC_LOG_VALID | GUC_LOG_NOTIFY_ON_HALF_FULL |
786352ff8bdSFrançois Tigeot 		(GUC_LOG_DPC_PAGES << GUC_LOG_DPC_SHIFT) |
787352ff8bdSFrançois Tigeot 		(GUC_LOG_ISR_PAGES << GUC_LOG_ISR_SHIFT) |
788352ff8bdSFrançois Tigeot 		(GUC_LOG_CRASH_PAGES << GUC_LOG_CRASH_SHIFT);
789352ff8bdSFrançois Tigeot 
790352ff8bdSFrançois Tigeot 	offset = i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT; /* in pages */
791352ff8bdSFrançois Tigeot 	guc->log_flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags;
792352ff8bdSFrançois Tigeot }
793352ff8bdSFrançois Tigeot 
794c0e85e96SFrançois Tigeot static void init_guc_policies(struct guc_policies *policies)
795c0e85e96SFrançois Tigeot {
796c0e85e96SFrançois Tigeot 	struct guc_policy *policy;
797c0e85e96SFrançois Tigeot 	u32 p, i;
798c0e85e96SFrançois Tigeot 
799c0e85e96SFrançois Tigeot 	policies->dpc_promote_time = 500000;
800c0e85e96SFrançois Tigeot 	policies->max_num_work_items = POLICY_MAX_NUM_WI;
801c0e85e96SFrançois Tigeot 
802c0e85e96SFrançois Tigeot 	for (p = 0; p < GUC_CTX_PRIORITY_NUM; p++) {
803c0e85e96SFrançois Tigeot 		for (i = GUC_RENDER_ENGINE; i < GUC_MAX_ENGINES_NUM; i++) {
804c0e85e96SFrançois Tigeot 			policy = &policies->policy[p][i];
805c0e85e96SFrançois Tigeot 
806c0e85e96SFrançois Tigeot 			policy->execution_quantum = 1000000;
807c0e85e96SFrançois Tigeot 			policy->preemption_time = 500000;
808c0e85e96SFrançois Tigeot 			policy->fault_time = 250000;
809c0e85e96SFrançois Tigeot 			policy->policy_flags = 0;
810c0e85e96SFrançois Tigeot 		}
811c0e85e96SFrançois Tigeot 	}
812c0e85e96SFrançois Tigeot 
813c0e85e96SFrançois Tigeot 	policies->is_valid = 1;
814c0e85e96SFrançois Tigeot }
815c0e85e96SFrançois Tigeot 
816c0e85e96SFrançois Tigeot static void guc_create_ads(struct intel_guc *guc)
817c0e85e96SFrançois Tigeot {
818c0e85e96SFrançois Tigeot 	struct drm_i915_private *dev_priv = guc_to_i915(guc);
819c0e85e96SFrançois Tigeot 	struct drm_i915_gem_object *obj;
820c0e85e96SFrançois Tigeot 	struct guc_ads *ads;
821c0e85e96SFrançois Tigeot 	struct guc_policies *policies;
822c0e85e96SFrançois Tigeot 	struct guc_mmio_reg_state *reg_state;
823*8621f407SFrançois Tigeot 	struct intel_engine_cs *engine;
824c0e85e96SFrançois Tigeot 	struct vm_page *page;
825*8621f407SFrançois Tigeot 	u32 size;
826c0e85e96SFrançois Tigeot 
827c0e85e96SFrançois Tigeot 	/* The ads obj includes the struct itself and buffers passed to GuC */
828c0e85e96SFrançois Tigeot 	size = sizeof(struct guc_ads) + sizeof(struct guc_policies) +
829c0e85e96SFrançois Tigeot 			sizeof(struct guc_mmio_reg_state) +
830c0e85e96SFrançois Tigeot 			GUC_S3_SAVE_SPACE_PAGES * PAGE_SIZE;
831c0e85e96SFrançois Tigeot 
832c0e85e96SFrançois Tigeot 	obj = guc->ads_obj;
833c0e85e96SFrançois Tigeot 	if (!obj) {
834c0e85e96SFrançois Tigeot 		obj = gem_allocate_guc_obj(dev_priv->dev, PAGE_ALIGN(size));
835c0e85e96SFrançois Tigeot 		if (!obj)
836c0e85e96SFrançois Tigeot 			return;
837c0e85e96SFrançois Tigeot 
838c0e85e96SFrançois Tigeot 		guc->ads_obj = obj;
839c0e85e96SFrançois Tigeot 	}
840c0e85e96SFrançois Tigeot 
841c0e85e96SFrançois Tigeot 	page = i915_gem_object_get_page(obj, 0);
842c0e85e96SFrançois Tigeot 	ads = kmap(page);
843c0e85e96SFrançois Tigeot 
844c0e85e96SFrançois Tigeot 	/*
845c0e85e96SFrançois Tigeot 	 * The GuC requires a "Golden Context" when it reinitialises
846c0e85e96SFrançois Tigeot 	 * engines after a reset. Here we use the Render ring default
847c0e85e96SFrançois Tigeot 	 * context, which must already exist and be pinned in the GGTT,
848c0e85e96SFrançois Tigeot 	 * so its address won't change after we've told the GuC where
849c0e85e96SFrançois Tigeot 	 * to find it.
850c0e85e96SFrançois Tigeot 	 */
851*8621f407SFrançois Tigeot 	engine = &dev_priv->engine[RCS];
852*8621f407SFrançois Tigeot 	ads->golden_context_lrca = engine->status_page.gfx_addr;
853c0e85e96SFrançois Tigeot 
854*8621f407SFrançois Tigeot 	for_each_engine(engine, dev_priv)
855*8621f407SFrançois Tigeot 		ads->eng_state_size[engine->guc_id] = intel_lr_context_size(engine);
856c0e85e96SFrançois Tigeot 
857c0e85e96SFrançois Tigeot 	/* GuC scheduling policies */
858*8621f407SFrançois Tigeot 	policies = (struct guc_policies *)((char *)ads + sizeof(struct guc_ads));
859c0e85e96SFrançois Tigeot 	init_guc_policies(policies);
860c0e85e96SFrançois Tigeot 
861c0e85e96SFrançois Tigeot 	ads->scheduler_policies = i915_gem_obj_ggtt_offset(obj) +
862c0e85e96SFrançois Tigeot 			sizeof(struct guc_ads);
863c0e85e96SFrançois Tigeot 
864c0e85e96SFrançois Tigeot 	/* MMIO reg state */
865*8621f407SFrançois Tigeot 	reg_state = (struct guc_mmio_reg_state *)((char *)policies + sizeof(struct guc_policies));
866c0e85e96SFrançois Tigeot 
867*8621f407SFrançois Tigeot 	for_each_engine(engine, dev_priv) {
868*8621f407SFrançois Tigeot 		reg_state->mmio_white_list[engine->guc_id].mmio_start =
869*8621f407SFrançois Tigeot 			engine->mmio_base + GUC_MMIO_WHITE_LIST_START;
870c0e85e96SFrançois Tigeot 
871c0e85e96SFrançois Tigeot 		/* Nothing to be saved or restored for now. */
872*8621f407SFrançois Tigeot 		reg_state->mmio_white_list[engine->guc_id].count = 0;
873c0e85e96SFrançois Tigeot 	}
874c0e85e96SFrançois Tigeot 
875c0e85e96SFrançois Tigeot 	ads->reg_state_addr = ads->scheduler_policies +
876c0e85e96SFrançois Tigeot 			sizeof(struct guc_policies);
877c0e85e96SFrançois Tigeot 
878c0e85e96SFrançois Tigeot 	ads->reg_state_buffer = ads->reg_state_addr +
879c0e85e96SFrançois Tigeot 			sizeof(struct guc_mmio_reg_state);
880c0e85e96SFrançois Tigeot 
881c0e85e96SFrançois Tigeot 	kunmap(page);
882c0e85e96SFrançois Tigeot }
883c0e85e96SFrançois Tigeot 
884352ff8bdSFrançois Tigeot /*
885352ff8bdSFrançois Tigeot  * Set up the memory resources to be shared with the GuC.  At this point,
886352ff8bdSFrançois Tigeot  * we require just one object that can be mapped through the GGTT.
887352ff8bdSFrançois Tigeot  */
888352ff8bdSFrançois Tigeot int i915_guc_submission_init(struct drm_device *dev)
889352ff8bdSFrançois Tigeot {
890352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
891352ff8bdSFrançois Tigeot 	const size_t ctxsize = sizeof(struct guc_context_desc);
892352ff8bdSFrançois Tigeot 	const size_t poolsize = GUC_MAX_GPU_CONTEXTS * ctxsize;
893352ff8bdSFrançois Tigeot 	const size_t gemsize = round_up(poolsize, PAGE_SIZE);
894352ff8bdSFrançois Tigeot 	struct intel_guc *guc = &dev_priv->guc;
895352ff8bdSFrançois Tigeot 
896352ff8bdSFrançois Tigeot 	if (!i915.enable_guc_submission)
897352ff8bdSFrançois Tigeot 		return 0; /* not enabled  */
898352ff8bdSFrançois Tigeot 
899352ff8bdSFrançois Tigeot 	if (guc->ctx_pool_obj)
900352ff8bdSFrançois Tigeot 		return 0; /* already allocated */
901352ff8bdSFrançois Tigeot 
902352ff8bdSFrançois Tigeot 	guc->ctx_pool_obj = gem_allocate_guc_obj(dev_priv->dev, gemsize);
903352ff8bdSFrançois Tigeot 	if (!guc->ctx_pool_obj)
904352ff8bdSFrançois Tigeot 		return -ENOMEM;
905352ff8bdSFrançois Tigeot 
906352ff8bdSFrançois Tigeot 	ida_init(&guc->ctx_ids);
907352ff8bdSFrançois Tigeot 
908352ff8bdSFrançois Tigeot 	guc_create_log(guc);
909352ff8bdSFrançois Tigeot 
910c0e85e96SFrançois Tigeot 	guc_create_ads(guc);
911c0e85e96SFrançois Tigeot 
912352ff8bdSFrançois Tigeot 	return 0;
913352ff8bdSFrançois Tigeot }
914352ff8bdSFrançois Tigeot 
915352ff8bdSFrançois Tigeot int i915_guc_submission_enable(struct drm_device *dev)
916352ff8bdSFrançois Tigeot {
917352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
918352ff8bdSFrançois Tigeot 	struct intel_guc *guc = &dev_priv->guc;
919c0e85e96SFrançois Tigeot 	struct intel_context *ctx = dev_priv->kernel_context;
920352ff8bdSFrançois Tigeot 	struct i915_guc_client *client;
921352ff8bdSFrançois Tigeot 
922352ff8bdSFrançois Tigeot 	/* client for execbuf submission */
923352ff8bdSFrançois Tigeot 	client = guc_client_alloc(dev, GUC_CTX_PRIORITY_KMD_NORMAL, ctx);
924352ff8bdSFrançois Tigeot 	if (!client) {
925352ff8bdSFrançois Tigeot 		DRM_ERROR("Failed to create execbuf guc_client\n");
926352ff8bdSFrançois Tigeot 		return -ENOMEM;
927352ff8bdSFrançois Tigeot 	}
928352ff8bdSFrançois Tigeot 
929352ff8bdSFrançois Tigeot 	guc->execbuf_client = client;
930352ff8bdSFrançois Tigeot 
931352ff8bdSFrançois Tigeot 	host2guc_sample_forcewake(guc, client);
932352ff8bdSFrançois Tigeot 
933352ff8bdSFrançois Tigeot 	return 0;
934352ff8bdSFrançois Tigeot }
935352ff8bdSFrançois Tigeot 
936352ff8bdSFrançois Tigeot void i915_guc_submission_disable(struct drm_device *dev)
937352ff8bdSFrançois Tigeot {
938352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
939352ff8bdSFrançois Tigeot 	struct intel_guc *guc = &dev_priv->guc;
940352ff8bdSFrançois Tigeot 
941352ff8bdSFrançois Tigeot 	guc_client_free(dev, guc->execbuf_client);
942352ff8bdSFrançois Tigeot 	guc->execbuf_client = NULL;
943352ff8bdSFrançois Tigeot }
944352ff8bdSFrançois Tigeot 
945352ff8bdSFrançois Tigeot void i915_guc_submission_fini(struct drm_device *dev)
946352ff8bdSFrançois Tigeot {
947352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
948352ff8bdSFrançois Tigeot 	struct intel_guc *guc = &dev_priv->guc;
949352ff8bdSFrançois Tigeot 
950c0e85e96SFrançois Tigeot 	gem_release_guc_obj(dev_priv->guc.ads_obj);
951c0e85e96SFrançois Tigeot 	guc->ads_obj = NULL;
952c0e85e96SFrançois Tigeot 
953352ff8bdSFrançois Tigeot 	gem_release_guc_obj(dev_priv->guc.log_obj);
954352ff8bdSFrançois Tigeot 	guc->log_obj = NULL;
955352ff8bdSFrançois Tigeot 
956352ff8bdSFrançois Tigeot 	if (guc->ctx_pool_obj)
957352ff8bdSFrançois Tigeot 		ida_destroy(&guc->ctx_ids);
958352ff8bdSFrançois Tigeot 	gem_release_guc_obj(guc->ctx_pool_obj);
959352ff8bdSFrançois Tigeot 	guc->ctx_pool_obj = NULL;
960352ff8bdSFrançois Tigeot }
961352ff8bdSFrançois Tigeot 
962352ff8bdSFrançois Tigeot /**
963352ff8bdSFrançois Tigeot  * intel_guc_suspend() - notify GuC entering suspend state
964352ff8bdSFrançois Tigeot  * @dev:	drm device
965352ff8bdSFrançois Tigeot  */
966352ff8bdSFrançois Tigeot int intel_guc_suspend(struct drm_device *dev)
967352ff8bdSFrançois Tigeot {
968352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
969352ff8bdSFrançois Tigeot 	struct intel_guc *guc = &dev_priv->guc;
970352ff8bdSFrançois Tigeot 	struct intel_context *ctx;
971352ff8bdSFrançois Tigeot 	u32 data[3];
972352ff8bdSFrançois Tigeot 
973352ff8bdSFrançois Tigeot 	if (!i915.enable_guc_submission)
974352ff8bdSFrançois Tigeot 		return 0;
975352ff8bdSFrançois Tigeot 
976c0e85e96SFrançois Tigeot 	ctx = dev_priv->kernel_context;
977352ff8bdSFrançois Tigeot 
978352ff8bdSFrançois Tigeot 	data[0] = HOST2GUC_ACTION_ENTER_S_STATE;
979352ff8bdSFrançois Tigeot 	/* any value greater than GUC_POWER_D0 */
980352ff8bdSFrançois Tigeot 	data[1] = GUC_POWER_D1;
981352ff8bdSFrançois Tigeot 	/* first page is shared data with GuC */
982352ff8bdSFrançois Tigeot 	data[2] = i915_gem_obj_ggtt_offset(ctx->engine[RCS].state);
983352ff8bdSFrançois Tigeot 
984352ff8bdSFrançois Tigeot 	return host2guc_action(guc, data, ARRAY_SIZE(data));
985352ff8bdSFrançois Tigeot }
986352ff8bdSFrançois Tigeot 
987352ff8bdSFrançois Tigeot 
988352ff8bdSFrançois Tigeot /**
989352ff8bdSFrançois Tigeot  * intel_guc_resume() - notify GuC resuming from suspend state
990352ff8bdSFrançois Tigeot  * @dev:	drm device
991352ff8bdSFrançois Tigeot  */
992352ff8bdSFrançois Tigeot int intel_guc_resume(struct drm_device *dev)
993352ff8bdSFrançois Tigeot {
994352ff8bdSFrançois Tigeot 	struct drm_i915_private *dev_priv = dev->dev_private;
995352ff8bdSFrançois Tigeot 	struct intel_guc *guc = &dev_priv->guc;
996352ff8bdSFrançois Tigeot 	struct intel_context *ctx;
997352ff8bdSFrançois Tigeot 	u32 data[3];
998352ff8bdSFrançois Tigeot 
999352ff8bdSFrançois Tigeot 	if (!i915.enable_guc_submission)
1000352ff8bdSFrançois Tigeot 		return 0;
1001352ff8bdSFrançois Tigeot 
1002c0e85e96SFrançois Tigeot 	ctx = dev_priv->kernel_context;
1003352ff8bdSFrançois Tigeot 
1004352ff8bdSFrançois Tigeot 	data[0] = HOST2GUC_ACTION_EXIT_S_STATE;
1005352ff8bdSFrançois Tigeot 	data[1] = GUC_POWER_D0;
1006352ff8bdSFrançois Tigeot 	/* first page is shared data with GuC */
1007352ff8bdSFrançois Tigeot 	data[2] = i915_gem_obj_ggtt_offset(ctx->engine[RCS].state);
1008352ff8bdSFrançois Tigeot 
1009352ff8bdSFrançois Tigeot 	return host2guc_action(guc, data, ARRAY_SIZE(data));
1010352ff8bdSFrançois Tigeot }
1011