|
@@ -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,44 @@ 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 = clk_enable(vop->hclk);
|
|
|
if (ret < 0) {
|
|
|
dev_err(vop->dev, "failed to enable hclk - %d\n", ret);
|
|
@@ -417,6 +451,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,26 +480,41 @@ 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);
|
|
|
}
|
|
@@ -742,7 +796,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 +813,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 +828,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 +843,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)
|
|
@@ -874,8 +920,8 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
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);
|
|
@@ -934,9 +980,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 +1127,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 +1146,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;
|
|
|
}
|
|
|
|
|
|
- drm_handle_vblank(vop->drm_dev, vop->pipe);
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
- return (vop->vsync_work_pending) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
|
|
|
+ /* Unhandled irqs are spurious. */
|
|
|
+ if (active_irqs)
|
|
|
+ DRM_ERROR("Unknown VOP IRQs: %#02x\n", active_irqs);
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static int vop_create_crtc(struct vop *vop)
|
|
@@ -1189,6 +1244,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 +1358,7 @@ static int vop_initial(struct vop *vop)
|
|
|
|
|
|
clk_disable(vop->hclk);
|
|
|
|
|
|
- vop->dpms = DRM_MODE_DPMS_OFF;
|
|
|
+ vop->is_enabled = false;
|
|
|
|
|
|
return 0;
|
|
|
|