diff options
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.patch | 285 |
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; + |