dri-devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: dri-devel@lists.freedesktop.org
Subject: Re: [PATCH 05/33] drm/omap: partial workaround for DRA7 DMM errata i878
Date: Tue, 23 Feb 2016 23:57:23 +0200	[thread overview]
Message-ID: <5240664.Dy4C1HiFai@avalon> (raw)
In-Reply-To: <1455875288-4370-6-git-send-email-tomi.valkeinen@ti.com>

Hi Tomi,

Thank you for the patch.

On Friday 19 February 2016 11:47:40 Tomi Valkeinen wrote:
> Errata i878 says that MPU should not be used to access RAM and DMM at
> the same time. As it's not possible to prevent MPU accessing RAM, we
> need to access DMM via a proxy.
> 
> This patch changes DMM driver to access DMM registers via sDMA. Instead
> of doing a normal readl/writel call to read/write a register, we use
> sDMA to copy 4 bytes from/to the DMM registers.
> 
> This patch provides only a partial workaround for i878, as not only DMM
> register reads/writes are affected, but also accesses to the DMM mapped
> buffers (framebuffers, usually).

Will this patch really improve the situation if the DMM mapping is accessed 
anyway ?

> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
>  Documentation/devicetree/bindings/arm/omap/dmm.txt |   3 +-
>  drivers/gpu/drm/omapdrm/omap_dmm_priv.h            |   8 ++
>  drivers/gpu/drm/omapdrm/omap_dmm_tiler.c           | 141 +++++++++++++++++-
>  3 files changed, 149 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/arm/omap/dmm.txt
> b/Documentation/devicetree/bindings/arm/omap/dmm.txt index
> 8bd6d0a238a8..e5fc2eb7f4da 100644
> --- a/Documentation/devicetree/bindings/arm/omap/dmm.txt
> +++ b/Documentation/devicetree/bindings/arm/omap/dmm.txt
> @@ -8,7 +8,8 @@ translation for initiators which need contiguous dma bus
> addresses.
> 
>  Required properties:
>  - compatible:	Should contain "ti,omap4-dmm" for OMAP4 family
> -		Should contain "ti,omap5-dmm" for OMAP5 and DRA7x family
> +		Should contain "ti,omap5-dmm" for OMAP5 family
> +		Should contain "ti,dra7-dmm" for DRA7x family

Isn't it DRA7xx instead of DRA7x ?

>  - reg:		Contains DMM register address range (base address and length)
>  - interrupts:	Should contain an interrupt-specifier for DMM_IRQ.
>  - ti,hwmods:	Name of the hwmod associated to DMM, which is typically 
"dmm"
> diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
> b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h index 9f32a83ca507..4d292ba5bcca
> 100644
> --- a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
> +++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
> @@ -155,10 +155,12 @@ struct refill_engine {
> 
>  struct dmm_platform_data {
>  	uint32_t cpu_cache_flags;
> +	bool errata_i878_wa;
>  };
> 
>  struct dmm {
>  	struct device *dev;
> +	u32 phys_base;
>  	void __iomem *base;
>  	int irq;
> 
> @@ -189,6 +191,12 @@ struct dmm {
>  	struct list_head alloc_head;
> 
>  	const struct dmm_platform_data *plat_data;
> +
> +	bool dmm_workaround;
> +	spinlock_t wa_lock;
> +	u32 *wa_dma_data;
> +	dma_addr_t wa_dma_handle;
> +	struct dma_chan *wa_dma_chan;
>  };
> 
>  #endif
> diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
> b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index fe5260477b52..3ec5c6633585
> 100644
> --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
> +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
> @@ -19,6 +19,7 @@
>  #include <linux/completion.h>
>  #include <linux/delay.h>
>  #include <linux/dma-mapping.h>
> +#include <linux/dmaengine.h>
>  #include <linux/errno.h>
>  #include <linux/init.h>
>  #include <linux/interrupt.h>
> @@ -79,14 +80,124 @@ static const uint32_t reg[][4] = {
>  			DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
>  };
> 
> +static int dmm_dma_copy(struct dmm *dmm, u32 src, u32 dst)

Anything wrong with using dma_addr_t ?

> +{
> +	struct dma_device *dma_dev = dmm->wa_dma_chan->device;
> +	struct dma_async_tx_descriptor *tx = NULL;

No need to initialize tx to NULL.

> +	enum dma_status status;
> +	dma_cookie_t cookie;
> +
> +	tx = dma_dev->device_prep_dma_memcpy(dmm->wa_dma_chan, dst, src, 4, 0);

Given that you're transferring between an I/O mapped register and system 
memory, I believe you should use dmaengine_prep_slave_single() instead, with a 
call to dmaengine_slave_config() to set the I/O mapped register physical 
address. You will also need to request a slave DMA channel instead of a memcpy 
DMA channel.

> +	if (!tx) {
> +		dev_err(dmm->dev, "Failed to prepare DMA memcpy\n");
> +		return -EIO;
> +	}
> +
> +	cookie = tx->tx_submit(tx);
> +	if (dma_submit_error(cookie)) {
> +		dev_err(dmm->dev, "Failed to do DMA tx_submit\n");
> +		return -EIO;
> +	}
> +
> +	dma_async_issue_pending(dmm->wa_dma_chan);
> +	status = dma_sync_wait(dmm->wa_dma_chan, cookie);
> +	if (status != DMA_COMPLETE)
> +		dev_err(dmm->dev, "i878 wa DMA copy failure\n");
> +
> +	dmaengine_terminate_all(dmm->wa_dma_chan);
> +	return 0;
> +}
> +
> +static u32 dmm_read_wa(struct dmm *dmm, u32 reg)
> +{
> +	u32 src, dst;
> +	int r;
> +
> +	src = (u32)(dmm->phys_base + reg);
> +	dst = (u32)dmm->wa_dma_handle;
> +
> +	r = dmm_dma_copy(dmm, src, dst);
> +	if (r) {
> +		dev_err(dmm->dev, "sDMA read transfer timeout\n");
> +		return readl(dmm->base + reg);
> +	}
> +
> +	return readl(dmm->wa_dma_data);

readl() has a memory barrier *after* the read, not before. Similarly writel() 
has its memory barrier *before* the write. I don't think that's what you want, 
shouldn't you use readl_relaxed() and writel_relaxed() with explicit barriers 
?

I'm also unsure whether readl and writel are the right primitives, as they 
target access to I/O memory, not system memory. Can't you use explicit 
barriers and direct access (return *dmm->wa_dma_data) ?

> +}
> +
> +static void dmm_write_wa(struct dmm *dmm, u32 val, u32 reg)
> +{
> +	u32 src, dst;
> +	int r;
> +
> +	writel(val, dmm->wa_dma_data);

Same comment here.

> +	src = (u32)dmm->wa_dma_handle;
> +	dst = (u32)(dmm->phys_base + reg);
> +
> +	r = dmm_dma_copy(dmm, src, dst);
> +	if (r) {
> +		dev_err(dmm->dev, "sDMA write transfer timeout\n");
> +		writel(val, dmm->base + reg);
> +	}
> +}
> +
>  static u32 dmm_read(struct dmm *dmm, u32 reg)
>  {
> -	return readl(dmm->base + reg);
> +	if (dmm->dmm_workaround) {
> +		u32 v;
> +		unsigned long flags;
> +
> +		spin_lock_irqsave(&dmm->wa_lock, flags);
> +		v = dmm_read_wa(dmm, reg);

dma_sync_wait() with a spinlock held doesn't seem like the best idea to me.

> +		spin_unlock_irqrestore(&dmm->wa_lock, flags);
> +
> +		return v;
> +	} else {
> +		return readl(dmm->base + reg);
> +	}
>  }
> 
>  static void dmm_write(struct dmm *dmm, u32 val, u32 reg)
>  {
> -	writel(val, dmm->base + reg);
> +	if (dmm->dmm_workaround) {
> +		unsigned long flags;
> +
> +		spin_lock_irqsave(&dmm->wa_lock, flags);
> +		dmm_write_wa(dmm, val, reg);

Ditto.

> +		spin_unlock_irqrestore(&dmm->wa_lock, flags);
> +	} else {
> +		writel(val, dmm->base + reg);
> +	}
> +}
> +
> +static int dmm_workaround_init(struct dmm *dmm)
> +{
> +	dma_cap_mask_t mask;
> +
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_MEMCPY, mask);

Nitpicking, I'd move those two lines down right before the 
dma_request_channel() call, as they are part of the same logical block of 
code.

> +	spin_lock_init(&dmm->wa_lock);
> +
> +	dmm->wa_dma_data = dma_alloc_coherent(dmm->dev, 4, &dmm->wa_dma_handle,
> GFP_KERNEL);

Maybe a macro instead of 4 ?

> +	if (!dmm->wa_dma_data)
> +		return -ENOMEM;
> +
> +	dmm->wa_dma_chan = dma_request_channel(mask, NULL, NULL);
> +	if (!dmm->wa_dma_chan) {
> +		dma_free_coherent(dmm->dev, 4, dmm->wa_dma_data, dmm->wa_dma_handle);
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static void dmm_workaround_uninit(struct dmm *dmm)
> +{
> +	dma_release_channel(dmm->wa_dma_chan);
> +
> +	dma_free_coherent(dmm->dev, 4, dmm->wa_dma_data, dmm->wa_dma_handle);
>  }
> 
>  /* simple allocator to grab next 16 byte aligned memory from txn */
> @@ -606,6 +717,9 @@ static int omap_dmm_remove(struct platform_device *dev)
>  		if (omap_dmm->dummy_page)
>  			__free_page(omap_dmm->dummy_page);
> 
> +		if (omap_dmm->dmm_workaround)
> +			dmm_workaround_uninit(omap_dmm);
> +
>  		if (omap_dmm->irq > 0)
>  			free_irq(omap_dmm->irq, omap_dmm);
> 
> @@ -653,6 +767,7 @@ static int omap_dmm_probe(struct platform_device *dev)
>  		goto fail;
>  	}
> 
> +	omap_dmm->phys_base = mem->start;
>  	omap_dmm->base = ioremap(mem->start, SZ_2K);
> 
>  	if (!omap_dmm->base) {
> @@ -668,6 +783,19 @@ static int omap_dmm_probe(struct platform_device *dev)
> 
>  	omap_dmm->dev = &dev->dev;
> 
> +	omap_dmm->dmm_workaround = omap_dmm->plat_data->errata_i878_wa;
> +
> +	if (omap_dmm->dmm_workaround) {

I'd test omap_dmm->plat_data->errata_i878_wa here, omap_dmm->dmm_workaround is 
initialized to false by kzalloc().

> +		int r;
> +		r = dmm_workaround_init(omap_dmm);

Can't you use the ret variable ? I know it's pre-initialized to -EFAULT, but 
it seems better to me to set it to explicit values in the three goto fail; 
error cases that would be impacted, especially given that -EFAULT is the wrong 
error code to return (and as it would touch that code, the 
platform_get_resource() + ioremap() calls can be optimized in a 
devm_ioremap_resource() in another cleanup patch).

> +		if (r) {
> +			omap_dmm->dmm_workaround = false;
> +			dev_err(&dev->dev, "failed to initialize work-around, WA not 
used\n");

As this is a non-fatal error, maybe dev_warn() ?

> +		} else {
> +			dev_info(&dev->dev, "workaround for errata i878 in use\n");
> +		}
> +	}
> +
>  	hwinfo = dmm_read(omap_dmm, DMM_PAT_HWINFO);
>  	omap_dmm->num_engines = (hwinfo >> 24) & 0x1F;
>  	omap_dmm->num_lut = (hwinfo >> 16) & 0x1F;
> @@ -1031,6 +1159,11 @@ static const struct dmm_platform_data
> dmm_omap5_platform_data = { .cpu_cache_flags = OMAP_BO_UNCACHED,
>  };
> 
> +static const struct dmm_platform_data dmm_dra7_platform_data = {
> +	.cpu_cache_flags = OMAP_BO_UNCACHED,
> +	.errata_i878_wa = true,
> +};
> +
>  static const struct of_device_id dmm_of_match[] = {
>  	{
>  		.compatible = "ti,omap4-dmm",
> @@ -1040,6 +1173,10 @@ static const struct of_device_id dmm_of_match[] = {
>  		.compatible = "ti,omap5-dmm",
>  		.data = &dmm_omap5_platform_data,
>  	},
> +	{
> +		.compatible = "ti,dra7-dmm",
> +		.data = &dmm_dra7_platform_data,
> +	},
>  	{},
>  };
>  #endif

-- 
Regards,

Laurent Pinchart

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

  reply	other threads:[~2016-02-23 21:57 UTC|newest]

Thread overview: 65+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-02-19  9:47 [PATCH 00/33] drm/omap: patches for v4.6 Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 01/33] drm/omap: HDMI: change enable/disable to avoid sync-losts Tomi Valkeinen
2016-02-23 10:22   ` Laurent Pinchart
2016-02-23 11:33     ` Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 02/33] HACK: drm/omap: always use blocking DMM fill Tomi Valkeinen
2016-02-23 10:27   ` Laurent Pinchart
2016-02-23 13:09     ` Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 03/33] HACK: drm/omap: fix memory barrier bug in DMM driver Tomi Valkeinen
2016-02-23 21:13   ` Laurent Pinchart
2016-02-24 10:34     ` Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 04/33] drm/omap: add dmm_read() and dmm_write() wrappers Tomi Valkeinen
2016-02-23 21:18   ` Laurent Pinchart
2016-02-19  9:47 ` [PATCH 05/33] drm/omap: partial workaround for DRA7 DMM errata i878 Tomi Valkeinen
2016-02-23 21:57   ` Laurent Pinchart [this message]
2016-02-24  9:14     ` Tomi Valkeinen
2017-09-29 12:29     ` Peter Ujfalusi
2016-02-19  9:47 ` [PATCH 06/33] drm/omap: drm_atomic_get_plane_state() may return ERR_PTR Tomi Valkeinen
2016-02-23 22:01   ` Laurent Pinchart
2016-02-19  9:47 ` [PATCH 07/33] drm/omap: tpd12s015: remove platform data support Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 08/33] drm/omap: tpd12s015: gpio descriptor API Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 09/33] drm/omap: tpd12s015: CT_CP_HPD as optional gpio Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 10/33] drm/omap: add define for DISPC_IRQ_WBUNCOMPLETEERROR Tomi Valkeinen
2016-02-23 22:04   ` Laurent Pinchart
2016-02-19  9:47 ` [PATCH 11/33] drm/omap: use dma_mapping_error in omap_gem_attach_pages Tomi Valkeinen
2016-02-23 22:10   ` Laurent Pinchart
2016-02-25 15:39     ` Tomi Valkeinen
2016-02-26  8:52       ` Laurent Pinchart
2016-02-26  9:07         ` Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 12/33] drm/omap: use dma_mapping_error in omap_gem_dma_sync Tomi Valkeinen
2016-02-23 22:14   ` Laurent Pinchart
2016-02-25 15:45     ` Tomi Valkeinen
2016-02-26  8:54       ` Laurent Pinchart
2016-02-19  9:47 ` [PATCH 13/33] drm/omap: print an error if display enable fails Tomi Valkeinen
2016-02-23 22:17   ` Laurent Pinchart
2016-02-19  9:47 ` [PATCH 14/33] drm/omap: gem: Clean up GEM objects memory flags Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 15/33] drm/omap: gem: Refactor GEM object allocation Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 16/33] drm/omap: gem: Implement dma_buf import Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 17/33] drm/omap: remove support for ext mem & sync Tomi Valkeinen
2016-02-23 22:42   ` Laurent Pinchart
2016-02-24  9:38     ` Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 18/33] drm/omap: increase vblank wait timeout Tomi Valkeinen
2016-02-23 22:43   ` Laurent Pinchart
2016-02-24  9:41     ` Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 19/33] drm/omap: DISPC: support double-pixel mode Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 20/33] drm/omap: support double-pixel Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 21/33] drm/omap: HDMI: support double-pixel pixel clock Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 22/33] drm/omap: HDMI: Fix HSW value Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 23/33] drm/omap: HDMI: fix WP timings for ilace Tomi Valkeinen
2016-02-19  9:47 ` [PATCH 24/33] drm/omap: DISPC: Fix field order for HDMI Tomi Valkeinen
2016-02-19  9:48 ` [PATCH 25/33] drm/omap: HDMI5: Fix FC HSW value Tomi Valkeinen
2016-02-19  9:48 ` [PATCH 26/33] drm/omap: HDMI5: clean up timings copy Tomi Valkeinen
2016-02-19  9:48 ` [PATCH 27/33] drm/omap: HDMI5: Add interlace support Tomi Valkeinen
2016-02-19  9:48 ` [PATCH 28/33] drm/omap: HDMI5: allow interlace Tomi Valkeinen
2016-02-19  9:48 ` [PATCH 29/33] drm/omap: verify that display x-res is divisible by 8 Tomi Valkeinen
2016-02-19  9:48 ` [PATCH 30/33] drm/omap: verify that fb plane pitches are the same Tomi Valkeinen
2016-02-23 23:02   ` Laurent Pinchart
2016-02-25 15:56     ` Tomi Valkeinen
2016-02-26  8:55       ` Laurent Pinchart
2016-02-26  9:12         ` Tomi Valkeinen
2016-02-26  9:27           ` Laurent Pinchart
2016-02-19  9:48 ` [PATCH 31/33] drm/omap: EBUSY status handling in omap_gem_fault() Tomi Valkeinen
2016-02-19  9:48 ` [PATCH 32/33] drm/omap: fix crtc->plane property delegation Tomi Valkeinen
2016-02-23 23:24   ` Laurent Pinchart
2016-02-19  9:48 ` [PATCH 33/33] drm/omap: check if rotation is supported before commit Tomi Valkeinen
2016-02-23 23:30   ` Laurent Pinchart

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5240664.Dy4C1HiFai@avalon \
    --to=laurent.pinchart@ideasonboard.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=tomi.valkeinen@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox