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)
committerChun-Kuang Hu <chunkuang.hu@kernel.org>
Tue, 5 Apr 2022 23:10:26 +0000 (07:10 +0800)
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>
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 17cd9b932298814236580dd427fe4f0b00304b78..70ab22964f3b5e5447115913933aea5610b07675 100644 (file)
@@ -97,14 +97,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);
 }
@@ -113,8 +127,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 662e91d9d45f6f23740868ad793bbbd29e2ab63b..1be4caf9ff963dfa99fe98402d8635fa32e90ae6 100644 (file)
@@ -95,24 +95,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 a6adc4ea48e1b02f74f67c88fc807b1322c9d7f2..42cc7052b0509d102f0fce3674e4f3937483bec9 100644 (file)
@@ -152,6 +152,7 @@ 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)
@@ -162,6 +163,14 @@ static void mtk_drm_crtc_destroy(struct drm_crtc *crtc)
                mtk_crtc->cmdq_client.chan = NULL;
        }
 #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);
 }
 
@@ -618,7 +627,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;
 }
@@ -927,6 +936,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 2e99aee13dfe4f5e2cd61bcc7c4e103bd2c7177f..5d7504a72b11c201b844875038ef05468d611262 100644 (file)
@@ -297,6 +297,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,
@@ -321,6 +323,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 ad267bb8fc9b5b8788a3bbd8ccabe324a1688f3c..1cbc6332282dc22373626837e945c3b27d260858 100644 (file)
@@ -48,9 +48,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)