// 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 ;
}
static void meson_rdma_writel(struct meson_drm *priv, uint32_t val,
uint32_t reg)
{
if (priv->rdma.offset >= (SZ_4K / RDMA_DESC_SIZE)) {
dev_warn_once(priv->dev, "%s: overflow\n" , __func__);
return ;
}
priv->rdma.addr[priv->rdma.offset++] = reg;
priv->rdma.addr[priv->rdma.offset++] = val;
}
/*
* This will add the register to the RDMA buffer and write it to the
* hardware at the same time .
* When meson_rdma_flush is called , the RDMA will replay the register
* writes in order .
*/
void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg)
{
meson_rdma_writel(priv, val, reg);
writel_relaxed(val, priv->io_base + _REG(reg));
}
void meson_rdma_flush(struct meson_drm *priv)
{
meson_rdma_stop(priv);
/* Start of Channel 1 register writes buffer */
writel(priv->rdma.addr_dma,
priv->io_base + _REG(RDMA_AHB_START_ADDR_1));
/* Last byte on Channel 1 register writes buffer */
writel(priv->rdma.addr_dma + (priv->rdma.offset * RDMA_DESC_SIZE) - 1 ,
priv->io_base + _REG(RDMA_AHB_END_ADDR_1));
/* Trigger Channel 1 on VSYNC event */
writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
FIELD_PREP(RDMA_ACCESS_TRIGGER_CHAN1,
RDMA_ACCESS_TRIGGER_VSYNC),
priv->io_base + _REG(RDMA_ACCESS_AUTO));
priv->rdma.offset = 0 ;
}
Messung V0.5 in Prozent C=93 H=100 G=96
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-10-01)
¤
*© Formatika GbR, Deutschland