brintos

brintos / linux-shallow public Read only

0
0
Text · 3.4 KiB · 1303821 Raw
136 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2019 BayLibre, SAS4 * Author: Neil Armstrong <narmstrong@baylibre.com>5 */6 7#include <linux/bitfield.h>8#include <linux/dma-mapping.h>9 10#include "meson_drv.h"11#include "meson_registers.h"12#include "meson_rdma.h"13 14/*15 * The VPU embeds a "Register DMA" that can write a sequence of registers16 * on the VPU AHB bus, either manually or triggered by an internal IRQ17 * event like VSYNC or a line input counter.18 * The initial implementation handles a single channel (over 8), triggered19 * by the VSYNC irq and does not handle the RDMA irq.20 */21 22#define RDMA_DESC_SIZE	(sizeof(uint32_t) * 2)23 24int meson_rdma_init(struct meson_drm *priv)25{26	if (!priv->rdma.addr) {27		/* Allocate a PAGE buffer */28		priv->rdma.addr =29			dma_alloc_coherent(priv->dev, SZ_4K,30					   &priv->rdma.addr_dma,31					   GFP_KERNEL);32		if (!priv->rdma.addr)33			return -ENOMEM;34	}35 36	priv->rdma.offset = 0;37 38	writel_relaxed(RDMA_CTRL_SW_RESET,39		       priv->io_base + _REG(RDMA_CTRL));40	writel_relaxed(RDMA_DEFAULT_CONFIG |41		       FIELD_PREP(RDMA_CTRL_AHB_WR_BURST, 3) |42		       FIELD_PREP(RDMA_CTRL_AHB_RD_BURST, 0),43		       priv->io_base + _REG(RDMA_CTRL));44 45	return 0;46}47 48void meson_rdma_free(struct meson_drm *priv)49{50	if (!priv->rdma.addr && !priv->rdma.addr_dma)51		return;52 53	meson_rdma_stop(priv);54 55	dma_free_coherent(priv->dev, SZ_4K,56			  priv->rdma.addr, priv->rdma.addr_dma);57 58	priv->rdma.addr = NULL;59	priv->rdma.addr_dma = (dma_addr_t)0;60}61 62void meson_rdma_setup(struct meson_drm *priv)63{64	/* Channel 1: Write Flag, No Address Increment */65	writel_bits_relaxed(RDMA_ACCESS_RW_FLAG_CHAN1 |66			    RDMA_ACCESS_ADDR_INC_CHAN1,67			    RDMA_ACCESS_RW_FLAG_CHAN1,68			    priv->io_base + _REG(RDMA_ACCESS_AUTO));69}70 71void meson_rdma_stop(struct meson_drm *priv)72{73	writel_bits_relaxed(RDMA_IRQ_CLEAR_CHAN1,74			    RDMA_IRQ_CLEAR_CHAN1,75			    priv->io_base + _REG(RDMA_CTRL));76 77	/* Stop Channel 1 */78	writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,79			    FIELD_PREP(RDMA_ACCESS_ADDR_INC_CHAN1,80				       RDMA_ACCESS_TRIGGER_STOP),81			    priv->io_base + _REG(RDMA_ACCESS_AUTO));82}83 84void meson_rdma_reset(struct meson_drm *priv)85{86	meson_rdma_stop(priv);87 88	priv->rdma.offset = 0;89}90 91static void meson_rdma_writel(struct meson_drm *priv, uint32_t val,92			      uint32_t reg)93{94	if (priv->rdma.offset >= (SZ_4K / RDMA_DESC_SIZE)) {95		dev_warn_once(priv->dev, "%s: overflow\n", __func__);96		return;97	}98 99	priv->rdma.addr[priv->rdma.offset++] = reg;100	priv->rdma.addr[priv->rdma.offset++] = val;101}102 103/*104 * This will add the register to the RDMA buffer and write it to the105 * hardware at the same time.106 * When meson_rdma_flush is called, the RDMA will replay the register107 * writes in order.108 */109void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg)110{111	meson_rdma_writel(priv, val, reg);112 113	writel_relaxed(val, priv->io_base + _REG(reg));114}115 116void meson_rdma_flush(struct meson_drm *priv)117{118	meson_rdma_stop(priv);119 120	/* Start of Channel 1 register writes buffer */121	writel(priv->rdma.addr_dma,122	       priv->io_base + _REG(RDMA_AHB_START_ADDR_1));123 124	/* Last byte on Channel 1 register writes buffer */125	writel(priv->rdma.addr_dma + (priv->rdma.offset * RDMA_DESC_SIZE) - 1,126	       priv->io_base + _REG(RDMA_AHB_END_ADDR_1));127 128	/* Trigger Channel 1 on VSYNC event */129	writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,130			    FIELD_PREP(RDMA_ACCESS_TRIGGER_CHAN1,131				       RDMA_ACCESS_TRIGGER_VSYNC),132			    priv->io_base + _REG(RDMA_ACCESS_AUTO));133 134	priv->rdma.offset = 0;135}136