drm/mediatek: Add vblank register/unregister callback functions
authorRex-BC Chen <rex-bc.chen@mediatek.com>
Mon, 21 Mar 2022 07:23:20 +0000 (15:23 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Jul 2022 14:35:07 +0000 (16:35 +0200)
[ Upstream commit b74d921b900b6ce38c6247c0a1c86be9f3746493 ]

We encountered a kernel panic issue that callback data will be NULL when
it's using in ovl irq handler. There is a timing issue between
mtk_disp_ovl_irq_handler() and mtk_ovl_disable_vblank().

To resolve this issue, we use the flow to register/unregister vblank cb:
- Register callback function and callback data when crtc creates.
- Unregister callback function and callback data when crtc destroies.

With this solution, we can assure callback data will not be NULL when
vblank is disable.

Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20220321072320.15019-1-rex-bc.chen@mediatek.com/
Fixes: 9b0704988b15 ("drm/mediatek: Register vblank callback function")
Signed-off-by: Rex-BC Chen <rex-bc.chen@mediatek.com>
Reviewed-by: jason-jh.lin <jason-jh.lin@mediatek.com>
Signed-off-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/gpu/drm/mediatek/mtk_disp_drv.h
drivers/gpu/drm/mediatek/mtk_disp_ovl.c
drivers/gpu/drm/mediatek/mtk_disp_rdma.c
drivers/gpu/drm/mediatek/mtk_drm_crtc.c
drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h

index 86c3068894b11645395af60b77301366fc54f5e3..974462831133b42aaf047080473ee31307092b05 100644 (file)
@@ -76,9 +76,11 @@ void mtk_ovl_layer_off(struct device *dev, unsigned int idx,
 void mtk_ovl_start(struct device *dev);
 void mtk_ovl_stop(struct device *dev);
 unsigned int mtk_ovl_supported_rotations(struct device *dev);
-void mtk_ovl_enable_vblank(struct device *dev,
-                          void (*vblank_cb)(void *),
-                          void *vblank_cb_data);
+void mtk_ovl_register_vblank_cb(struct device *dev,
+                               void (*vblank_cb)(void *),
+                               void *vblank_cb_data);
+void mtk_ovl_unregister_vblank_cb(struct device *dev);
+void mtk_ovl_enable_vblank(struct device *dev);
 void mtk_ovl_disable_vblank(struct device *dev);
 
 void mtk_rdma_bypass_shadow(struct device *dev);
@@ -93,9 +95,11 @@ void mtk_rdma_layer_config(struct device *dev, unsigned int idx,
                           struct cmdq_pkt *cmdq_pkt);
 void mtk_rdma_start(struct device *dev);
 void mtk_rdma_stop(struct device *dev);
-void mtk_rdma_enable_vblank(struct device *dev,
-                           void (*vblank_cb)(void *),
-                           void *vblank_cb_data);
+void mtk_rdma_register_vblank_cb(struct device *dev,
+                                void (*vblank_cb)(void *),
+                                void *vblank_cb_data);
+void mtk_rdma_unregister_vblank_cb(struct device *dev);
+void mtk_rdma_enable_vblank(struct device *dev);
 void mtk_rdma_disable_vblank(struct device *dev);
 
 #endif
index 5326989d520615775c0612378ccfaa86a1ef1ac2..411cf0f21661183db51df7050c4522bf61855b7a 100644 (file)
@@ -96,14 +96,28 @@ static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-void mtk_ovl_enable_vblank(struct device *dev,
-                          void (*vblank_cb)(void *),
-                          void *vblank_cb_data)
+void mtk_ovl_register_vblank_cb(struct device *dev,
+                               void (*vblank_cb)(void *),
+                               void *vblank_cb_data)
 {
        struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
 
        ovl->vblank_cb = vblank_cb;
        ovl->vblank_cb_data = vblank_cb_data;
+}
+
+void mtk_ovl_unregister_vblank_cb(struct device *dev)
+{
+       struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
+
+       ovl->vblank_cb = NULL;
+       ovl->vblank_cb_data = NULL;
+}
+
+void mtk_ovl_enable_vblank(struct device *dev)
+{
+       struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
+
        writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA);
        writel_relaxed(OVL_FME_CPL_INT, ovl->regs + DISP_REG_OVL_INTEN);
 }
@@ -112,8 +126,6 @@ void mtk_ovl_disable_vblank(struct device *dev)
 {
        struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
 
-       ovl->vblank_cb = NULL;
-       ovl->vblank_cb_data = NULL;
        writel_relaxed(0x0, ovl->regs + DISP_REG_OVL_INTEN);
 }
 
index 75d7f45579e2663745d18f1bb5b6e79f952c9e95..a6a6cb5f75af76e715e2aec07d01d0bb46ec0958 100644 (file)
@@ -94,24 +94,32 @@ static void rdma_update_bits(struct device *dev, unsigned int reg,
        writel(tmp, rdma->regs + reg);
 }
 
-void mtk_rdma_enable_vblank(struct device *dev,
-                           void (*vblank_cb)(void *),
-                           void *vblank_cb_data)
+void mtk_rdma_register_vblank_cb(struct device *dev,
+                                void (*vblank_cb)(void *),
+                                void *vblank_cb_data)
 {
        struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
 
        rdma->vblank_cb = vblank_cb;
        rdma->vblank_cb_data = vblank_cb_data;
-       rdma_update_bits(dev, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT,
-                        RDMA_FRAME_END_INT);
 }
 
-void mtk_rdma_disable_vblank(struct device *dev)
+void mtk_rdma_unregister_vblank_cb(struct device *dev)
 {
        struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
 
        rdma->vblank_cb = NULL;
        rdma->vblank_cb_data = NULL;
+}
+
+void mtk_rdma_enable_vblank(struct device *dev)
+{
+       rdma_update_bits(dev, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT,
+                        RDMA_FRAME_END_INT);
+}
+
+void mtk_rdma_disable_vblank(struct device *dev)
+{
        rdma_update_bits(dev, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0);
 }
 
index ffa54b416ca76aa60b07cc922dd13fe5b9751ecf..34bb6c713a908c20426b9e37f1d6228d325b3663 100644 (file)
@@ -152,11 +152,20 @@ static void mtk_drm_cmdq_pkt_destroy(struct cmdq_pkt *pkt)
 static void mtk_drm_crtc_destroy(struct drm_crtc *crtc)
 {
        struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+       int i;
 
        mtk_mutex_put(mtk_crtc->mutex);
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
        mtk_drm_cmdq_pkt_destroy(&mtk_crtc->cmdq_handle);
 #endif
+
+       for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
+               struct mtk_ddp_comp *comp;
+
+               comp = mtk_crtc->ddp_comp[i];
+               mtk_ddp_comp_unregister_vblank_cb(comp);
+       }
+
        drm_crtc_cleanup(crtc);
 }
 
@@ -570,7 +579,7 @@ static int mtk_drm_crtc_enable_vblank(struct drm_crtc *crtc)
        struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
        struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
 
-       mtk_ddp_comp_enable_vblank(comp, mtk_crtc_ddp_irq, &mtk_crtc->base);
+       mtk_ddp_comp_enable_vblank(comp);
 
        return 0;
 }
@@ -870,6 +879,9 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
                        if (comp->funcs->ctm_set)
                                has_ctm = true;
                }
+
+               mtk_ddp_comp_register_vblank_cb(comp, mtk_crtc_ddp_irq,
+                                               &mtk_crtc->base);
        }
 
        for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
index 99cbf44463e402191e3d893cc1332bd2440fe734..22d23668b4840734cc0ec12acc0f0a19d1a6caf4 100644 (file)
@@ -276,6 +276,8 @@ static const struct mtk_ddp_comp_funcs ddp_ovl = {
        .config = mtk_ovl_config,
        .start = mtk_ovl_start,
        .stop = mtk_ovl_stop,
+       .register_vblank_cb = mtk_ovl_register_vblank_cb,
+       .unregister_vblank_cb = mtk_ovl_unregister_vblank_cb,
        .enable_vblank = mtk_ovl_enable_vblank,
        .disable_vblank = mtk_ovl_disable_vblank,
        .supported_rotations = mtk_ovl_supported_rotations,
@@ -292,6 +294,8 @@ static const struct mtk_ddp_comp_funcs ddp_rdma = {
        .config = mtk_rdma_config,
        .start = mtk_rdma_start,
        .stop = mtk_rdma_stop,
+       .register_vblank_cb = mtk_rdma_register_vblank_cb,
+       .unregister_vblank_cb = mtk_rdma_unregister_vblank_cb,
        .enable_vblank = mtk_rdma_enable_vblank,
        .disable_vblank = mtk_rdma_disable_vblank,
        .layer_nr = mtk_rdma_layer_nr,
index bb914d976cf5d3a73233621e1838cf420a3fd53d..25cb50f2391fa6308f2bc068984b55dc5634fe41 100644 (file)
@@ -47,9 +47,11 @@ struct mtk_ddp_comp_funcs {
                       unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
        void (*start)(struct device *dev);
        void (*stop)(struct device *dev);
-       void (*enable_vblank)(struct device *dev,
-                             void (*vblank_cb)(void *),
-                             void *vblank_cb_data);
+       void (*register_vblank_cb)(struct device *dev,
+                                  void (*vblank_cb)(void *),
+                                  void *vblank_cb_data);
+       void (*unregister_vblank_cb)(struct device *dev);
+       void (*enable_vblank)(struct device *dev);
        void (*disable_vblank)(struct device *dev);
        unsigned int (*supported_rotations)(struct device *dev);
        unsigned int (*layer_nr)(struct device *dev);
@@ -110,12 +112,25 @@ static inline void mtk_ddp_comp_stop(struct mtk_ddp_comp *comp)
                comp->funcs->stop(comp->dev);
 }
 
-static inline void mtk_ddp_comp_enable_vblank(struct mtk_ddp_comp *comp,
-                                             void (*vblank_cb)(void *),
-                                             void *vblank_cb_data)
+static inline void mtk_ddp_comp_register_vblank_cb(struct mtk_ddp_comp *comp,
+                                                  void (*vblank_cb)(void *),
+                                                  void *vblank_cb_data)
+{
+       if (comp->funcs && comp->funcs->register_vblank_cb)
+               comp->funcs->register_vblank_cb(comp->dev, vblank_cb,
+                                               vblank_cb_data);
+}
+
+static inline void mtk_ddp_comp_unregister_vblank_cb(struct mtk_ddp_comp *comp)
+{
+       if (comp->funcs && comp->funcs->unregister_vblank_cb)
+               comp->funcs->unregister_vblank_cb(comp->dev);
+}
+
+static inline void mtk_ddp_comp_enable_vblank(struct mtk_ddp_comp *comp)
 {
        if (comp->funcs && comp->funcs->enable_vblank)
-               comp->funcs->enable_vblank(comp->dev, vblank_cb, vblank_cb_data);
+               comp->funcs->enable_vblank(comp->dev);
 }
 
 static inline void mtk_ddp_comp_disable_vblank(struct mtk_ddp_comp *comp)