diff options
Diffstat (limited to 'drivers/gpu/drm/rockchip')
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 143 |
5 files changed, 142 insertions, 47 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 9bb4fd27d52f..3962176ee713 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -129,6 +129,7 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) struct rockchip_drm_private *private; struct dma_iommu_mapping *mapping; struct device *dev = drm_dev->dev; + struct drm_connector *connector; int ret; private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); @@ -171,6 +172,23 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) if (ret) goto err_detach_device; + /* + * All components are now added, we can publish the connector sysfs + * entries to userspace. This will generate hotplug events and so + * userspace will expect to be able to access DRM at this point. + */ + list_for_each_entry(connector, &drm_dev->mode_config.connector_list, + head) { + ret = drm_connector_register(connector); + if (ret) { + dev_err(drm_dev->dev, + "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n", + connector->base.id, + connector->name, ret); + goto err_unbind; + } + } + /* init kms poll for handling hpd */ drm_kms_helper_poll_init(drm_dev); @@ -200,6 +218,7 @@ err_vblank_cleanup: drm_vblank_cleanup(drm_dev); err_kms_helper_poll_fini: drm_kms_helper_poll_fini(drm_dev); +err_unbind: component_unbind_all(dev, drm_dev); err_detach_device: arm_iommu_detach_device(dev); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c index a5d889a8716b..5b0dc0f6fd94 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c @@ -71,7 +71,7 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, size = mode_cmd.pitches[0] * mode_cmd.height; - rk_obj = rockchip_gem_create_object(dev, size); + rk_obj = rockchip_gem_create_object(dev, size, true); if (IS_ERR(rk_obj)) return -ENOMEM; @@ -106,7 +106,7 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, fb = helper->fb; drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); - drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); + drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); offset = fbi->var.xoffset * bytes_per_pixel; offset += fbi->var.yoffset * fb->pitches[0]; @@ -119,6 +119,9 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n", fb->width, fb->height, fb->depth, rk_obj->kvaddr, offset, size); + + fbi->skip_vt_switch = true; + return 0; err_drm_framebuffer_unref: diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 7ca8799ef784..eb2282cc4a56 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -22,7 +22,8 @@ #include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" -static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj) +static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj, + bool alloc_kmap) { struct drm_gem_object *obj = &rk_obj->base; struct drm_device *drm = obj->dev; @@ -30,7 +31,9 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj) init_dma_attrs(&rk_obj->dma_attrs); dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs); - /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */ + if (!alloc_kmap) + dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs); + rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size, &rk_obj->dma_addr, GFP_KERNEL, &rk_obj->dma_attrs); @@ -103,7 +106,8 @@ int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma) } struct rockchip_gem_object * - rockchip_gem_create_object(struct drm_device *drm, unsigned int size) + rockchip_gem_create_object(struct drm_device *drm, unsigned int size, + bool alloc_kmap) { struct rockchip_gem_object *rk_obj; struct drm_gem_object *obj; @@ -119,7 +123,7 @@ struct rockchip_gem_object * drm_gem_private_object_init(drm, obj, size); - ret = rockchip_gem_alloc_buf(rk_obj); + ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap); if (ret) goto err_free_rk_obj; @@ -163,7 +167,7 @@ rockchip_gem_create_with_handle(struct drm_file *file_priv, struct drm_gem_object *obj; int ret; - rk_obj = rockchip_gem_create_object(drm, size); + rk_obj = rockchip_gem_create_object(drm, size, false); if (IS_ERR(rk_obj)) return ERR_CAST(rk_obj); @@ -282,6 +286,9 @@ void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) { struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); + if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs)) + return NULL; + return rk_obj->kvaddr; } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h index 67bcebe90003..ad22618473a4 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h @@ -41,7 +41,8 @@ int rockchip_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma); struct rockchip_gem_object * - rockchip_gem_create_object(struct drm_device *drm, unsigned int size); + rockchip_gem_create_object(struct drm_device *drm, unsigned int size, + bool alloc_kmap); void rockchip_gem_free_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 9a5c571b95fc..ccb0ce073ef2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -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; |