FROMLIST: drm/rockchip: vop: export line flag function
authorYakir Yang <ykk@rock-chips.com>
Thu, 2 Jun 2016 12:16:22 +0000 (20:16 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Fri, 2 Dec 2016 09:07:19 +0000 (17:07 +0800)
VOP have integrated a hardware counter which indicate the exact display
line that vop is scanning. And if we're interested in a specific line,
we can set the line number to vop line_flag register, and then vop would
generate a line_flag interrupt for it.

For example eDP PSR function is interested in the vertical blanking
period, then driver could set the line number to zero.

This patch have exported a symbol that allow other driver to listen the
line flag event with given timeout limit:
- rockchip_drm_wait_line_flag()

BUG=chrome-os-partner:54785
TEST=Success to call rockchip_drm_wait_line_flag()

Change-Id: Id7605bada87c8ead0e549d4ff113ee49d29ad126
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
(am from https://patchwork.kernel.org/patch/9231675/)
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
Reviewed-on: https://chromium-review.googlesource.com/349084
Commit-Ready: Sean Paul <seanpaul@google.com>
Tested-by: Sean Paul <seanpaul@google.com>
Reviewed-by: Sean Paul <seanpaul@google.com>
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_drm_vop.h
drivers/gpu/drm/rockchip/rockchip_vop_reg.c

index e7499f8f990089f3739d432c439c181714ceb64d..c0bf1bfdfced25bb88957cb7d6e6afbe81d6520d 100644 (file)
@@ -126,6 +126,8 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
                                   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
                                    struct device *dev);
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+                               unsigned int mstimeout);
 
 int rockchip_drm_register_subdrv(struct drm_rockchip_subdrv *subdrv);
 int rockchip_drm_unregister_subdrv(struct drm_rockchip_subdrv *subdrv);
index 19219c079b4fb686c2a358c110e49645023b368e..b10476fca40b373345af4665310e75a44d8c20c5 100644 (file)
 #define VOP_INTR_GET(vop, name) \
                vop_read_reg(vop, 0, &vop->data->ctrl->name)
 
-#define VOP_INTR_SET(vop, name, mask, v) \
+#define VOP_INTR_SET(vop, name, v) \
+               REG_SET(vop, name, 0, vop->data->intr->name, \
+                       v, false)
+#define VOP_INTR_SET_MASK(vop, name, mask, v) \
                REG_SET_MASK(vop, name, 0, vop->data->intr->name, \
                             mask, v, false)
 
                                mask |= 1 << i; \
                        } \
                } \
-               VOP_INTR_SET(vop, name, mask, reg); \
+               VOP_INTR_SET_MASK(vop, name, mask, reg); \
        } while (0)
 #define VOP_INTR_GET_TYPE(vop, name, type) \
                vop_get_intr_type(vop, &vop->data->intr->name, type)
@@ -174,6 +177,8 @@ struct vop {
        struct completion wait_update_complete;
        struct drm_pending_vblank_event *event;
 
+       struct completion line_flag_completion;
+
        const struct vop_data *data;
        int num_wins;
 
@@ -675,6 +680,71 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
        spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ *     the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+                         |
+ *                |                         |
+ *                v                         v
+ *                | Vsync | Vbp |  Vactive  | Vfp |
+ *                        ^     ^           ^     ^
+ *                        |     |           |     |
+ *                        |     |           |     |
+ * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
+ * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
+ * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
+ * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
+ */
+static bool vop_line_flag_irq_is_enabled(struct vop *vop)
+{
+       uint32_t line_flag_irq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vop->irq_lock, flags);
+
+       line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
+
+       spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+       return !!line_flag_irq;
+}
+
+static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+{
+       unsigned long flags;
+
+       if (WARN_ON(!vop->is_enabled))
+               return;
+
+       spin_lock_irqsave(&vop->irq_lock, flags);
+
+       VOP_INTR_SET(vop, line_flag_num[0], line_num);
+       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
+
+       spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_line_flag_irq_disable(struct vop *vop)
+{
+       unsigned long flags;
+
+       if (WARN_ON(!vop->is_enabled))
+               return;
+
+       spin_lock_irqsave(&vop->irq_lock, flags);
+
+       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
+
+       spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
        struct vop *vop = to_vop(crtc);
@@ -1698,6 +1768,12 @@ static irqreturn_t vop_isr(int irq, void *data)
                ret = IRQ_HANDLED;
        }
 
+       if (active_irqs & LINE_FLAG_INTR) {
+               complete(&vop->line_flag_completion);
+               active_irqs &= ~LINE_FLAG_INTR;
+               ret = IRQ_HANDLED;
+       }
+
        if (active_irqs & FS_INTR) {
                drm_crtc_handle_vblank(crtc);
                vop_handle_vblank(vop);
@@ -1833,6 +1909,7 @@ static int vop_create_crtc(struct vop *vop)
 
        init_completion(&vop->dsp_hold_completion);
        init_completion(&vop->wait_update_complete);
+       init_completion(&vop->line_flag_completion);
        crtc->port = port;
        rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
 
@@ -1964,6 +2041,49 @@ static int vop_win_init(struct vop *vop)
        return 0;
 }
 
+/**
+ * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * @crtc: CRTC to enable line flag
+ * @line_num: interested line number
+ * @mstimeout: millisecond for timeout
+ *
+ * Driver would hold here until the interested line flag interrupt have
+ * happened or timeout to wait.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+                               unsigned int mstimeout)
+{
+       struct vop *vop = to_vop(crtc);
+       unsigned long jiffies_left;
+
+       if (!crtc || !vop->is_enabled)
+               return -ENODEV;
+
+       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+               return -EINVAL;
+
+       if (vop_line_flag_irq_is_enabled(vop))
+               return -EBUSY;
+
+       reinit_completion(&vop->line_flag_completion);
+       vop_line_flag_irq_enable(vop, line_num);
+
+       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+                                                  msecs_to_jiffies(mstimeout));
+       vop_line_flag_irq_disable(vop);
+
+       if (jiffies_left == 0) {
+               dev_err(vop->dev, "Timeout waiting for IRQ\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
        struct platform_device *pdev = to_platform_device(dev);
index 85a3bc67d2b6150e135c886222d6784262898342..5d2d3ced7689286e337363146d01806f6fd45e8e 100644 (file)
@@ -132,7 +132,7 @@ struct vop_ctrl {
 struct vop_intr {
        const int *intrs;
        uint32_t nintrs;
-       struct vop_reg line_flag_num;
+       struct vop_reg line_flag_num[2];
        struct vop_reg enable;
        struct vop_reg clear;
        struct vop_reg status;
index 533b9b24e25e26c067e4b21b9964a5d83a0412f7..c4930a2542ac0c5962677a935a7b2abfd25481d5 100644 (file)
@@ -252,6 +252,7 @@ static const int rk3288_vop_intrs[] = {
 static const struct vop_intr rk3288_vop_intr = {
        .intrs = rk3288_vop_intrs,
        .nintrs = ARRAY_SIZE(rk3288_vop_intrs),
+       .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
        .status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0),
        .enable = VOP_REG(RK3288_INTR_CTRL0, 0xf, 4),
        .clear = VOP_REG(RK3288_INTR_CTRL0, 0xf, 8),
@@ -286,6 +287,8 @@ static const int rk3368_vop_intrs[] = {
 static const struct vop_intr rk3368_vop_intr = {
        .intrs = rk3368_vop_intrs,
        .nintrs = ARRAY_SIZE(rk3368_vop_intrs),
+       .line_flag_num[0] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 0),
+       .line_flag_num[1] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 16),
        .status = VOP_REG_MASK(RK3368_INTR_STATUS, 0x3fff, 0),
        .enable = VOP_REG_MASK(RK3368_INTR_EN, 0x3fff, 0),
        .clear = VOP_REG_MASK(RK3368_INTR_CLEAR, 0x3fff, 0),
@@ -370,6 +373,8 @@ static const struct vop_data rk3368_vop = {
 static const struct vop_intr rk3366_vop_intr = {
        .intrs = rk3368_vop_intrs,
        .nintrs = ARRAY_SIZE(rk3368_vop_intrs),
+       .line_flag_num[0] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 0),
+       .line_flag_num[1] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 16),
        .status = VOP_REG_MASK(RK3366_INTR_STATUS0, 0xffff, 0),
        .enable = VOP_REG_MASK(RK3366_INTR_EN0, 0xffff, 0),
        .clear = VOP_REG_MASK(RK3366_INTR_CLEAR0, 0xffff, 0),
@@ -580,6 +585,7 @@ static const int rk3036_vop_intrs[] = {
 static const struct vop_intr rk3036_intr = {
        .intrs = rk3036_vop_intrs,
        .nintrs = ARRAY_SIZE(rk3036_vop_intrs),
+       .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
        .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
        .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4),
        .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8),