807 lines · 22.3 KB · C
RawDownload
/* SPDX-License-Identifier: GPL-2.0-only *//* * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> */ #ifndef __MSM_GPU_H__#define __MSM_GPU_H__ #include <linux/adreno-smmu-priv.h>#include <linux/clk.h>#include <linux/devfreq.h>#include <linux/interconnect.h>#include <linux/pm_opp.h>#include <linux/regulator/consumer.h> #include "msm_drv.h"#include "msm_fence.h"#include "msm_gpu_trace.h"#include "msm_ringbuffer.h"#include "msm_gem.h" struct msm_gem_submit;struct msm_gem_vm_log_entry;struct msm_gpu_state;struct msm_context;struct msm_perfcntr_group;struct msm_perfcntr_stream; struct msm_gpu_config {	const char *ioname;	unsigned int nr_rings;}; /* So far, with hardware that I've seen to date, we can have: *  + zero, one, or two z180 2d cores *  + a3xx or a2xx 3d core, which share a common CP (the firmware *    for the CP seems to implement some different PM4 packet types *    but the basics of cmdstream submission are the same) * * Which means that the eventual complete "class" hierarchy, once * support for all past and present hw is in place, becomes: *  + msm_gpu *    + adreno_gpu *      + a3xx_gpu *      + a2xx_gpu *    + z180_gpu */struct msm_gpu_funcs {	int (*get_param)(struct msm_gpu *gpu, struct msm_context *ctx,			 uint32_t param, uint64_t *value, uint32_t *len);	int (*set_param)(struct msm_gpu *gpu, struct msm_context *ctx,			 uint32_t param, uint64_t value, uint32_t len);	int (*hw_init)(struct msm_gpu *gpu); 	/**	 * @ucode_load: Optional hook to upload fw to GEM objs	 */	int (*ucode_load)(struct msm_gpu *gpu); 	int (*pm_suspend)(struct msm_gpu *gpu);	int (*pm_resume)(struct msm_gpu *gpu);	void (*submit)(struct msm_gpu *gpu, struct msm_gem_submit *submit);	void (*flush)(struct msm_gpu *gpu, struct msm_ringbuffer *ring);	irqreturn_t (*irq)(struct msm_gpu *irq);	struct msm_ringbuffer *(*active_ring)(struct msm_gpu *gpu);	void (*recover)(struct msm_gpu *gpu);	void (*destroy)(struct msm_gpu *gpu);#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)	/* show GPU status in debugfs: */	void (*show)(struct msm_gpu *gpu, struct msm_gpu_state *state,			struct drm_printer *p);	/* for generation specific debugfs: */	void (*debugfs_init)(struct msm_gpu *gpu, struct drm_minor *minor);#endif	/* note: gpu_busy() can assume that we have been pm_resumed */	u64 (*gpu_busy)(struct msm_gpu *gpu, unsigned long *out_sample_rate);	struct msm_gpu_state *(*gpu_state_get)(struct msm_gpu *gpu);	int (*gpu_state_put)(struct msm_gpu_state *state);	unsigned long (*gpu_get_freq)(struct msm_gpu *gpu);	/* note: gpu_set_freq() can assume that we have been pm_resumed */	void (*gpu_set_freq)(struct msm_gpu *gpu, struct dev_pm_opp *opp,			     bool suspended);	struct drm_gpuvm *(*create_vm)(struct msm_gpu *gpu, struct platform_device *pdev);	struct drm_gpuvm *(*create_private_vm)(struct msm_gpu *gpu, bool kernel_managed);	uint32_t (*get_rptr)(struct msm_gpu *gpu, struct msm_ringbuffer *ring); 	/**	 * progress: Has the GPU made progress?	 *	 * Return true if GPU position in cmdstream has advanced (or changed)	 * since the last call.  To avoid false negatives, this should account	 * for cmdstream that is buffered in this FIFO upstream of the CP fw.	 */	bool (*progress)(struct msm_gpu *gpu, struct msm_ringbuffer *ring);	void (*sysprof_setup)(struct msm_gpu *gpu, bool force_on); 	/* Configure perfcntr SELect regs: */	void (*perfcntr_configure)(struct msm_gpu *gpu, struct msm_ringbuffer *ring,				   const struct msm_perfcntr_stream *stream); 	/* Flush perfcntrs before reading (optional): */	void (*perfcntr_flush)(struct msm_gpu *gpu);}; /* Additional state for iommu faults: */struct msm_gpu_fault_info {	u64 ttbr0;	unsigned long iova;	int flags;	const char *type;	const char *block; 	/* Information about what we think/expect is the current SMMU state,	 * for example expected_ttbr0 should match smmu_info.ttbr0 which	 * was read back from SMMU registers.	 */	phys_addr_t pgtbl_ttbr0;	u64 ptes[4];	int asid;}; /** * struct msm_gpu_devfreq - devfreq related state */struct msm_gpu_devfreq {	/** @devfreq: devfreq instance */	struct devfreq *devfreq;	/** @lock: lock for "suspended", "busy_cycles", and "time" */	struct mutex lock;	/**	 * @idle_freq:	 * Shadow frequency used while the GPU is idle.  From the PoV of	 * the devfreq governor, we are continuing to sample busyness and	 * adjust frequency while the GPU is idle, but we use this shadow	 * value as the GPU is actually clamped to minimum frequency while	 * it is inactive.	 */	unsigned long idle_freq;	/**	 * @boost_freq:	 * A PM QoS constraint to boost min freq for a period of time	 * until the boost expires.	 */	struct dev_pm_qos_request boost_freq;	/**	 * @busy_cycles: Last busy counter value, for calculating elapsed busy	 * cycles since last sampling period.	 */	u64 busy_cycles;	/** @time: Time of last sampling period.  */	ktime_t time;	/** @idle_time: Time of last transition to idle.  */	ktime_t idle_time;	/**	 * @idle_work:	 * Used to delay clamping to idle freq on active->idle transition.	 */	struct msm_hrtimer_work idle_work;	/**	 * @boost_work:	 * Used to reset the boost_constraint after the boost period has	 * elapsed	 */	struct msm_hrtimer_work boost_work; 	/** @suspended: tracks if we're suspended */	bool suspended;}; struct msm_gpu {	const char *name;	struct drm_device *dev;	struct platform_device *pdev;	const struct msm_gpu_funcs *funcs; 	struct adreno_smmu_priv adreno_smmu; 	struct msm_ringbuffer *rb[MSM_GPU_MAX_RINGS];	int nr_rings; 	/**	 * sysprof_active:	 *	 * The count of contexts that have enabled system profiling.	 */	refcount_t sysprof_active; 	/**	 * lock:	 *	 * General lock for serializing all the gpu things.	 *	 * TODO move to per-ring locking where feasible (ie. submit/retire	 * path, etc)	 */	struct mutex lock; 	/**	 * active_submits:	 *	 * The number of submitted but not yet retired submits, used to	 * determine transitions between active and idle.	 *	 * Protected by active_lock	 */	int active_submits; 	/** lock: protects active_submits and idle/active transitions */	struct mutex active_lock; 	/* does gpu need hw_init? */	bool needs_hw_init; 	/**	 * global_faults: number of GPU hangs not attributed to a particular	 * address space	 */	int global_faults; 	void __iomem *mmio;	int irq; 	struct drm_gpuvm *vm; 	/* Power Control: */	struct regulator *gpu_reg, *gpu_cx;	struct clk_bulk_data *grp_clks;	int nr_clocks;	struct clk *ebi1_clk, *core_clk, *rbbmtimer_clk;	uint32_t fast_rate; 	/* Hang and Inactivity Detection:	 */#define DRM_MSM_INACTIVE_PERIOD   66 /* in ms (roughly four frames) */ #define DRM_MSM_HANGCHECK_DEFAULT_PERIOD 500 /* in ms */#define DRM_MSM_HANGCHECK_PROGRESS_RETRIES 3	struct timer_list hangcheck_timer; 	/* work for handling GPU recovery: */	struct kthread_work recover_work; 	/** retire_event: notified when submits are retired: */	wait_queue_head_t retire_event; 	/* work for handling active-list retiring: */	struct kthread_work retire_work; 	/* worker for retire/recover: */	struct kthread_worker *worker; 	struct drm_gem_object *memptrs_bo; 	struct msm_gpu_devfreq devfreq; 	uint32_t suspend_count; 	struct msm_gpu_state *crashstate; 	/* True if the hardware supports expanded apriv (a650 and newer) */	bool hw_apriv; 	/**	 * @allow_relocs: allow relocs in SUBMIT ioctl	 *	 * Mesa won't use relocs for driver version 1.4.0 and later.  This	 * switch-over happened early enough in mesa a6xx bringup that we	 * can disallow relocs for a6xx and newer.	 */	bool allow_relocs; 	struct thermal_cooling_device *cooling; 	const struct msm_perfcntr_group *perfcntr_groups;	unsigned num_perfcntr_groups; 	struct msm_perfcntr_state *perfcntrs; 	/** @perfcntr_lock: protects perfcntr related state */	struct mutex perfcntr_lock;}; static inline struct msm_gpu *dev_to_gpu(struct device *dev){	struct adreno_smmu_priv *adreno_smmu = dev_get_drvdata(dev); 	if (!adreno_smmu)		return NULL; 	return container_of(adreno_smmu, struct msm_gpu, adreno_smmu);} static inline booladreno_smmu_has_prr(struct msm_gpu *gpu){	struct adreno_smmu_priv *adreno_smmu = dev_get_drvdata(&gpu->pdev->dev); 	if (!adreno_smmu)		return false; 	return adreno_smmu && adreno_smmu->set_prr_addr;} /* It turns out that all targets use the same ringbuffer size */#define MSM_GPU_RINGBUFFER_SZ SZ_32K#define MSM_GPU_RINGBUFFER_BLKSIZE 32 #define MSM_GPU_RB_CNTL_DEFAULT \		(AXXX_CP_RB_CNTL_BUFSZ(ilog2(MSM_GPU_RINGBUFFER_SZ / 8)) | \		AXXX_CP_RB_CNTL_BLKSZ(ilog2(MSM_GPU_RINGBUFFER_BLKSIZE / 8))) static inline bool msm_gpu_active(struct msm_gpu *gpu){	int i; 	for (i = 0; i < gpu->nr_rings; i++) {		struct msm_ringbuffer *ring = gpu->rb[i]; 		if (fence_after(ring->fctx->last_fence, ring->memptrs->fence))			return true;	} 	return false;} /** * struct msm_perfcntr_group_state - Tracking for the currently allocated counter state */struct msm_perfcntr_group_state {	/**	 * @allocated_counters:	 *	 * allocated counters for global counter collection.  The	 * corresponding counters are allocated from highest to	 * lowest, to minimize chance of conflict with old userspace	 * allocating from lowest to highest.	 */	unsigned allocated_counters; 	/**	 * @countables:	 *	 * The corresponding SELect reg values for the allocated counters	 */	uint32_t countables[];}; /** * struct msm_perfcntr_state - overall global perfcntr state */struct msm_perfcntr_state {	/** @stream: current global counter stream if active */	struct msm_perfcntr_stream *stream; 	/** @sel_seqno: counter for sel_fence */	uint32_t sel_seqno; 	/**	 * @groups: Global perfcntr stream group state.	 *	 * Conceptually this is part of msm_perfcntr_stream state, but is	 * statically pre-allocated when the gpu is initialized to simplify	 * error path cleanup in PERFCNTR_CONFIG ioctl.  (__free(kfree)	 * doesn't really help with variable length arrays of allocated	 * pointers.)	 */	struct msm_perfcntr_group_state *groups[];}; static inline boolmsm_gpu_sysprof_no_perfcntr_zap(struct msm_gpu *gpu){	return (refcount_read(&gpu->sysprof_active) > 1) ||		(gpu->perfcntrs && READ_ONCE(gpu->perfcntrs->stream));} static inline boolmsm_gpu_sysprof_no_ifpc(struct msm_gpu *gpu){	return refcount_read(&gpu->sysprof_active) > 1;} /* * The number of priority levels provided by drm gpu scheduler.  The * DRM_SCHED_PRIORITY_KERNEL priority level is treated specially in some * cases, so we don't use it (no need for kernel generated jobs). */#define NR_SCHED_PRIORITIES (1 + DRM_SCHED_PRIORITY_LOW - DRM_SCHED_PRIORITY_HIGH) /** * struct msm_context - per-drm_file context */struct msm_context {	/** @ctxlock: synchronizes access to submitqueues list, etc */	struct rw_semaphore ctxlock;	/** @submitqueues: list of &msm_gpu_submitqueue created by userspace */	struct list_head submitqueues;	/**	 * @queueid:	 * Counter incremented each time a submitqueue is created, used to	 * assign &msm_gpu_submitqueue.id	 */	int queueid;	/**	 * @closed: The device file associated with this context has been closed.	 * Once the device is closed, any submits that have not been written	 * to the ring buffer are no-op'd.	 */	bool closed;	/**	 * @userspace_managed_vm:	 * Has userspace opted-in to userspace managed VM (ie. VM_BIND) via	 * MSM_PARAM_EN_VM_BIND?	 */	bool userspace_managed_vm;	/**	 * @vm:	 * The per-process GPU address-space.  Do not access directly, use	 * msm_context_vm().	 */	struct drm_gpuvm *vm;	/** @ref: the reference count */	struct kref ref;	/**	 * @seqno:	 * A unique per-process sequence number.  Used to detect context	 * switches, without relying on keeping a, potentially dangling,	 * pointer to the previous context.	 */	int seqno;	/**	 * @sysprof:	 * The value of MSM_PARAM_SYSPROF set by userspace.  This is	 * intended to be used by system profiling tools like Mesa's	 * pps-producer (perfetto), and restricted to CAP_SYS_ADMIN.	 *	 * Setting a value of 1 will preserve performance counters across	 * context switches.  Setting a value of 2 will in addition	 * suppress suspend.  (Performance counters lose state across	 * power collapse, which is undesirable for profiling in some	 * cases.)	 *	 * The value automatically reverts to zero when the drm device	 * file is closed.	 */	int sysprof;	/**	 * @comm: Overridden task comm, see MSM_PARAM_COMM	 *	 * Accessed under msm_gpu::lock	 */	char *comm;	/**	 * @cmdline: Overridden task cmdline, see MSM_PARAM_CMDLINE	 *	 * Accessed under msm_gpu::lock	 */	char *cmdline;	/**	 * @elapsed_ns:	 * The total (cumulative) elapsed time GPU was busy with rendering	 * from this context in ns.	 */	uint64_t elapsed_ns;	/**	 * @cycles:	 * The total (cumulative) GPU cycles elapsed attributed to this	 * context.	 */	uint64_t cycles;	/**	 * @entities:	 * Table of per-priority-level sched entities used by submitqueues	 * associated with this &drm_file.  Because some userspace apps	 * make assumptions about rendering from multiple gl contexts	 * (of the same priority) within the process happening in FIFO	 * order without requiring any fencing beyond MakeCurrent(), we	 * create at most one &drm_sched_entity per-process per-priority-	 * level.	 */	struct drm_sched_entity *entities[NR_SCHED_PRIORITIES * MSM_GPU_MAX_RINGS];	/**	 * @ctx_mem:	 * Total amount of memory of GEM buffers with handles attached for	 * this context.	 */	atomic64_t ctx_mem; 	/**	 * @perfcntrs: Per-context reserved perfcntrs state	 */	struct msm_perfcntr_context_state *perfctx;}; struct drm_gpuvm *msm_context_vm(struct drm_device *dev, struct msm_context *ctx); /** * msm_context_is_vmbind() - has userspace opted in to VM_BIND? *

Lines 1–500 of 807.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t