drm/rockchip: support setting specail pll for hdmi
authorMark Yao <mark.yao@rock-chips.com>
Mon, 12 Jun 2017 07:23:11 +0000 (15:23 +0800)
committerMark Yao <mark.yao@rock-chips.com>
Tue, 13 Jun 2017 01:20:59 +0000 (09:20 +0800)
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 <mark.yao@rock-chips.com>
Suggested-by: Heiko Stuebner <heiko@sntech.de>
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_vop.c

index cca9ca20e83c8b86c12bcee7c56bc5cf8480e8a4..63498ceb8a0b1a2a55feef8ea15a38a77a21d267 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_graph.h>
+#include <linux/clk.h>
 #include <linux/component.h>
 #include <linux/fence.h>
 #include <linux/console.h>
@@ -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);
index 7c0f742f1a0aac59d2694d3a37611d5b64944d3f..1898668761065d80a25fdfa10af64436319189ef 100644 (file)
@@ -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
index 5bef49fcc3f37a138002ebb6e1d6cd3dacecab3d..54c05751f8dfad84f02541a7669b1f54858537f1 100644 (file)
@@ -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");