Commit 8965ad84 authored by james qian wang (Arm Technology China)'s avatar james qian wang (Arm Technology China) Committed by Liviu Dudau

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: default avatarJames Qian Wang (Arm Technology China) <james.qian.wang@arm.com>
Signed-off-by: default avatarLiviu Dudau <Liviu.Dudau@arm.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190618081013.13638-3-james.qian.wang@arm.com
parent ed22c6d9
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
* Author: James.Qian.Wang <james.qian.wang@arm.com> * Author: James.Qian.Wang <james.qian.wang@arm.com>
* *
*/ */
#include <drm/drm_print.h>
#include "d71_dev.h" #include "d71_dev.h"
#include "komeda_kms.h" #include "komeda_kms.h"
#include "malidp_io.h" #include "malidp_io.h"
...@@ -1064,6 +1062,10 @@ static void d71_timing_ctrlr_update(struct komeda_component *c, ...@@ -1064,6 +1062,10 @@ static void d71_timing_ctrlr_update(struct komeda_component *c,
/* configure bs control register */ /* configure bs control register */
value = BS_CTRL_EN | BS_CTRL_VM; 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); malidp_write32(reg, BLK_CONTROL, value);
} }
......
...@@ -28,7 +28,7 @@ static void komeda_crtc_update_clock_ratio(struct komeda_crtc_state *kcrtc_st) ...@@ -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; 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); kcrtc_st->clock_ratio = div64_u64(aclk << 32, pxlclk);
} }
...@@ -74,14 +74,6 @@ komeda_crtc_atomic_check(struct drm_crtc *crtc, ...@@ -74,14 +74,6 @@ komeda_crtc_atomic_check(struct drm_crtc *crtc,
return 0; 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 /* For active a crtc, mainly need two parts of preparation
* 1. adjust display operation mode. * 1. adjust display operation mode.
* 2. enable needed clk * 2. enable needed clk
...@@ -118,7 +110,7 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc) ...@@ -118,7 +110,7 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc)
* to enable it again. * to enable it again.
*/ */
if (new_mode != KOMEDA_MODE_DUAL_DISP) { 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) if (err)
DRM_ERROR("failed to set aclk.\n"); DRM_ERROR("failed to set aclk.\n");
err = clk_prepare_enable(mdev->aclk); err = clk_prepare_enable(mdev->aclk);
...@@ -342,29 +334,58 @@ komeda_crtc_atomic_flush(struct drm_crtc *crtc, ...@@ -342,29 +334,58 @@ komeda_crtc_atomic_flush(struct drm_crtc *crtc,
komeda_crtc_do_flush(crtc, old); 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 static enum drm_mode_status
komeda_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *m) komeda_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *m)
{ {
struct komeda_dev *mdev = crtc->dev->dev_private; struct komeda_dev *mdev = crtc->dev->dev_private;
struct komeda_crtc *kcrtc = to_kcrtc(crtc); struct komeda_crtc *kcrtc = to_kcrtc(crtc);
struct komeda_pipeline *master = kcrtc->master; struct komeda_pipeline *master = kcrtc->master;
long mode_clk, pxlclk; unsigned long min_pxlclk, min_aclk;
if (m->flags & DRM_MODE_FLAG_INTERLACE) if (m->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE; return MODE_NO_INTERLACE;
mode_clk = m->clock * 1000; min_pxlclk = m->clock * 1000;
pxlclk = clk_round_rate(master->pxlclk, mode_clk); if (master->dual_link)
if (pxlclk != mode_clk) { min_pxlclk /= 2;
DRM_DEBUG_ATOMIC("pxlclk doesn't support %ld Hz\n", mode_clk);
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; return MODE_NOCLOCK;
} }
/* main engine clock must be faster than pxlclk*/ min_aclk = komeda_calc_min_aclk_rate(to_kcrtc(crtc), min_pxlclk);
if (clk_round_rate(mdev->aclk, mode_clk) < pxlclk) { if (clk_round_rate(mdev->aclk, min_aclk) < min_aclk) {
DRM_DEBUG_ATOMIC("engine clk can't satisfy the requirement of %s-clk: %ld.\n", DRM_DEBUG_ATOMIC("engine clk can't satisfy the requirement of %s-clk: %lu.\n",
m->name, pxlclk); m->name, min_pxlclk);
return MODE_CLOCK_HIGH; return MODE_CLOCK_HIGH;
} }
...@@ -380,6 +401,14 @@ static bool komeda_crtc_mode_fixup(struct drm_crtc *crtc, ...@@ -380,6 +401,14 @@ static bool komeda_crtc_mode_fixup(struct drm_crtc *crtc,
unsigned long clk_rate; unsigned long clk_rate;
drm_mode_set_crtcinfo(adjusted_mode, 0); 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; clk_rate = adjusted_mode->crtc_clock * 1000;
/* crtc_clock will be used as the komeda output pixel clock */ /* 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, ...@@ -492,10 +521,8 @@ int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
else else
sprintf(str, "None"); sprintf(str, "None");
DRM_INFO("crtc%d: master(pipe-%d) slave(%s) output: %s.\n", DRM_INFO("CRTC-%d: master(pipe-%d) slave(%s).\n",
kms->n_crtcs, master->id, str, kms->n_crtcs, master->id, str);
master->of_output_dev ?
master->of_output_dev->full_name : "None");
kms->n_crtcs++; kms->n_crtcs++;
} }
......
...@@ -121,11 +121,14 @@ static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np) ...@@ -121,11 +121,14 @@ static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np)
pipe->pxlclk = clk; pipe->pxlclk = clk;
/* enum ports */ /* enum ports */
pipe->of_output_dev = pipe->of_output_links[0] =
of_graph_get_remote_node(np, KOMEDA_OF_PORT_OUTPUT, 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 = pipe->of_output_port =
of_graph_get_port_by_id(np, KOMEDA_OF_PORT_OUTPUT); 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; pipe->of_node = np;
return 0; return 0;
......
...@@ -83,11 +83,12 @@ static int compare_of(struct device *dev, void *data) ...@@ -83,11 +83,12 @@ static int compare_of(struct device *dev, void *data)
static void komeda_add_slave(struct device *master, static void komeda_add_slave(struct device *master,
struct component_match **match, struct component_match **match,
struct device_node *np, int port) struct device_node *np,
u32 port, u32 endpoint)
{ {
struct device_node *remote; struct device_node *remote;
remote = of_graph_get_remote_node(np, port, 0); remote = of_graph_get_remote_node(np, port, endpoint);
if (remote) { if (remote) {
drm_of_component_match_add(master, match, compare_of, remote); drm_of_component_match_add(master, match, compare_of, remote);
of_node_put(remote); of_node_put(remote);
...@@ -108,7 +109,8 @@ static int komeda_platform_probe(struct platform_device *pdev) ...@@ -108,7 +109,8 @@ static int komeda_platform_probe(struct platform_device *pdev)
continue; continue;
/* add connector */ /* 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); return component_master_add_with_match(dev, &komeda_master_ops, match);
......
...@@ -166,7 +166,7 @@ static inline bool has_flip_h(u32 rot) ...@@ -166,7 +166,7 @@ static inline bool has_flip_h(u32 rot)
return !!(rotation & DRM_MODE_REFLECT_X); 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); int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
......
...@@ -54,7 +54,8 @@ void komeda_pipeline_destroy(struct komeda_dev *mdev, ...@@ -54,7 +54,8 @@ void komeda_pipeline_destroy(struct komeda_dev *mdev,
clk_put(pipe->pxlclk); 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_output_port);
of_node_put(pipe->of_node); of_node_put(pipe->of_node);
...@@ -246,9 +247,15 @@ static void komeda_pipeline_dump(struct komeda_pipeline *pipe) ...@@ -246,9 +247,15 @@ static void komeda_pipeline_dump(struct komeda_pipeline *pipe)
struct komeda_component *c; struct komeda_component *c;
int id; 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->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) { dp_for_each_set_bit(id, pipe->avail_comps) {
c = komeda_pipeline_get_component(pipe, id); c = komeda_pipeline_get_component(pipe, id);
...@@ -305,6 +312,12 @@ static void komeda_pipeline_assemble(struct komeda_pipeline *pipe) ...@@ -305,6 +312,12 @@ static void komeda_pipeline_assemble(struct komeda_pipeline *pipe)
layer->right = komeda_get_layer_split_right_layer(pipe, layer); 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 /* if pipeline_A accept another pipeline_B's component as input, treat
......
...@@ -416,8 +416,10 @@ struct komeda_pipeline { ...@@ -416,8 +416,10 @@ struct komeda_pipeline {
struct device_node *of_node; struct device_node *of_node;
/** @of_output_port: pipeline output port */ /** @of_output_port: pipeline output port */
struct device_node *of_output_port; struct device_node *of_output_port;
/** @of_output_dev: output connector device node */ /** @of_output_links: output connector device nodes */
struct device_node *of_output_dev; struct device_node *of_output_links[2];
/** @dual_link: true if of_output_links[0] and [1] are both valid */
bool dual_link;
}; };
/** /**
......
...@@ -473,7 +473,7 @@ komeda_scaler_check_cfg(struct komeda_scaler *scaler, ...@@ -473,7 +473,7 @@ komeda_scaler_check_cfg(struct komeda_scaler *scaler,
err = pipe->funcs->downscaling_clk_check(pipe, err = pipe->funcs->downscaling_clk_check(pipe,
&kcrtc_st->base.adjusted_mode, &kcrtc_st->base.adjusted_mode,
komeda_calc_aclk(kcrtc_st), dflow); komeda_crtc_get_aclk(kcrtc_st), dflow);
if (err) { if (err) {
DRM_DEBUG_ATOMIC("aclk can't satisfy the clock requirement of the downscaling\n"); DRM_DEBUG_ATOMIC("aclk can't satisfy the clock requirement of the downscaling\n");
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment