[PATCH 2/3] drm/meson: add RDMA module driver

Neil Armstrong narmstrong at baylibre.com
Thu Oct 17 09:37:28 UTC 2019


On 15/10/2019 13:33, Neil Armstrong wrote:
> The VPU embeds a "Register DMA" that can write a sequence of registers
> on the VPU AHB bus, either manually or triggered by an internal IRQ
> event like VSYNC or a line input counter.
> 
> The initial implementation handles a single channel (over 8), triggered
> by the VSYNC irq and does not handle the RDMA irq.
> 
> The RDMA will be usefull to reset and program the AFBC decoder unit
> on each vsync without involving the interrupt handler that can
> be masked for a long period of time, producing display glitches.
> 
> Signed-off-by: Neil Armstrong <narmstrong at baylibre.com>
> ---
>  drivers/gpu/drm/meson/Makefile     |   2 +-
>  drivers/gpu/drm/meson/meson_drv.c  |  14 +++-
>  drivers/gpu/drm/meson/meson_drv.h  |   6 ++
>  drivers/gpu/drm/meson/meson_rdma.c | 123 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/meson/meson_rdma.h |  20 +++++
>  5 files changed, 161 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/gpu/drm/meson/meson_rdma.c
>  create mode 100644 drivers/gpu/drm/meson/meson_rdma.h
> 
> diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile
> index b1fa055aaed3..9e36f0c7b816 100644
> --- a/drivers/gpu/drm/meson/Makefile
> +++ b/drivers/gpu/drm/meson/Makefile
> @@ -1,7 +1,7 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
>  meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_overlay.o
> -meson-drm-y += meson_osd_afbcd.o
> +meson-drm-y += meson_osd_afbcd.o meson_rdma.o
>  
>  obj-$(CONFIG_DRM_MESON) += meson-drm.o
>  obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o
> diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
> index 0f31e70bb94f..2200d8b5252e 100644
> --- a/drivers/gpu/drm/meson/meson_drv.c
> +++ b/drivers/gpu/drm/meson/meson_drv.c
> @@ -33,6 +33,7 @@
>  #include "meson_venc_cvbs.h"
>  #include "meson_viu.h"
>  #include "meson_vpp.h"
> +#include "meson_rdma.h"
>  
>  #define DRIVER_NAME "meson"
>  #define DRIVER_DESC "Amlogic Meson DRM driver"
> @@ -295,8 +296,11 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
>  	meson_venc_init(priv);
>  	meson_vpp_init(priv);
>  	meson_viu_init(priv);
> -	if (priv->afbcd.ops)
> -		priv->afbcd.ops->init(priv);
> +	if (priv->afbcd.ops) {
> +		ret = priv->afbcd.ops->init(priv);
> +		if (ret)
> +			return ret;
> +	}
>  
>  	/* Encoder Initialization */
>  
> @@ -367,12 +371,16 @@ static void meson_drv_unbind(struct device *dev)
>  		meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2);
>  	}
>  
> +	if (priv->afbcd.ops) {
> +		priv->afbcd.ops->reset(priv);
> +		meson_rdma_free(priv);
> +	}
> +
>  	drm_dev_unregister(drm);
>  	drm_irq_uninstall(drm);
>  	drm_kms_helper_poll_fini(drm);
>  	drm_mode_config_cleanup(drm);
>  	drm_dev_put(drm);
> -
>  }
>  
>  static const struct component_master_ops meson_drv_master_ops = {
> diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h
> index de25349be8aa..9995d74c5ded 100644
> --- a/drivers/gpu/drm/meson/meson_drv.h
> +++ b/drivers/gpu/drm/meson/meson_drv.h
> @@ -139,6 +139,12 @@ struct meson_drm {
>  		u64 modifier;
>  		u32 format;
>  	} afbcd;
> +
> +	struct {
> +		dma_addr_t addr_phys;
> +		uint32_t *addr;
> +		unsigned int offset;
> +	} rdma;
>  };
>  
>  static inline int meson_vpu_is_compatible(struct meson_drm *priv,
> diff --git a/drivers/gpu/drm/meson/meson_rdma.c b/drivers/gpu/drm/meson/meson_rdma.c
> new file mode 100644
> index 000000000000..13fd9b173439
> --- /dev/null
> +++ b/drivers/gpu/drm/meson/meson_rdma.c
> @@ -0,0 +1,123 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 BayLibre, SAS
> + * Author: Neil Armstrong <narmstrong at baylibre.com>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/dma-mapping.h>
> +
> +#include "meson_drv.h"
> +#include "meson_registers.h"
> +#include "meson_rdma.h"
> +
> +/*
> + * The VPU embeds a "Register DMA" that can write a sequence of registers
> + * on the VPU AHB bus, either manually or triggered by an internal IRQ
> + * event like VSYNC or a line input counter.
> + * The initial implementation handles a single channel (over 8), triggered
> + * by the VSYNC irq and does not handle the RDMA irq.
> + */
> +
> +int meson_rdma_init(struct meson_drm *priv)
> +{
> +	/* Allocate a PAGE buffer */

Should be "Allocate a 4K buffer"

> +	priv->rdma.addr = dma_alloc_coherent(priv->dev, SZ_4K,
> +					     &priv->rdma.addr_phys,
> +					     GFP_KERNEL);
> +	if (!priv->rdma.addr)
> +		return -ENOMEM;
> +
> +	priv->rdma.offset = 0;
> +
> +	writel_relaxed(RDMA_CTRL_SW_RESET,
> +		       priv->io_base + _REG(RDMA_CTRL));
> +	writel_relaxed(RDMA_DEFAULT_CONFIG |
> +		       FIELD_PREP(RDMA_CTRL_AHB_WR_BURST, 3) |
> +		       FIELD_PREP(RDMA_CTRL_AHB_RD_BURST, 0),
> +		       priv->io_base + _REG(RDMA_CTRL));
> +
> +	return 0;
> +}
> +
> +void meson_rdma_free(struct meson_drm *priv)
> +{
> +	if (!priv->rdma.addr && !priv->rdma.addr_phys)
> +		return;
> +
> +	meson_rdma_stop(priv);
> +
> +	dma_free_coherent(priv->dev, SZ_4K,
> +			  priv->rdma.addr, priv->rdma.addr_phys);
> +
> +	priv->rdma.addr = NULL;
> +	priv->rdma.addr_phys = (dma_addr_t)NULL;
> +}
> +
> +void meson_rdma_setup(struct meson_drm *priv)
> +{
> +	/* Channel 1: Write Flag, No Address Increment */
> +	writel_bits_relaxed(RDMA_ACCESS_RW_FLAG_CHAN1 |
> +			    RDMA_ACCESS_ADDR_INC_CHAN1,
> +			    RDMA_ACCESS_RW_FLAG_CHAN1,
> +			    priv->io_base + _REG(RDMA_ACCESS_AUTO));
> +}
> +
> +void meson_rdma_stop(struct meson_drm *priv)
> +{
> +	writel_bits_relaxed(RDMA_IRQ_CLEAR_CHAN1,
> +			    RDMA_IRQ_CLEAR_CHAN1,
> +			    priv->io_base + _REG(RDMA_CTRL));
> +
> +	/* Stop Channel 1 */
> +	writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
> +			    FIELD_PREP(RDMA_ACCESS_ADDR_INC_CHAN1,
> +				       RDMA_ACCESS_TRIGGER_STOP),
> +			    priv->io_base + _REG(RDMA_ACCESS_AUTO));
> +}

A meson_rdma_reset() call is missing to reset priv->rdma.offset
and stop the RDMA and should be called by afbcd_reset instead of stop
other wise the meson_rdma_writel() will overflow when not using AFBC.

> +
> +static void meson_rdma_writel(struct meson_drm *priv, uint32_t val,
> +			      uint32_t reg)
> +{
> +	if (priv->rdma.offset == SZ_4K) {

Found a regression here, should be SZ_4K/8

> +		dev_warn_once(priv->dev, "%s: overflow\n", __func__);
> +		return;
> +	}
> +
> +	priv->rdma.addr[priv->rdma.offset++] = reg;
> +	priv->rdma.addr[priv->rdma.offset++] = val;
> +}
> +
> +/*
> + * This will add the register to the RDMA buffer and write it to the
> + * hardware at the same time.
> + * When meson_rdma_flush is called, the RDMA will replay the register
> + * writes in order.
> + */
> +void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg)
> +{
> +	meson_rdma_writel(priv, val, reg);
> +
> +	writel_relaxed(val, priv->io_base + _REG(reg));
> +}
> +
> +void meson_rdma_flush(struct meson_drm *priv)
> +{
> +	meson_rdma_stop(priv);
> +
> +	/* Start of Channel 1 register writes buffer */
> +	writel(priv->rdma.addr_phys,
> +	       priv->io_base + _REG(RDMA_AHB_START_ADDR_1));
> +
> +	/* Last byte on Channel 1 register writes buffer */
> +	writel(priv->rdma.addr_phys + (priv->rdma.offset * 8) - 1,

8 should have a define

> +	       priv->io_base + _REG(RDMA_AHB_END_ADDR_1));
> +
> +	/* Trigger Channel 1 on VSYNC event */
> +	writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
> +			    FIELD_PREP(RDMA_ACCESS_TRIGGER_CHAN1,
> +				       RDMA_ACCESS_TRIGGER_VSYNC),
> +			    priv->io_base + _REG(RDMA_ACCESS_AUTO));
> +
> +	priv->rdma.offset = 0;
> +}
> diff --git a/drivers/gpu/drm/meson/meson_rdma.h b/drivers/gpu/drm/meson/meson_rdma.h
> new file mode 100644
> index 000000000000..84c882f97d28
> --- /dev/null
> +++ b/drivers/gpu/drm/meson/meson_rdma.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2019 BayLibre, SAS
> + * Author: Neil Armstrong <narmstrong at baylibre.com>
> + */
> +
> +#ifndef __MESON_RDMA_H
> +#define __MESON_RDMA_H
> +
> +#include "meson_drv.h"
> +
> +int meson_rdma_init(struct meson_drm *priv);
> +void meson_rdma_free(struct meson_drm *priv);
> +void meson_rdma_setup(struct meson_drm *priv);
> +void meson_rdma_stop(struct meson_drm *priv);
> +
> +void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg);
> +void meson_rdma_flush(struct meson_drm *priv);
> +
> +#endif /* __MESON_RDMA_H */
> 

Will send a V2 shortly with these fixes.

Neil


More information about the dri-devel mailing list