drm: mali-dp: Add plane upscaling support
authorMihail Atanassov <mihail.atanassov@arm.com>
Mon, 13 Feb 2017 15:14:05 +0000 (15:14 +0000)
committerLiviu Dudau <Liviu.Dudau@arm.com>
Mon, 24 Apr 2017 12:28:08 +0000 (13:28 +0100)
Enable the scaling engine for upscaling a single plane using the polyphase
scaler. No image enhancement support or downscaling yet*, and composition
result scaling is not implemented.

* Downscaling a plane requires mclk > pxlclk.

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_drv.c
drivers/gpu/drm/arm/malidp_drv.h
drivers/gpu/drm/arm/malidp_hw.c
drivers/gpu/drm/arm/malidp_hw.h
drivers/gpu/drm/arm/malidp_planes.c
drivers/gpu/drm/arm/malidp_regs.h

index 8f2e1ae51ba0d697ce1010ca6cc471c207bfb268..ceee2a77bfb43dc9c6fca63798845fb8f7b2962e 100644 (file)
@@ -247,6 +247,82 @@ static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc,
        return 0;
 }
 
+static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc,
+                                           struct drm_crtc_state *state)
+{
+       struct malidp_crtc_state *cs = to_malidp_crtc_state(state);
+       struct malidp_se_config *s = &cs->scaler_config;
+       struct drm_plane *plane;
+       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;
+
+       if (!scaling) {
+               s->scale_enable = false;
+               return 0;
+       }
+
+       /* The scaling engine can only handle one plane at a time. */
+       if (scaling & (scaling - 1))
+               return -EINVAL;
+
+       drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
+               struct malidp_plane *mp = to_malidp_plane(plane);
+               u64 crtc_w, crtc_h;
+               u32 phase;
+
+               if (!(mp->layer->id & scaling))
+                       continue;
+
+               /*
+                * Convert crtc_[w|h] to U32.32, then divide by U16.16 src_[w|h]
+                * to get the U16.16 result.
+                */
+               crtc_w = (u64)pstate->crtc_w << 32;
+               crtc_h = (u64)pstate->crtc_h << 32;
+               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->input_w = pstate->src_w >> 16;
+               s->input_h = pstate->src_h >> 16;
+               s->output_w = pstate->crtc_w;
+               s->output_h = pstate->crtc_h;
+
+#define SE_N_PHASE 4
+#define SE_SHIFT_N_PHASE 12
+               /* Calculate initial_phase and delta_phase for horizontal. */
+               phase = s->input_w;
+               s->h_init_phase =
+                               ((phase << SE_N_PHASE) / s->output_w + 1) / 2;
+
+               phase = s->input_w;
+               phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
+               s->h_delta_phase = phase / s->output_w;
+
+               /* Same for vertical. */
+               phase = s->input_h;
+               s->v_init_phase =
+                               ((phase << SE_N_PHASE) / s->output_h + 1) / 2;
+
+               phase = s->input_h;
+               phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE);
+               s->v_delta_phase = phase / s->output_h;
+#undef SE_N_PHASE
+#undef SE_SHIFT_N_PHASE
+               s->plane_src_id = mp->layer->id;
+       }
+
+       s->scale_enable = true;
+       s->hcoeff = malidp_se_select_coeffs(h_upscale_factor);
+       s->vcoeff = malidp_se_select_coeffs(v_upscale_factor);
+       return 0;
+}
+
 static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
                                    struct drm_crtc_state *state)
 {
@@ -325,6 +401,7 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
 
        ret = malidp_crtc_atomic_check_gamma(crtc, state);
        ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state);
+       ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state);
 
        return ret;
 }
@@ -353,6 +430,9 @@ static struct drm_crtc_state *malidp_crtc_duplicate_state(struct drm_crtc *crtc)
               sizeof(state->gamma_coeffs));
        memcpy(state->coloradj_coeffs, old_state->coloradj_coeffs,
               sizeof(state->coloradj_coeffs));
+       memcpy(&state->scaler_config, &old_state->scaler_config,
+              sizeof(state->scaler_config));
+       state->scaled_planes_mask = 0;
 
        return &state->base;
 }
index 24bc96f9e91c1b36b57d1072d8e4f24eef4180c2..baca2dce1b92ac435067a514f192bf0e68b8d180 100644 (file)
@@ -110,6 +110,52 @@ void malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc,
        }
 }
 
+static void malidp_atomic_commit_se_config(struct drm_crtc *crtc,
+                                          struct drm_crtc_state *old_state)
+{
+       struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state);
+       struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state);
+       struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
+       struct malidp_hw_device *hwdev = malidp->dev;
+       struct malidp_se_config *s = &cs->scaler_config;
+       struct malidp_se_config *old_s = &old_cs->scaler_config;
+       u32 se_control = hwdev->map.se_base +
+                        ((hwdev->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ?
+                        0x10 : 0xC);
+       u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL;
+       u32 scr = se_control + MALIDP_SE_SCALING_CONTROL;
+       u32 val;
+
+       /* Set SE_CONTROL */
+       if (!s->scale_enable) {
+               val = malidp_hw_read(hwdev, se_control);
+               val &= ~MALIDP_SE_SCALING_EN;
+               malidp_hw_write(hwdev, val, se_control);
+               return;
+       }
+
+       hwdev->se_set_scaling_coeffs(hwdev, s, old_s);
+       val = malidp_hw_read(hwdev, se_control);
+       val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN;
+
+       val |= MALIDP_SE_RGBO_IF_EN;
+       malidp_hw_write(hwdev, val, se_control);
+
+       /* Set IN_SIZE & OUT_SIZE. */
+       val = MALIDP_SE_SET_V_SIZE(s->input_h) |
+             MALIDP_SE_SET_H_SIZE(s->input_w);
+       malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE);
+       val = MALIDP_SE_SET_V_SIZE(s->output_h) |
+             MALIDP_SE_SET_H_SIZE(s->output_w);
+       malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE);
+
+       /* Set phase regs. */
+       malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH);
+       malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH);
+       malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH);
+       malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH);
+}
+
 /*
  * set the "config valid" bit and wait until the hardware acts on it
  */
@@ -179,6 +225,7 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
        for_each_crtc_in_state(state, crtc, old_crtc_state, i) {
                malidp_atomic_commit_update_gamma(crtc, old_crtc_state);
                malidp_atomic_commit_update_coloradj(crtc, old_crtc_state);
+               malidp_atomic_commit_se_config(crtc, old_crtc_state);
        }
 
        drm_atomic_helper_commit_planes(drm, state, 0);
index 75070efda2fc820ef02d4f276e427d37a7c613a0..040311ffcaecc84bf08ef2f64b88f03719bbde5f 100644 (file)
@@ -53,6 +53,9 @@ struct malidp_crtc_state {
        struct drm_crtc_state base;
        u32 gamma_coeffs[MALIDP_COEFFTAB_NUM_COEFFS];
        u32 coloradj_coeffs[MALIDP_COLORADJ_NUM_COEFFS];
+       struct malidp_se_config scaler_config;
+       /* Bitfield of all the planes that have requested a scaled output. */
+       u8 scaled_planes_mask;
 };
 
 #define to_malidp_crtc_state(x) container_of(x, struct malidp_crtc_state, base)
index b8025b7c1aca18cee57bb5847850541fda13adec..e444c23a8261f492be8e7ca8ca6b04ca5a90f348 100644 (file)
@@ -86,6 +86,80 @@ static const struct malidp_layer malidp550_layers[] = {
        { DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE, MALIDP550_DE_LS_R1_STRIDE },
 };
 
+#define SE_N_SCALING_COEFFS    96
+static const u16 dp500_se_scaling_coeffs[][SE_N_SCALING_COEFFS] = {
+       [MALIDP_UPSCALING_COEFFS - 1] = {
+               0x0000, 0x0001, 0x0007, 0x0011, 0x001e, 0x002e, 0x003f, 0x0052,
+               0x0064, 0x0073, 0x007d, 0x0080, 0x007a, 0x006c, 0x0053, 0x002f,
+               0x0000, 0x3fc6, 0x3f83, 0x3f39, 0x3eea, 0x3e9b, 0x3e4f, 0x3e0a,
+               0x3dd4, 0x3db0, 0x3da2, 0x3db1, 0x3dde, 0x3e2f, 0x3ea5, 0x3f40,
+               0x0000, 0x00e5, 0x01ee, 0x0315, 0x0456, 0x05aa, 0x0709, 0x086c,
+               0x09c9, 0x0b15, 0x0c4a, 0x0d5d, 0x0e4a, 0x0f06, 0x0f91, 0x0fe5,
+               0x1000, 0x0fe5, 0x0f91, 0x0f06, 0x0e4a, 0x0d5d, 0x0c4a, 0x0b15,
+               0x09c9, 0x086c, 0x0709, 0x05aa, 0x0456, 0x0315, 0x01ee, 0x00e5,
+               0x0000, 0x3f40, 0x3ea5, 0x3e2f, 0x3dde, 0x3db1, 0x3da2, 0x3db0,
+               0x3dd4, 0x3e0a, 0x3e4f, 0x3e9b, 0x3eea, 0x3f39, 0x3f83, 0x3fc6,
+               0x0000, 0x002f, 0x0053, 0x006c, 0x007a, 0x0080, 0x007d, 0x0073,
+               0x0064, 0x0052, 0x003f, 0x002e, 0x001e, 0x0011, 0x0007, 0x0001
+       },
+       [MALIDP_DOWNSCALING_1_5_COEFFS - 1] = {
+               0x0059, 0x004f, 0x0041, 0x002e, 0x0016, 0x3ffb, 0x3fd9, 0x3fb4,
+               0x3f8c, 0x3f62, 0x3f36, 0x3f09, 0x3edd, 0x3eb3, 0x3e8d, 0x3e6c,
+               0x3e52, 0x3e3f, 0x3e35, 0x3e37, 0x3e46, 0x3e61, 0x3e8c, 0x3ec5,
+               0x3f0f, 0x3f68, 0x3fd1, 0x004a, 0x00d3, 0x0169, 0x020b, 0x02b8,
+               0x036e, 0x042d, 0x04f2, 0x05b9, 0x0681, 0x0745, 0x0803, 0x08ba,
+               0x0965, 0x0a03, 0x0a91, 0x0b0d, 0x0b75, 0x0bc6, 0x0c00, 0x0c20,
+               0x0c28, 0x0c20, 0x0c00, 0x0bc6, 0x0b75, 0x0b0d, 0x0a91, 0x0a03,
+               0x0965, 0x08ba, 0x0803, 0x0745, 0x0681, 0x05b9, 0x04f2, 0x042d,
+               0x036e, 0x02b8, 0x020b, 0x0169, 0x00d3, 0x004a, 0x3fd1, 0x3f68,
+               0x3f0f, 0x3ec5, 0x3e8c, 0x3e61, 0x3e46, 0x3e37, 0x3e35, 0x3e3f,
+               0x3e52, 0x3e6c, 0x3e8d, 0x3eb3, 0x3edd, 0x3f09, 0x3f36, 0x3f62,
+               0x3f8c, 0x3fb4, 0x3fd9, 0x3ffb, 0x0016, 0x002e, 0x0041, 0x004f
+       },
+       [MALIDP_DOWNSCALING_2_COEFFS - 1] = {
+               0x3f19, 0x3f03, 0x3ef0, 0x3edf, 0x3ed0, 0x3ec5, 0x3ebd, 0x3eb9,
+               0x3eb9, 0x3ebf, 0x3eca, 0x3ed9, 0x3eef, 0x3f0a, 0x3f2c, 0x3f52,
+               0x3f7f, 0x3fb0, 0x3fe8, 0x0026, 0x006a, 0x00b4, 0x0103, 0x0158,
+               0x01b1, 0x020d, 0x026c, 0x02cd, 0x032f, 0x0392, 0x03f4, 0x0455,
+               0x04b4, 0x051e, 0x0585, 0x05eb, 0x064c, 0x06a8, 0x06fe, 0x074e,
+               0x0796, 0x07d5, 0x080c, 0x0839, 0x085c, 0x0875, 0x0882, 0x0887,
+               0x0881, 0x0887, 0x0882, 0x0875, 0x085c, 0x0839, 0x080c, 0x07d5,
+               0x0796, 0x074e, 0x06fe, 0x06a8, 0x064c, 0x05eb, 0x0585, 0x051e,
+               0x04b4, 0x0455, 0x03f4, 0x0392, 0x032f, 0x02cd, 0x026c, 0x020d,
+               0x01b1, 0x0158, 0x0103, 0x00b4, 0x006a, 0x0026, 0x3fe8, 0x3fb0,
+               0x3f7f, 0x3f52, 0x3f2c, 0x3f0a, 0x3eef, 0x3ed9, 0x3eca, 0x3ebf,
+               0x3eb9, 0x3eb9, 0x3ebd, 0x3ec5, 0x3ed0, 0x3edf, 0x3ef0, 0x3f03
+       },
+       [MALIDP_DOWNSCALING_2_75_COEFFS - 1] = {
+               0x3f51, 0x3f60, 0x3f71, 0x3f84, 0x3f98, 0x3faf, 0x3fc8, 0x3fe3,
+               0x0000, 0x001f, 0x0040, 0x0064, 0x008a, 0x00b1, 0x00da, 0x0106,
+               0x0133, 0x0160, 0x018e, 0x01bd, 0x01ec, 0x021d, 0x024e, 0x0280,
+               0x02b2, 0x02e4, 0x0317, 0x0349, 0x037c, 0x03ad, 0x03df, 0x0410,
+               0x0440, 0x0468, 0x048f, 0x04b3, 0x04d6, 0x04f8, 0x0516, 0x0533,
+               0x054e, 0x0566, 0x057c, 0x0590, 0x05a0, 0x05ae, 0x05ba, 0x05c3,
+               0x05c9, 0x05c3, 0x05ba, 0x05ae, 0x05a0, 0x0590, 0x057c, 0x0566,
+               0x054e, 0x0533, 0x0516, 0x04f8, 0x04d6, 0x04b3, 0x048f, 0x0468,
+               0x0440, 0x0410, 0x03df, 0x03ad, 0x037c, 0x0349, 0x0317, 0x02e4,
+               0x02b2, 0x0280, 0x024e, 0x021d, 0x01ec, 0x01bd, 0x018e, 0x0160,
+               0x0133, 0x0106, 0x00da, 0x00b1, 0x008a, 0x0064, 0x0040, 0x001f,
+               0x0000, 0x3fe3, 0x3fc8, 0x3faf, 0x3f98, 0x3f84, 0x3f71, 0x3f60
+       },
+       [MALIDP_DOWNSCALING_4_COEFFS - 1] = {
+               0x0094, 0x00a9, 0x00be, 0x00d4, 0x00ea, 0x0101, 0x0118, 0x012f,
+               0x0148, 0x0160, 0x017a, 0x0193, 0x01ae, 0x01c8, 0x01e4, 0x01ff,
+               0x021c, 0x0233, 0x024a, 0x0261, 0x0278, 0x028f, 0x02a6, 0x02bd,
+               0x02d4, 0x02eb, 0x0302, 0x0319, 0x032f, 0x0346, 0x035d, 0x0374,
+               0x038a, 0x0397, 0x03a3, 0x03af, 0x03bb, 0x03c6, 0x03d1, 0x03db,
+               0x03e4, 0x03ed, 0x03f6, 0x03fe, 0x0406, 0x040d, 0x0414, 0x041a,
+               0x0420, 0x041a, 0x0414, 0x040d, 0x0406, 0x03fe, 0x03f6, 0x03ed,
+               0x03e4, 0x03db, 0x03d1, 0x03c6, 0x03bb, 0x03af, 0x03a3, 0x0397,
+               0x038a, 0x0374, 0x035d, 0x0346, 0x032f, 0x0319, 0x0302, 0x02eb,
+               0x02d4, 0x02bd, 0x02a6, 0x028f, 0x0278, 0x0261, 0x024a, 0x0233,
+               0x021c, 0x01ff, 0x01e4, 0x01c8, 0x01ae, 0x0193, 0x017a, 0x0160,
+               0x0148, 0x012f, 0x0118, 0x0101, 0x00ea, 0x00d4, 0x00be, 0x00a9
+       },
+};
+
 #define MALIDP_DE_DEFAULT_PREFETCH_START       5
 
 static int malidp500_query_hw(struct malidp_hw_device *hwdev)
@@ -211,6 +285,55 @@ static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16
        return w * drm_format_plane_cpp(fmt, 0) * 8;
 }
 
+static void malidp500_se_write_pp_coefftab(struct malidp_hw_device *hwdev,
+                                          u32 direction,
+                                          u16 addr,
+                                          u8 coeffs_id)
+{
+       int i;
+       u16 scaling_control = MALIDP500_SE_CONTROL + MALIDP_SE_SCALING_CONTROL;
+
+       malidp_hw_write(hwdev,
+                       direction | (addr & MALIDP_SE_COEFFTAB_ADDR_MASK),
+                       scaling_control + MALIDP_SE_COEFFTAB_ADDR);
+       for (i = 0; i < ARRAY_SIZE(dp500_se_scaling_coeffs); ++i)
+               malidp_hw_write(hwdev, MALIDP_SE_SET_COEFFTAB_DATA(
+                               dp500_se_scaling_coeffs[coeffs_id][i]),
+                               scaling_control + MALIDP_SE_COEFFTAB_DATA);
+}
+
+static int malidp500_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
+                                          struct malidp_se_config *se_config,
+                                          struct malidp_se_config *old_config)
+{
+       /* Get array indices into dp500_se_scaling_coeffs. */
+       u8 h = (u8)se_config->hcoeff - 1;
+       u8 v = (u8)se_config->vcoeff - 1;
+
+       if (WARN_ON(h >= ARRAY_SIZE(dp500_se_scaling_coeffs) ||
+                   v >= ARRAY_SIZE(dp500_se_scaling_coeffs)))
+               return -EINVAL;
+
+       if ((h == v) && (se_config->hcoeff != old_config->hcoeff ||
+                        se_config->vcoeff != old_config->vcoeff)) {
+               malidp500_se_write_pp_coefftab(hwdev,
+                                              (MALIDP_SE_V_COEFFTAB |
+                                               MALIDP_SE_H_COEFFTAB),
+                                              0, v);
+       } else {
+               if (se_config->vcoeff != old_config->vcoeff)
+                       malidp500_se_write_pp_coefftab(hwdev,
+                                                      MALIDP_SE_V_COEFFTAB,
+                                                      0, v);
+               if (se_config->hcoeff != old_config->hcoeff)
+                       malidp500_se_write_pp_coefftab(hwdev,
+                                                      MALIDP_SE_H_COEFFTAB,
+                                                      0, h);
+       }
+
+       return 0;
+}
+
 static int malidp550_query_hw(struct malidp_hw_device *hwdev)
 {
        u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
@@ -384,6 +507,20 @@ static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16
        return w * bytes_per_col;
 }
 
+static int malidp550_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
+                                          struct malidp_se_config *se_config,
+                                          struct malidp_se_config *old_config)
+{
+       u32 mask = MALIDP550_SE_CTL_VCSEL(MALIDP550_SE_CTL_SEL_MASK) |
+                  MALIDP550_SE_CTL_HCSEL(MALIDP550_SE_CTL_SEL_MASK);
+       u32 new_value = MALIDP550_SE_CTL_VCSEL(se_config->vcoeff) |
+                       MALIDP550_SE_CTL_HCSEL(se_config->hcoeff);
+
+       malidp_hw_clearbits(hwdev, mask, MALIDP550_SE_CONTROL);
+       malidp_hw_setbits(hwdev, new_value, MALIDP550_SE_CONTROL);
+       return 0;
+}
+
 static int malidp650_query_hw(struct malidp_hw_device *hwdev)
 {
        u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
@@ -448,6 +585,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
                .set_config_valid = malidp500_set_config_valid,
                .modeset = malidp500_modeset,
                .rotmem_required = malidp500_rotmem_required,
+               .se_set_scaling_coeffs = malidp500_se_set_scaling_coeffs,
                .features = MALIDP_DEVICE_LV_HAS_3_STRIDES,
        },
        [MALIDP_550] = {
@@ -483,6 +621,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
                .set_config_valid = malidp550_set_config_valid,
                .modeset = malidp550_modeset,
                .rotmem_required = malidp550_rotmem_required,
+               .se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
                .features = 0,
        },
        [MALIDP_650] = {
@@ -519,6 +658,7 @@ const struct malidp_hw_device malidp_device[MALIDP_MAX_DEVICES] = {
                .set_config_valid = malidp550_set_config_valid,
                .modeset = malidp550_modeset,
                .rotmem_required = malidp550_rotmem_required,
+               .se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
                .features = 0,
        },
 };
index 470fe71724a373a987de714f6a5dafb902ffa42b..52a65a8a056ff8001a5f7a1ab2912acc135e9442 100644 (file)
@@ -61,6 +61,25 @@ struct malidp_layer {
        u16 stride_offset;      /* Offset to the first stride register. */
 };
 
+enum malidp_scaling_coeff_set {
+       MALIDP_UPSCALING_COEFFS = 1,
+       MALIDP_DOWNSCALING_1_5_COEFFS = 2,
+       MALIDP_DOWNSCALING_2_COEFFS = 3,
+       MALIDP_DOWNSCALING_2_75_COEFFS = 4,
+       MALIDP_DOWNSCALING_4_COEFFS = 5,
+};
+
+struct malidp_se_config {
+       u8 scale_enable : 1;
+       u8 hcoeff : 3;
+       u8 vcoeff : 3;
+       u8 plane_src_id;
+       u16 input_w, input_h;
+       u16 output_w, output_h;
+       u32 h_init_phase, h_delta_phase;
+       u32 v_init_phase, v_delta_phase;
+};
+
 /* regmap features */
 #define MALIDP_REGMAP_HAS_CLEARIRQ     (1 << 0)
 
@@ -153,6 +172,10 @@ struct malidp_hw_device {
         */
        int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt);
 
+       int (*se_set_scaling_coeffs)(struct malidp_hw_device *hwdev,
+                                    struct malidp_se_config *se_config,
+                                    struct malidp_se_config *old_config);
+
        u8 features;
 
        u8 min_line_size;
@@ -250,6 +273,28 @@ static inline bool malidp_hw_pitch_valid(struct malidp_hw_device *hwdev,
        return !(pitch & (hwdev->map.bus_align_bytes - 1));
 }
 
+/* U16.16 */
+#define FP_1_00000     0x00010000      /* 1.0 */
+#define FP_0_66667     0x0000AAAA      /* 0.6667 = 1/1.5 */
+#define FP_0_50000     0x00008000      /* 0.5 = 1/2 */
+#define FP_0_36363     0x00005D17      /* 0.36363 = 1/2.75 */
+#define FP_0_25000     0x00004000      /* 0.25 = 1/4 */
+
+static inline enum malidp_scaling_coeff_set
+malidp_se_select_coeffs(u32 upscale_factor)
+{
+       return (upscale_factor >= FP_1_00000) ? MALIDP_UPSCALING_COEFFS :
+              (upscale_factor >= FP_0_66667) ? MALIDP_DOWNSCALING_1_5_COEFFS :
+              (upscale_factor >= FP_0_50000) ? MALIDP_DOWNSCALING_2_COEFFS :
+              (upscale_factor >= FP_0_36363) ? MALIDP_DOWNSCALING_2_75_COEFFS :
+              MALIDP_DOWNSCALING_4_COEFFS;
+}
+
+#undef FP_0_25000
+#undef FP_0_36363
+#undef FP_0_50000
+#undef FP_0_66667
+#undef FP_1_00000
 /*
  * background color components are defined as 12bits values,
  * they will be shifted right when stored on hardware that
index 8106e22f9349a72f0c2a3802dd7e4f3bb16e5d8e..814fda23ceade6159fab04455facbbff35ba65e7 100644 (file)
@@ -25,6 +25,9 @@
 #define MALIDP_LAYER_FORMAT            0x000
 #define MALIDP_LAYER_CONTROL           0x004
 #define   LAYER_ENABLE                 (1 << 0)
+#define   LAYER_FLOWCFG_MASK           7
+#define   LAYER_FLOWCFG(x)             (((x) & LAYER_FLOWCFG_MASK) << 1)
+#define     LAYER_FLOWCFG_SCALE_SE     3
 #define   LAYER_ROT_OFFSET             8
 #define   LAYER_H_FLIP                 (1 << 10)
 #define   LAYER_V_FLIP                 (1 << 11)
@@ -133,16 +136,50 @@ static const struct drm_plane_funcs malidp_de_plane_funcs = {
        .atomic_print_state = malidp_plane_atomic_print_state,
 };
 
+static int malidp_se_check_scaling(struct malidp_plane *mp,
+                                  struct drm_plane_state *state)
+{
+       struct drm_crtc_state *crtc_state =
+               drm_atomic_get_existing_crtc_state(state->state, state->crtc);
+       struct malidp_crtc_state *mc;
+       struct drm_rect clip = { 0 };
+       u32 src_w, src_h;
+       int ret;
+
+       if (!crtc_state)
+               return -EINVAL;
+
+       clip.x2 = crtc_state->adjusted_mode.hdisplay;
+       clip.y2 = crtc_state->adjusted_mode.vdisplay;
+       ret = drm_plane_helper_check_state(state, &clip, 0, INT_MAX, true, true);
+       if (ret)
+               return ret;
+
+       src_w = state->src_w >> 16;
+       src_h = state->src_h >> 16;
+       if ((state->crtc_w == src_w) && (state->crtc_h == src_h)) {
+               /* Scaling not necessary for this plane. */
+               mc->scaled_planes_mask &= ~(mp->layer->id);
+               return 0;
+       }
+
+       if (mp->layer->id & (DE_SMART | DE_GRAPHICS2))
+               return -EINVAL;
+
+       mc = to_malidp_crtc_state(crtc_state);
+
+       mc->scaled_planes_mask |= mp->layer->id;
+       /* Defer scaling requirements calculation to the crtc check. */
+       return 0;
+}
+
 static int malidp_de_plane_check(struct drm_plane *plane,
                                 struct drm_plane_state *state)
 {
        struct malidp_plane *mp = to_malidp_plane(plane);
        struct malidp_plane_state *ms = to_malidp_plane_state(state);
-       struct drm_crtc_state *crtc_state;
        struct drm_framebuffer *fb;
-       struct drm_rect clip = { 0 };
        int i, ret;
-       u32 src_w, src_h;
 
        if (!state->crtc || !state->fb)
                return 0;
@@ -163,9 +200,6 @@ static int malidp_de_plane_check(struct drm_plane *plane,
                }
        }
 
-       src_w = state->src_w >> 16;
-       src_h = state->src_h >> 16;
-
        if ((state->crtc_w > mp->hwdev->max_line_size) ||
            (state->crtc_h > mp->hwdev->max_line_size) ||
            (state->crtc_w < mp->hwdev->min_line_size) ||
@@ -182,22 +216,16 @@ static int malidp_de_plane_check(struct drm_plane *plane,
            (state->fb->pitches[1] != state->fb->pitches[2]))
                return -EINVAL;
 
+       ret = malidp_se_check_scaling(mp, state);
+       if (ret)
+               return ret;
+
        /* packed RGB888 / BGR888 can't be rotated or flipped */
        if (state->rotation != DRM_ROTATE_0 &&
            (fb->format->format == DRM_FORMAT_RGB888 ||
             fb->format->format == DRM_FORMAT_BGR888))
                return -EINVAL;
 
-       crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc);
-       clip.x2 = crtc_state->adjusted_mode.hdisplay;
-       clip.y2 = crtc_state->adjusted_mode.vdisplay;
-       ret = drm_plane_helper_check_state(state, &clip,
-                                          DRM_PLANE_HELPER_NO_SCALING,
-                                          DRM_PLANE_HELPER_NO_SCALING,
-                                          true, true);
-       if (ret)
-               return ret;
-
        ms->rotmem_size = 0;
        if (state->rotation & MALIDP_ROTATED_MASK) {
                int val;
@@ -302,6 +330,16 @@ static void malidp_de_plane_update(struct drm_plane *plane,
        val &= ~LAYER_COMP_MASK;
        val |= LAYER_COMP_PIXEL;
 
+       val &= ~LAYER_FLOWCFG(LAYER_FLOWCFG_MASK);
+       if (plane->state->crtc) {
+               struct malidp_crtc_state *m =
+                       to_malidp_crtc_state(plane->state->crtc->state);
+
+               if (m->scaler_config.scale_enable &&
+                   m->scaler_config.plane_src_id == mp->layer->id)
+                       val |= LAYER_FLOWCFG(LAYER_FLOWCFG_SCALE_SE);
+       }
+
        /* set the 'enable layer' bit */
        val |= LAYER_ENABLE;
 
@@ -314,7 +352,8 @@ static void malidp_de_plane_disable(struct drm_plane *plane,
 {
        struct malidp_plane *mp = to_malidp_plane(plane);
 
-       malidp_hw_clearbits(mp->hwdev, LAYER_ENABLE,
+       malidp_hw_clearbits(mp->hwdev,
+                           LAYER_ENABLE | LAYER_FLOWCFG(LAYER_FLOWCFG_MASK),
                            mp->layer->base + MALIDP_LAYER_CONTROL);
 }
 
index 4bf338d4172e71ae44d919e5393759e35ba1a4dd..91d2ba5729bf9855b5d302b03a63ac6d61fe89f5 100644 (file)
 #define MALIDP_COEF_TABLE_ADDR         0x00030
 #define MALIDP_COEF_TABLE_DATA         0x00034
 
+/* Scaling engine registers and masks. */
+#define   MALIDP_SE_SCALING_EN                 (1 << 0)
+#define   MALIDP_SE_ALPHA_EN                   (1 << 1)
+#define   MALIDP_SE_RGBO_IF_EN                 (1 << 4)
+#define   MALIDP550_SE_CTL_SEL_MASK            7
+#define   MALIDP550_SE_CTL_VCSEL(x) \
+               (((x) & MALIDP550_SE_CTL_SEL_MASK) << 20)
+#define   MALIDP550_SE_CTL_HCSEL(x) \
+               (((x) & MALIDP550_SE_CTL_SEL_MASK) << 16)
+
+/* Blocks with offsets from SE_CONTROL register. */
+#define MALIDP_SE_LAYER_CONTROL                        0x14
+#define   MALIDP_SE_L0_IN_SIZE                 0x00
+#define   MALIDP_SE_L0_OUT_SIZE                        0x04
+#define   MALIDP_SE_SET_V_SIZE(x)              (((x) & 0x1fff) << 16)
+#define   MALIDP_SE_SET_H_SIZE(x)              (((x) & 0x1fff) << 0)
+#define MALIDP_SE_SCALING_CONTROL              0x24
+#define   MALIDP_SE_H_INIT_PH                  0x00
+#define   MALIDP_SE_H_DELTA_PH                 0x04
+#define   MALIDP_SE_V_INIT_PH                  0x08
+#define   MALIDP_SE_V_DELTA_PH                 0x0c
+#define   MALIDP_SE_COEFFTAB_ADDR              0x10
+#define     MALIDP_SE_COEFFTAB_ADDR_MASK       0x7f
+#define     MALIDP_SE_V_COEFFTAB               (1 << 8)
+#define     MALIDP_SE_H_COEFFTAB               (1 << 9)
+#define     MALIDP_SE_SET_V_COEFFTAB_ADDR(x) \
+               (MALIDP_SE_V_COEFFTAB | ((x) & MALIDP_SE_COEFFTAB_ADDR_MASK))
+#define     MALIDP_SE_SET_H_COEFFTAB_ADDR(x) \
+               (MALIDP_SE_H_COEFFTAB | ((x) & MALIDP_SE_COEFFTAB_ADDR_MASK))
+#define   MALIDP_SE_COEFFTAB_DATA              0x14
+#define     MALIDP_SE_COEFFTAB_DATA_MASK       0x3fff
+#define     MALIDP_SE_SET_COEFFTAB_DATA(x) \
+               ((x) & MALIDP_SE_COEFFTAB_DATA_MASK)
+
 /* register offsets and bits specific to DP500 */
 #define MALIDP500_ADDR_SPACE_SIZE      0x01000
 #define MALIDP500_DC_BASE              0x00000
 #define MALIDP500_DE_LG2_BASE          0x00300
 #define MALIDP500_DE_LG2_PTR_BASE      0x0031c
 #define MALIDP500_SE_BASE              0x00c00
+#define MALIDP500_SE_CONTROL           0x00c0c
 #define MALIDP500_SE_PTR_BASE          0x00e0c
 #define MALIDP500_DC_IRQ_BASE          0x00f00
 #define MALIDP500_CONFIG_VALID         0x00f00
 #define MALIDP550_DE_LS_PTR_BASE       0x0042c
 #define MALIDP550_DE_PERF_BASE         0x00500
 #define MALIDP550_SE_BASE              0x08000
+#define MALIDP550_SE_CONTROL           0x08010
 #define MALIDP550_DC_BASE              0x0c000
 #define MALIDP550_DC_CONTROL           0x0c010
 #define   MALIDP550_DC_CONFIG_REQ      (1 << 16)