diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index cca9ca20e83c..63498ceb8a0b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -913,6 +914,30 @@ static int rockchip_drm_bind(struct device *dev) drm_dev->dev_private = private; + private->hdmi_pll.pll = devm_clk_get(dev, "hdmi-tmds-pll"); + if (PTR_ERR(private->hdmi_pll.pll) == -ENOENT) { + private->hdmi_pll.pll = NULL; + } else if (PTR_ERR(private->hdmi_pll.pll) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_free; + } else if (IS_ERR(private->hdmi_pll.pll)) { + dev_err(dev, "failed to get hdmi-tmds-pll\n"); + ret = PTR_ERR(private->hdmi_pll.pll); + goto err_free; + } + + private->default_pll.pll = devm_clk_get(dev, "default-vop-pll"); + if (PTR_ERR(private->default_pll.pll) == -ENOENT) { + private->default_pll.pll = NULL; + } else if (PTR_ERR(private->default_pll.pll) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_free; + } else if (IS_ERR(private->default_pll.pll)) { + dev_err(dev, "failed to get default vop pll\n"); + ret = PTR_ERR(private->default_pll.pll); + goto err_free; + } + #ifdef CONFIG_DRM_DMA_SYNC private->cpu_fence_context = fence_context_alloc(1); atomic_set(&private->cpu_fence_seqno, 0); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 7c0f742f1a0a..189866876106 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -69,8 +69,14 @@ struct rockchip_atomic_commit { struct mutex lock; }; +struct rockchip_dclk_pll { + struct clk *pll; + unsigned int use_count; +}; + struct rockchip_crtc_state { struct drm_crtc_state base; + struct rockchip_dclk_pll *pll; int left_margin; int right_margin; int top_margin; @@ -135,6 +141,8 @@ struct rockchip_drm_private { /* protect drm_mm on multi-threads */ struct mutex mm_lock; struct drm_mm mm; + struct rockchip_dclk_pll default_pll; + struct rockchip_dclk_pll hdmi_pll; }; #ifndef MODULE diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 5bef49fcc3f3..54c05751f8df 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -212,6 +212,8 @@ struct vop { struct clk *dclk; /* vop share memory frequency */ struct clk *aclk; + /* vop source handling, optional */ + struct clk *dclk_source; /* vop dclk reset */ struct reset_control *dclk_rst; @@ -1665,6 +1667,13 @@ static void vop_crtc_enable(struct drm_crtc *crtc) val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : BIT(VSYNC_POSITIVE); VOP_CTRL_SET(vop, pin_pol, val); + + if (vop->dclk_source && s->pll && s->pll->pll) { + if (!clk_set_parent(vop->dclk_source, s->pll->pll)) + DRM_DEV_ERROR(vop->dev, + "failed to set dclk's parents\n"); + } + switch (s->output_type) { case DRM_MODE_CONNECTOR_LVDS: VOP_CTRL_SET(vop, rgb_en, 1); @@ -1858,6 +1867,40 @@ static int vop_afbdc_atomic_check(struct drm_crtc *crtc, return 0; } +static void vop_dclk_source_generate(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct rockchip_drm_private *private = crtc->dev->dev_private; + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct rockchip_crtc_state *old_s = to_rockchip_crtc_state(crtc->state); + struct vop *vop = to_vop(crtc); + + if (!vop->dclk_source) + return; + + if (crtc_state->active) { + WARN_ON(s->pll && !s->pll->use_count); + if (!s->pll || s->pll->use_count > 1 || + s->output_type != old_s->output_type) { + if (s->pll) + s->pll->use_count--; + + if (s->output_type != DRM_MODE_CONNECTOR_HDMIA && + !private->default_pll.use_count) + s->pll = &private->default_pll; + else + s->pll = &private->hdmi_pll; + + s->pll->use_count++; + } + } else if (s->pll) { + s->pll->use_count--; + s->pll = NULL; + } + if (s->pll && s->pll != old_s->pll) + crtc_state->mode_changed = true; +} + static int vop_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) { @@ -1932,6 +1975,8 @@ static int vop_crtc_atomic_check(struct drm_crtc *crtc, s->dsp_layer_sel = dsp_layer_sel; + vop_dclk_source_generate(crtc, crtc_state); + err_free_pzpos: kfree(pzpos); return ret; @@ -2746,6 +2791,16 @@ static int vop_bind(struct device *dev, struct device *master, void *data) return PTR_ERR(vop->dclk); } + vop->dclk_source = devm_clk_get(vop->dev, "dclk_source"); + if (PTR_ERR(vop->dclk_source) == -ENOENT) { + vop->dclk_source = NULL; + } else if (PTR_ERR(vop->dclk_source) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(vop->dclk_source)) { + dev_err(vop->dev, "failed to get dclk source parent\n"); + return PTR_ERR(vop->dclk_source); + } + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "cannot find irq for vop\n");