[PATCH] [RFC] drm: mxsfb: Implement LCDIF scanout CRC32 support
Liu Ying
victor.liu at oss.nxp.com
Mon Feb 7 05:13:27 UTC 2022
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 <marex at denx.de>
> Cc: Alexander Stein <alexander.stein at ew.tq-group.com>
> Cc: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> Cc: Lucas Stach <l.stach at pengutronix.de>
> Cc: Peng Fan <peng.fan at nxp.com>
> Cc: Robby Cai <robby.cai at nxp.com>
> Cc: Sam Ravnborg <sam at ravnborg.org>
> Cc: Stefan Agner <stefan at 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
More information about the dri-devel
mailing list