On 2022-04-11 22:35, Marek Vasut wrote:
> The LCDIF controller as present in i.MX28/i.MX6SX/i.MX8M Mini/Nano has
> CRC_STAT register, which contains CRC32 of the frame as it was clocked
> out of the DPI interface of the LCDIF. This is most likely meant as a
> functional safety feature.
> 
> Unfortunately, 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.
> 
> Doing this calculation in kernel for each frame is unrealistic due to the
> CPU demand, so attach the CRC collected from hardware to a frame instead.
> The DRM subsystem already has an interface for this purpose and the CRC
> can be accessed e.g. via debugfs:
> "
> $ echo auto > /sys/kernel/debug/dri/1/crtc-0/crc/control
> $ cat /sys/kernel/debug/dri/1/crtc-0/crc/data
> 0x0000408c 0xa4e5cdd8
> 0x0000408d 0x72f537b4
> "
> The per-frame CRC can be used by userspace e.g. during automated testing,
> to verify that whatever buffer was sent to be scanned out was actually
> scanned out of the LCDIF correctly.
> 
> 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>
> ---
> V2: Check crtc for non-NULL before dereferencing it in 
> mxsfb_crtc_set_crc_source
> ---
>  drivers/gpu/drm/mxsfb/mxsfb_drv.c  | 15 +++++++-
>  drivers/gpu/drm/mxsfb/mxsfb_drv.h  |  3 ++
>  drivers/gpu/drm/mxsfb/mxsfb_kms.c  | 61 ++++++++++++++++++++++++++++--
>  drivers/gpu/drm/mxsfb/mxsfb_regs.h |  1 +
>  4 files changed, 75 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c
> b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
> index 94cafff7f68d5..ccf4107476ecc 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>

Doesn't look like this is used?

With that addressed.
Acked-by: Stefan Agner <ste...@agner.ch>

--
Stefan

>  #include <linux/dma-mapping.h>
>  #include <linux/io.h>
>  #include <linux/module.h>
> @@ -52,6 +53,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = {
>               .hs_wdth_shift  = 24,
>               .has_overlay    = false,
>               .has_ctrl2      = false,
> +             .has_crc32      = false,
>       },
>       [MXSFB_V4] = {
>               .transfer_count = LCDC_V4_TRANSFER_COUNT,
> @@ -61,6 +63,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = {
>               .hs_wdth_shift  = 18,
>               .has_overlay    = false,
>               .has_ctrl2      = true,
> +             .has_crc32      = true,
>       },
>       [MXSFB_V6] = {
>               .transfer_count = LCDC_V4_TRANSFER_COUNT,
> @@ -70,6 +73,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = {
>               .hs_wdth_shift  = 18,
>               .has_overlay    = true,
>               .has_ctrl2      = true,
> +             .has_crc32      = true,
>       },
>  };
>  
> @@ -145,12 +149,19 @@ static irqreturn_t mxsfb_irq_handler(int irq, void 
> *data)
>  {
>       struct drm_device *drm = data;
>       struct mxsfb_drm_private *mxsfb = drm->dev_private;
> -     u32 reg;
> +     u32 reg, crc;
> +     u64 vbc;
>  
>       reg = readl(mxsfb->base + LCDC_CTRL1);
>  
> -     if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
> +     if (reg & CTRL1_CUR_FRAME_DONE_IRQ) {
>               drm_crtc_handle_vblank(&mxsfb->crtc);
> +             if (mxsfb->crc_active) {
> +                     crc = readl(mxsfb->base + LCDC_V4_CRC_STAT);
> +                     vbc = drm_crtc_accurate_vblank_count(&mxsfb->crtc);
> +                     drm_crtc_add_crc_entry(&mxsfb->crtc, true, vbc, &crc);
> +             }
> +     }
>  
>       writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
>  
> diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h
> b/drivers/gpu/drm/mxsfb/mxsfb_drv.h
> index ddb5b0417a82c..d160d921b25fc 100644
> --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h
> +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h
> @@ -23,6 +23,7 @@ struct mxsfb_devdata {
>       unsigned int    hs_wdth_shift;
>       bool            has_overlay;
>       bool            has_ctrl2;
> +     bool            has_crc32;
>  };
>  
>  struct mxsfb_drm_private {
> @@ -44,6 +45,8 @@ struct mxsfb_drm_private {
>       struct drm_encoder              encoder;
>       struct drm_connector            *connector;
>       struct drm_bridge               *bridge;
> +
> +     bool                            crc_active;
>  };
>  
>  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 c7f14ef1edc25..323087944ac56 100644
> --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c
> +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c
> @@ -454,6 +454,41 @@ static void mxsfb_crtc_disable_vblank(struct
> drm_crtc *crtc)
>       writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
>  }
>  
> +static int mxsfb_crtc_set_crc_source(struct drm_crtc *crtc, const char 
> *source)
> +{
> +     struct mxsfb_drm_private *mxsfb;
> +
> +     if (!crtc)
> +             return -ENODEV;
> +
> +     mxsfb = to_mxsfb_drm_private(crtc->dev);
> +
> +     if (source && strcmp(source, "auto") == 0)
> +             mxsfb->crc_active = true;
> +     else if (!source)
> +             mxsfb->crc_active = false;
> +     else
> +             return -EINVAL;
> +
> +     return 0;
> +}
> +
> +static int mxsfb_crtc_verify_crc_source(struct drm_crtc *crtc,
> +                                     const char *source, size_t *values_cnt)
> +{
> +     if (!crtc)
> +             return -ENODEV;
> +
> +     if (source && strcmp(source, "auto") != 0) {
> +             DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",
> +                              source, crtc->name);
> +             return -EINVAL;
> +     }
> +
> +     *values_cnt = 1;
> +     return 0;
> +}
> +
>  static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {
>       .atomic_check = mxsfb_crtc_atomic_check,
>       .atomic_flush = mxsfb_crtc_atomic_flush,
> @@ -472,6 +507,19 @@ static const struct drm_crtc_funcs mxsfb_crtc_funcs = {
>       .disable_vblank = mxsfb_crtc_disable_vblank,
>  };
>  
> +static const struct drm_crtc_funcs mxsfb_crtc_with_crc_funcs = {
> +     .reset = drm_atomic_helper_crtc_reset,
> +     .destroy = drm_crtc_cleanup,
> +     .set_config = drm_atomic_helper_set_config,
> +     .page_flip = drm_atomic_helper_page_flip,
> +     .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> +     .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> +     .enable_vblank = mxsfb_crtc_enable_vblank,
> +     .disable_vblank = mxsfb_crtc_disable_vblank,
> +     .set_crc_source = mxsfb_crtc_set_crc_source,
> +     .verify_crc_source = mxsfb_crtc_verify_crc_source,
> +};
> +
>  /*
> -----------------------------------------------------------------------------
>   * Encoder
>   */
> @@ -655,9 +703,16 @@ int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb)
>       }
>  
>       drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
> -     ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
> -                                     &mxsfb->planes.primary, NULL,
> -                                     &mxsfb_crtc_funcs, NULL);
> +     if (mxsfb->devdata->has_crc32) {
> +             ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
> +                                             &mxsfb->planes.primary, NULL,
> +                                             &mxsfb_crtc_with_crc_funcs,
> +                                             NULL);
> +     } else {
> +             ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
> +                                             &mxsfb->planes.primary, NULL,
> +                                             &mxsfb_crtc_funcs, NULL);
> +     }
>       if (ret)
>               return ret;
>  
> 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