drm/tegra: hub: Implement basic scaling support
authorThierry Reding <treding@nvidia.com>
Thu, 27 May 2021 18:27:21 +0000 (20:27 +0200)
committerThierry Reding <treding@nvidia.com>
Mon, 31 May 2021 12:30:11 +0000 (14:30 +0200)
Parameterize code in several places to allow scaling of windows. Note
that this currently still relies on static programming of the various
metering and memory pool allocation registers. This seems to work for
the common cases, but may eventually need to be updated to support
use-cases with multiple windows and higher bandwidth and latency
requirements.

Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/dc.h
drivers/gpu/drm/tegra/hub.c

index 455c3fdef8dc996af522143ee6d3aa2af757f09e..5e13f1cfd749a119c7474e8267127f528931884e 100644 (file)
@@ -714,12 +714,24 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR 0x442
 #define DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR  0x446
 
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPA 0x500
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPB 0x501
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPC 0x502
+#define  MAX_PIXELS_5TAP444(x) ((x) & 0xffff)
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPD 0x503
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPE 0x504
+#define  MAX_PIXELS_2TAP444(x) ((x) & 0xffff)
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPF 0x505
+
 #define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL    0x702
 #define OWNER_MASK (0xf << 0)
 #define OWNER(x) (((x) & 0xf) << 0)
 
 #define DC_WIN_CROPPED_SIZE                    0x706
 
+#define DC_WIN_SET_INPUT_SCALER_H_START_PHASE  0x707
+#define DC_WIN_SET_INPUT_SCALER_V_START_PHASE  0x708
+
 #define DC_WIN_PLANAR_STORAGE                  0x709
 #define PITCH(x) (((x) >> 6) & 0x1fff)
 
@@ -727,6 +739,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define  PITCH_U(x) ((((x) >> 6) & 0x1fff) <<  0)
 #define  PITCH_V(x) ((((x) >> 6) & 0x1fff) << 16)
 
+#define DC_WIN_SET_INPUT_SCALER_HPHASE_INCR    0x70b
+#define DC_WIN_SET_INPUT_SCALER_VPHASE_INCR    0x70c
+
 #define DC_WIN_SET_PARAMS                      0x70d
 #define  CLAMP_BEFORE_BLEND (1 << 15)
 #define  DEGAMMA_NONE (0 << 13)
@@ -747,6 +762,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define  VERTICAL_TAPS_2 (1 << 0)
 #define  VERTICAL_TAPS_5 (4 << 0)
 
+#define DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_COEFF 0x70f
+#define  COEFF_INDEX(x) (((x) & 0xff) << 15)
+#define  COEFF_DATA(x) (((x) & 0x3ff) << 0)
+
 #define DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE      0x711
 #define  INPUT_SCALER_USE422  (1 << 2)
 #define  INPUT_SCALER_VBYPASS (1 << 1)
index 94e1ccfb6235a464f5232b024b475a9726e9f8f7..b910155f80c4787c07e5f8836354f75598925fc6 100644 (file)
@@ -23,6 +23,8 @@
 #include "dc.h"
 #include "plane.h"
 
+#define NFB 24
+
 static const u32 tegra_shared_plane_formats[] = {
        DRM_FORMAT_ARGB1555,
        DRM_FORMAT_RGB565,
@@ -292,6 +294,74 @@ static int tegra_shared_plane_set_owner(struct tegra_plane *plane,
        return 0;
 }
 
+static void tegra_shared_plane_setup_scaler(struct tegra_plane *plane)
+{
+       static const unsigned int coeffs[192] = {
+               0x00000000, 0x3c70e400, 0x3bb037e4, 0x0c51cc9c,
+               0x00100001, 0x3bf0dbfa, 0x3d00f406, 0x3fe003ff,
+               0x00300002, 0x3b80cbf5, 0x3da1040d, 0x3fb003fe,
+               0x00400002, 0x3b20bff1, 0x3e511015, 0x3f9003fc,
+               0x00500002, 0x3ad0b3ed, 0x3f21201d, 0x3f5003fb,
+               0x00500003, 0x3aa0a3e9, 0x3ff13026, 0x3f2007f9,
+               0x00500403, 0x3a7097e6, 0x00e1402f, 0x3ee007f7,
+               0x00500403, 0x3a608be4, 0x01d14c38, 0x3ea00bf6,
+               0x00500403, 0x3a507fe2, 0x02e15c42, 0x3e500ff4,
+               0x00500402, 0x3a6073e1, 0x03f16c4d, 0x3e000ff2,
+               0x00400402, 0x3a706be0, 0x05117858, 0x3db013f0,
+               0x00300402, 0x3a905fe0, 0x06318863, 0x3d6017ee,
+               0x00300402, 0x3ab057e0, 0x0771986e, 0x3d001beb,
+               0x00200001, 0x3af04fe1, 0x08a1a47a, 0x3cb023e9,
+               0x00100001, 0x3b2047e2, 0x09e1b485, 0x3c6027e7,
+               0x00100000, 0x3b703fe2, 0x0b11c091, 0x3c002fe6,
+               0x3f203800, 0x0391103f, 0x3ff0a014, 0x0811606c,
+               0x3f2037ff, 0x0351083c, 0x03e11842, 0x3f203c00,
+               0x3f302fff, 0x03010439, 0x04311c45, 0x3f104401,
+               0x3f302fff, 0x02c0fc35, 0x04812448, 0x3f104802,
+               0x3f4027ff, 0x0270f832, 0x04c1284b, 0x3f205003,
+               0x3f4023ff, 0x0230f030, 0x0511304e, 0x3f205403,
+               0x3f601fff, 0x01f0e82d, 0x05613451, 0x3f205c04,
+               0x3f701bfe, 0x01b0e02a, 0x05a13c54, 0x3f306006,
+               0x3f7017fe, 0x0170d827, 0x05f14057, 0x3f406807,
+               0x3f8017ff, 0x0140d424, 0x0641445a, 0x3f406c08,
+               0x3fa013ff, 0x0100cc22, 0x0681485d, 0x3f507409,
+               0x3fa00fff, 0x00d0c41f, 0x06d14c60, 0x3f607c0b,
+               0x3fc00fff, 0x0090bc1c, 0x07115063, 0x3f80840c,
+               0x3fd00bff, 0x0070b41a, 0x07515465, 0x3f908c0e,
+               0x3fe007ff, 0x0040b018, 0x07915868, 0x3fb0900f,
+               0x3ff00400, 0x0010a816, 0x07d15c6a, 0x3fd09811,
+               0x00a04c0e, 0x0460f442, 0x0240a827, 0x05c15859,
+               0x0090440d, 0x0440f040, 0x0480fc43, 0x00b05010,
+               0x0080400c, 0x0410ec3e, 0x04910044, 0x00d05411,
+               0x0070380b, 0x03f0e83d, 0x04b10846, 0x00e05812,
+               0x0060340a, 0x03d0e43b, 0x04d10c48, 0x00f06013,
+               0x00503009, 0x03b0e039, 0x04e11449, 0x01106415,
+               0x00402c08, 0x0390d838, 0x05011c4b, 0x01206c16,
+               0x00302807, 0x0370d436, 0x0511204c, 0x01407018,
+               0x00302406, 0x0340d034, 0x0531244e, 0x01507419,
+               0x00202005, 0x0320cc32, 0x05412c50, 0x01707c1b,
+               0x00101c04, 0x0300c431, 0x05613451, 0x0180801d,
+               0x00101803, 0x02e0c02f, 0x05713853, 0x01a0881e,
+               0x00101002, 0x02b0bc2d, 0x05814054, 0x01c08c20,
+               0x00000c02, 0x02a0b82c, 0x05914455, 0x01e09421,
+               0x00000801, 0x0280b02a, 0x05a14c57, 0x02009c23,
+               0x00000400, 0x0260ac28, 0x05b15458, 0x0220a025,
+       };
+       unsigned int ratio, row, column;
+
+       for (ratio = 0; ratio <= 2; ratio++) {
+               for (row = 0; row <= 15; row++) {
+                       for (column = 0; column <= 3; column++) {
+                               unsigned int index = (ratio << 6) + (row << 2) + column;
+                               u32 value;
+
+                               value = COEFF_INDEX(index) | COEFF_DATA(coeffs[index]);
+                               tegra_plane_writel(plane, value,
+                                                  DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_COEFF);
+                       }
+               }
+       }
+}
+
 static void tegra_dc_assign_shared_plane(struct tegra_dc *dc,
                                         struct tegra_plane *plane)
 {
@@ -337,6 +407,8 @@ static void tegra_dc_assign_shared_plane(struct tegra_dc *dc,
        value |= THREAD_GROUP_ENABLE;
        tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP);
 
+       tegra_shared_plane_setup_scaler(plane);
+
        tegra_shared_plane_update(plane);
        tegra_shared_plane_activate(plane);
 }
@@ -444,6 +516,18 @@ static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
        host1x_client_suspend(&dc->client);
 }
 
+static inline u32 compute_phase_incr(fixed20_12 in, unsigned int out)
+{
+       u64 tmp, tmp1, tmp2;
+
+       tmp = (u64)dfixed_trunc(in);
+       tmp2 = (u64)out;
+       tmp1 = (tmp << NFB) + (tmp2 >> 1);
+       do_div(tmp1, tmp2);
+
+       return lower_32_bits(tmp1);
+}
+
 static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
                                             struct drm_atomic_state *state)
 {
@@ -454,10 +538,10 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
        unsigned int zpos = new_state->normalized_zpos;
        struct drm_framebuffer *fb = new_state->fb;
        struct tegra_plane *p = to_tegra_plane(plane);
+       u32 value, min_width, bypass = 0;
        dma_addr_t base, addr_flag = 0;
        unsigned int bpc;
        bool yuv, planar;
-       u32 value;
        int err;
 
        /* rien ne va plus */
@@ -495,12 +579,48 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
        value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos);
        tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL);
 
-       /* bypass scaling */
+       /* scaling */
+       min_width = min(new_state->src_w >> 16, new_state->crtc_w);
+
+       value = tegra_plane_readl(p, DC_WINC_PRECOMP_WGRP_PIPE_CAPC);
+
+       if (min_width < MAX_PIXELS_5TAP444(value)) {
+               value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
+       } else {
+               value = tegra_plane_readl(p, DC_WINC_PRECOMP_WGRP_PIPE_CAPE);
+
+               if (min_width < MAX_PIXELS_2TAP444(value))
+                       value = HORIZONTAL_TAPS_2 | VERTICAL_TAPS_2;
+               else
+                       dev_err(dc->dev, "invalid minimum width: %u\n", min_width);
+       }
+
        value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
        tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER);
 
-       value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS;
-       tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
+       if (new_state->src_w != new_state->crtc_w << 16) {
+               fixed20_12 width = dfixed_init(new_state->src_w >> 16);
+               u32 incr = compute_phase_incr(width, new_state->crtc_w) & ~0x1;
+               u32 init = (1 << (NFB - 1)) + (incr >> 1);
+
+               tegra_plane_writel(p, incr, DC_WIN_SET_INPUT_SCALER_HPHASE_INCR);
+               tegra_plane_writel(p, init, DC_WIN_SET_INPUT_SCALER_H_START_PHASE);
+       } else {
+               bypass |= INPUT_SCALER_HBYPASS;
+       }
+
+       if (new_state->src_h != new_state->crtc_h << 16) {
+               fixed20_12 height = dfixed_init(new_state->src_h >> 16);
+               u32 incr = compute_phase_incr(height, new_state->crtc_h) & ~0x1;
+               u32 init = (1 << (NFB - 1)) + (incr >> 1);
+
+               tegra_plane_writel(p, incr, DC_WIN_SET_INPUT_SCALER_VPHASE_INCR);
+               tegra_plane_writel(p, init, DC_WIN_SET_INPUT_SCALER_V_START_PHASE);
+       } else {
+               bypass |= INPUT_SCALER_VBYPASS;
+       }
+
+       tegra_plane_writel(p, bypass, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
 
        /* disable compression */
        tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
@@ -531,7 +651,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
        value = WIN_ENABLE | COLOR_EXPAND;
        tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
 
-       value = V_SIZE(new_state->crtc_h) | H_SIZE(new_state->crtc_w);
+       value = V_SIZE(new_state->src_h >> 16) | H_SIZE(new_state->src_w >> 16);
        tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE);
 
        tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI);