drivers/gpu/drm/meson/meson_rdma.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/meson/meson_rdma.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/meson/meson_rdma.c
Extension
.c
Size
3521 bytes
Lines
136
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) 2019 BayLibre, SAS
 * Author: Neil Armstrong <narmstrong@baylibre.com>
 */

#include <linux/bitfield.h>
#include <linux/dma-mapping.h>

#include "meson_drv.h"
#include "meson_registers.h"
#include "meson_rdma.h"

/*
 * The VPU embeds a "Register DMA" that can write a sequence of registers
 * on the VPU AHB bus, either manually or triggered by an internal IRQ
 * event like VSYNC or a line input counter.
 * The initial implementation handles a single channel (over 8), triggered
 * by the VSYNC irq and does not handle the RDMA irq.
 */

#define RDMA_DESC_SIZE	(sizeof(uint32_t) * 2)

int meson_rdma_init(struct meson_drm *priv)
{
	if (!priv->rdma.addr) {
		/* Allocate a PAGE buffer */
		priv->rdma.addr =
			dma_alloc_coherent(priv->dev, SZ_4K,
					   &priv->rdma.addr_dma,
					   GFP_KERNEL);
		if (!priv->rdma.addr)
			return -ENOMEM;
	}

	priv->rdma.offset = 0;

	writel_relaxed(RDMA_CTRL_SW_RESET,
		       priv->io_base + _REG(RDMA_CTRL));
	writel_relaxed(RDMA_DEFAULT_CONFIG |
		       FIELD_PREP(RDMA_CTRL_AHB_WR_BURST, 3) |
		       FIELD_PREP(RDMA_CTRL_AHB_RD_BURST, 0),
		       priv->io_base + _REG(RDMA_CTRL));

	return 0;
}

void meson_rdma_free(struct meson_drm *priv)
{
	if (!priv->rdma.addr && !priv->rdma.addr_dma)
		return;

	meson_rdma_stop(priv);

	dma_free_coherent(priv->dev, SZ_4K,
			  priv->rdma.addr, priv->rdma.addr_dma);

	priv->rdma.addr = NULL;
	priv->rdma.addr_dma = (dma_addr_t)0;
}

void meson_rdma_setup(struct meson_drm *priv)
{
	/* Channel 1: Write Flag, No Address Increment */
	writel_bits_relaxed(RDMA_ACCESS_RW_FLAG_CHAN1 |
			    RDMA_ACCESS_ADDR_INC_CHAN1,
			    RDMA_ACCESS_RW_FLAG_CHAN1,
			    priv->io_base + _REG(RDMA_ACCESS_AUTO));
}

void meson_rdma_stop(struct meson_drm *priv)
{
	writel_bits_relaxed(RDMA_IRQ_CLEAR_CHAN1,
			    RDMA_IRQ_CLEAR_CHAN1,
			    priv->io_base + _REG(RDMA_CTRL));

	/* Stop Channel 1 */
	writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
			    FIELD_PREP(RDMA_ACCESS_ADDR_INC_CHAN1,
				       RDMA_ACCESS_TRIGGER_STOP),
			    priv->io_base + _REG(RDMA_ACCESS_AUTO));
}

void meson_rdma_reset(struct meson_drm *priv)
{
	meson_rdma_stop(priv);

	priv->rdma.offset = 0;
}

Annotation

Implementation Notes