Merge branches 'acpi-resources', 'acpi-battery', 'acpi-doc' and 'acpi-pnp'
[linux-2.6-block.git] / drivers / gpu / drm / rockchip / rockchip_drm_vop.c
index 9a5c571b95fceb97dae7bfadf1b9782ede0f686e..4557f335a8a56f243ad4aa326a1826572116d3a5 100644 (file)
@@ -81,7 +81,7 @@ struct vop {
        struct drm_crtc crtc;
        struct device *dev;
        struct drm_device *drm_dev;
-       unsigned int dpms;
+       bool is_enabled;
 
        int connector_type;
        int connector_out_mode;
@@ -89,6 +89,7 @@ struct vop {
        /* mutex vsync_ work */
        struct mutex vsync_mutex;
        bool vsync_work_pending;
+       struct completion dsp_hold_completion;
 
        const struct vop_data *data;
 
@@ -382,11 +383,50 @@ static bool is_alpha_support(uint32_t format)
        }
 }
 
+static void vop_dsp_hold_valid_irq_enable(struct vop *vop)
+{
+       unsigned long flags;
+
+       if (WARN_ON(!vop->is_enabled))
+               return;
+
+       spin_lock_irqsave(&vop->irq_lock, flags);
+
+       vop_mask_write(vop, INTR_CTRL0, DSP_HOLD_VALID_INTR_MASK,
+                      DSP_HOLD_VALID_INTR_EN(1));
+
+       spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
+{
+       unsigned long flags;
+
+       if (WARN_ON(!vop->is_enabled))
+               return;
+
+       spin_lock_irqsave(&vop->irq_lock, flags);
+
+       vop_mask_write(vop, INTR_CTRL0, DSP_HOLD_VALID_INTR_MASK,
+                      DSP_HOLD_VALID_INTR_EN(0));
+
+       spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
        struct vop *vop = to_vop(crtc);
        int ret;
 
+       if (vop->is_enabled)
+               return;
+
+       ret = pm_runtime_get_sync(vop->dev);
+       if (ret < 0) {
+               dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
+               return;
+       }
+
        ret = clk_enable(vop->hclk);
        if (ret < 0) {
                dev_err(vop->dev, "failed to enable hclk - %d\n", ret);
@@ -417,6 +457,11 @@ static void vop_enable(struct drm_crtc *crtc)
                goto err_disable_aclk;
        }
 
+       /*
+        * At here, vop clock & iommu is enable, R/W vop regs would be safe.
+        */
+       vop->is_enabled = true;
+
        spin_lock(&vop->reg_lock);
 
        VOP_CTRL_SET(vop, standby, 0);
@@ -441,28 +486,44 @@ static void vop_disable(struct drm_crtc *crtc)
 {
        struct vop *vop = to_vop(crtc);
 
-       drm_vblank_off(crtc->dev, vop->pipe);
+       if (!vop->is_enabled)
+               return;
 
-       disable_irq(vop->irq);
+       drm_vblank_off(crtc->dev, vop->pipe);
 
        /*
-        * TODO: Since standby doesn't take effect until the next vblank,
-        * when we turn off dclk below, the vop is probably still active.
+        * Vop standby will take effect at end of current frame,
+        * if dsp hold valid irq happen, it means standby complete.
+        *
+        * we must wait standby complete when we want to disable aclk,
+        * if not, memory bus maybe dead.
         */
+       reinit_completion(&vop->dsp_hold_completion);
+       vop_dsp_hold_valid_irq_enable(vop);
+
        spin_lock(&vop->reg_lock);
 
        VOP_CTRL_SET(vop, standby, 1);
 
        spin_unlock(&vop->reg_lock);
+
+       wait_for_completion(&vop->dsp_hold_completion);
+
+       vop_dsp_hold_valid_irq_disable(vop);
+
+       disable_irq(vop->irq);
+
+       vop->is_enabled = false;
+
        /*
-        * disable dclk to stop frame scan, so we can safely detach iommu,
+        * vop standby complete, so iommu detach is safe.
         */
-       clk_disable(vop->dclk);
-
        rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
 
+       clk_disable(vop->dclk);
        clk_disable(vop->aclk);
        clk_disable(vop->hclk);
+       pm_runtime_put(vop->dev);
 }
 
 /*
@@ -742,7 +803,7 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
        struct vop *vop = to_vop(crtc);
        unsigned long flags;
 
-       if (vop->dpms != DRM_MODE_DPMS_ON)
+       if (!vop->is_enabled)
                return -EPERM;
 
        spin_lock_irqsave(&vop->irq_lock, flags);
@@ -759,8 +820,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
        struct vop *vop = to_vop(crtc);
        unsigned long flags;
 
-       if (vop->dpms != DRM_MODE_DPMS_ON)
+       if (!vop->is_enabled)
                return;
+
        spin_lock_irqsave(&vop->irq_lock, flags);
        vop_mask_write(vop, INTR_CTRL0, FS_INTR_MASK, FS_INTR_EN(0));
        spin_unlock_irqrestore(&vop->irq_lock, flags);
@@ -773,15 +835,8 @@ static const struct rockchip_crtc_funcs private_crtc_funcs = {
 
 static void vop_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
-       struct vop *vop = to_vop(crtc);
-
        DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
 
-       if (vop->dpms == mode) {
-               DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
-               return;
-       }
-
        switch (mode) {
        case DRM_MODE_DPMS_ON:
                vop_enable(crtc);
@@ -795,8 +850,6 @@ static void vop_crtc_dpms(struct drm_crtc *crtc, int mode)
                DRM_DEBUG_KMS("unspecified mode %d\n", mode);
                break;
        }
-
-       vop->dpms = mode;
 }
 
 static void vop_crtc_prepare(struct drm_crtc *crtc)
@@ -847,7 +900,7 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
        u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
        u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
        u16 vact_end = vact_st + vdisplay;
-       int ret;
+       int ret, ret_clk;
        uint32_t val;
 
        /*
@@ -869,13 +922,14 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
        default:
                DRM_ERROR("unsupport connector_type[%d]\n",
                          vop->connector_type);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        };
        VOP_CTRL_SET(vop, out_mode, vop->connector_out_mode);
 
        val = 0x8;
-       val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
-       val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
+       val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
+       val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
        VOP_CTRL_SET(vop, pin_pol, val);
 
        VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
@@ -892,7 +946,7 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
 
        ret = vop_crtc_mode_set_base(crtc, x, y, fb);
        if (ret)
-               return ret;
+               goto out;
 
        /*
         * reset dclk, take all mode config affect, so the clk would run in
@@ -903,13 +957,14 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
        reset_control_deassert(vop->dclk_rst);
 
        clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
-       ret = clk_enable(vop->dclk);
-       if (ret < 0) {
-               dev_err(vop->dev, "failed to enable dclk - %d\n", ret);
-               return ret;
+out:
+       ret_clk = clk_enable(vop->dclk);
+       if (ret_clk < 0) {
+               dev_err(vop->dev, "failed to enable dclk - %d\n", ret_clk);
+               return ret_clk;
        }
 
-       return 0;
+       return ret;
 }
 
 static void vop_crtc_commit(struct drm_crtc *crtc)
@@ -934,9 +989,9 @@ static int vop_crtc_page_flip(struct drm_crtc *crtc,
        struct drm_framebuffer *old_fb = crtc->primary->fb;
        int ret;
 
-       /* when the page flip is requested, crtc's dpms should be on */
-       if (vop->dpms > DRM_MODE_DPMS_ON) {
-               DRM_DEBUG("failed page flip request at dpms[%d].\n", vop->dpms);
+       /* when the page flip is requested, crtc should be on */
+       if (!vop->is_enabled) {
+               DRM_DEBUG("page flip request rejected because crtc is off.\n");
                return 0;
        }
 
@@ -1081,6 +1136,7 @@ static irqreturn_t vop_isr(int irq, void *data)
        struct vop *vop = data;
        uint32_t intr0_reg, active_irqs;
        unsigned long flags;
+       int ret = IRQ_NONE;
 
        /*
         * INTR_CTRL0 register has interrupt status, enable and clear bits, we
@@ -1099,15 +1155,23 @@ static irqreturn_t vop_isr(int irq, void *data)
        if (!active_irqs)
                return IRQ_NONE;
 
-       /* Only Frame Start Interrupt is enabled; other irqs are spurious. */
-       if (!(active_irqs & FS_INTR)) {
-               DRM_ERROR("Unknown VOP IRQs: %#02x\n", active_irqs);
-               return IRQ_NONE;
+       if (active_irqs & DSP_HOLD_VALID_INTR) {
+               complete(&vop->dsp_hold_completion);
+               active_irqs &= ~DSP_HOLD_VALID_INTR;
+               ret = IRQ_HANDLED;
+       }
+
+       if (active_irqs & FS_INTR) {
+               drm_handle_vblank(vop->drm_dev, vop->pipe);
+               active_irqs &= ~FS_INTR;
+               ret = (vop->vsync_work_pending) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
        }
 
-       drm_handle_vblank(vop->drm_dev, vop->pipe);
+       /* Unhandled irqs are spurious. */
+       if (active_irqs)
+               DRM_ERROR("Unknown VOP IRQs: %#02x\n", active_irqs);
 
-       return (vop->vsync_work_pending) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+       return ret;
 }
 
 static int vop_create_crtc(struct vop *vop)
@@ -1189,6 +1253,7 @@ static int vop_create_crtc(struct vop *vop)
                goto err_cleanup_crtc;
        }
 
+       init_completion(&vop->dsp_hold_completion);
        crtc->port = port;
        vop->pipe = drm_crtc_index(crtc);
        rockchip_register_crtc_funcs(drm_dev, &private_crtc_funcs, vop->pipe);
@@ -1302,7 +1367,7 @@ static int vop_initial(struct vop *vop)
 
        clk_disable(vop->hclk);
 
-       vop->dpms = DRM_MODE_DPMS_OFF;
+       vop->is_enabled = false;
 
        return 0;
 
@@ -1344,7 +1409,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
        struct vop *vop;
        struct resource *res;
        size_t alloc_size;
-       int ret;
+       int ret, irq;
 
        of_id = of_match_device(vop_driver_dt_match, dev);
        vop_data = of_id->data;
@@ -1380,11 +1445,12 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
                return ret;
        }
 
-       vop->irq = platform_get_irq(pdev, 0);
-       if (vop->irq < 0) {
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
                dev_err(dev, "cannot find irq for vop\n");
-               return vop->irq;
+               return irq;
        }
+       vop->irq = (unsigned int)irq;
 
        spin_lock_init(&vop->reg_lock);
        spin_lock_init(&vop->irq_lock);