Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: mxsfb: Implement LCDIF scanout CRC32 support

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.

Acked-by: Lucas Stach <l.stach@pengutronix.de>
Acked-by: Stefan Agner <stefan@agner.ch>
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Alexander Stein <alexander.stein@ew.tq-group.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Lucas Stach <l.stach@pengutronix.de>
Cc: Peng Fan <peng.fan@nxp.com>
Cc: Robby Cai <robby.cai@nxp.com>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Stefan Agner <stefan@agner.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20220429212313.305556-1-marex@denx.de

+74 -5
+12 -2
drivers/gpu/drm/mxsfb/mxsfb_drv.c
··· 52 52 .hs_wdth_shift = 24, 53 53 .has_overlay = false, 54 54 .has_ctrl2 = false, 55 + .has_crc32 = false, 55 56 }, 56 57 [MXSFB_V4] = { 57 58 .transfer_count = LCDC_V4_TRANSFER_COUNT, ··· 62 61 .hs_wdth_shift = 18, 63 62 .has_overlay = false, 64 63 .has_ctrl2 = true, 64 + .has_crc32 = true, 65 65 }, 66 66 [MXSFB_V6] = { 67 67 .transfer_count = LCDC_V4_TRANSFER_COUNT, ··· 72 70 .hs_wdth_shift = 18, 73 71 .has_overlay = true, 74 72 .has_ctrl2 = true, 73 + .has_crc32 = true, 75 74 }, 76 75 }; 77 76 ··· 160 157 { 161 158 struct drm_device *drm = data; 162 159 struct mxsfb_drm_private *mxsfb = drm->dev_private; 163 - u32 reg; 160 + u32 reg, crc; 161 + u64 vbc; 164 162 165 163 reg = readl(mxsfb->base + LCDC_CTRL1); 166 164 167 - if (reg & CTRL1_CUR_FRAME_DONE_IRQ) 165 + if (reg & CTRL1_CUR_FRAME_DONE_IRQ) { 168 166 drm_crtc_handle_vblank(&mxsfb->crtc); 167 + if (mxsfb->crc_active) { 168 + crc = readl(mxsfb->base + LCDC_V4_CRC_STAT); 169 + vbc = drm_crtc_accurate_vblank_count(&mxsfb->crtc); 170 + drm_crtc_add_crc_entry(&mxsfb->crtc, true, vbc, &crc); 171 + } 172 + } 169 173 170 174 writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); 171 175
+3
drivers/gpu/drm/mxsfb/mxsfb_drv.h
··· 23 23 unsigned int hs_wdth_shift; 24 24 bool has_overlay; 25 25 bool has_ctrl2; 26 + bool has_crc32; 26 27 }; 27 28 28 29 struct mxsfb_drm_private { ··· 45 44 struct drm_encoder encoder; 46 45 struct drm_connector *connector; 47 46 struct drm_bridge *bridge; 47 + 48 + bool crc_active; 48 49 }; 49 50 50 51 static inline struct mxsfb_drm_private *
+58 -3
drivers/gpu/drm/mxsfb/mxsfb_kms.c
··· 439 439 writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); 440 440 } 441 441 442 + static int mxsfb_crtc_set_crc_source(struct drm_crtc *crtc, const char *source) 443 + { 444 + struct mxsfb_drm_private *mxsfb; 445 + 446 + if (!crtc) 447 + return -ENODEV; 448 + 449 + mxsfb = to_mxsfb_drm_private(crtc->dev); 450 + 451 + if (source && strcmp(source, "auto") == 0) 452 + mxsfb->crc_active = true; 453 + else if (!source) 454 + mxsfb->crc_active = false; 455 + else 456 + return -EINVAL; 457 + 458 + return 0; 459 + } 460 + 461 + static int mxsfb_crtc_verify_crc_source(struct drm_crtc *crtc, 462 + const char *source, size_t *values_cnt) 463 + { 464 + if (!crtc) 465 + return -ENODEV; 466 + 467 + if (source && strcmp(source, "auto") != 0) { 468 + DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n", 469 + source, crtc->name); 470 + return -EINVAL; 471 + } 472 + 473 + *values_cnt = 1; 474 + return 0; 475 + } 476 + 442 477 static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = { 443 478 .atomic_check = mxsfb_crtc_atomic_check, 444 479 .atomic_flush = mxsfb_crtc_atomic_flush, ··· 490 455 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 491 456 .enable_vblank = mxsfb_crtc_enable_vblank, 492 457 .disable_vblank = mxsfb_crtc_disable_vblank, 458 + }; 459 + 460 + static const struct drm_crtc_funcs mxsfb_crtc_with_crc_funcs = { 461 + .reset = drm_atomic_helper_crtc_reset, 462 + .destroy = drm_crtc_cleanup, 463 + .set_config = drm_atomic_helper_set_config, 464 + .page_flip = drm_atomic_helper_page_flip, 465 + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 466 + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 467 + .enable_vblank = mxsfb_crtc_enable_vblank, 468 + .disable_vblank = mxsfb_crtc_disable_vblank, 469 + .set_crc_source = mxsfb_crtc_set_crc_source, 470 + .verify_crc_source = mxsfb_crtc_verify_crc_source, 493 471 }; 494 472 495 473 /* ----------------------------------------------------------------------------- ··· 693 645 } 694 646 695 647 drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs); 696 - ret = drm_crtc_init_with_planes(mxsfb->drm, crtc, 697 - &mxsfb->planes.primary, NULL, 698 - &mxsfb_crtc_funcs, NULL); 648 + if (mxsfb->devdata->has_crc32) { 649 + ret = drm_crtc_init_with_planes(mxsfb->drm, crtc, 650 + &mxsfb->planes.primary, NULL, 651 + &mxsfb_crtc_with_crc_funcs, 652 + NULL); 653 + } else { 654 + ret = drm_crtc_init_with_planes(mxsfb->drm, crtc, 655 + &mxsfb->planes.primary, NULL, 656 + &mxsfb_crtc_funcs, NULL); 657 + } 699 658 if (ret) 700 659 return ret; 701 660
+1
drivers/gpu/drm/mxsfb/mxsfb_regs.h
··· 26 26 #define LCDC_VDCTRL2 0x90 27 27 #define LCDC_VDCTRL3 0xa0 28 28 #define LCDC_VDCTRL4 0xb0 29 + #define LCDC_V4_CRC_STAT 0x1a0 29 30 #define LCDC_V4_DEBUG0 0x1d0 30 31 #define LCDC_V3_DEBUG0 0x1f0 31 32 #define LCDC_AS_CTRL 0x210