157692c94SEric Anholt // SPDX-License-Identifier: GPL-2.0+ 257692c94SEric Anholt /* Copyright (C) 2015-2018 Broadcom */ 357692c94SEric Anholt 4*220989e7SSam Ravnborg #include <linux/delay.h> 5*220989e7SSam Ravnborg #include <linux/mutex.h> 6*220989e7SSam Ravnborg #include <linux/spinlock_types.h> 7*220989e7SSam Ravnborg #include <linux/workqueue.h> 8*220989e7SSam Ravnborg 957692c94SEric Anholt #include <drm/drm_encoder.h> 1057692c94SEric Anholt #include <drm/drm_gem.h> 1140609d48SEric Anholt #include <drm/drm_gem_shmem_helper.h> 1257692c94SEric Anholt #include <drm/gpu_scheduler.h> 13*220989e7SSam Ravnborg 141584f16cSEric Anholt #include "uapi/drm/v3d_drm.h" 1557692c94SEric Anholt 16*220989e7SSam Ravnborg struct clk; 17*220989e7SSam Ravnborg struct device; 18*220989e7SSam Ravnborg struct platform_device; 19*220989e7SSam Ravnborg struct reset_control; 20*220989e7SSam Ravnborg 2157692c94SEric Anholt #define GMP_GRANULARITY (128 * 1024) 2257692c94SEric Anholt 231584f16cSEric Anholt /* Enum for each of the V3D queues. */ 2457692c94SEric Anholt enum v3d_queue { 2557692c94SEric Anholt V3D_BIN, 2657692c94SEric Anholt V3D_RENDER, 271584f16cSEric Anholt V3D_TFU, 28d223f98fSEric Anholt V3D_CSD, 29d223f98fSEric Anholt V3D_CACHE_CLEAN, 3057692c94SEric Anholt }; 3157692c94SEric Anholt 32d223f98fSEric Anholt #define V3D_MAX_QUEUES (V3D_CACHE_CLEAN + 1) 3357692c94SEric Anholt 3457692c94SEric Anholt struct v3d_queue_state { 3557692c94SEric Anholt struct drm_gpu_scheduler sched; 3657692c94SEric Anholt 3757692c94SEric Anholt u64 fence_context; 3857692c94SEric Anholt u64 emit_seqno; 3957692c94SEric Anholt }; 4057692c94SEric Anholt 4157692c94SEric Anholt struct v3d_dev { 4257692c94SEric Anholt struct drm_device drm; 4357692c94SEric Anholt 4457692c94SEric Anholt /* Short representation (e.g. 33, 41) of the V3D tech version 4557692c94SEric Anholt * and revision. 4657692c94SEric Anholt */ 4757692c94SEric Anholt int ver; 48eea9b97bSEric Anholt bool single_irq_line; 4957692c94SEric Anholt 5057692c94SEric Anholt struct device *dev; 5157692c94SEric Anholt struct platform_device *pdev; 5257692c94SEric Anholt void __iomem *hub_regs; 5357692c94SEric Anholt void __iomem *core_regs[3]; 5457692c94SEric Anholt void __iomem *bridge_regs; 5557692c94SEric Anholt void __iomem *gca_regs; 5657692c94SEric Anholt struct clk *clk; 57eea9b97bSEric Anholt struct reset_control *reset; 5857692c94SEric Anholt 5957692c94SEric Anholt /* Virtual and DMA addresses of the single shared page table. */ 6057692c94SEric Anholt volatile u32 *pt; 6157692c94SEric Anholt dma_addr_t pt_paddr; 6257692c94SEric Anholt 6357692c94SEric Anholt /* Virtual and DMA addresses of the MMU's scratch page. When 6457692c94SEric Anholt * a read or write is invalid in the MMU, it will be 6557692c94SEric Anholt * redirected here. 6657692c94SEric Anholt */ 6757692c94SEric Anholt void *mmu_scratch; 6857692c94SEric Anholt dma_addr_t mmu_scratch_paddr; 6938c2c791SEric Anholt /* virtual address bits from V3D to the MMU. */ 7038c2c791SEric Anholt int va_width; 7157692c94SEric Anholt 7257692c94SEric Anholt /* Number of V3D cores. */ 7357692c94SEric Anholt u32 cores; 7457692c94SEric Anholt 7557692c94SEric Anholt /* Allocator managing the address space. All units are in 7657692c94SEric Anholt * number of pages. 7757692c94SEric Anholt */ 7857692c94SEric Anholt struct drm_mm mm; 7957692c94SEric Anholt spinlock_t mm_lock; 8057692c94SEric Anholt 8157692c94SEric Anholt struct work_struct overflow_mem_work; 8257692c94SEric Anholt 83a783a09eSEric Anholt struct v3d_bin_job *bin_job; 84a783a09eSEric Anholt struct v3d_render_job *render_job; 851584f16cSEric Anholt struct v3d_tfu_job *tfu_job; 86d223f98fSEric Anholt struct v3d_csd_job *csd_job; 8757692c94SEric Anholt 8857692c94SEric Anholt struct v3d_queue_state queue[V3D_MAX_QUEUES]; 8957692c94SEric Anholt 9057692c94SEric Anholt /* Spinlock used to synchronize the overflow memory 9157692c94SEric Anholt * management against bin job submission. 9257692c94SEric Anholt */ 9357692c94SEric Anholt spinlock_t job_lock; 9457692c94SEric Anholt 9557692c94SEric Anholt /* Protects bo_stats */ 9657692c94SEric Anholt struct mutex bo_lock; 9757692c94SEric Anholt 9857692c94SEric Anholt /* Lock taken when resetting the GPU, to keep multiple 9957692c94SEric Anholt * processes from trying to park the scheduler threads and 10057692c94SEric Anholt * reset at once. 10157692c94SEric Anholt */ 10257692c94SEric Anholt struct mutex reset_lock; 10357692c94SEric Anholt 1047122b68bSEric Anholt /* Lock taken when creating and pushing the GPU scheduler 1057122b68bSEric Anholt * jobs, to keep the sched-fence seqnos in order. 1067122b68bSEric Anholt */ 1077122b68bSEric Anholt struct mutex sched_lock; 1087122b68bSEric Anholt 109d223f98fSEric Anholt /* Lock taken during a cache clean and when initiating an L2 110d223f98fSEric Anholt * flush, to keep L2 flushes from interfering with the 111d223f98fSEric Anholt * synchronous L2 cleans. 112d223f98fSEric Anholt */ 113d223f98fSEric Anholt struct mutex cache_clean_lock; 114d223f98fSEric Anholt 11557692c94SEric Anholt struct { 11657692c94SEric Anholt u32 num_allocated; 11757692c94SEric Anholt u32 pages_allocated; 11857692c94SEric Anholt } bo_stats; 11957692c94SEric Anholt }; 12057692c94SEric Anholt 12157692c94SEric Anholt static inline struct v3d_dev * 12257692c94SEric Anholt to_v3d_dev(struct drm_device *dev) 12357692c94SEric Anholt { 12457692c94SEric Anholt return (struct v3d_dev *)dev->dev_private; 12557692c94SEric Anholt } 12657692c94SEric Anholt 127d223f98fSEric Anholt static inline bool 128d223f98fSEric Anholt v3d_has_csd(struct v3d_dev *v3d) 129d223f98fSEric Anholt { 130d223f98fSEric Anholt return v3d->ver >= 41; 131d223f98fSEric Anholt } 132d223f98fSEric Anholt 13357692c94SEric Anholt /* The per-fd struct, which tracks the MMU mappings. */ 13457692c94SEric Anholt struct v3d_file_priv { 13557692c94SEric Anholt struct v3d_dev *v3d; 13657692c94SEric Anholt 13757692c94SEric Anholt struct drm_sched_entity sched_entity[V3D_MAX_QUEUES]; 13857692c94SEric Anholt }; 13957692c94SEric Anholt 14057692c94SEric Anholt struct v3d_bo { 14140609d48SEric Anholt struct drm_gem_shmem_object base; 14257692c94SEric Anholt 14357692c94SEric Anholt struct drm_mm_node node; 14457692c94SEric Anholt 14557692c94SEric Anholt /* List entry for the BO's position in 146a783a09eSEric Anholt * v3d_render_job->unref_list 14757692c94SEric Anholt */ 14857692c94SEric Anholt struct list_head unref_head; 14957692c94SEric Anholt }; 15057692c94SEric Anholt 15157692c94SEric Anholt static inline struct v3d_bo * 15257692c94SEric Anholt to_v3d_bo(struct drm_gem_object *bo) 15357692c94SEric Anholt { 15457692c94SEric Anholt return (struct v3d_bo *)bo; 15557692c94SEric Anholt } 15657692c94SEric Anholt 15757692c94SEric Anholt struct v3d_fence { 15857692c94SEric Anholt struct dma_fence base; 15957692c94SEric Anholt struct drm_device *dev; 16057692c94SEric Anholt /* v3d seqno for signaled() test */ 16157692c94SEric Anholt u64 seqno; 16257692c94SEric Anholt enum v3d_queue queue; 16357692c94SEric Anholt }; 16457692c94SEric Anholt 16557692c94SEric Anholt static inline struct v3d_fence * 16657692c94SEric Anholt to_v3d_fence(struct dma_fence *fence) 16757692c94SEric Anholt { 16857692c94SEric Anholt return (struct v3d_fence *)fence; 16957692c94SEric Anholt } 17057692c94SEric Anholt 17157692c94SEric Anholt #define V3D_READ(offset) readl(v3d->hub_regs + offset) 17257692c94SEric Anholt #define V3D_WRITE(offset, val) writel(val, v3d->hub_regs + offset) 17357692c94SEric Anholt 17457692c94SEric Anholt #define V3D_BRIDGE_READ(offset) readl(v3d->bridge_regs + offset) 17557692c94SEric Anholt #define V3D_BRIDGE_WRITE(offset, val) writel(val, v3d->bridge_regs + offset) 17657692c94SEric Anholt 17757692c94SEric Anholt #define V3D_GCA_READ(offset) readl(v3d->gca_regs + offset) 17857692c94SEric Anholt #define V3D_GCA_WRITE(offset, val) writel(val, v3d->gca_regs + offset) 17957692c94SEric Anholt 18057692c94SEric Anholt #define V3D_CORE_READ(core, offset) readl(v3d->core_regs[core] + offset) 18157692c94SEric Anholt #define V3D_CORE_WRITE(core, offset, val) writel(val, v3d->core_regs[core] + offset) 18257692c94SEric Anholt 18357692c94SEric Anholt struct v3d_job { 18457692c94SEric Anholt struct drm_sched_job base; 18557692c94SEric Anholt 186a783a09eSEric Anholt struct kref refcount; 187a783a09eSEric Anholt 188a783a09eSEric Anholt struct v3d_dev *v3d; 189a783a09eSEric Anholt 190a783a09eSEric Anholt /* This is the array of BOs that were looked up at the start 191a783a09eSEric Anholt * of submission. 192a783a09eSEric Anholt */ 193a783a09eSEric Anholt struct drm_gem_object **bo; 194a783a09eSEric Anholt u32 bo_count; 19557692c94SEric Anholt 196dffa9b7aSEric Anholt /* Array of struct dma_fence * to block on before submitting this job. 197dffa9b7aSEric Anholt */ 198dffa9b7aSEric Anholt struct xarray deps; 199dffa9b7aSEric Anholt unsigned long last_dep; 20057692c94SEric Anholt 20157692c94SEric Anholt /* v3d fence to be signaled by IRQ handler when the job is complete. */ 2023f0b646eSEric Anholt struct dma_fence *irq_fence; 20357692c94SEric Anholt 204a783a09eSEric Anholt /* scheduler fence for when the job is considered complete and 205a783a09eSEric Anholt * the BO reservations can be released. 206a783a09eSEric Anholt */ 207a783a09eSEric Anholt struct dma_fence *done_fence; 208a783a09eSEric Anholt 209a783a09eSEric Anholt /* Callback for the freeing of the job on refcount going to 0. */ 210a783a09eSEric Anholt void (*free)(struct kref *ref); 211a783a09eSEric Anholt }; 212a783a09eSEric Anholt 213a783a09eSEric Anholt struct v3d_bin_job { 214a783a09eSEric Anholt struct v3d_job base; 215a783a09eSEric Anholt 21657692c94SEric Anholt /* GPU virtual addresses of the start/end of the CL job. */ 21757692c94SEric Anholt u32 start, end; 218624bb0c0SEric Anholt 219624bb0c0SEric Anholt u32 timedout_ctca, timedout_ctra; 22057692c94SEric Anholt 221a783a09eSEric Anholt /* Corresponding render job, for attaching our overflow memory. */ 222a783a09eSEric Anholt struct v3d_render_job *render; 22357692c94SEric Anholt 22457692c94SEric Anholt /* Submitted tile memory allocation start/size, tile state. */ 22557692c94SEric Anholt u32 qma, qms, qts; 22657692c94SEric Anholt }; 22757692c94SEric Anholt 228a783a09eSEric Anholt struct v3d_render_job { 229a783a09eSEric Anholt struct v3d_job base; 230a783a09eSEric Anholt 231a783a09eSEric Anholt /* GPU virtual addresses of the start/end of the CL job. */ 232a783a09eSEric Anholt u32 start, end; 233a783a09eSEric Anholt 234a783a09eSEric Anholt u32 timedout_ctca, timedout_ctra; 235a783a09eSEric Anholt 236a783a09eSEric Anholt /* List of overflow BOs used in the job that need to be 237a783a09eSEric Anholt * released once the job is complete. 238a783a09eSEric Anholt */ 239a783a09eSEric Anholt struct list_head unref_list; 240a783a09eSEric Anholt }; 241a783a09eSEric Anholt 2421584f16cSEric Anholt struct v3d_tfu_job { 243a783a09eSEric Anholt struct v3d_job base; 2441584f16cSEric Anholt 2451584f16cSEric Anholt struct drm_v3d_submit_tfu args; 2461584f16cSEric Anholt }; 2471584f16cSEric Anholt 248d223f98fSEric Anholt struct v3d_csd_job { 249d223f98fSEric Anholt struct v3d_job base; 250d223f98fSEric Anholt 251d223f98fSEric Anholt u32 timedout_batches; 252d223f98fSEric Anholt 253d223f98fSEric Anholt struct drm_v3d_submit_csd args; 254d223f98fSEric Anholt }; 255d223f98fSEric Anholt 25657692c94SEric Anholt /** 25757692c94SEric Anholt * _wait_for - magic (register) wait macro 25857692c94SEric Anholt * 25957692c94SEric Anholt * Does the right thing for modeset paths when run under kdgb or similar atomic 26057692c94SEric Anholt * contexts. Note that it's important that we check the condition again after 26157692c94SEric Anholt * having timed out, since the timeout could be due to preemption or similar and 26257692c94SEric Anholt * we've never had a chance to check the condition before the timeout. 26357692c94SEric Anholt */ 26457692c94SEric Anholt #define wait_for(COND, MS) ({ \ 26557692c94SEric Anholt unsigned long timeout__ = jiffies + msecs_to_jiffies(MS) + 1; \ 26657692c94SEric Anholt int ret__ = 0; \ 26757692c94SEric Anholt while (!(COND)) { \ 26857692c94SEric Anholt if (time_after(jiffies, timeout__)) { \ 26957692c94SEric Anholt if (!(COND)) \ 27057692c94SEric Anholt ret__ = -ETIMEDOUT; \ 27157692c94SEric Anholt break; \ 27257692c94SEric Anholt } \ 27357692c94SEric Anholt msleep(1); \ 27457692c94SEric Anholt } \ 27557692c94SEric Anholt ret__; \ 27657692c94SEric Anholt }) 27757692c94SEric Anholt 27857692c94SEric Anholt static inline unsigned long nsecs_to_jiffies_timeout(const u64 n) 27957692c94SEric Anholt { 28057692c94SEric Anholt /* nsecs_to_jiffies64() does not guard against overflow */ 28157692c94SEric Anholt if (NSEC_PER_SEC % HZ && 28257692c94SEric Anholt div_u64(n, NSEC_PER_SEC) >= MAX_JIFFY_OFFSET / HZ) 28357692c94SEric Anholt return MAX_JIFFY_OFFSET; 28457692c94SEric Anholt 28557692c94SEric Anholt return min_t(u64, MAX_JIFFY_OFFSET, nsecs_to_jiffies64(n) + 1); 28657692c94SEric Anholt } 28757692c94SEric Anholt 28857692c94SEric Anholt /* v3d_bo.c */ 28940609d48SEric Anholt struct drm_gem_object *v3d_create_object(struct drm_device *dev, size_t size); 29057692c94SEric Anholt void v3d_free_object(struct drm_gem_object *gem_obj); 29157692c94SEric Anholt struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv, 29257692c94SEric Anholt size_t size); 29357692c94SEric Anholt int v3d_create_bo_ioctl(struct drm_device *dev, void *data, 29457692c94SEric Anholt struct drm_file *file_priv); 29557692c94SEric Anholt int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data, 29657692c94SEric Anholt struct drm_file *file_priv); 29757692c94SEric Anholt int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data, 29857692c94SEric Anholt struct drm_file *file_priv); 29957692c94SEric Anholt struct drm_gem_object *v3d_prime_import_sg_table(struct drm_device *dev, 30057692c94SEric Anholt struct dma_buf_attachment *attach, 30157692c94SEric Anholt struct sg_table *sgt); 30257692c94SEric Anholt 30357692c94SEric Anholt /* v3d_debugfs.c */ 30457692c94SEric Anholt int v3d_debugfs_init(struct drm_minor *minor); 30557692c94SEric Anholt 30657692c94SEric Anholt /* v3d_fence.c */ 30757692c94SEric Anholt extern const struct dma_fence_ops v3d_fence_ops; 30857692c94SEric Anholt struct dma_fence *v3d_fence_create(struct v3d_dev *v3d, enum v3d_queue queue); 30957692c94SEric Anholt 31057692c94SEric Anholt /* v3d_gem.c */ 31157692c94SEric Anholt int v3d_gem_init(struct drm_device *dev); 31257692c94SEric Anholt void v3d_gem_destroy(struct drm_device *dev); 31357692c94SEric Anholt int v3d_submit_cl_ioctl(struct drm_device *dev, void *data, 31457692c94SEric Anholt struct drm_file *file_priv); 3151584f16cSEric Anholt int v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, 3161584f16cSEric Anholt struct drm_file *file_priv); 317d223f98fSEric Anholt int v3d_submit_csd_ioctl(struct drm_device *dev, void *data, 318d223f98fSEric Anholt struct drm_file *file_priv); 31957692c94SEric Anholt int v3d_wait_bo_ioctl(struct drm_device *dev, void *data, 32057692c94SEric Anholt struct drm_file *file_priv); 321a783a09eSEric Anholt void v3d_job_put(struct v3d_job *job); 32257692c94SEric Anholt void v3d_reset(struct v3d_dev *v3d); 32357692c94SEric Anholt void v3d_invalidate_caches(struct v3d_dev *v3d); 324d223f98fSEric Anholt void v3d_clean_caches(struct v3d_dev *v3d); 32557692c94SEric Anholt 32657692c94SEric Anholt /* v3d_irq.c */ 327fc227715SEric Anholt int v3d_irq_init(struct v3d_dev *v3d); 32857692c94SEric Anholt void v3d_irq_enable(struct v3d_dev *v3d); 32957692c94SEric Anholt void v3d_irq_disable(struct v3d_dev *v3d); 33057692c94SEric Anholt void v3d_irq_reset(struct v3d_dev *v3d); 33157692c94SEric Anholt 33257692c94SEric Anholt /* v3d_mmu.c */ 33357692c94SEric Anholt int v3d_mmu_get_offset(struct drm_file *file_priv, struct v3d_bo *bo, 33457692c94SEric Anholt u32 *offset); 33557692c94SEric Anholt int v3d_mmu_set_page_table(struct v3d_dev *v3d); 33657692c94SEric Anholt void v3d_mmu_insert_ptes(struct v3d_bo *bo); 33757692c94SEric Anholt void v3d_mmu_remove_ptes(struct v3d_bo *bo); 33857692c94SEric Anholt 33957692c94SEric Anholt /* v3d_sched.c */ 34057692c94SEric Anholt int v3d_sched_init(struct v3d_dev *v3d); 34157692c94SEric Anholt void v3d_sched_fini(struct v3d_dev *v3d); 342