drm: mali-dp: Check the mclk rate and allow up/down scaling
authorMihail Atanassov <mihail.atanassov@arm.com>
Mon, 13 Feb 2017 15:09:01 +0000 (15:09 +0000)
committerLiviu Dudau <Liviu.Dudau@arm.com>
Mon, 24 Apr 2017 12:28:09 +0000 (13:28 +0100)
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
drivers/gpu/drm/arm/malidp_hw.c
drivers/gpu/drm/arm/malidp_hw.h

index b0f0365efd235f570c1fd006fc8707f596e8a6fd..19f1f3b346912c591850148775bd8c6542daf81c 100644 (file)
@@ -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;
 }
 
index e444c23a8261f492be8e7ca8ca6b04ca5a90f348..28360b8542f724de1d049884ae1b5dfa4a7b9b0e 100644 (file)
@@ -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,
        },
 };
index a93ae0a951a3ddf2691758fdd8a59921ef9e9e88..849ad9a30c3af4b31e98526b954e2b0819275534 100644 (file)
@@ -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;