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