linux/drivers/gpu/drm/meson/meson_rdma.c
<<
>>
Prefs
   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}
 136