OSDN Git Service

drm/vc4: plane: Add support for DRM_FORMAT_P030
authorDave Stevenson <dave.stevenson@raspberrypi.com>
Wed, 15 Dec 2021 09:17:38 +0000 (10:17 +0100)
committerMaxime Ripard <maxime@cerno.tech>
Thu, 16 Dec 2021 10:23:22 +0000 (11:23 +0100)
The P030 format, used with the DRM_FORMAT_MOD_BROADCOM_SAND128 modifier,
is a format output by the video decoder on the BCM2711.

Add native support to the KMS planes for that format.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20211215091739.135042-3-maxime@cerno.tech
drivers/gpu/drm/vc4/vc4_plane.c

index ac761c6..022cd12 100644 (file)
@@ -33,6 +33,7 @@ static const struct hvs_format {
        u32 hvs; /* HVS_FORMAT_* */
        u32 pixel_order;
        u32 pixel_order_hvs5;
+       bool hvs5_only;
 } hvs_formats[] = {
        {
                .drm = DRM_FORMAT_XRGB8888,
@@ -128,6 +129,12 @@ static const struct hvs_format {
                .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
                .pixel_order = HVS_PIXEL_ORDER_XYCRCB,
        },
+       {
+               .drm = DRM_FORMAT_P030,
+               .hvs = HVS_PIXEL_FORMAT_YCBCR_10BIT,
+               .pixel_order = HVS_PIXEL_ORDER_XYCBCR,
+               .hvs5_only = true,
+       },
 };
 
 static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
@@ -762,47 +769,90 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
        case DRM_FORMAT_MOD_BROADCOM_SAND128:
        case DRM_FORMAT_MOD_BROADCOM_SAND256: {
                uint32_t param = fourcc_mod_broadcom_param(fb->modifier);
-               u32 tile_w, tile, x_off, pix_per_tile;
-
-               hvs_format = HVS_PIXEL_FORMAT_H264;
-
-               switch (base_format_mod) {
-               case DRM_FORMAT_MOD_BROADCOM_SAND64:
-                       tiling = SCALER_CTL0_TILING_64B;
-                       tile_w = 64;
-                       break;
-               case DRM_FORMAT_MOD_BROADCOM_SAND128:
-                       tiling = SCALER_CTL0_TILING_128B;
-                       tile_w = 128;
-                       break;
-               case DRM_FORMAT_MOD_BROADCOM_SAND256:
-                       tiling = SCALER_CTL0_TILING_256B_OR_T;
-                       tile_w = 256;
-                       break;
-               default:
-                       break;
-               }
 
                if (param > SCALER_TILE_HEIGHT_MASK) {
-                       DRM_DEBUG_KMS("SAND height too large (%d)\n", param);
+                       DRM_DEBUG_KMS("SAND height too large (%d)\n",
+                                     param);
                        return -EINVAL;
                }
 
-               pix_per_tile = tile_w / fb->format->cpp[0];
-               tile = vc4_state->src_x / pix_per_tile;
-               x_off = vc4_state->src_x % pix_per_tile;
+               if (fb->format->format == DRM_FORMAT_P030) {
+                       hvs_format = HVS_PIXEL_FORMAT_YCBCR_10BIT;
+                       tiling = SCALER_CTL0_TILING_128B;
+               } else {
+                       hvs_format = HVS_PIXEL_FORMAT_H264;
+
+                       switch (base_format_mod) {
+                       case DRM_FORMAT_MOD_BROADCOM_SAND64:
+                               tiling = SCALER_CTL0_TILING_64B;
+                               break;
+                       case DRM_FORMAT_MOD_BROADCOM_SAND128:
+                               tiling = SCALER_CTL0_TILING_128B;
+                               break;
+                       case DRM_FORMAT_MOD_BROADCOM_SAND256:
+                               tiling = SCALER_CTL0_TILING_256B_OR_T;
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+               }
 
                /* Adjust the base pointer to the first pixel to be scanned
                 * out.
+                *
+                * For P030, y_ptr [31:4] is the 128bit word for the start pixel
+                * y_ptr [3:0] is the pixel (0-11) contained within that 128bit
+                * word that should be taken as the first pixel.
+                * Ditto uv_ptr [31:4] vs [3:0], however [3:0] contains the
+                * element within the 128bit word, eg for pixel 3 the value
+                * should be 6.
                 */
                for (i = 0; i < num_planes; i++) {
+                       u32 tile_w, tile, x_off, pix_per_tile;
+
+                       if (fb->format->format == DRM_FORMAT_P030) {
+                               /*
+                                * Spec says: bits [31:4] of the given address
+                                * should point to the 128-bit word containing
+                                * the desired starting pixel, and bits[3:0]
+                                * should be between 0 and 11, indicating which
+                                * of the 12-pixels in that 128-bit word is the
+                                * first pixel to be used
+                                */
+                               u32 remaining_pixels = vc4_state->src_x % 96;
+                               u32 aligned = remaining_pixels / 12;
+                               u32 last_bits = remaining_pixels % 12;
+
+                               x_off = aligned * 16 + last_bits;
+                               tile_w = 128;
+                               pix_per_tile = 96;
+                       } else {
+                               switch (base_format_mod) {
+                               case DRM_FORMAT_MOD_BROADCOM_SAND64:
+                                       tile_w = 64;
+                                       break;
+                               case DRM_FORMAT_MOD_BROADCOM_SAND128:
+                                       tile_w = 128;
+                                       break;
+                               case DRM_FORMAT_MOD_BROADCOM_SAND256:
+                                       tile_w = 256;
+                                       break;
+                               default:
+                                       return -EINVAL;
+                               }
+                               pix_per_tile = tile_w / fb->format->cpp[0];
+                               x_off = (vc4_state->src_x % pix_per_tile) /
+                                       (i ? h_subsample : 1) *
+                                       fb->format->cpp[i];
+                       }
+
+                       tile = vc4_state->src_x / pix_per_tile;
+
                        vc4_state->offsets[i] += param * tile_w * tile;
                        vc4_state->offsets[i] += src_y /
                                                 (i ? v_subsample : 1) *
                                                 tile_w;
-                       vc4_state->offsets[i] += x_off /
-                                                (i ? h_subsample : 1) *
-                                                fb->format->cpp[i];
+                       vc4_state->offsets[i] += x_off & ~(i ? 1 : 0);
                }
 
                pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT);
@@ -955,7 +1005,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
 
        /* Pitch word 1/2 */
        for (i = 1; i < num_planes; i++) {
-               if (hvs_format != HVS_PIXEL_FORMAT_H264) {
+               if (hvs_format != HVS_PIXEL_FORMAT_H264 &&
+                   hvs_format != HVS_PIXEL_FORMAT_YCBCR_10BIT) {
                        vc4_dlist_write(vc4_state,
                                        VC4_SET_FIELD(fb->pitches[i],
                                                      SCALER_SRC_PITCH));
@@ -1315,6 +1366,13 @@ static bool vc4_format_mod_supported(struct drm_plane *plane,
                default:
                        return false;
                }
+       case DRM_FORMAT_P030:
+               switch (fourcc_mod_broadcom_mod(modifier)) {
+               case DRM_FORMAT_MOD_BROADCOM_SAND128:
+                       return true;
+               default:
+                       return false;
+               }
        case DRM_FORMAT_RGBX1010102:
        case DRM_FORMAT_BGRX1010102:
        case DRM_FORMAT_RGBA1010102:
@@ -1347,8 +1405,11 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
        struct drm_plane *plane = NULL;
        struct vc4_plane *vc4_plane;
        u32 formats[ARRAY_SIZE(hvs_formats)];
+       int num_formats = 0;
        int ret = 0;
        unsigned i;
+       bool hvs5 = of_device_is_compatible(dev->dev->of_node,
+                                           "brcm,bcm2711-vc5");
        static const uint64_t modifiers[] = {
                DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED,
                DRM_FORMAT_MOD_BROADCOM_SAND128,
@@ -1363,13 +1424,17 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
        if (!vc4_plane)
                return ERR_PTR(-ENOMEM);
 
-       for (i = 0; i < ARRAY_SIZE(hvs_formats); i++)
-               formats[i] = hvs_formats[i].drm;
+       for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
+               if (!hvs_formats[i].hvs5_only || hvs5) {
+                       formats[num_formats] = hvs_formats[i].drm;
+                       num_formats++;
+               }
+       }
 
        plane = &vc4_plane->base;
        ret = drm_universal_plane_init(dev, plane, 0,
                                       &vc4_plane_funcs,
-                                      formats, ARRAY_SIZE(formats),
+                                      formats, num_formats,
                                       modifiers, type, NULL);
        if (ret)
                return ERR_PTR(ret);