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",
 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. */
                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);
 
        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;
 }
 
 
  * 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>
        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);
        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);
                .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] = {
                .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] = {
                .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,
        },
 };