OSDN Git Service

drm/komeda: Enable dual-link support
authorjames qian wang (Arm Technology China) <james.qian.wang@arm.com>
Tue, 18 Jun 2019 08:10:49 +0000 (09:10 +0100)
committerLiviu Dudau <Liviu.Dudau@arm.com>
Fri, 2 Aug 2019 16:31:39 +0000 (17:31 +0100)
Komeda HW can support dual-link which splits display frame to two halves
(left/link0, right/link1) and output them by two output links.
Due to the halved pixel rate of each link, the pxlclk of dual-link can be
reduced two times compare with single-link.

For enabling dual-link:
- The DT need to configure two output-links for the pipeline node.
- Komeda enable dual-link when both link0 and link1 have been connected.

Example of how the pipeline node will look like for dual-link setup

pipe0: pipeline@0 {
clocks = <&fpgaosc2>;
clock-names = "pxclk";
reg = <0>;

#address-cells = <1>;
#size-cells = <0>;

port@0 {
reg = <0>;

#address-cells = <1>;
#size-cells = <0>;
dp0_pipe0_link0: endpoint@0 {
reg = <0>;
remote-endpoint = <&dlink_connector_in0>;

};
dp0_pipe0_link1: endpoint@1 {
reg = <1>;
remote-endpoint = <&dlink_connector_in1>;
};
};
};

Signed-off-by: James Qian Wang (Arm Technology China) <james.qian.wang@arm.com>
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190618081013.13638-3-james.qian.wang@arm.com
drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
drivers/gpu/drm/arm/display/komeda/komeda_dev.c
drivers/gpu/drm/arm/display/komeda/komeda_drv.c
drivers/gpu/drm/arm/display/komeda/komeda_kms.h
drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c
drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c

index e9f340d..55a8cc9 100644 (file)
@@ -4,8 +4,6 @@
  * Author: James.Qian.Wang <james.qian.wang@arm.com>
  *
  */
-
-#include <drm/drm_print.h>
 #include "d71_dev.h"
 #include "komeda_kms.h"
 #include "malidp_io.h"
@@ -1064,6 +1062,10 @@ static void d71_timing_ctrlr_update(struct komeda_component *c,
 
        /* configure bs control register */
        value = BS_CTRL_EN | BS_CTRL_VM;
+       if (c->pipeline->dual_link) {
+               malidp_write32(reg, BS_DRIFT_TO, hfront_porch + 16);
+               value |= BS_CTRL_DL;
+       }
 
        malidp_write32(reg, BLK_CONTROL, value);
 }
index c6a3808..fa9a459 100644 (file)
@@ -28,7 +28,7 @@ static void komeda_crtc_update_clock_ratio(struct komeda_crtc_state *kcrtc_st)
        }
 
        pxlclk = kcrtc_st->base.adjusted_mode.crtc_clock * 1000;
-       aclk = komeda_calc_aclk(kcrtc_st);
+       aclk = komeda_crtc_get_aclk(kcrtc_st);
 
        kcrtc_st->clock_ratio = div64_u64(aclk << 32, pxlclk);
 }
@@ -74,14 +74,6 @@ komeda_crtc_atomic_check(struct drm_crtc *crtc,
        return 0;
 }
 
-unsigned long komeda_calc_aclk(struct komeda_crtc_state *kcrtc_st)
-{
-       struct komeda_dev *mdev = kcrtc_st->base.crtc->dev->dev_private;
-       unsigned long aclk = kcrtc_st->base.adjusted_mode.crtc_clock;
-
-       return clk_round_rate(mdev->aclk, aclk * 1000);
-}
-
 /* For active a crtc, mainly need two parts of preparation
  * 1. adjust display operation mode.
  * 2. enable needed clk
@@ -118,7 +110,7 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc)
         * to enable it again.
         */
        if (new_mode != KOMEDA_MODE_DUAL_DISP) {
-               err = clk_set_rate(mdev->aclk, komeda_calc_aclk(kcrtc_st));
+               err = clk_set_rate(mdev->aclk, komeda_crtc_get_aclk(kcrtc_st));
                if (err)
                        DRM_ERROR("failed to set aclk.\n");
                err = clk_prepare_enable(mdev->aclk);
@@ -342,29 +334,58 @@ komeda_crtc_atomic_flush(struct drm_crtc *crtc,
        komeda_crtc_do_flush(crtc, old);
 }
 
+/* Returns the minimum frequency of the aclk rate (main engine clock) in Hz */
+static unsigned long
+komeda_calc_min_aclk_rate(struct komeda_crtc *kcrtc,
+                         unsigned long pxlclk)
+{
+       /* Once dual-link one display pipeline drives two display outputs,
+        * the aclk needs run on the double rate of pxlclk
+        */
+       if (kcrtc->master->dual_link)
+               return pxlclk * 2;
+       else
+               return pxlclk;
+}
+
+/* Get current aclk rate that specified by state */
+unsigned long komeda_crtc_get_aclk(struct komeda_crtc_state *kcrtc_st)
+{
+       struct drm_crtc *crtc = kcrtc_st->base.crtc;
+       struct komeda_dev *mdev = crtc->dev->dev_private;
+       unsigned long pxlclk = kcrtc_st->base.adjusted_mode.crtc_clock * 1000;
+       unsigned long min_aclk;
+
+       min_aclk = komeda_calc_min_aclk_rate(to_kcrtc(crtc), pxlclk);
+
+       return clk_round_rate(mdev->aclk, min_aclk);
+}
+
 static enum drm_mode_status
 komeda_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *m)
 {
        struct komeda_dev *mdev = crtc->dev->dev_private;
        struct komeda_crtc *kcrtc = to_kcrtc(crtc);
        struct komeda_pipeline *master = kcrtc->master;
-       long mode_clk, pxlclk;
+       unsigned long min_pxlclk, min_aclk;
 
        if (m->flags & DRM_MODE_FLAG_INTERLACE)
                return MODE_NO_INTERLACE;
 
-       mode_clk = m->clock * 1000;
-       pxlclk = clk_round_rate(master->pxlclk, mode_clk);
-       if (pxlclk != mode_clk) {
-               DRM_DEBUG_ATOMIC("pxlclk doesn't support %ld Hz\n", mode_clk);
+       min_pxlclk = m->clock * 1000;
+       if (master->dual_link)
+               min_pxlclk /= 2;
+
+       if (min_pxlclk != clk_round_rate(master->pxlclk, min_pxlclk)) {
+               DRM_DEBUG_ATOMIC("pxlclk doesn't support %lu Hz\n", min_pxlclk);
 
                return MODE_NOCLOCK;
        }
 
-       /* main engine clock must be faster than pxlclk*/
-       if (clk_round_rate(mdev->aclk, mode_clk) < pxlclk) {
-               DRM_DEBUG_ATOMIC("engine clk can't satisfy the requirement of %s-clk: %ld.\n",
-                                m->name, pxlclk);
+       min_aclk = komeda_calc_min_aclk_rate(to_kcrtc(crtc), min_pxlclk);
+       if (clk_round_rate(mdev->aclk, min_aclk) < min_aclk) {
+               DRM_DEBUG_ATOMIC("engine clk can't satisfy the requirement of %s-clk: %lu.\n",
+                                m->name, min_pxlclk);
 
                return MODE_CLOCK_HIGH;
        }
@@ -380,6 +401,14 @@ static bool komeda_crtc_mode_fixup(struct drm_crtc *crtc,
        unsigned long clk_rate;
 
        drm_mode_set_crtcinfo(adjusted_mode, 0);
+       /* In dual link half the horizontal settings */
+       if (kcrtc->master->dual_link) {
+               adjusted_mode->crtc_clock /= 2;
+               adjusted_mode->crtc_hdisplay /= 2;
+               adjusted_mode->crtc_hsync_start /= 2;
+               adjusted_mode->crtc_hsync_end /= 2;
+               adjusted_mode->crtc_htotal /= 2;
+       }
 
        clk_rate = adjusted_mode->crtc_clock * 1000;
        /* crtc_clock will be used as the komeda output pixel clock */
@@ -492,10 +521,8 @@ int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
                else
                        sprintf(str, "None");
 
-               DRM_INFO("crtc%d: master(pipe-%d) slave(%s) output: %s.\n",
-                        kms->n_crtcs, master->id, str,
-                        master->of_output_dev ?
-                        master->of_output_dev->full_name : "None");
+               DRM_INFO("CRTC-%d: master(pipe-%d) slave(%s).\n",
+                        kms->n_crtcs, master->id, str);
 
                kms->n_crtcs++;
        }
index 5a11898..1ff7f4b 100644 (file)
@@ -121,11 +121,14 @@ static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np)
        pipe->pxlclk = clk;
 
        /* enum ports */
-       pipe->of_output_dev =
+       pipe->of_output_links[0] =
                of_graph_get_remote_node(np, KOMEDA_OF_PORT_OUTPUT, 0);
+       pipe->of_output_links[1] =
+               of_graph_get_remote_node(np, KOMEDA_OF_PORT_OUTPUT, 1);
        pipe->of_output_port =
                of_graph_get_port_by_id(np, KOMEDA_OF_PORT_OUTPUT);
 
+       pipe->dual_link = pipe->of_output_links[0] && pipe->of_output_links[1];
        pipe->of_node = np;
 
        return 0;
index cfa5068..69ace6f 100644 (file)
@@ -83,11 +83,12 @@ static int compare_of(struct device *dev, void *data)
 
 static void komeda_add_slave(struct device *master,
                             struct component_match **match,
-                            struct device_node *np, int port)
+                            struct device_node *np,
+                            u32 port, u32 endpoint)
 {
        struct device_node *remote;
 
-       remote = of_graph_get_remote_node(np, port, 0);
+       remote = of_graph_get_remote_node(np, port, endpoint);
        if (remote) {
                drm_of_component_match_add(master, match, compare_of, remote);
                of_node_put(remote);
@@ -108,7 +109,8 @@ static int komeda_platform_probe(struct platform_device *pdev)
                        continue;
 
                /* add connector */
-               komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT);
+               komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT, 0);
+               komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT, 1);
        }
 
        return component_master_add_with_match(dev, &komeda_master_ops, match);
index 2e2a68c..45c498e 100644 (file)
@@ -166,7 +166,7 @@ static inline bool has_flip_h(u32 rot)
                return !!(rotation & DRM_MODE_REFLECT_X);
 }
 
-unsigned long komeda_calc_aclk(struct komeda_crtc_state *kcrtc_st);
+unsigned long komeda_crtc_get_aclk(struct komeda_crtc_state *kcrtc_st);
 
 int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
 
index 78e44d9..452e505 100644 (file)
@@ -54,7 +54,8 @@ void komeda_pipeline_destroy(struct komeda_dev *mdev,
 
        clk_put(pipe->pxlclk);
 
-       of_node_put(pipe->of_output_dev);
+       of_node_put(pipe->of_output_links[0]);
+       of_node_put(pipe->of_output_links[1]);
        of_node_put(pipe->of_output_port);
        of_node_put(pipe->of_node);
 
@@ -246,9 +247,15 @@ static void komeda_pipeline_dump(struct komeda_pipeline *pipe)
        struct komeda_component *c;
        int id;
 
-       DRM_INFO("Pipeline-%d: n_layers: %d, n_scalers: %d, output: %s\n",
+       DRM_INFO("Pipeline-%d: n_layers: %d, n_scalers: %d, output: %s.\n",
                 pipe->id, pipe->n_layers, pipe->n_scalers,
-                pipe->of_output_dev ? pipe->of_output_dev->full_name : "none");
+                pipe->dual_link ? "dual-link" : "single-link");
+       DRM_INFO("      output_link[0]: %s.\n",
+                pipe->of_output_links[0] ?
+                pipe->of_output_links[0]->full_name : "none");
+       DRM_INFO("      output_link[1]: %s.\n",
+                pipe->of_output_links[1] ?
+                pipe->of_output_links[1]->full_name : "none");
 
        dp_for_each_set_bit(id, pipe->avail_comps) {
                c = komeda_pipeline_get_component(pipe, id);
@@ -305,6 +312,12 @@ static void komeda_pipeline_assemble(struct komeda_pipeline *pipe)
 
                layer->right = komeda_get_layer_split_right_layer(pipe, layer);
        }
+
+       if (pipe->dual_link && !pipe->ctrlr->supports_dual_link) {
+               pipe->dual_link = false;
+               DRM_WARN("PIPE-%d doesn't support dual-link, ignore DT dual-link configuration.\n",
+                        pipe->id);
+       }
 }
 
 /* if pipeline_A accept another pipeline_B's component as input, treat
index a90bcbb..a7a84e6 100644 (file)
@@ -416,8 +416,10 @@ struct komeda_pipeline {
        struct device_node *of_node;
        /** @of_output_port: pipeline output port */
        struct device_node *of_output_port;
-       /** @of_output_dev: output connector device node */
-       struct device_node *of_output_dev;
+       /** @of_output_links: output connector device nodes */
+       struct device_node *of_output_links[2];
+       /** @dual_link: true if of_output_links[0] and [1] are both valid */
+       bool dual_link;
 };
 
 /**
index 950235a..ea26bc9 100644 (file)
@@ -473,7 +473,7 @@ komeda_scaler_check_cfg(struct komeda_scaler *scaler,
 
                err = pipe->funcs->downscaling_clk_check(pipe,
                                        &kcrtc_st->base.adjusted_mode,
-                                       komeda_calc_aclk(kcrtc_st), dflow);
+                                       komeda_crtc_get_aclk(kcrtc_st), dflow);
                if (err) {
                        DRM_DEBUG_ATOMIC("aclk can't satisfy the clock requirement of the downscaling\n");
                        return err;