meson_rdma.c (3521B)
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}