diff mbox series

[12/12] drm: mali-dp: Check the mclk rate and allow up/down scaling

Message ID 20170421091848.4666-13-Liviu.Dudau@arm.com
State New
Headers show
Series drm/mali-dp: Collate the pending patches in a series | expand

Commit Message

Liviu Dudau April 21, 2017, 9:18 a.m. UTC
From: Mihail Atanassov <mihail.atanassov@arm.com>

When downscaling, mclk needs to be sufficiently higher than pxlclk in
order to be able to fetch the higher-resolution data and produce output
pixels. When not scaling, or when upscaling, mclk can be equal to
pxlclk. Since the driver doesn't control mclk, just ensure that the
requirement is satisfied with the current clock rate.

Signed-off-by: Mihail Atanassov <mihail.atanassov@arm.com>
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
---
 drivers/gpu/drm/arm/malidp_crtc.c | 23 ++++++-------
 drivers/gpu/drm/arm/malidp_hw.c   | 70 +++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/arm/malidp_hw.h   |  4 +++
 3 files changed, 85 insertions(+), 12 deletions(-)
diff mbox series

Patch

diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c
index b0f0365efd23..19f1f3b34691 100644
--- a/drivers/gpu/drm/arm/malidp_crtc.c
+++ b/drivers/gpu/drm/arm/malidp_crtc.c
@@ -36,13 +36,6 @@  static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc,
 	long rate, req_rate = mode->crtc_clock * 1000;
 
 	if (req_rate) {
-		rate = clk_round_rate(hwdev->mclk, req_rate);
-		if (rate < req_rate) {
-			DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n",
-					 mode->crtc_clock);
-			return false;
-		}
-
 		rate = clk_round_rate(hwdev->pxlclk, req_rate);
 		if (rate != req_rate) {
 			DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
@@ -250,17 +243,21 @@  static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc,
 static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
 					    struct drm_crtc_state *state)
 {
+	struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
+	struct malidp_hw_device *hwdev = malidp->dev;
 	struct malidp_crtc_state *cs = to_malidp_crtc_state(state);
 	struct malidp_se_config *s = &cs->scaler_config;
 	struct drm_plane *plane;
+	struct videomode vm;
 	const struct drm_plane_state *pstate;
 	u32 h_upscale_factor = 0; /* U16.16 */
 	u32 v_upscale_factor = 0; /* U16.16 */
 	u8 scaling = cs->scaled_planes_mask;
+	int ret;
 
 	if (!scaling) {
 		s->scale_enable = false;
-		return 0;
+		goto mclk_calc;
 	}
 
 	/* The scaling engine can only handle one plane at a time. */
@@ -284,10 +281,6 @@  static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
 		h_upscale_factor = (u32)(crtc_w / pstate->src_w);
 		v_upscale_factor = (u32)(crtc_h / pstate->src_h);
 
-		/* Downscaling won't work when mclk == pxlclk. */
-		if (!(h_upscale_factor >> 16) || !(v_upscale_factor >> 16))
-			return -EINVAL;
-
 		s->enhancer_enable = ((h_upscale_factor >> 16) >= 2 ||
 				      (v_upscale_factor >> 16) >= 2);
 
@@ -323,6 +316,12 @@  static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
 	s->scale_enable = true;
 	s->hcoeff = malidp_se_select_coeffs(h_upscale_factor);
 	s->vcoeff = malidp_se_select_coeffs(v_upscale_factor);
+
+mclk_calc:
+	drm_display_mode_to_videomode(&state->adjusted_mode, &vm);
+	ret = hwdev->se_calc_mclk(hwdev, s, &vm);
+	if (ret < 0)
+		return -EINVAL;
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
index e444c23a8261..28360b8542f7 100644
--- a/drivers/gpu/drm/arm/malidp_hw.c
+++ b/drivers/gpu/drm/arm/malidp_hw.c
@@ -12,6 +12,7 @@ 
  * in an attempt to provide to the rest of the driver code a unified view
  */
 
+#include <linux/clk.h>
 #include <linux/types.h>
 #include <linux/io.h>
 #include <drm/drmP.h>
@@ -334,6 +335,39 @@  static int malidp500_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
 	return 0;
 }
 
+static long malidp500_se_calc_mclk(struct malidp_hw_device *hwdev,
+				   struct malidp_se_config *se_config,
+				   struct videomode *vm)
+{
+	unsigned long mclk;
+	unsigned long pxlclk = vm->pixelclock; /* Hz */
+	unsigned long htotal = vm->hactive + vm->hfront_porch +
+			       vm->hback_porch + vm->hsync_len;
+	unsigned long input_size = se_config->input_w * se_config->input_h;
+	unsigned long a = 10;
+	long ret;
+
+	/*
+	 * mclk = max(a, 1.5) * pxlclk
+	 *
+	 * To avoid float calculaiton, using 15 instead of 1.5 and div by
+	 * 10 to get mclk.
+	 */
+	if (se_config->scale_enable) {
+		a = 15 * input_size / (htotal * se_config->output_h);
+		if (a < 15)
+			a = 15;
+	}
+	mclk = a * pxlclk / 10;
+	ret = clk_get_rate(hwdev->mclk);
+	if (ret < mclk) {
+		DRM_DEBUG_DRIVER("mclk requirement of %lu kHz can't be met.\n",
+				 mclk / 1000);
+		return -EINVAL;
+	}
+	return ret;
+}
+
 static int malidp550_query_hw(struct malidp_hw_device *hwdev)
 {
 	u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
@@ -521,6 +555,39 @@  static int malidp550_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
 	return 0;
 }
 
+static long malidp550_se_calc_mclk(struct malidp_hw_device *hwdev,
+				   struct malidp_se_config *se_config,
+				   struct videomode *vm)
+{
+	unsigned long mclk;
+	unsigned long pxlclk = vm->pixelclock;
+	unsigned long htotal = vm->hactive + vm->hfront_porch +
+			       vm->hback_porch + vm->hsync_len;
+	unsigned long numerator = 1, denominator = 1;
+	long ret;
+
+	if (se_config->scale_enable) {
+		numerator = max(se_config->input_w, se_config->output_w) *
+			    se_config->input_h;
+		numerator += se_config->output_w *
+			     (se_config->output_h -
+			      min(se_config->input_h, se_config->output_h));
+		denominator = (htotal - 2) * se_config->output_h;
+	}
+
+	/* mclk can't be slower than pxlclk. */
+	if (numerator < denominator)
+		numerator = denominator = 1;
+	mclk = (pxlclk * numerator) / denominator;
+	ret = clk_get_rate(hwdev->mclk);
+	if (ret < mclk) {
+		DRM_DEBUG_DRIVER("mclk requirement of %lu kHz can't be met.\n",
+				 mclk / 1000);
+		return -EINVAL;
+	}
+	return ret;
+}
+
 static int malidp650_query_hw(struct malidp_hw_device *hwdev)
 {
 	u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
@@ -586,6 +653,7 @@  const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
 		.modeset = malidp500_modeset,
 		.rotmem_required = malidp500_rotmem_required,
 		.se_set_scaling_coeffs = malidp500_se_set_scaling_coeffs,
+		.se_calc_mclk = malidp500_se_calc_mclk,
 		.features = MALIDP_DEVICE_LV_HAS_3_STRIDES,
 	},
 	[MALIDP_550] = {
@@ -622,6 +690,7 @@  const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
 		.modeset = malidp550_modeset,
 		.rotmem_required = malidp550_rotmem_required,
 		.se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
+		.se_calc_mclk = malidp550_se_calc_mclk,
 		.features = 0,
 	},
 	[MALIDP_650] = {
@@ -659,6 +728,7 @@  const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
 		.modeset = malidp550_modeset,
 		.rotmem_required = malidp550_rotmem_required,
 		.se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
+		.se_calc_mclk = malidp550_se_calc_mclk,
 		.features = 0,
 	},
 };
diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h
index a93ae0a951a3..849ad9a30c3a 100644
--- a/drivers/gpu/drm/arm/malidp_hw.h
+++ b/drivers/gpu/drm/arm/malidp_hw.h
@@ -177,6 +177,10 @@  struct malidp_hw_device {
 				     struct malidp_se_config *se_config,
 				     struct malidp_se_config *old_config);
 
+	long (*se_calc_mclk)(struct malidp_hw_device *hwdev,
+			     struct malidp_se_config *se_config,
+			     struct videomode *vm);
+
 	u8 features;
 
 	u8 min_line_size;