From 10a90aa97ece1ec095073bebb8b764ea8cef51dc Mon Sep 17 00:00:00 2001 From: Mark Yao Date: Mon, 12 Jun 2017 15:23:11 +0800 Subject: [PATCH] drm/rockchip: support setting specail pll for hdmi In order to get lower jitter clock for hdmi tmds, Hardware design that: direct get tmds clock from vpll, bypass vop. This design can make hdmi good works, but also limit hdmi's clock source, the vop which hdmi use need also assign to vpll, and use same clock rate, it's hardware limitation. This patch add a mechanism to select dclk's parent pll, then can allocate correct pll for hdmi. Change-Id: I9e3b4b6d3756c409782df0605706be4203d69a32 Signed-off-by: Mark Yao Suggested-by: Heiko Stuebner --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 25 ++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 8 +++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 55 +++++++++++++++++++++ 3 files changed, 88 insertions(+) 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"); -- 2.34.1