drivers/gpu/drm/msm/adreno/a6xx_preempt.c

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

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/msm/adreno/a6xx_preempt.c
Extension
.c
Size
11639 bytes
Lines
403
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source 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

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
/* Copyright (c) 2023 Collabora, Ltd. */
/* Copyright (c) 2024 Valve Corporation */

#include "msm_gem.h"
#include "a6xx_gpu.h"
#include "a6xx_gmu.xml.h"
#include "a6xx_preempt.h"
#include "msm_mmu.h"
#include "msm_gpu_trace.h"

static void a6xx_preempt_timer(struct timer_list *t)
{
	struct a6xx_gpu *a6xx_gpu = timer_container_of(a6xx_gpu, t,
						       preempt_timer);
	struct msm_gpu *gpu = &a6xx_gpu->base.base;
	struct drm_device *dev = gpu->dev;

	if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_FAULTED))
		return;

	dev_err(dev->dev, "%s: preemption timed out\n", gpu->name);
	kthread_queue_work(gpu->worker, &gpu->recover_work);
}

static void preempt_prepare_postamble(struct a6xx_gpu *a6xx_gpu)
{
	u32 *postamble = a6xx_gpu->preempt_postamble_ptr;
	u32 count = 0;

	postamble[count++] = PKT7(CP_REG_RMW, 3);
	postamble[count++] = REG_A6XX_RBBM_PERFCTR_SRAM_INIT_CMD;
	postamble[count++] = 0;
	postamble[count++] = 1;

	postamble[count++] = PKT7(CP_WAIT_REG_MEM, 6);
	postamble[count++] = CP_WAIT_REG_MEM_0_FUNCTION(WRITE_EQ);
	postamble[count++] = CP_WAIT_REG_MEM_POLL_ADDR_LO(
				REG_A6XX_RBBM_PERFCTR_SRAM_INIT_STATUS);
	postamble[count++] = CP_WAIT_REG_MEM_POLL_ADDR_HI(0);
	postamble[count++] = CP_WAIT_REG_MEM_3_REF(0x1);
	postamble[count++] = CP_WAIT_REG_MEM_4_MASK(0x1);
	postamble[count++] = CP_WAIT_REG_MEM_5_DELAY_LOOP_CYCLES(0);

	a6xx_gpu->preempt_postamble_len = count;

	a6xx_gpu->postamble_enabled = true;
}

static void preempt_disable_postamble(struct a6xx_gpu *a6xx_gpu)
{
	u32 *postamble = a6xx_gpu->preempt_postamble_ptr;

	/*
	 * Disable the postamble by replacing the first packet header with a NOP
	 * that covers the whole buffer.
	 */
	*postamble = PKT7(CP_NOP, (a6xx_gpu->preempt_postamble_len - 1));

	a6xx_gpu->postamble_enabled = false;
}

/*
 * Set preemption keepalive vote. Please note that this vote is different from the one used in
 * a6xx_irq()
 */
static void a6xx_preempt_keepalive_vote(struct msm_gpu *gpu, bool on)
{
	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);

	if (adreno_has_gmu_wrapper(adreno_gpu))
		return;

	gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_PWR_COL_PREEMPT_KEEPALIVE, on);
}

void a6xx_preempt_irq(struct msm_gpu *gpu)
{
	uint32_t status;
	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
	struct drm_device *dev = gpu->dev;

	if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_PENDING))
		return;

	/* Delete the preemption watchdog timer */
	timer_delete(&a6xx_gpu->preempt_timer);

Annotation

Implementation Notes