aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjames qian wang (Arm Technology China) <james.qian.wang@arm.com>2019-06-18 09:10:49 +0100
committerLiviu Dudau <Liviu.Dudau@arm.com>2019-08-02 17:31:39 +0100
commit8965ad8433eadadc5a41df2915eec4a144e8aed6 (patch)
tree1fe520b642d8e193e0aa2c456ec3efd4580b810a
parentdrm/komeda: Use drm_display_mode "crtc_" prefixed hardware timings (diff)
downloadlinux-dev-8965ad8433eadadc5a41df2915eec4a144e8aed6.tar.xz
linux-dev-8965ad8433eadadc5a41df2915eec4a144e8aed6.zip
drm/komeda: Enable dual-link support
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
-rw-r--r--drivers/gpu/drm/arm/display/komeda/d71/d71_component.c6
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_crtc.c73
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_dev.c5
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_drv.c8
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_kms.h2
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c19
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h6
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c2
8 files changed, 85 insertions, 36 deletions
diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
index e9f340d4b41f..55a8cc94808a 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
@@ -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);
}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
index c6a3808614dc..fa9a4593bb37 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
@@ -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++;
}
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c
index 5a118984de33..1ff7f4b2c620 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c
@@ -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;
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c
index cfa5068d9d1e..69ace6f9055d 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c
@@ -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);
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
index 2e2a68cc8c40..45c498e15e7a 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
@@ -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);
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c
index 78e44d9e1520..452e505a1fd3 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.c
@@ -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
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
index a90bcbb3cb23..a7a84e66549d 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
@@ -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;
};
/**
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
index 950235af1e79..ea26bc9c2d00 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
@@ -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;