diff mbox series

[v1,5/6] drm/komeda: Add engine clock requirement check for the downscaling

Message ID 20190415070828.24075-6-james.qian.wang@arm.com (mailing list archive)
State New, archived
Headers show
Series Added scaler support for komeda | expand

Commit Message

James Qian Wang April 15, 2019, 7:09 a.m. UTC
For downscaling there is a restriction, the downscaling needed engine
clock can not acceed the real engine clock, and the clock requirement
mostly depend on the specific HW, to solve this problem:
1. Add a pipeline func - downscaling_clk_check for CORE to query the real
   HW if downscaling can be supported.
2. Add new property clock ratio which is the ratio of:
     (mclk << 32) / pxlclk
   then User driver can use this ratio to do the clock check to avoid post
   an invalid downscaling to kernel.

Signed-off-by: James Qian Wang (Arm Technology China) <james.qian.wang@arm.com>
---
 .../arm/display/komeda/d71/d71_component.c    | 45 +++++++++++++
 .../gpu/drm/arm/display/komeda/d71/d71_dev.c  |  2 +-
 .../gpu/drm/arm/display/komeda/d71/d71_dev.h  |  2 +
 .../gpu/drm/arm/display/komeda/komeda_crtc.c  | 67 ++++++++++++++++++-
 .../gpu/drm/arm/display/komeda/komeda_kms.h   |  8 +++
 .../drm/arm/display/komeda/komeda_pipeline.h  |  9 ++-
 .../display/komeda/komeda_pipeline_state.c    | 17 ++++-
 7 files changed, 144 insertions(+), 6 deletions(-)
diff mbox series

Patch

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 5a4c8fd122da..bf214dc9e372 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_component.c
@@ -677,6 +677,47 @@  static int d71_scaler_init(struct d71_dev *d71,
 	return 0;
 }
 
+static int d71_downscaling_clk_check(struct komeda_pipeline *pipe,
+				     struct drm_display_mode *mode,
+				     unsigned long mclk_rate,
+				     struct komeda_data_flow_cfg *dflow)
+{
+	u32 h_in = dflow->in_w;
+	u32 v_in = dflow->in_h;
+	u32 v_out = dflow->out_h;
+	u64 fraction, denominator;
+
+	/* D71 downscaling must satisfy the following equation
+	 *
+	 *   MCLK                   h_in * v_in
+	 * ------- >= ---------------------------------------------
+	 *  PXLCLK     (h_total - (1 + 2 * v_in / v_out)) * v_out
+	 *
+	 * In only horizontal downscaling situation, the right side should be
+	 * multiplied by (h_total - 3) / (h_active - 3), then equation becomes
+	 *
+	 *   MCLK          h_in
+	 * ------- >= ----------------
+	 *  PXLCLK     (h_active - 3)
+	 *
+	 * To avoid precision lost the equation 1 will be convert to:
+	 *
+	 *   MCLK             h_in * v_in
+	 * ------- >= -----------------------------------
+	 *  PXLCLK     (h_total -1 ) * v_out -  2 * v_in
+	 */
+	if (v_in == v_out) {
+		fraction = h_in;
+		denominator = mode->hdisplay - 3;
+	} else {
+		fraction = h_in * v_in;
+		denominator = (mode->htotal - 1) * v_out -  2 * v_in;
+	}
+
+	return mclk_rate * denominator >= mode->clock * 1000 * fraction ?
+	       0 : -EINVAL;
+}
+
 static void d71_improc_update(struct komeda_component *c,
 			      struct komeda_component_state *state)
 {
@@ -938,3 +979,7 @@  int d71_probe_block(struct d71_dev *d71,
 
 	return err;
 }
+
+struct komeda_pipeline_funcs d71_pipeline_funcs = {
+	.downscaling_clk_check = d71_downscaling_clk_check,
+};
diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
index 34506ef7ad40..dd99e06180fd 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
@@ -390,7 +390,7 @@  static int d71_enum_resources(struct komeda_dev *mdev)
 
 	for (i = 0; i < d71->num_pipelines; i++) {
 		pipe = komeda_pipeline_add(mdev, sizeof(struct d71_pipeline),
-					   NULL);
+					   &d71_pipeline_funcs);
 		if (IS_ERR(pipe)) {
 			err = PTR_ERR(pipe);
 			goto err_cleanup;
diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h
index 7465c57d9774..40f87955ee16 100644
--- a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h
+++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.h
@@ -43,6 +43,8 @@  struct d71_dev {
 
 #define to_d71_pipeline(x)	container_of(x, struct d71_pipeline, base)
 
+extern struct komeda_pipeline_funcs d71_pipeline_funcs;
+
 int d71_probe_block(struct d71_dev *d71,
 		    struct block_header *blk, u32 __iomem *reg);
 void d71_read_block_header(u32 __iomem *reg, struct block_header *blk);
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
index c0e98605def8..817ae892f7b4 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
@@ -18,6 +18,22 @@ 
 #include "komeda_dev.h"
 #include "komeda_kms.h"
 
+static void komeda_crtc_update_clock_ratio(struct komeda_crtc_state *kcrtc_st)
+{
+	u64 pxlclk, mclk;
+
+	if (!kcrtc_st->base.active) {
+		kcrtc_st->clock_ratio = 0;
+		return;
+	}
+
+	pxlclk = kcrtc_st->base.adjusted_mode.clock * 1000;
+	mclk = komeda_calc_mclk(kcrtc_st) << 32;
+
+	do_div(mclk, pxlclk);
+	kcrtc_st->clock_ratio = mclk;
+}
+
 /**
  * komeda_crtc_atomic_check - build display output data flow
  * @crtc: DRM crtc
@@ -38,6 +54,9 @@  komeda_crtc_atomic_check(struct drm_crtc *crtc,
 	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(state);
 	int err;
 
+	if (drm_atomic_crtc_needs_modeset(state))
+		komeda_crtc_update_clock_ratio(kcrtc_st);
+
 	if (state->active) {
 		err = komeda_build_display_data_flow(kcrtc, kcrtc_st);
 		if (err)
@@ -52,11 +71,12 @@  komeda_crtc_atomic_check(struct drm_crtc *crtc,
 	return 0;
 }
 
-u32 komeda_calc_mclk(struct komeda_crtc_state *kcrtc_st)
+unsigned long komeda_calc_mclk(struct komeda_crtc_state *kcrtc_st)
 {
-	unsigned long mclk = kcrtc_st->base.adjusted_mode.clock * 1000;
+	struct komeda_dev *mdev = kcrtc_st->base.crtc->dev->dev_private;
+	unsigned long pxlclk = kcrtc_st->base.adjusted_mode.clock;
 
-	return mclk;
+	return clk_round_rate(mdev->mclk, pxlclk * 1000);
 }
 
 /* For active a crtc, mainly need two parts of preparation
@@ -404,6 +424,7 @@  komeda_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
 	__drm_atomic_helper_crtc_duplicate_state(crtc, &new->base);
 
 	new->affected_pipes = old->active_pipes;
+	new->clock_ratio = old->clock_ratio;
 
 	return &new->base;
 }
@@ -432,6 +453,25 @@  static void komeda_crtc_vblank_disable(struct drm_crtc *crtc)
 	mdev->funcs->on_off_vblank(mdev, kcrtc->master->id, false);
 }
 
+static int
+komeda_crtc_atomic_get_property(struct drm_crtc *crtc,
+				const struct drm_crtc_state *state,
+				struct drm_property *property, uint64_t *val)
+{
+	struct komeda_crtc *kcrtc = to_kcrtc(crtc);
+	struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(state);
+
+	if (property == kcrtc->clock_ratio_property) {
+		*val = kcrtc_st->clock_ratio;
+		pr_info("Get property: %llu.\n", kcrtc_st->clock_ratio);
+	} else {
+		DRM_DEBUG_DRIVER("Unknown property %s\n", property->name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static const struct drm_crtc_funcs komeda_crtc_funcs = {
 	.gamma_set		= drm_atomic_helper_legacy_gamma_set,
 	.destroy		= drm_crtc_cleanup,
@@ -442,6 +482,7 @@  static const struct drm_crtc_funcs komeda_crtc_funcs = {
 	.atomic_destroy_state	= komeda_crtc_atomic_destroy_state,
 	.enable_vblank		= komeda_crtc_vblank_enable,
 	.disable_vblank		= komeda_crtc_vblank_disable,
+	.atomic_get_property	= komeda_crtc_atomic_get_property,
 };
 
 int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
@@ -477,6 +518,22 @@  int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
 	return 0;
 }
 
+static int komeda_crtc_create_clock_ratio_property(struct komeda_crtc *kcrtc)
+{
+	struct drm_crtc *crtc = &kcrtc->base;
+	struct drm_property *prop;
+
+	prop = drm_property_create_range(crtc->dev, DRM_MODE_PROP_ATOMIC,
+					 "CLOCK_RATIO", 0, U64_MAX);
+	if (!prop)
+		return -ENOMEM;
+
+	drm_object_attach_property(&crtc->base, prop, 0);
+	kcrtc->clock_ratio_property = prop;
+
+	return 0;
+}
+
 static struct drm_plane *
 get_crtc_primary(struct komeda_kms_dev *kms, struct komeda_crtc *crtc)
 {
@@ -513,6 +570,10 @@  static int komeda_crtc_add(struct komeda_kms_dev *kms,
 
 	crtc->port = kcrtc->master->of_output_port;
 
+	err = komeda_crtc_create_clock_ratio_property(kcrtc);
+	if (err)
+		return err;
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
index f16e9e577593..db59a9042beb 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h
@@ -79,6 +79,9 @@  struct komeda_crtc {
 
 	/** @disable_done: this flip_done is for tracing the disable */
 	struct completion *disable_done;
+
+	/** @clock_ratio_property: property for ratio of (mclk << 32)/pxlclk */
+	struct drm_property *clock_ratio_property;
 };
 
 /**
@@ -101,6 +104,9 @@  struct komeda_crtc_state {
 	 * the active pipelines in once display instance
 	 */
 	u32 active_pipes;
+
+	/** @clock_ratio: ratio of (mclk << 32)/pxlclk */
+	u64 clock_ratio;
 };
 
 /** struct komeda_kms_dev - for gather KMS related things */
@@ -142,6 +148,8 @@  is_only_changed_connector(struct drm_crtc_state *st, struct drm_connector *conn)
 	return BIT(drm_connector_index(conn)) == changed_connectors;
 }
 
+unsigned long komeda_calc_mclk(struct komeda_crtc_state *kcrtc_st);
+
 int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
 
 int komeda_kms_add_crtcs(struct komeda_kms_dev *kms, struct komeda_dev *mdev);
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
index bfad7d03f801..b26c8552a2d1 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h
@@ -316,8 +316,15 @@  struct komeda_data_flow_cfg {
 	u8 needs_scaling : 1;
 };
 
-/** struct komeda_pipeline_funcs */
+/* struct komeda_pipeline_funcs */
 struct komeda_pipeline_funcs {
+	/* check if the mclk (main engine clock) can satisfy the clock
+	 * requirements of the downscaling that specified by dflow
+	 */
+	int (*downscaling_clk_check)(struct komeda_pipeline *pipe,
+				     struct drm_display_mode *mode,
+				     unsigned long mclk_rate,
+				     struct komeda_data_flow_cfg *dflow);
 	/* dump_register: Optional, dump registers to seq_file */
 	void (*dump_register)(struct komeda_pipeline *pipe,
 			      struct seq_file *sf);
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 c6f19969fc10..53eb40941281 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c
@@ -385,6 +385,7 @@  static bool scaling_ratio_valid(u32 size_in, u32 size_out,
 
 static int
 komeda_scaler_check_cfg(struct komeda_scaler *scaler,
+			struct komeda_crtc_state *kcrtc_st,
 			struct komeda_data_flow_cfg *dflow)
 {
 	u32 hsize_in, vsize_in, hsize_out, vsize_out;
@@ -426,6 +427,20 @@  komeda_scaler_check_cfg(struct komeda_scaler *scaler,
 		DRM_DEBUG_ATOMIC("Invalid vertical scaling ratio");
 		return -EINVAL;
 	}
+
+	if (hsize_in > hsize_out || vsize_in > vsize_out) {
+		struct komeda_pipeline *pipe = scaler->base.pipeline;
+		int err;
+
+		err = pipe->funcs->downscaling_clk_check(pipe,
+					&kcrtc_st->base.adjusted_mode,
+					komeda_calc_mclk(kcrtc_st), dflow);
+		if (err) {
+			DRM_DEBUG_ATOMIC("mclk can't satisfy the clock requirement of the downscaling");
+			return err;
+		}
+	}
+
 	return 0;
 }
 
@@ -450,7 +465,7 @@  komeda_scaler_validate(void *user,
 		return -EINVAL;
 	}
 
-	err = komeda_scaler_check_cfg(scaler, dflow);
+	err = komeda_scaler_check_cfg(scaler, kcrtc_st, dflow);
 	if (err)
 		return err;