aboutsummaryrefslogtreecommitdiffstats
path: root/testing/linux-amlogic/0024-drm-meson-Add-primary-plane-scaling.patch
diff options
context:
space:
mode:
Diffstat (limited to 'testing/linux-amlogic/0024-drm-meson-Add-primary-plane-scaling.patch')
-rw-r--r--testing/linux-amlogic/0024-drm-meson-Add-primary-plane-scaling.patch285
1 files changed, 285 insertions, 0 deletions
diff --git a/testing/linux-amlogic/0024-drm-meson-Add-primary-plane-scaling.patch b/testing/linux-amlogic/0024-drm-meson-Add-primary-plane-scaling.patch
new file mode 100644
index 0000000000..472355a105
--- /dev/null
+++ b/testing/linux-amlogic/0024-drm-meson-Add-primary-plane-scaling.patch
@@ -0,0 +1,285 @@
+From 7489078416fdb581625f99ec87b828c2e22794ee Mon Sep 17 00:00:00 2001
+From: Neil Armstrong <narmstrong@baylibre.com>
+Date: Tue, 30 Oct 2018 14:29:10 +0100
+Subject: [PATCH] drm/meson: Add primary plane scaling
+
+This patch adds support for the Primary Plane scaling.
+
+On the Amlogic GX SoCs, the primary plane is used as On-Screen-Display
+layer on top of video, and it's needed to keep the OSD layer to a lower
+size as the physical display size to :
+- lower the memory bandwidth
+- lower the OSD rendering
+- lower the memory usage
+
+This use-case is used when setting the display mode to 3840x2160 and the
+OSD layer is rendered using the GPU. In this case, the GXBB & GXL cannot
+work on more than 2000x2000 buffer, thus needing the OSD layer to be kept
+at 1920x1080 and upscaled to 3840x2160 in hardware.
+
+The primary plane atomic check still allow 1:1 scaling, allowing native
+3840x2160 if needed by user-space applications.
+
+---
+ drivers/gpu/drm/meson/meson_plane.c | 186 +++++++++++++++++++++++++++---------
+ 1 file changed, 141 insertions(+), 45 deletions(-)
+
+diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c
+index f915a79..12a47b4 100644
+--- a/drivers/gpu/drm/meson/meson_plane.c
++++ b/drivers/gpu/drm/meson/meson_plane.c
+@@ -24,6 +24,7 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/mutex.h>
++#include <linux/bitfield.h>
+ #include <linux/platform_device.h>
+ #include <drm/drmP.h>
+ #include <drm/drm_atomic.h>
+@@ -39,12 +40,50 @@
+ #include "meson_canvas.h"
+ #include "meson_registers.h"
+
++/* OSD_SCI_WH_M1 */
++#define SCI_WH_M1_W(w) FIELD_PREP(GENMASK(28, 16), w)
++#define SCI_WH_M1_H(h) FIELD_PREP(GENMASK(12, 0), h)
++
++/* OSD_SCO_H_START_END */
++/* OSD_SCO_V_START_END */
++#define SCO_HV_START(start) FIELD_PREP(GENMASK(27, 16), start)
++#define SCO_HV_END(end) FIELD_PREP(GENMASK(11, 0), end)
++
++/* OSD_SC_CTRL0 */
++#define SC_CTRL0_PATH_EN BIT(3)
++#define SC_CTRL0_SEL_OSD1 BIT(2)
++
++/* OSD_VSC_CTRL0 */
++#define VSC_BANK_LEN(value) FIELD_PREP(GENMASK(2, 0), value)
++#define VSC_TOP_INI_RCV_NUM(value) FIELD_PREP(GENMASK(6, 3), value)
++#define VSC_TOP_RPT_L0_NUM(value) FIELD_PREP(GENMASK(9, 8), value)
++#define VSC_BOT_INI_RCV_NUM(value) FIELD_PREP(GENMASK(14, 11), value)
++#define VSC_BOT_RPT_L0_NUM(value) FIELD_PREP(GENMASK(17, 16), value)
++#define VSC_PROG_INTERLACE BIT(23)
++#define VSC_VERTICAL_SCALER_EN BIT(24)
++
++/* OSD_VSC_INI_PHASE */
++#define VSC_INI_PHASE_BOT(bottom) FIELD_PREP(GENMASK(31, 16), bottom)
++#define VSC_INI_PHASE_TOP(top) FIELD_PREP(GENMASK(15, 0), top)
++
++/* OSD_HSC_CTRL0 */
++#define HSC_BANK_LENGTH(value) FIELD_PREP(GENMASK(2, 0), value)
++#define HSC_INI_RCV_NUM0(value) FIELD_PREP(GENMASK(6, 3), value)
++#define HSC_RPT_P0_NUM0(value) FIELD_PREP(GENMASK(9, 8), value)
++#define HSC_HORIZ_SCALER_EN BIT(22)
++
++/* VPP_OSD_VSC_PHASE_STEP */
++/* VPP_OSD_HSC_PHASE_STEP */
++#define SC_PHASE_STEP(value) FIELD_PREP(GENMASK(27, 0), value)
++
+ struct meson_plane {
+ struct drm_plane base;
+ struct meson_drm *priv;
+ };
+ #define to_meson_plane(x) container_of(x, struct meson_plane, base)
+
++#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
++
+ static int meson_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+ {
+@@ -57,10 +96,15 @@ static int meson_plane_atomic_check(struct drm_plane *plane,
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
++ /*
++ * Only allow :
++ * - Upscaling up to 5x, vertical and horizontal
++ * - Final coordinates must match crtc size
++ */
+ return drm_atomic_helper_check_plane_state(state, crtc_state,
++ FRAC_16_16(1, 5),
+ DRM_PLANE_HELPER_NO_SCALING,
+- DRM_PLANE_HELPER_NO_SCALING,
+- true, true);
++ false, true);
+ }
+
+ /* Takes a fixed 16.16 number and converts it to integer. */
+@@ -74,22 +118,19 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
+ {
+ struct meson_plane *meson_plane = to_meson_plane(plane);
+ struct drm_plane_state *state = plane->state;
+- struct drm_framebuffer *fb = state->fb;
++ struct drm_rect dest = drm_plane_state_dest(state);
+ struct meson_drm *priv = meson_plane->priv;
++ struct drm_framebuffer *fb = state->fb;
+ struct drm_gem_cma_object *gem;
+- struct drm_rect src = {
+- .x1 = (state->src_x),
+- .y1 = (state->src_y),
+- .x2 = (state->src_x + state->src_w),
+- .y2 = (state->src_y + state->src_h),
+- };
+- struct drm_rect dest = {
+- .x1 = state->crtc_x,
+- .y1 = state->crtc_y,
+- .x2 = state->crtc_x + state->crtc_w,
+- .y2 = state->crtc_y + state->crtc_h,
+- };
+ unsigned long flags;
++ int vsc_ini_rcv_num, vsc_ini_rpt_p0_num;
++ int vsc_bot_rcv_num, vsc_bot_rpt_p0_num;
++ int hsc_ini_rcv_num, hsc_ini_rpt_p0_num;
++ int hf_phase_step, vf_phase_step;
++ int src_w, src_h, dst_w, dst_h;
++ int bot_ini_phase;
++ int hf_bank_len;
++ int vf_bank_len;
+ u8 canvas_id_osd1;
+
+ /*
+@@ -143,6 +184,27 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
+ break;
+ };
+
++ /* Default scaler parameters */
++ vsc_bot_rcv_num = 0;
++ vsc_bot_rpt_p0_num = 0;
++ hf_bank_len = 4;
++ vf_bank_len = 4;
++
++ if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) {
++ vsc_bot_rcv_num = 6;
++ vsc_bot_rpt_p0_num = 2;
++ }
++
++ hsc_ini_rcv_num = hf_bank_len;
++ vsc_ini_rcv_num = vf_bank_len;
++ hsc_ini_rpt_p0_num = (hf_bank_len / 2) - 1;
++ vsc_ini_rpt_p0_num = (vf_bank_len / 2) - 1;
++
++ src_w = fixed16_to_int(state->src_w);
++ src_h = fixed16_to_int(state->src_h);
++ dst_w = state->crtc_w;
++ dst_h = state->crtc_h;
++
+ /*
+ * When the output is interlaced, the OSD must switch between
+ * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0
+@@ -151,41 +213,73 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
+ * is configured for 2:1 scaling with interlace options enabled.
+ */
+ if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) {
+- priv->viu.osd1_interlace = true;
+-
+ dest.y1 /= 2;
+ dest.y2 /= 2;
++ dst_h /= 2;
++ }
+
+- priv->viu.osd_sc_ctrl0 = BIT(3)| /* Enable scaler */
+- BIT(2); /* Select OSD1 */
++ hf_phase_step = ((src_w << 18) / dst_w) << 6;
++ vf_phase_step = (src_h << 20) / dst_h;
+
+- /* 2:1 scaling */
+- priv->viu.osd_sc_i_wh_m1 = ((drm_rect_width(&dest) - 1) << 16) |
+- (drm_rect_height(&dest) - 1);
+- priv->viu.osd_sc_o_h_start_end = (dest.x1 << 16) | dest.x2;
+- priv->viu.osd_sc_o_v_start_end = (dest.y1 << 16) | dest.y2;
++ if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE)
++ bot_ini_phase = ((vf_phase_step / 2) >> 4);
++ else
++ bot_ini_phase = 0;
++
++ vf_phase_step = (vf_phase_step << 4);
++
++ /* In interlaced mode, scaler is always active */
++ if (src_h != dst_h || src_w != dst_w) {
++ priv->viu.osd_sc_i_wh_m1 = SCI_WH_M1_W(src_w - 1) |
++ SCI_WH_M1_H(src_h - 1);
++ priv->viu.osd_sc_o_h_start_end = SCO_HV_START(dest.x1) |
++ SCO_HV_END(dest.x2 - 1);
++ priv->viu.osd_sc_o_v_start_end = SCO_HV_START(dest.y1) |
++ SCO_HV_END(dest.y2 - 1);
++ /* Enable OSD Scaler */
++ priv->viu.osd_sc_ctrl0 = SC_CTRL0_PATH_EN | SC_CTRL0_SEL_OSD1;
++ } else {
++ priv->viu.osd_sc_i_wh_m1 = 0;
++ priv->viu.osd_sc_o_h_start_end = 0;
++ priv->viu.osd_sc_o_v_start_end = 0;
++ priv->viu.osd_sc_ctrl0 = 0;
++ }
+
+- /* 2:1 vertical scaling values */
+- priv->viu.osd_sc_v_ini_phase = BIT(16);
+- priv->viu.osd_sc_v_phase_step = BIT(25);
++ /* In interlaced mode, vertical scaler is always active */
++ if (src_h != dst_h) {
+ priv->viu.osd_sc_v_ctrl0 =
+- (4 << 0) | /* osd_vsc_bank_length */
+- (4 << 3) | /* osd_vsc_top_ini_rcv_num0 */
+- (1 << 8) | /* osd_vsc_top_rpt_p0_num0 */
+- (6 << 11) | /* osd_vsc_bot_ini_rcv_num0 */
+- (2 << 16) | /* osd_vsc_bot_rpt_p0_num0 */
+- BIT(23) | /* osd_prog_interlace */
+- BIT(24); /* Enable vertical scaler */
+-
+- /* No horizontal scaling */
++ VSC_BANK_LEN(vf_bank_len) |
++ VSC_TOP_INI_RCV_NUM(vsc_ini_rcv_num) |
++ VSC_TOP_RPT_L0_NUM(vsc_ini_rpt_p0_num) |
++ VSC_VERTICAL_SCALER_EN;
++
++ if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE)
++ priv->viu.osd_sc_v_ctrl0 |=
++ VSC_BOT_INI_RCV_NUM(vsc_bot_rcv_num) |
++ VSC_BOT_RPT_L0_NUM(vsc_bot_rpt_p0_num) |
++ VSC_PROG_INTERLACE;
++
++ priv->viu.osd_sc_v_phase_step = SC_PHASE_STEP(vf_phase_step);
++ priv->viu.osd_sc_v_ini_phase = VSC_INI_PHASE_BOT(bot_ini_phase);
++ } else {
++ priv->viu.osd_sc_v_ctrl0 = 0;
++ priv->viu.osd_sc_v_phase_step = 0;
++ priv->viu.osd_sc_v_ini_phase = 0;
++ }
++
++ /* Horizontal scaler is only used if width does not match */
++ if (src_w != dst_w) {
++ priv->viu.osd_sc_h_ctrl0 =
++ HSC_BANK_LENGTH(hf_bank_len) |
++ HSC_INI_RCV_NUM0(hsc_ini_rcv_num) |
++ HSC_RPT_P0_NUM0(hsc_ini_rpt_p0_num) |
++ HSC_HORIZ_SCALER_EN;
++ priv->viu.osd_sc_h_phase_step = SC_PHASE_STEP(hf_phase_step);
+ priv->viu.osd_sc_h_ini_phase = 0;
+- priv->viu.osd_sc_h_phase_step = 0;
+- priv->viu.osd_sc_h_ctrl0 = 0;
+ } else {
+- priv->viu.osd1_interlace = false;
+- priv->viu.osd_sc_ctrl0 = 0;
+ priv->viu.osd_sc_h_ctrl0 = 0;
+- priv->viu.osd_sc_v_ctrl0 = 0;
++ priv->viu.osd_sc_h_phase_step = 0;
++ priv->viu.osd_sc_h_ini_phase = 0;
+ }
+
+ /*
+@@ -193,10 +287,12 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
+ * where x2 is exclusive.
+ * e.g. +30x1920 would be (1919 << 16) | 30
+ */
+- priv->viu.osd1_blk0_cfg[1] = ((fixed16_to_int(src.x2) - 1) << 16) |
+- fixed16_to_int(src.x1);
+- priv->viu.osd1_blk0_cfg[2] = ((fixed16_to_int(src.y2) - 1) << 16) |
+- fixed16_to_int(src.y1);
++ priv->viu.osd1_blk0_cfg[1] =
++ ((fixed16_to_int(state->src.x2) - 1) << 16) |
++ fixed16_to_int(state->src.x1);
++ priv->viu.osd1_blk0_cfg[2] =
++ ((fixed16_to_int(state->src.y2) - 1) << 16) |
++ fixed16_to_int(state->src.y1);
+ priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1;
+ priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1;
+