Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2019 BayLibre, SAS
0004  * Author: Neil Armstrong <narmstrong@baylibre.com>
0005  */
0006 
0007 #include <linux/bitfield.h>
0008 #include <linux/dma-mapping.h>
0009 
0010 #include "meson_drv.h"
0011 #include "meson_registers.h"
0012 #include "meson_rdma.h"
0013 
0014 /*
0015  * The VPU embeds a "Register DMA" that can write a sequence of registers
0016  * on the VPU AHB bus, either manually or triggered by an internal IRQ
0017  * event like VSYNC or a line input counter.
0018  * The initial implementation handles a single channel (over 8), triggered
0019  * by the VSYNC irq and does not handle the RDMA irq.
0020  */
0021 
0022 #define RDMA_DESC_SIZE  (sizeof(uint32_t) * 2)
0023 
0024 int meson_rdma_init(struct meson_drm *priv)
0025 {
0026     if (!priv->rdma.addr) {
0027         /* Allocate a PAGE buffer */
0028         priv->rdma.addr =
0029             dma_alloc_coherent(priv->dev, SZ_4K,
0030                        &priv->rdma.addr_dma,
0031                        GFP_KERNEL);
0032         if (!priv->rdma.addr)
0033             return -ENOMEM;
0034     }
0035 
0036     priv->rdma.offset = 0;
0037 
0038     writel_relaxed(RDMA_CTRL_SW_RESET,
0039                priv->io_base + _REG(RDMA_CTRL));
0040     writel_relaxed(RDMA_DEFAULT_CONFIG |
0041                FIELD_PREP(RDMA_CTRL_AHB_WR_BURST, 3) |
0042                FIELD_PREP(RDMA_CTRL_AHB_RD_BURST, 0),
0043                priv->io_base + _REG(RDMA_CTRL));
0044 
0045     return 0;
0046 }
0047 
0048 void meson_rdma_free(struct meson_drm *priv)
0049 {
0050     if (!priv->rdma.addr && !priv->rdma.addr_dma)
0051         return;
0052 
0053     meson_rdma_stop(priv);
0054 
0055     dma_free_coherent(priv->dev, SZ_4K,
0056               priv->rdma.addr, priv->rdma.addr_dma);
0057 
0058     priv->rdma.addr = NULL;
0059     priv->rdma.addr_dma = (dma_addr_t)0;
0060 }
0061 
0062 void meson_rdma_setup(struct meson_drm *priv)
0063 {
0064     /* Channel 1: Write Flag, No Address Increment */
0065     writel_bits_relaxed(RDMA_ACCESS_RW_FLAG_CHAN1 |
0066                 RDMA_ACCESS_ADDR_INC_CHAN1,
0067                 RDMA_ACCESS_RW_FLAG_CHAN1,
0068                 priv->io_base + _REG(RDMA_ACCESS_AUTO));
0069 }
0070 
0071 void meson_rdma_stop(struct meson_drm *priv)
0072 {
0073     writel_bits_relaxed(RDMA_IRQ_CLEAR_CHAN1,
0074                 RDMA_IRQ_CLEAR_CHAN1,
0075                 priv->io_base + _REG(RDMA_CTRL));
0076 
0077     /* Stop Channel 1 */
0078     writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
0079                 FIELD_PREP(RDMA_ACCESS_ADDR_INC_CHAN1,
0080                        RDMA_ACCESS_TRIGGER_STOP),
0081                 priv->io_base + _REG(RDMA_ACCESS_AUTO));
0082 }
0083 
0084 void meson_rdma_reset(struct meson_drm *priv)
0085 {
0086     meson_rdma_stop(priv);
0087 
0088     priv->rdma.offset = 0;
0089 }
0090 
0091 static void meson_rdma_writel(struct meson_drm *priv, uint32_t val,
0092                   uint32_t reg)
0093 {
0094     if (priv->rdma.offset >= (SZ_4K / RDMA_DESC_SIZE)) {
0095         dev_warn_once(priv->dev, "%s: overflow\n", __func__);
0096         return;
0097     }
0098 
0099     priv->rdma.addr[priv->rdma.offset++] = reg;
0100     priv->rdma.addr[priv->rdma.offset++] = val;
0101 }
0102 
0103 /*
0104  * This will add the register to the RDMA buffer and write it to the
0105  * hardware at the same time.
0106  * When meson_rdma_flush is called, the RDMA will replay the register
0107  * writes in order.
0108  */
0109 void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg)
0110 {
0111     meson_rdma_writel(priv, val, reg);
0112 
0113     writel_relaxed(val, priv->io_base + _REG(reg));
0114 }
0115 
0116 void meson_rdma_flush(struct meson_drm *priv)
0117 {
0118     meson_rdma_stop(priv);
0119 
0120     /* Start of Channel 1 register writes buffer */
0121     writel(priv->rdma.addr_dma,
0122            priv->io_base + _REG(RDMA_AHB_START_ADDR_1));
0123 
0124     /* Last byte on Channel 1 register writes buffer */
0125     writel(priv->rdma.addr_dma + (priv->rdma.offset * RDMA_DESC_SIZE) - 1,
0126            priv->io_base + _REG(RDMA_AHB_END_ADDR_1));
0127 
0128     /* Trigger Channel 1 on VSYNC event */
0129     writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
0130                 FIELD_PREP(RDMA_ACCESS_TRIGGER_CHAN1,
0131                        RDMA_ACCESS_TRIGGER_VSYNC),
0132                 priv->io_base + _REG(RDMA_ACCESS_AUTO));
0133 
0134     priv->rdma.offset = 0;
0135 }