drivers/gpu/drm/msm/msm_perfcntr.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/msm/msm_perfcntr.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/msm/msm_perfcntr.c
Extension
.c
Size
18557 bytes
Lines
671
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct file_operations stream_fops = {
	.owner		= THIS_MODULE,
	.release	= msm_perfcntrs_stream_release,
	.poll		= msm_perfcntrs_stream_poll,
	.read		= msm_perfcntrs_stream_read,
};

static void
sel_worker(struct work_struct *w)
{
	struct msm_perfcntr_stream *stream =
		container_of(w, typeof(*stream), sel_work);
	struct msm_gpu *gpu = stream->gpu;
	/* Reprogram SEL regs on highest priority rb: */
	struct msm_ringbuffer *ring = stream->gpu->rb[0];

	/*
	 * If in the process of resuming, wait for that.  Otherwise sel_worker
	 * which is enqueued in the resume path can be scheduled before the
	 * resume completes.
	 */
	pm_runtime_barrier(&gpu->pdev->dev);

	/*
	 * sel_work could end up scheduled before suspend, but running
	 * after.  See msm_perfcntr_suspend_locked()
	 *
	 * So if we end up running sel_work after the GPU is already
	 * suspended, just bail.  It will be scheduled again after
	 * the GPU is resumed.
	 */
	if (!pm_runtime_get_if_active(&gpu->pdev->dev))
		return;

	scoped_guard (mutex, &gpu->lock) {
		guard(mutex)(&gpu->perfcntr_lock);

		if (stream == gpu->perfcntrs->stream) {
			msm_gpu_hw_init(gpu);
			gpu->funcs->perfcntr_configure(gpu, ring, stream);
		}
	}

	pm_runtime_put_autosuspend(&gpu->pdev->dev);
}

static void
sample_write(struct msm_perfcntr_stream *stream, int *head, const void *buf, size_t sz)
{
	/*
	 * FIFO size is power-of-two, and guaranteed to have enough space to
	 * fit what we are writing.  So we should not hit the wrap-around
	 * point writing things that are power-of-two sized
	 */
	WARN_ON(CIRC_SPACE_TO_END(*head, stream->fifo.tail, stream->fifo_size) < sz);

	memcpy(&stream->fifo.buf[*head], buf, sz);

	/* Advance head, wrapping around if necessary: */
	*head = (*head + sz) & (stream->fifo_size - 1);
}

static void
sample_write_u32(struct msm_perfcntr_stream *stream, int *head, uint32_t val)
{
	sample_write(stream, head, &val, sizeof(val));
}

static void
sample_write_u64(struct msm_perfcntr_stream *stream, int *head, uint64_t val)
{
	sample_write(stream, head, &val, sizeof(val));
}

static void
sample_worker(struct kthread_work *work)
{
	struct msm_perfcntr_stream *stream =
		container_of(work, typeof(*stream), sample_work);
	struct msm_gpu *gpu = stream->gpu;
	struct msm_rbmemptrs *memptrs = gpu->rb[0]->memptrs;

	if (memptrs->perfcntr_fence != stream->sel_fence)
		return;

	/*
	 * Ensure we have enough space to capture a sample period's
	 * worth of data:
	 */
	if (stream->period_size > fifo_space(stream)) {

Annotation

Implementation Notes