Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright (C) 2019 BayLibre, SAS
  4 * 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 registers
 16 * on the VPU AHB bus, either manually or triggered by an internal IRQ
 17 * event like VSYNC or a line input counter.
 18 * The initial implementation handles a single channel (over 8), triggered
 19 * 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 the
105 * hardware at the same time.
106 * When meson_rdma_flush is called, the RDMA will replay the register
107 * 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}