157692c94SEric Anholt // SPDX-License-Identifier: GPL-2.0+ 257692c94SEric Anholt /* Copyright (C) 2014-2018 Broadcom */ 357692c94SEric Anholt 457692c94SEric Anholt #include <drm/drmP.h> 557692c94SEric Anholt #include <drm/drm_syncobj.h> 657692c94SEric Anholt #include <linux/module.h> 757692c94SEric Anholt #include <linux/platform_device.h> 857692c94SEric Anholt #include <linux/pm_runtime.h> 9eea9b97bSEric Anholt #include <linux/reset.h> 1057692c94SEric Anholt #include <linux/device.h> 1157692c94SEric Anholt #include <linux/io.h> 1257692c94SEric Anholt #include <linux/sched/signal.h> 1357692c94SEric Anholt 1457692c94SEric Anholt #include "uapi/drm/v3d_drm.h" 1557692c94SEric Anholt #include "v3d_drv.h" 1657692c94SEric Anholt #include "v3d_regs.h" 1757692c94SEric Anholt #include "v3d_trace.h" 1857692c94SEric Anholt 1957692c94SEric Anholt static void 2057692c94SEric Anholt v3d_init_core(struct v3d_dev *v3d, int core) 2157692c94SEric Anholt { 2257692c94SEric Anholt /* Set OVRTMUOUT, which means that the texture sampler uniform 2357692c94SEric Anholt * configuration's tmu output type field is used, instead of 2457692c94SEric Anholt * using the hardware default behavior based on the texture 2557692c94SEric Anholt * type. If you want the default behavior, you can still put 2657692c94SEric Anholt * "2" in the indirect texture state's output_type field. 2757692c94SEric Anholt */ 28a7dde1b7SEric Anholt if (v3d->ver < 40) 2957692c94SEric Anholt V3D_CORE_WRITE(core, V3D_CTL_MISCCFG, V3D_MISCCFG_OVRTMUOUT); 3057692c94SEric Anholt 3157692c94SEric Anholt /* Whenever we flush the L2T cache, we always want to flush 3257692c94SEric Anholt * the whole thing. 3357692c94SEric Anholt */ 3457692c94SEric Anholt V3D_CORE_WRITE(core, V3D_CTL_L2TFLSTA, 0); 3557692c94SEric Anholt V3D_CORE_WRITE(core, V3D_CTL_L2TFLEND, ~0); 3657692c94SEric Anholt } 3757692c94SEric Anholt 3857692c94SEric Anholt /* Sets invariant state for the HW. */ 3957692c94SEric Anholt static void 4057692c94SEric Anholt v3d_init_hw_state(struct v3d_dev *v3d) 4157692c94SEric Anholt { 4257692c94SEric Anholt v3d_init_core(v3d, 0); 4357692c94SEric Anholt } 4457692c94SEric Anholt 4557692c94SEric Anholt static void 4657692c94SEric Anholt v3d_idle_axi(struct v3d_dev *v3d, int core) 4757692c94SEric Anholt { 4857692c94SEric Anholt V3D_CORE_WRITE(core, V3D_GMP_CFG, V3D_GMP_CFG_STOP_REQ); 4957692c94SEric Anholt 5057692c94SEric Anholt if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS) & 5157692c94SEric Anholt (V3D_GMP_STATUS_RD_COUNT_MASK | 5257692c94SEric Anholt V3D_GMP_STATUS_WR_COUNT_MASK | 5357692c94SEric Anholt V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) { 5457692c94SEric Anholt DRM_ERROR("Failed to wait for safe GMP shutdown\n"); 5557692c94SEric Anholt } 5657692c94SEric Anholt } 5757692c94SEric Anholt 5857692c94SEric Anholt static void 5957692c94SEric Anholt v3d_idle_gca(struct v3d_dev *v3d) 6057692c94SEric Anholt { 6157692c94SEric Anholt if (v3d->ver >= 41) 6257692c94SEric Anholt return; 6357692c94SEric Anholt 6457692c94SEric Anholt V3D_GCA_WRITE(V3D_GCA_SAFE_SHUTDOWN, V3D_GCA_SAFE_SHUTDOWN_EN); 6557692c94SEric Anholt 6657692c94SEric Anholt if (wait_for((V3D_GCA_READ(V3D_GCA_SAFE_SHUTDOWN_ACK) & 6757692c94SEric Anholt V3D_GCA_SAFE_SHUTDOWN_ACK_ACKED) == 6857692c94SEric Anholt V3D_GCA_SAFE_SHUTDOWN_ACK_ACKED, 100)) { 6957692c94SEric Anholt DRM_ERROR("Failed to wait for safe GCA shutdown\n"); 7057692c94SEric Anholt } 7157692c94SEric Anholt } 7257692c94SEric Anholt 7357692c94SEric Anholt static void 74eea9b97bSEric Anholt v3d_reset_by_bridge(struct v3d_dev *v3d) 7557692c94SEric Anholt { 7657692c94SEric Anholt int version = V3D_BRIDGE_READ(V3D_TOP_GR_BRIDGE_REVISION); 7757692c94SEric Anholt 7857692c94SEric Anholt if (V3D_GET_FIELD(version, V3D_TOP_GR_BRIDGE_MAJOR) == 2) { 7957692c94SEric Anholt V3D_BRIDGE_WRITE(V3D_TOP_GR_BRIDGE_SW_INIT_0, 8057692c94SEric Anholt V3D_TOP_GR_BRIDGE_SW_INIT_0_V3D_CLK_108_SW_INIT); 8157692c94SEric Anholt V3D_BRIDGE_WRITE(V3D_TOP_GR_BRIDGE_SW_INIT_0, 0); 8257692c94SEric Anholt 8357692c94SEric Anholt /* GFXH-1383: The SW_INIT may cause a stray write to address 0 8457692c94SEric Anholt * of the unit, so reset it to its power-on value here. 8557692c94SEric Anholt */ 8657692c94SEric Anholt V3D_WRITE(V3D_HUB_AXICFG, V3D_HUB_AXICFG_MAX_LEN_MASK); 8757692c94SEric Anholt } else { 8857692c94SEric Anholt WARN_ON_ONCE(V3D_GET_FIELD(version, 8957692c94SEric Anholt V3D_TOP_GR_BRIDGE_MAJOR) != 7); 9057692c94SEric Anholt V3D_BRIDGE_WRITE(V3D_TOP_GR_BRIDGE_SW_INIT_1, 9157692c94SEric Anholt V3D_TOP_GR_BRIDGE_SW_INIT_1_V3D_CLK_108_SW_INIT); 9257692c94SEric Anholt V3D_BRIDGE_WRITE(V3D_TOP_GR_BRIDGE_SW_INIT_1, 0); 9357692c94SEric Anholt } 94eea9b97bSEric Anholt } 95eea9b97bSEric Anholt 96eea9b97bSEric Anholt static void 97eea9b97bSEric Anholt v3d_reset_v3d(struct v3d_dev *v3d) 98eea9b97bSEric Anholt { 99eea9b97bSEric Anholt if (v3d->reset) 100eea9b97bSEric Anholt reset_control_reset(v3d->reset); 101eea9b97bSEric Anholt else 102eea9b97bSEric Anholt v3d_reset_by_bridge(v3d); 10357692c94SEric Anholt 10457692c94SEric Anholt v3d_init_hw_state(v3d); 10557692c94SEric Anholt } 10657692c94SEric Anholt 10757692c94SEric Anholt void 10857692c94SEric Anholt v3d_reset(struct v3d_dev *v3d) 10957692c94SEric Anholt { 11057692c94SEric Anholt struct drm_device *dev = &v3d->drm; 11157692c94SEric Anholt 11257692c94SEric Anholt DRM_ERROR("Resetting GPU.\n"); 11357692c94SEric Anholt trace_v3d_reset_begin(dev); 11457692c94SEric Anholt 11557692c94SEric Anholt /* XXX: only needed for safe powerdown, not reset. */ 11657692c94SEric Anholt if (false) 11757692c94SEric Anholt v3d_idle_axi(v3d, 0); 11857692c94SEric Anholt 11957692c94SEric Anholt v3d_idle_gca(v3d); 12057692c94SEric Anholt v3d_reset_v3d(v3d); 12157692c94SEric Anholt 12257692c94SEric Anholt v3d_mmu_set_page_table(v3d); 12357692c94SEric Anholt v3d_irq_reset(v3d); 12457692c94SEric Anholt 12557692c94SEric Anholt trace_v3d_reset_end(dev); 12657692c94SEric Anholt } 12757692c94SEric Anholt 12857692c94SEric Anholt static void 12957692c94SEric Anholt v3d_flush_l3(struct v3d_dev *v3d) 13057692c94SEric Anholt { 13157692c94SEric Anholt if (v3d->ver < 41) { 13257692c94SEric Anholt u32 gca_ctrl = V3D_GCA_READ(V3D_GCA_CACHE_CTRL); 13357692c94SEric Anholt 13457692c94SEric Anholt V3D_GCA_WRITE(V3D_GCA_CACHE_CTRL, 13557692c94SEric Anholt gca_ctrl | V3D_GCA_CACHE_CTRL_FLUSH); 13657692c94SEric Anholt 13757692c94SEric Anholt if (v3d->ver < 33) { 13857692c94SEric Anholt V3D_GCA_WRITE(V3D_GCA_CACHE_CTRL, 13957692c94SEric Anholt gca_ctrl & ~V3D_GCA_CACHE_CTRL_FLUSH); 14057692c94SEric Anholt } 14157692c94SEric Anholt } 14257692c94SEric Anholt } 14357692c94SEric Anholt 1447b9d2fe4SEric Anholt /* Invalidates the (read-only) L2C cache. This was the L2 cache for 1457b9d2fe4SEric Anholt * uniforms and instructions on V3D 3.2. 1467b9d2fe4SEric Anholt */ 14757692c94SEric Anholt static void 1487b9d2fe4SEric Anholt v3d_invalidate_l2c(struct v3d_dev *v3d, int core) 14957692c94SEric Anholt { 1507b9d2fe4SEric Anholt if (v3d->ver > 32) 1517b9d2fe4SEric Anholt return; 1527b9d2fe4SEric Anholt 15357692c94SEric Anholt V3D_CORE_WRITE(core, V3D_CTL_L2CACTL, 15457692c94SEric Anholt V3D_L2CACTL_L2CCLR | 15557692c94SEric Anholt V3D_L2CACTL_L2CENA); 15657692c94SEric Anholt } 15757692c94SEric Anholt 15857692c94SEric Anholt /* Invalidates texture L2 cachelines */ 15957692c94SEric Anholt static void 16057692c94SEric Anholt v3d_flush_l2t(struct v3d_dev *v3d, int core) 16157692c94SEric Anholt { 16251c1b6f9SEric Anholt /* While there is a busy bit (V3D_L2TCACTL_L2TFLS), we don't 16351c1b6f9SEric Anholt * need to wait for completion before dispatching the job -- 16451c1b6f9SEric Anholt * L2T accesses will be stalled until the flush has completed. 16551c1b6f9SEric Anholt */ 16657692c94SEric Anholt V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, 16757692c94SEric Anholt V3D_L2TCACTL_L2TFLS | 16857692c94SEric Anholt V3D_SET_FIELD(V3D_L2TCACTL_FLM_FLUSH, V3D_L2TCACTL_FLM)); 16957692c94SEric Anholt } 17057692c94SEric Anholt 17157692c94SEric Anholt /* Invalidates the slice caches. These are read-only caches. */ 17257692c94SEric Anholt static void 17357692c94SEric Anholt v3d_invalidate_slices(struct v3d_dev *v3d, int core) 17457692c94SEric Anholt { 17557692c94SEric Anholt V3D_CORE_WRITE(core, V3D_CTL_SLCACTL, 17657692c94SEric Anholt V3D_SET_FIELD(0xf, V3D_SLCACTL_TVCCS) | 17757692c94SEric Anholt V3D_SET_FIELD(0xf, V3D_SLCACTL_TDCCS) | 17857692c94SEric Anholt V3D_SET_FIELD(0xf, V3D_SLCACTL_UCC) | 17957692c94SEric Anholt V3D_SET_FIELD(0xf, V3D_SLCACTL_ICC)); 18057692c94SEric Anholt } 18157692c94SEric Anholt 18257692c94SEric Anholt void 18357692c94SEric Anholt v3d_invalidate_caches(struct v3d_dev *v3d) 18457692c94SEric Anholt { 185aa5beec3SEric Anholt /* Invalidate the caches from the outside in. That way if 186aa5beec3SEric Anholt * another CL's concurrent use of nearby memory were to pull 187aa5beec3SEric Anholt * an invalidated cacheline back in, we wouldn't leave stale 188aa5beec3SEric Anholt * data in the inner cache. 189aa5beec3SEric Anholt */ 19057692c94SEric Anholt v3d_flush_l3(v3d); 1917b9d2fe4SEric Anholt v3d_invalidate_l2c(v3d, 0); 19257692c94SEric Anholt v3d_flush_l2t(v3d, 0); 193aa5beec3SEric Anholt v3d_invalidate_slices(v3d, 0); 19457692c94SEric Anholt } 19557692c94SEric Anholt 19657692c94SEric Anholt /* Takes the reservation lock on all the BOs being referenced, so that 19757692c94SEric Anholt * at queue submit time we can update the reservations. 19857692c94SEric Anholt * 19957692c94SEric Anholt * We don't lock the RCL the tile alloc/state BOs, or overflow memory 20057692c94SEric Anholt * (all of which are on exec->unref_list). They're entirely private 20157692c94SEric Anholt * to v3d, so we don't attach dma-buf fences to them. 20257692c94SEric Anholt */ 20357692c94SEric Anholt static int 204d4c3022aSEric Anholt v3d_lock_bo_reservations(struct drm_gem_object **bos, 2051584f16cSEric Anholt int bo_count, 20657692c94SEric Anholt struct ww_acquire_ctx *acquire_ctx) 20757692c94SEric Anholt { 20857692c94SEric Anholt int i, ret; 20957692c94SEric Anholt 210d4c3022aSEric Anholt ret = drm_gem_lock_reservations(bos, bo_count, acquire_ctx); 211c2b3e61aSEric Anholt if (ret) 21257692c94SEric Anholt return ret; 21357692c94SEric Anholt 21457692c94SEric Anholt /* Reserve space for our shared (read-only) fence references, 21557692c94SEric Anholt * before we commit the CL to the hardware. 21657692c94SEric Anholt */ 2171584f16cSEric Anholt for (i = 0; i < bo_count; i++) { 218d4c3022aSEric Anholt ret = reservation_object_reserve_shared(bos[i]->resv, 1); 21957692c94SEric Anholt if (ret) { 220d4c3022aSEric Anholt drm_gem_unlock_reservations(bos, bo_count, 2211584f16cSEric Anholt acquire_ctx); 22257692c94SEric Anholt return ret; 22357692c94SEric Anholt } 22457692c94SEric Anholt } 22557692c94SEric Anholt 22657692c94SEric Anholt return 0; 22757692c94SEric Anholt } 22857692c94SEric Anholt 22957692c94SEric Anholt /** 230*a783a09eSEric Anholt * v3d_lookup_bos() - Sets up job->bo[] with the GEM objects 23157692c94SEric Anholt * referenced by the job. 23257692c94SEric Anholt * @dev: DRM device 23357692c94SEric Anholt * @file_priv: DRM file for this fd 234*a783a09eSEric Anholt * @job: V3D job being set up 23557692c94SEric Anholt * 23657692c94SEric Anholt * The command validator needs to reference BOs by their index within 23757692c94SEric Anholt * the submitted job's BO list. This does the validation of the job's 23857692c94SEric Anholt * BO list and reference counting for the lifetime of the job. 23957692c94SEric Anholt * 24057692c94SEric Anholt * Note that this function doesn't need to unreference the BOs on 24157692c94SEric Anholt * failure, because that will happen at v3d_exec_cleanup() time. 24257692c94SEric Anholt */ 24357692c94SEric Anholt static int 244*a783a09eSEric Anholt v3d_lookup_bos(struct drm_device *dev, 24557692c94SEric Anholt struct drm_file *file_priv, 246*a783a09eSEric Anholt struct v3d_job *job, 247*a783a09eSEric Anholt u64 bo_handles, 248*a783a09eSEric Anholt u32 bo_count) 24957692c94SEric Anholt { 25057692c94SEric Anholt u32 *handles; 25157692c94SEric Anholt int ret = 0; 25257692c94SEric Anholt int i; 25357692c94SEric Anholt 254*a783a09eSEric Anholt job->bo_count = bo_count; 25557692c94SEric Anholt 256*a783a09eSEric Anholt if (!job->bo_count) { 25757692c94SEric Anholt /* See comment on bo_index for why we have to check 25857692c94SEric Anholt * this. 25957692c94SEric Anholt */ 26057692c94SEric Anholt DRM_DEBUG("Rendering requires BOs\n"); 26157692c94SEric Anholt return -EINVAL; 26257692c94SEric Anholt } 26357692c94SEric Anholt 264*a783a09eSEric Anholt job->bo = kvmalloc_array(job->bo_count, 26557692c94SEric Anholt sizeof(struct drm_gem_cma_object *), 26657692c94SEric Anholt GFP_KERNEL | __GFP_ZERO); 267*a783a09eSEric Anholt if (!job->bo) { 26857692c94SEric Anholt DRM_DEBUG("Failed to allocate validated BO pointers\n"); 26957692c94SEric Anholt return -ENOMEM; 27057692c94SEric Anholt } 27157692c94SEric Anholt 272*a783a09eSEric Anholt handles = kvmalloc_array(job->bo_count, sizeof(u32), GFP_KERNEL); 27357692c94SEric Anholt if (!handles) { 27457692c94SEric Anholt ret = -ENOMEM; 27557692c94SEric Anholt DRM_DEBUG("Failed to allocate incoming GEM handles\n"); 27657692c94SEric Anholt goto fail; 27757692c94SEric Anholt } 27857692c94SEric Anholt 27957692c94SEric Anholt if (copy_from_user(handles, 280*a783a09eSEric Anholt (void __user *)(uintptr_t)bo_handles, 281*a783a09eSEric Anholt job->bo_count * sizeof(u32))) { 28257692c94SEric Anholt ret = -EFAULT; 28357692c94SEric Anholt DRM_DEBUG("Failed to copy in GEM handles\n"); 28457692c94SEric Anholt goto fail; 28557692c94SEric Anholt } 28657692c94SEric Anholt 28757692c94SEric Anholt spin_lock(&file_priv->table_lock); 288*a783a09eSEric Anholt for (i = 0; i < job->bo_count; i++) { 28957692c94SEric Anholt struct drm_gem_object *bo = idr_find(&file_priv->object_idr, 29057692c94SEric Anholt handles[i]); 29157692c94SEric Anholt if (!bo) { 29257692c94SEric Anholt DRM_DEBUG("Failed to look up GEM BO %d: %d\n", 29357692c94SEric Anholt i, handles[i]); 29457692c94SEric Anholt ret = -ENOENT; 29557692c94SEric Anholt spin_unlock(&file_priv->table_lock); 29657692c94SEric Anholt goto fail; 29757692c94SEric Anholt } 29857692c94SEric Anholt drm_gem_object_get(bo); 299*a783a09eSEric Anholt job->bo[i] = bo; 30057692c94SEric Anholt } 30157692c94SEric Anholt spin_unlock(&file_priv->table_lock); 30257692c94SEric Anholt 30357692c94SEric Anholt fail: 30457692c94SEric Anholt kvfree(handles); 30557692c94SEric Anholt return ret; 30657692c94SEric Anholt } 30757692c94SEric Anholt 30857692c94SEric Anholt static void 309*a783a09eSEric Anholt v3d_job_free(struct kref *ref) 31057692c94SEric Anholt { 311*a783a09eSEric Anholt struct v3d_job *job = container_of(ref, struct v3d_job, refcount); 312*a783a09eSEric Anholt int i; 31357692c94SEric Anholt 314*a783a09eSEric Anholt for (i = 0; i < job->bo_count; i++) { 3151584f16cSEric Anholt if (job->bo[i]) 316d4c3022aSEric Anholt drm_gem_object_put_unlocked(job->bo[i]); 3171584f16cSEric Anholt } 318*a783a09eSEric Anholt kvfree(job->bo); 3191584f16cSEric Anholt 320*a783a09eSEric Anholt dma_fence_put(job->in_fence); 321*a783a09eSEric Anholt dma_fence_put(job->irq_fence); 322*a783a09eSEric Anholt dma_fence_put(job->done_fence); 323*a783a09eSEric Anholt 324*a783a09eSEric Anholt pm_runtime_mark_last_busy(job->v3d->dev); 325*a783a09eSEric Anholt pm_runtime_put_autosuspend(job->v3d->dev); 3261584f16cSEric Anholt 3271584f16cSEric Anholt kfree(job); 3281584f16cSEric Anholt } 3291584f16cSEric Anholt 330*a783a09eSEric Anholt static void 331*a783a09eSEric Anholt v3d_render_job_free(struct kref *ref) 3321584f16cSEric Anholt { 333*a783a09eSEric Anholt struct v3d_render_job *job = container_of(ref, struct v3d_render_job, 334*a783a09eSEric Anholt base.refcount); 335*a783a09eSEric Anholt struct v3d_bo *bo, *save; 336*a783a09eSEric Anholt 337*a783a09eSEric Anholt list_for_each_entry_safe(bo, save, &job->unref_list, unref_head) { 338*a783a09eSEric Anholt drm_gem_object_put_unlocked(&bo->base.base); 339*a783a09eSEric Anholt } 340*a783a09eSEric Anholt 341*a783a09eSEric Anholt v3d_job_free(ref); 342*a783a09eSEric Anholt } 343*a783a09eSEric Anholt 344*a783a09eSEric Anholt void v3d_job_put(struct v3d_job *job) 345*a783a09eSEric Anholt { 346*a783a09eSEric Anholt kref_put(&job->refcount, job->free); 3471584f16cSEric Anholt } 3481584f16cSEric Anholt 34957692c94SEric Anholt int 35057692c94SEric Anholt v3d_wait_bo_ioctl(struct drm_device *dev, void *data, 35157692c94SEric Anholt struct drm_file *file_priv) 35257692c94SEric Anholt { 35357692c94SEric Anholt int ret; 35457692c94SEric Anholt struct drm_v3d_wait_bo *args = data; 35557692c94SEric Anholt ktime_t start = ktime_get(); 35657692c94SEric Anholt u64 delta_ns; 35757692c94SEric Anholt unsigned long timeout_jiffies = 35857692c94SEric Anholt nsecs_to_jiffies_timeout(args->timeout_ns); 35957692c94SEric Anholt 36057692c94SEric Anholt if (args->pad != 0) 36157692c94SEric Anholt return -EINVAL; 36257692c94SEric Anholt 3638d668309SRob Herring ret = drm_gem_reservation_object_wait(file_priv, args->handle, 3648d668309SRob Herring true, timeout_jiffies); 36557692c94SEric Anholt 36657692c94SEric Anholt /* Decrement the user's timeout, in case we got interrupted 36757692c94SEric Anholt * such that the ioctl will be restarted. 36857692c94SEric Anholt */ 36957692c94SEric Anholt delta_ns = ktime_to_ns(ktime_sub(ktime_get(), start)); 37057692c94SEric Anholt if (delta_ns < args->timeout_ns) 37157692c94SEric Anholt args->timeout_ns -= delta_ns; 37257692c94SEric Anholt else 37357692c94SEric Anholt args->timeout_ns = 0; 37457692c94SEric Anholt 37557692c94SEric Anholt /* Asked to wait beyond the jiffie/scheduler precision? */ 37657692c94SEric Anholt if (ret == -ETIME && args->timeout_ns) 37757692c94SEric Anholt ret = -EAGAIN; 37857692c94SEric Anholt 37957692c94SEric Anholt return ret; 38057692c94SEric Anholt } 38157692c94SEric Anholt 382*a783a09eSEric Anholt static int 383*a783a09eSEric Anholt v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, 384*a783a09eSEric Anholt struct v3d_job *job, void (*free)(struct kref *ref), 385*a783a09eSEric Anholt u32 in_sync) 386*a783a09eSEric Anholt { 387*a783a09eSEric Anholt int ret; 388*a783a09eSEric Anholt 389*a783a09eSEric Anholt job->v3d = v3d; 390*a783a09eSEric Anholt job->free = free; 391*a783a09eSEric Anholt 392*a783a09eSEric Anholt ret = pm_runtime_get_sync(v3d->dev); 393*a783a09eSEric Anholt if (ret < 0) 394*a783a09eSEric Anholt return ret; 395*a783a09eSEric Anholt 396*a783a09eSEric Anholt ret = drm_syncobj_find_fence(file_priv, in_sync, 0, 0, &job->in_fence); 397*a783a09eSEric Anholt if (ret == -EINVAL) { 398*a783a09eSEric Anholt pm_runtime_put_autosuspend(v3d->dev); 399*a783a09eSEric Anholt return ret; 400*a783a09eSEric Anholt } 401*a783a09eSEric Anholt 402*a783a09eSEric Anholt kref_init(&job->refcount); 403*a783a09eSEric Anholt 404*a783a09eSEric Anholt return 0; 405*a783a09eSEric Anholt } 406*a783a09eSEric Anholt 407*a783a09eSEric Anholt static int 408*a783a09eSEric Anholt v3d_push_job(struct v3d_file_priv *v3d_priv, 409*a783a09eSEric Anholt struct v3d_job *job, enum v3d_queue queue) 410*a783a09eSEric Anholt { 411*a783a09eSEric Anholt int ret; 412*a783a09eSEric Anholt 413*a783a09eSEric Anholt ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], 414*a783a09eSEric Anholt v3d_priv); 415*a783a09eSEric Anholt if (ret) 416*a783a09eSEric Anholt return ret; 417*a783a09eSEric Anholt 418*a783a09eSEric Anholt job->done_fence = dma_fence_get(&job->base.s_fence->finished); 419*a783a09eSEric Anholt 420*a783a09eSEric Anholt /* put by scheduler job completion */ 421*a783a09eSEric Anholt kref_get(&job->refcount); 422*a783a09eSEric Anholt 423*a783a09eSEric Anholt drm_sched_entity_push_job(&job->base, &v3d_priv->sched_entity[queue]); 424*a783a09eSEric Anholt 425*a783a09eSEric Anholt return 0; 426*a783a09eSEric Anholt } 427*a783a09eSEric Anholt 428*a783a09eSEric Anholt static void 429*a783a09eSEric Anholt v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv, 430*a783a09eSEric Anholt struct v3d_job *job, 431*a783a09eSEric Anholt struct ww_acquire_ctx *acquire_ctx, 432*a783a09eSEric Anholt u32 out_sync) 433*a783a09eSEric Anholt { 434*a783a09eSEric Anholt struct drm_syncobj *sync_out; 435*a783a09eSEric Anholt int i; 436*a783a09eSEric Anholt 437*a783a09eSEric Anholt for (i = 0; i < job->bo_count; i++) { 438*a783a09eSEric Anholt /* XXX: Use shared fences for read-only objects. */ 439*a783a09eSEric Anholt reservation_object_add_excl_fence(job->bo[i]->resv, 440*a783a09eSEric Anholt job->done_fence); 441*a783a09eSEric Anholt } 442*a783a09eSEric Anholt 443*a783a09eSEric Anholt drm_gem_unlock_reservations(job->bo, job->bo_count, acquire_ctx); 444*a783a09eSEric Anholt 445*a783a09eSEric Anholt /* Update the return sync object for the job */ 446*a783a09eSEric Anholt sync_out = drm_syncobj_find(file_priv, out_sync); 447*a783a09eSEric Anholt if (sync_out) { 448*a783a09eSEric Anholt drm_syncobj_replace_fence(sync_out, job->done_fence); 449*a783a09eSEric Anholt drm_syncobj_put(sync_out); 450*a783a09eSEric Anholt } 451*a783a09eSEric Anholt } 452*a783a09eSEric Anholt 45357692c94SEric Anholt /** 45457692c94SEric Anholt * v3d_submit_cl_ioctl() - Submits a job (frame) to the V3D. 45557692c94SEric Anholt * @dev: DRM device 45657692c94SEric Anholt * @data: ioctl argument 45757692c94SEric Anholt * @file_priv: DRM file for this fd 45857692c94SEric Anholt * 45957692c94SEric Anholt * This is the main entrypoint for userspace to submit a 3D frame to 46057692c94SEric Anholt * the GPU. Userspace provides the binner command list (if 46157692c94SEric Anholt * applicable), and the kernel sets up the render command list to draw 46257692c94SEric Anholt * to the framebuffer described in the ioctl, using the command lists 46357692c94SEric Anholt * that the 3D engine's binner will produce. 46457692c94SEric Anholt */ 46557692c94SEric Anholt int 46657692c94SEric Anholt v3d_submit_cl_ioctl(struct drm_device *dev, void *data, 46757692c94SEric Anholt struct drm_file *file_priv) 46857692c94SEric Anholt { 46957692c94SEric Anholt struct v3d_dev *v3d = to_v3d_dev(dev); 47057692c94SEric Anholt struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 47157692c94SEric Anholt struct drm_v3d_submit_cl *args = data; 472*a783a09eSEric Anholt struct v3d_bin_job *bin = NULL; 473*a783a09eSEric Anholt struct v3d_render_job *render; 47457692c94SEric Anholt struct ww_acquire_ctx acquire_ctx; 47557692c94SEric Anholt int ret = 0; 47657692c94SEric Anholt 47755a9b748SEric Anholt trace_v3d_submit_cl_ioctl(&v3d->drm, args->rcl_start, args->rcl_end); 47855a9b748SEric Anholt 47957692c94SEric Anholt if (args->pad != 0) { 48057692c94SEric Anholt DRM_INFO("pad must be zero: %d\n", args->pad); 48157692c94SEric Anholt return -EINVAL; 48257692c94SEric Anholt } 48357692c94SEric Anholt 484*a783a09eSEric Anholt render = kcalloc(1, sizeof(*render), GFP_KERNEL); 485*a783a09eSEric Anholt if (!render) 48657692c94SEric Anholt return -ENOMEM; 48757692c94SEric Anholt 488*a783a09eSEric Anholt render->start = args->rcl_start; 489*a783a09eSEric Anholt render->end = args->rcl_end; 490*a783a09eSEric Anholt INIT_LIST_HEAD(&render->unref_list); 491*a783a09eSEric Anholt 492*a783a09eSEric Anholt ret = v3d_job_init(v3d, file_priv, &render->base, 493*a783a09eSEric Anholt v3d_render_job_free, args->in_sync_rcl); 494*a783a09eSEric Anholt if (ret) { 495*a783a09eSEric Anholt kfree(render); 49657692c94SEric Anholt return ret; 49757692c94SEric Anholt } 49857692c94SEric Anholt 499*a783a09eSEric Anholt if (args->bcl_start != args->bcl_end) { 500*a783a09eSEric Anholt bin = kcalloc(1, sizeof(*bin), GFP_KERNEL); 501*a783a09eSEric Anholt if (!bin) 502*a783a09eSEric Anholt return -ENOMEM; 50357692c94SEric Anholt 504*a783a09eSEric Anholt ret = v3d_job_init(v3d, file_priv, &bin->base, 505*a783a09eSEric Anholt v3d_job_free, args->in_sync_bcl); 506*a783a09eSEric Anholt if (ret) { 507*a783a09eSEric Anholt v3d_job_put(&render->base); 508*a783a09eSEric Anholt return ret; 509*a783a09eSEric Anholt } 51057692c94SEric Anholt 511*a783a09eSEric Anholt bin->start = args->bcl_start; 512*a783a09eSEric Anholt bin->end = args->bcl_end; 513*a783a09eSEric Anholt bin->qma = args->qma; 514*a783a09eSEric Anholt bin->qms = args->qms; 515*a783a09eSEric Anholt bin->qts = args->qts; 516*a783a09eSEric Anholt bin->render = render; 517*a783a09eSEric Anholt } 51857692c94SEric Anholt 519*a783a09eSEric Anholt ret = v3d_lookup_bos(dev, file_priv, &render->base, 520*a783a09eSEric Anholt args->bo_handles, args->bo_handle_count); 52157692c94SEric Anholt if (ret) 52257692c94SEric Anholt goto fail; 52357692c94SEric Anholt 524*a783a09eSEric Anholt ret = v3d_lock_bo_reservations(render->base.bo, render->base.bo_count, 5251584f16cSEric Anholt &acquire_ctx); 52657692c94SEric Anholt if (ret) 52757692c94SEric Anholt goto fail; 52857692c94SEric Anholt 5297122b68bSEric Anholt mutex_lock(&v3d->sched_lock); 530*a783a09eSEric Anholt if (bin) { 531*a783a09eSEric Anholt ret = v3d_push_job(v3d_priv, &bin->base, V3D_BIN); 53257692c94SEric Anholt if (ret) 53357692c94SEric Anholt goto fail_unreserve; 53457692c94SEric Anholt 535*a783a09eSEric Anholt render->bin_done_fence = dma_fence_get(bin->base.done_fence); 53657692c94SEric Anholt } 53757692c94SEric Anholt 538*a783a09eSEric Anholt ret = v3d_push_job(v3d_priv, &render->base, V3D_RENDER); 53957692c94SEric Anholt if (ret) 54057692c94SEric Anholt goto fail_unreserve; 5417122b68bSEric Anholt mutex_unlock(&v3d->sched_lock); 54257692c94SEric Anholt 543*a783a09eSEric Anholt v3d_attach_fences_and_unlock_reservation(file_priv, 544*a783a09eSEric Anholt &render->base, &acquire_ctx, 545*a783a09eSEric Anholt args->out_sync); 54657692c94SEric Anholt 547*a783a09eSEric Anholt if (bin) 548*a783a09eSEric Anholt v3d_job_put(&bin->base); 549*a783a09eSEric Anholt v3d_job_put(&render->base); 55057692c94SEric Anholt 55157692c94SEric Anholt return 0; 55257692c94SEric Anholt 55357692c94SEric Anholt fail_unreserve: 5547122b68bSEric Anholt mutex_unlock(&v3d->sched_lock); 555*a783a09eSEric Anholt drm_gem_unlock_reservations(render->base.bo, 556*a783a09eSEric Anholt render->base.bo_count, &acquire_ctx); 55757692c94SEric Anholt fail: 558*a783a09eSEric Anholt if (bin) 559*a783a09eSEric Anholt v3d_job_put(&bin->base); 560*a783a09eSEric Anholt v3d_job_put(&render->base); 56157692c94SEric Anholt 56257692c94SEric Anholt return ret; 56357692c94SEric Anholt } 56457692c94SEric Anholt 5651584f16cSEric Anholt /** 5661584f16cSEric Anholt * v3d_submit_tfu_ioctl() - Submits a TFU (texture formatting) job to the V3D. 5671584f16cSEric Anholt * @dev: DRM device 5681584f16cSEric Anholt * @data: ioctl argument 5691584f16cSEric Anholt * @file_priv: DRM file for this fd 5701584f16cSEric Anholt * 5711584f16cSEric Anholt * Userspace provides the register setup for the TFU, which we don't 5721584f16cSEric Anholt * need to validate since the TFU is behind the MMU. 5731584f16cSEric Anholt */ 5741584f16cSEric Anholt int 5751584f16cSEric Anholt v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, 5761584f16cSEric Anholt struct drm_file *file_priv) 5771584f16cSEric Anholt { 5781584f16cSEric Anholt struct v3d_dev *v3d = to_v3d_dev(dev); 5791584f16cSEric Anholt struct v3d_file_priv *v3d_priv = file_priv->driver_priv; 5801584f16cSEric Anholt struct drm_v3d_submit_tfu *args = data; 5811584f16cSEric Anholt struct v3d_tfu_job *job; 5821584f16cSEric Anholt struct ww_acquire_ctx acquire_ctx; 5831584f16cSEric Anholt int ret = 0; 5841584f16cSEric Anholt 58555a9b748SEric Anholt trace_v3d_submit_tfu_ioctl(&v3d->drm, args->iia); 58655a9b748SEric Anholt 5871584f16cSEric Anholt job = kcalloc(1, sizeof(*job), GFP_KERNEL); 5881584f16cSEric Anholt if (!job) 5891584f16cSEric Anholt return -ENOMEM; 5901584f16cSEric Anholt 591*a783a09eSEric Anholt ret = v3d_job_init(v3d, file_priv, &job->base, 592*a783a09eSEric Anholt v3d_job_free, args->in_sync); 593*a783a09eSEric Anholt if (ret) { 5941584f16cSEric Anholt kfree(job); 5951584f16cSEric Anholt return ret; 5961584f16cSEric Anholt } 5971584f16cSEric Anholt 598*a783a09eSEric Anholt job->base.bo = kcalloc(ARRAY_SIZE(args->bo_handles), 599*a783a09eSEric Anholt sizeof(*job->base.bo), GFP_KERNEL); 600*a783a09eSEric Anholt if (!job->base.bo) { 601*a783a09eSEric Anholt v3d_job_put(&job->base); 602*a783a09eSEric Anholt return -ENOMEM; 603*a783a09eSEric Anholt } 6041584f16cSEric Anholt 6051584f16cSEric Anholt job->args = *args; 6061584f16cSEric Anholt 6071584f16cSEric Anholt spin_lock(&file_priv->table_lock); 608*a783a09eSEric Anholt for (job->base.bo_count = 0; 609*a783a09eSEric Anholt job->base.bo_count < ARRAY_SIZE(args->bo_handles); 610*a783a09eSEric Anholt job->base.bo_count++) { 6111584f16cSEric Anholt struct drm_gem_object *bo; 6121584f16cSEric Anholt 613*a783a09eSEric Anholt if (!args->bo_handles[job->base.bo_count]) 6141584f16cSEric Anholt break; 6151584f16cSEric Anholt 6161584f16cSEric Anholt bo = idr_find(&file_priv->object_idr, 617*a783a09eSEric Anholt args->bo_handles[job->base.bo_count]); 6181584f16cSEric Anholt if (!bo) { 6191584f16cSEric Anholt DRM_DEBUG("Failed to look up GEM BO %d: %d\n", 620*a783a09eSEric Anholt job->base.bo_count, 621*a783a09eSEric Anholt args->bo_handles[job->base.bo_count]); 6221584f16cSEric Anholt ret = -ENOENT; 6231584f16cSEric Anholt spin_unlock(&file_priv->table_lock); 6241584f16cSEric Anholt goto fail; 6251584f16cSEric Anholt } 6261584f16cSEric Anholt drm_gem_object_get(bo); 627*a783a09eSEric Anholt job->base.bo[job->base.bo_count] = bo; 6281584f16cSEric Anholt } 6291584f16cSEric Anholt spin_unlock(&file_priv->table_lock); 6301584f16cSEric Anholt 631*a783a09eSEric Anholt ret = v3d_lock_bo_reservations(job->base.bo, job->base.bo_count, 632*a783a09eSEric Anholt &acquire_ctx); 6331584f16cSEric Anholt if (ret) 6341584f16cSEric Anholt goto fail; 6351584f16cSEric Anholt 6361584f16cSEric Anholt mutex_lock(&v3d->sched_lock); 637*a783a09eSEric Anholt ret = v3d_push_job(v3d_priv, &job->base, V3D_TFU); 6381584f16cSEric Anholt if (ret) 6391584f16cSEric Anholt goto fail_unreserve; 6401584f16cSEric Anholt mutex_unlock(&v3d->sched_lock); 6411584f16cSEric Anholt 642*a783a09eSEric Anholt v3d_attach_fences_and_unlock_reservation(file_priv, 643*a783a09eSEric Anholt &job->base, &acquire_ctx, 644*a783a09eSEric Anholt args->out_sync); 6451584f16cSEric Anholt 646*a783a09eSEric Anholt v3d_job_put(&job->base); 6471584f16cSEric Anholt 6481584f16cSEric Anholt return 0; 6491584f16cSEric Anholt 6501584f16cSEric Anholt fail_unreserve: 6511584f16cSEric Anholt mutex_unlock(&v3d->sched_lock); 652*a783a09eSEric Anholt drm_gem_unlock_reservations(job->base.bo, job->base.bo_count, 653*a783a09eSEric Anholt &acquire_ctx); 6541584f16cSEric Anholt fail: 655*a783a09eSEric Anholt v3d_job_put(&job->base); 6561584f16cSEric Anholt 6571584f16cSEric Anholt return ret; 6581584f16cSEric Anholt } 6591584f16cSEric Anholt 66057692c94SEric Anholt int 66157692c94SEric Anholt v3d_gem_init(struct drm_device *dev) 66257692c94SEric Anholt { 66357692c94SEric Anholt struct v3d_dev *v3d = to_v3d_dev(dev); 66457692c94SEric Anholt u32 pt_size = 4096 * 1024; 66557692c94SEric Anholt int ret, i; 66657692c94SEric Anholt 66757692c94SEric Anholt for (i = 0; i < V3D_MAX_QUEUES; i++) 66857692c94SEric Anholt v3d->queue[i].fence_context = dma_fence_context_alloc(1); 66957692c94SEric Anholt 67057692c94SEric Anholt spin_lock_init(&v3d->mm_lock); 67157692c94SEric Anholt spin_lock_init(&v3d->job_lock); 67257692c94SEric Anholt mutex_init(&v3d->bo_lock); 67357692c94SEric Anholt mutex_init(&v3d->reset_lock); 6747122b68bSEric Anholt mutex_init(&v3d->sched_lock); 67557692c94SEric Anholt 67657692c94SEric Anholt /* Note: We don't allocate address 0. Various bits of HW 67757692c94SEric Anholt * treat 0 as special, such as the occlusion query counters 67857692c94SEric Anholt * where 0 means "disabled". 67957692c94SEric Anholt */ 68057692c94SEric Anholt drm_mm_init(&v3d->mm, 1, pt_size / sizeof(u32) - 1); 68157692c94SEric Anholt 68257692c94SEric Anholt v3d->pt = dma_alloc_wc(v3d->dev, pt_size, 68357692c94SEric Anholt &v3d->pt_paddr, 68457692c94SEric Anholt GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO); 68557692c94SEric Anholt if (!v3d->pt) { 68657692c94SEric Anholt drm_mm_takedown(&v3d->mm); 68757692c94SEric Anholt dev_err(v3d->dev, 68857692c94SEric Anholt "Failed to allocate page tables. " 68957692c94SEric Anholt "Please ensure you have CMA enabled.\n"); 69057692c94SEric Anholt return -ENOMEM; 69157692c94SEric Anholt } 69257692c94SEric Anholt 69357692c94SEric Anholt v3d_init_hw_state(v3d); 69457692c94SEric Anholt v3d_mmu_set_page_table(v3d); 69557692c94SEric Anholt 69657692c94SEric Anholt ret = v3d_sched_init(v3d); 69757692c94SEric Anholt if (ret) { 69857692c94SEric Anholt drm_mm_takedown(&v3d->mm); 69957692c94SEric Anholt dma_free_coherent(v3d->dev, 4096 * 1024, (void *)v3d->pt, 70057692c94SEric Anholt v3d->pt_paddr); 70157692c94SEric Anholt } 70257692c94SEric Anholt 70357692c94SEric Anholt return 0; 70457692c94SEric Anholt } 70557692c94SEric Anholt 70657692c94SEric Anholt void 70757692c94SEric Anholt v3d_gem_destroy(struct drm_device *dev) 70857692c94SEric Anholt { 70957692c94SEric Anholt struct v3d_dev *v3d = to_v3d_dev(dev); 71057692c94SEric Anholt 71157692c94SEric Anholt v3d_sched_fini(v3d); 71257692c94SEric Anholt 713*a783a09eSEric Anholt /* Waiting for jobs to finish would need to be done before 71457692c94SEric Anholt * unregistering V3D. 71557692c94SEric Anholt */ 71614d1d190SEric Anholt WARN_ON(v3d->bin_job); 71714d1d190SEric Anholt WARN_ON(v3d->render_job); 71857692c94SEric Anholt 71957692c94SEric Anholt drm_mm_takedown(&v3d->mm); 72057692c94SEric Anholt 72157692c94SEric Anholt dma_free_coherent(v3d->dev, 4096 * 1024, (void *)v3d->pt, v3d->pt_paddr); 72257692c94SEric Anholt } 723