Hi Marek,

On Sun, 2022-02-06 at 19:56 +0100, Marek Vasut wrote:
> The LCDIF controller as present in i.MX6SX/i.MX8M Mini/Nano has a CRC_STAT
> register, which contains CRC32 of the frame as it was clocked out of the
> DPI interface of the LCDIF. This is likely meant as a functional safety
> register.
> 
> Unfortunatelly, there is zero documentation on how the CRC32 is calculated,
> there is no documentation of the polynomial, the init value, nor on which
> data is the checksum applied.
> 
> By applying brute-force on 8 pixel / 2 line frame, which is the minimum
> size LCDIF would work with, it turns out the polynomial is CRC32_POLY_LE
> 0xedb88320 , init value is 0xffffffff , the input data are bitrev32()
> of the entire frame and the resulting CRC has to be also bitrev32()ed.

No idea how the HW calculates the CRC value.
I didn't hear anyone internal tried this feature.

> 
> Doing this calculation in software for each frame is unrealistic due to
> the CPU demand, implement at least a sysfs attribute which permits testing
> the current frame on demand.

Why not using the existing debugfs CRC support implemented
in drivers/gpu/drm/drm_debugfs_crc.c?

> 
> Unfortunatelly, this functionality has another problem. On all of those SoCs,
> it is possible to overload interconnect e.g. by concurrent USB and uSDHC
> transfers, at which point the LCDIF LFIFO suffers an UNDERFLOW condition,
> which results in the image being shifted to the right by exactly LFIFO size
> pixels. On i.MX8M Mini, the LFIFO is 76x256 bits = 2432 Byte ~= 810 pixel
> at 24bpp. In this case, the LCDIF does not assert UNDERFLOW_IRQ bit, the
> frame CRC32 indicated in CRC_STAT register matches the CRC32 of the frame
> in DRAM, the RECOVER_ON_UNDERFLOW bit has no effect, so if this mode of
> failure occurs, the failure gets undetected and uncorrected.

Hmmm, interesting, no UNDERFLOW_IRQ bit asserted when LCDIF suffers an
UNDERFLOW condition?  Are you sure LCDIF really underflows?
If the shifted image is seen on a MIPI DSI display, could that be a
MIPI DSI or DPHY issue, like wrong horizontal parameter(s)?


> 
> Signed-off-by: Marek Vasut <ma...@denx.de>
> Cc: Alexander Stein <alexander.st...@ew.tq-group.com>
> Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
> Cc: Lucas Stach <l.st...@pengutronix.de>
> Cc: Peng Fan <peng....@nxp.com>
> Cc: Robby Cai <robby....@nxp.com>
> Cc: Sam Ravnborg <s...@ravnborg.org>
> Cc: Stefan Agner <ste...@agner.ch>
> ---
>  drivers/gpu/drm/mxsfb/mxsfb_drv.c  | 38 ++++++++++++++++++++++++++++++
>  drivers/gpu/drm/mxsfb/mxsfb_drv.h  |  3 +++
>  drivers/gpu/drm/mxsfb/mxsfb_kms.c  | 11 +++++----
>  drivers/gpu/drm/mxsfb/mxsfb_regs.h |  1 +
>  4 files changed, 49 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c 
> b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
> index 4ff3c6195dd0c..6f296b398f28c 100644
> --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c
> +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
> @@ -9,6 +9,7 @@
>   */
>  
>  #include <linux/clk.h>
> +#include <linux/crc32.h>
>  #include <linux/dma-mapping.h>
>  #include <linux/io.h>
>  #include <linux/module.h>
> @@ -292,6 +293,37 @@ static void mxsfb_unload(struct drm_device *drm)
>       pm_runtime_disable(drm->dev);
>  }
>  
> +static ssize_t mxsfb_frame_checksum_show(struct device *dev,
> +                                          struct device_attribute *attr,
> +                                          char *buf)
> +{
> +     struct drm_device *drm = dev_get_drvdata(dev);
> +     struct mxsfb_drm_private *mxsfb = drm->dev_private;
> +     u32 hwcrc = readl(mxsfb->base, LCDC_V4_CRC_STAT);

Access register without relevant clock(s) enabled?

LCDC_V4_CRC_STAT seems to hint that there should be some verion control
logic for MXSFB_V3/4/6.

Regards,
Liu Ying

> +     u32 swcrc = 0xffffffff;
> +     int i;
> +
> +     if (mxsfb->gem_vaddr) {
> +             for (i = 0; i < mxsfb->gem_size / 4; i++) {
> +                     u32 data = bitrev32(((u32 *)mxsfb->gem_vaddr)[i]);
> +                     swcrc = crc32(swcrc, &data, 4);
> +             }
> +             swcrc = bitrev32(swcrc);
> +     }
> +
> +     return sysfs_emit(buf, "HW:%08x,SW:%08x,OK:%d\n", hwcrc, swcrc, hwcrc 
> == swcrc);
> +}
> +static DEVICE_ATTR(frame_checksum, 0444, mxsfb_frame_checksum_show, NULL);
> +
> +static struct attribute *mxsfb_attributes[] = {
> +     &dev_attr_frame_checksum.attr,
> +     NULL,
> +};
> +
> +static const struct attribute_group mxsfb_attr_group = {
> +     .attrs = mxsfb_attributes,
> +};
> +
>  DEFINE_DRM_GEM_CMA_FOPS(fops);
>  
>  static const struct drm_driver mxsfb_driver = {
> @@ -335,10 +367,16 @@ static int mxsfb_probe(struct platform_device *pdev)
>       if (ret)
>               goto err_unload;
>  
> +     ret = devm_device_add_group(drm->dev, &mxsfb_attr_group);
> +     if (ret)
> +             goto err_attr;
> +
>       drm_fbdev_generic_setup(drm, 32);
>  
>       return 0;
>  
> +err_attr:
> +     drm_dev_unregister(drm);
>  err_unload:
>       mxsfb_unload(drm);
>  err_free:
> diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h 
> b/drivers/gpu/drm/mxsfb/mxsfb_drv.h
> index ddb5b0417a82c..0a3e5dd1e8bab 100644
> --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h
> +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h
> @@ -44,6 +44,9 @@ struct mxsfb_drm_private {
>       struct drm_encoder              encoder;
>       struct drm_connector            *connector;
>       struct drm_bridge               *bridge;
> +
> +     void                            *gem_vaddr;
> +     size_t                          gem_size;
>  };
>  
>  static inline struct mxsfb_drm_private *
> diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c 
> b/drivers/gpu/drm/mxsfb/mxsfb_kms.c
> index 03743a84c8e79..2a4edf5a2ac57 100644
> --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c
> +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c
> @@ -196,7 +196,7 @@ static int mxsfb_reset_block(struct mxsfb_drm_private 
> *mxsfb)
>       return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE);
>  }
>  
> -static dma_addr_t mxsfb_get_fb_paddr(struct drm_plane *plane)
> +static dma_addr_t mxsfb_get_fb_paddr(struct mxsfb_drm_private *mxsfb, struct 
> drm_plane *plane)
>  {
>       struct drm_framebuffer *fb = plane->state->fb;
>       struct drm_gem_cma_object *gem;
> @@ -208,6 +208,9 @@ static dma_addr_t mxsfb_get_fb_paddr(struct drm_plane 
> *plane)
>       if (!gem)
>               return 0;
>  
> +     mxsfb->gem_vaddr = gem->vaddr;
> +     mxsfb->gem_size = gem->base.size;
> +
>       return gem->paddr;
>  }
>  
> @@ -370,7 +373,7 @@ static void mxsfb_crtc_atomic_enable(struct drm_crtc 
> *crtc,
>       mxsfb_crtc_mode_set_nofb(mxsfb, bus_format);
>  
>       /* Write cur_buf as well to avoid an initial corrupt frame */
> -     paddr = mxsfb_get_fb_paddr(crtc->primary);
> +     paddr = mxsfb_get_fb_paddr(mxsfb, crtc->primary);
>       if (paddr) {
>               writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf);
>               writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
> @@ -476,7 +479,7 @@ static void mxsfb_plane_primary_atomic_update(struct 
> drm_plane *plane,
>       struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
>       dma_addr_t paddr;
>  
> -     paddr = mxsfb_get_fb_paddr(plane);
> +     paddr = mxsfb_get_fb_paddr(mxsfb, plane);
>       if (paddr)
>               writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
>  }
> @@ -492,7 +495,7 @@ static void mxsfb_plane_overlay_atomic_update(struct 
> drm_plane *plane,
>       dma_addr_t paddr;
>       u32 ctrl;
>  
> -     paddr = mxsfb_get_fb_paddr(plane);
> +     paddr = mxsfb_get_fb_paddr(mxsfb, plane);
>       if (!paddr) {
>               writel(0, mxsfb->base + LCDC_AS_CTRL);
>               return;
> diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h 
> b/drivers/gpu/drm/mxsfb/mxsfb_regs.h
> index 694fea13e893e..cf813a1da1d78 100644
> --- a/drivers/gpu/drm/mxsfb/mxsfb_regs.h
> +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h
> @@ -26,6 +26,7 @@
>  #define LCDC_VDCTRL2                 0x90
>  #define LCDC_VDCTRL3                 0xa0
>  #define LCDC_VDCTRL4                 0xb0
> +#define LCDC_V4_CRC_STAT             0x1a0
>  #define LCDC_V4_DEBUG0                       0x1d0
>  #define LCDC_V3_DEBUG0                       0x1f0
>  #define LCDC_AS_CTRL                 0x210

Reply via email to