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

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
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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);
}
+50 −23
Original line number Diff line number Diff line
@@ -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++;
	}
+4 −1
Original line number Diff line number Diff line
@@ -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;
+5 −3
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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);

Loading