diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index e8313739b54d..0fa713c550b1 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -573,6 +573,17 @@ static void mtk_crtc_ddp_irq(void *data)
 	struct drm_crtc *crtc = data;
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
 	struct mtk_drm_private *priv = crtc->dev->dev_private;
+	static int skip;
+
+	if (mtk_crtc->crc.cnt && crtc->crc.opened) {
+		if (++skip > 1) {
+			drm_crtc_add_crc_entry(crtc, true,
+					       drm_crtc_vblank_count(crtc),
+					       (u32 *)mtk_crtc->crc.va);
+		}
+	} else {
+		skip = 0;
+	}
 
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
 	if (!priv->data->shadow_register && !mtk_crtc->cmdq_client.chan)
@@ -605,6 +616,34 @@ static void mtk_drm_crtc_disable_vblank(struct drm_crtc *crtc)
 	mtk_ddp_comp_disable_vblank(comp);
 }
 
+static int mtk_drm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src)
+{
+	if (src && strcmp(src, "auto") != 0) {
+		DRM_DEBUG_DRIVER("%s(crtc-%d): unknown source '%s'\n",
+				 __func__, drm_crtc_index(crtc), src);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mtk_drm_crtc_verify_crc_source(struct drm_crtc *crtc,
+					  const char *src,
+					  size_t *cnt)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+
+	if (src && strcmp(src, "auto") != 0) {
+		DRM_DEBUG_DRIVER("%s(crtc-%d): unknown source '%s'\n",
+				 __func__, drm_crtc_index(crtc), src);
+		return -EINVAL;
+	}
+
+	*cnt = (size_t)mtk_crtc->crc.cnt;
+
+	return 0;
+}
+
 int mtk_drm_crtc_plane_check(struct drm_crtc *crtc, struct drm_plane *plane,
 			     struct mtk_plane_state *state)
 {
@@ -737,6 +776,8 @@ static const struct drm_crtc_funcs mtk_crtc_funcs = {
 	.atomic_destroy_state	= mtk_drm_crtc_destroy_state,
 	.enable_vblank		= mtk_drm_crtc_enable_vblank,
 	.disable_vblank		= mtk_drm_crtc_disable_vblank,
+	.set_crc_source		= mtk_drm_crtc_set_crc_source,
+	.verify_crc_source	= mtk_drm_crtc_verify_crc_source,
 };
 
 static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
@@ -919,6 +960,18 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
 
 			if (comp->funcs->ctm_set)
 				has_ctm = true;
+
+			if (comp->funcs->crc_cnt) {
+				mtk_crtc->crc.cnt = comp->funcs->crc_cnt(comp->dev);
+				mtk_crtc->crc.va = dma_alloc_coherent(dev,
+								      mtk_crtc->crc.cnt * 4,
+								      &mtk_crtc->crc.pa,
+								      GFP_KERNEL);
+				if (!mtk_crtc->crc.va || !mtk_crtc->crc.pa) {
+					dev_err(dev, "failed to allocate CRC\n");
+					return -ENOMEM;
+				}
+			}
 		}
 
 		mtk_ddp_comp_register_vblank_cb(comp, mtk_crtc_ddp_irq,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
index 34cd1bfed8b3..8303464f494c 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
@@ -14,6 +14,23 @@
 #define MTK_MAX_BPC	10
 #define MTK_MIN_BPC	3
 
+/*
+ * struct mtk_drm_crc - CRC info of the CRTC
+ * @cnt: how many CRCs the CRTC supports
+ * @va: virtual address for CPU to read the CRCs
+ * @pa: physical address for GCE to stored the CRCs
+ *
+ * Hardware components could generate more than one CRC,
+ * for example, one for odd lines, another for even lines of the frame buffer,
+ * and each CRC takes 4 bytes in memory, here we record how many CRC the
+ * generator supports, and access them as an array from the specified address.
+ */
+struct mtk_drm_crc {
+	u32 cnt;
+	void *va;
+	dma_addr_t pa;
+};
+
 /*
  * struct mtk_drm_crtc - MediaTek specific crtc structure.
  * @base: crtc object.
@@ -24,6 +41,7 @@
  * @mutex: handle to one of the ten disp_mutex streams
  * @ddp_comp_nr: number of components in ddp_comp
  * @ddp_comp: array of pointers the mtk_ddp_comp structures used by this crtc
+ * @crc: CRC info of the CRTC
  *
  * TODO: Needs update: this header is missing a bunch of member descriptions.
  */
@@ -56,6 +74,8 @@ struct mtk_drm_crtc {
 	/* lock for display hardware access */
 	struct mutex			hw_lock;
 	bool				config_updating;
+
+	struct mtk_drm_crc		crc;
 };
 
 void mtk_drm_crtc_commit(struct drm_crtc *crtc);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index febcaeef16a1..3b67c3dc0525 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -45,6 +45,10 @@ enum mtk_ddp_comp_type {
 
 struct mtk_ddp_comp;
 struct cmdq_pkt;
+
+/* struct mtk_ddp_comp_funcs - function pointers of the ddp components
+ * @crc_cnt: how many CRCs the component supports
+ */
 struct mtk_ddp_comp_funcs {
 	int (*clk_enable)(struct device *dev);
 	void (*clk_disable)(struct device *dev);
@@ -80,6 +84,7 @@ struct mtk_ddp_comp_funcs {
 	void (*disconnect)(struct device *dev, struct device *mmsys_dev, unsigned int next);
 	void (*add)(struct device *dev, struct mtk_mutex *mutex);
 	void (*remove)(struct device *dev, struct mtk_mutex *mutex);
+	u32 (*crc_cnt)(struct device *dev);
 };
 
 struct mtk_ddp_comp {
