Merge branch 'for-upstream/mali-dp' of git://linux-arm.org/linux-ld into drm-next
[sfrench/cifs-2.6.git] / drivers / gpu / drm / vc4 / vc4_plane.c
index 75db62cbe468df2324d395a2a33446c0a81cf172..d098337c10e9394d843c192ab08595566f631c59 100644 (file)
@@ -258,6 +258,52 @@ static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane)
        }
 }
 
+static int vc4_plane_margins_adj(struct drm_plane_state *pstate)
+{
+       struct vc4_plane_state *vc4_pstate = to_vc4_plane_state(pstate);
+       unsigned int left, right, top, bottom, adjhdisplay, adjvdisplay;
+       struct drm_crtc_state *crtc_state;
+
+       crtc_state = drm_atomic_get_new_crtc_state(pstate->state,
+                                                  pstate->crtc);
+
+       vc4_crtc_get_margins(crtc_state, &left, &right, &top, &bottom);
+       if (!left && !right && !top && !bottom)
+               return 0;
+
+       if (left + right >= crtc_state->mode.hdisplay ||
+           top + bottom >= crtc_state->mode.vdisplay)
+               return -EINVAL;
+
+       adjhdisplay = crtc_state->mode.hdisplay - (left + right);
+       vc4_pstate->crtc_x = DIV_ROUND_CLOSEST(vc4_pstate->crtc_x *
+                                              adjhdisplay,
+                                              crtc_state->mode.hdisplay);
+       vc4_pstate->crtc_x += left;
+       if (vc4_pstate->crtc_x > crtc_state->mode.hdisplay - left)
+               vc4_pstate->crtc_x = crtc_state->mode.hdisplay - left;
+
+       adjvdisplay = crtc_state->mode.vdisplay - (top + bottom);
+       vc4_pstate->crtc_y = DIV_ROUND_CLOSEST(vc4_pstate->crtc_y *
+                                              adjvdisplay,
+                                              crtc_state->mode.vdisplay);
+       vc4_pstate->crtc_y += top;
+       if (vc4_pstate->crtc_y > crtc_state->mode.vdisplay - top)
+               vc4_pstate->crtc_y = crtc_state->mode.vdisplay - top;
+
+       vc4_pstate->crtc_w = DIV_ROUND_CLOSEST(vc4_pstate->crtc_w *
+                                              adjhdisplay,
+                                              crtc_state->mode.hdisplay);
+       vc4_pstate->crtc_h = DIV_ROUND_CLOSEST(vc4_pstate->crtc_h *
+                                              adjvdisplay,
+                                              crtc_state->mode.vdisplay);
+
+       if (!vc4_pstate->crtc_w || !vc4_pstate->crtc_h)
+               return -EINVAL;
+
+       return 0;
+}
+
 static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
 {
        struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
@@ -306,6 +352,10 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
        vc4_state->crtc_w = state->dst.x2 - state->dst.x1;
        vc4_state->crtc_h = state->dst.y2 - state->dst.y1;
 
+       ret = vc4_plane_margins_adj(state);
+       if (ret)
+               return ret;
+
        vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0],
                                                       vc4_state->crtc_w);
        vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0],
@@ -492,8 +542,9 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
        bool mix_plane_alpha;
        bool covers_screen;
        u32 scl0, scl1, pitch0;
-       u32 tiling;
+       u32 tiling, src_y;
        u32 hvs_format = format->hvs;
+       unsigned int rotation;
        int ret, i;
 
        if (vc4_state->dlist_initialized)
@@ -520,6 +571,16 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
        h_subsample = drm_format_horz_chroma_subsampling(format->drm);
        v_subsample = drm_format_vert_chroma_subsampling(format->drm);
 
+       rotation = drm_rotation_simplify(state->rotation,
+                                        DRM_MODE_ROTATE_0 |
+                                        DRM_MODE_REFLECT_X |
+                                        DRM_MODE_REFLECT_Y);
+
+       /* We must point to the last line when Y reflection is enabled. */
+       src_y = vc4_state->src_y;
+       if (rotation & DRM_MODE_REFLECT_Y)
+               src_y += vc4_state->src_h[0] - 1;
+
        switch (base_format_mod) {
        case DRM_FORMAT_MOD_LINEAR:
                tiling = SCALER_CTL0_TILING_LINEAR;
@@ -529,9 +590,10 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
                 * out.
                 */
                for (i = 0; i < num_planes; i++) {
-                       vc4_state->offsets[i] += vc4_state->src_y /
+                       vc4_state->offsets[i] += src_y /
                                                 (i ? v_subsample : 1) *
                                                 fb->pitches[i];
+
                        vc4_state->offsets[i] += vc4_state->src_x /
                                                 (i ? h_subsample : 1) *
                                                 fb->format->cpp[i];
@@ -557,22 +619,38 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
                u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift);
                u32 tiles_l = vc4_state->src_x >> tile_w_shift;
                u32 tiles_r = tiles_w - tiles_l;
-               u32 tiles_t = vc4_state->src_y >> tile_h_shift;
+               u32 tiles_t = src_y >> tile_h_shift;
                /* Intra-tile offsets, which modify the base address (the
                 * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that
                 * base address).
                 */
-               u32 tile_y = (vc4_state->src_y >> 4) & 1;
-               u32 subtile_y = (vc4_state->src_y >> 2) & 3;
-               u32 utile_y = vc4_state->src_y & 3;
+               u32 tile_y = (src_y >> 4) & 1;
+               u32 subtile_y = (src_y >> 2) & 3;
+               u32 utile_y = src_y & 3;
                u32 x_off = vc4_state->src_x & tile_w_mask;
-               u32 y_off = vc4_state->src_y & tile_h_mask;
+               u32 y_off = src_y & tile_h_mask;
+
+               /* When Y reflection is requested we must set the
+                * SCALER_PITCH0_TILE_LINE_DIR flag to tell HVS that all lines
+                * after the initial one should be fetched in descending order,
+                * which makes sense since we start from the last line and go
+                * backward.
+                * Don't know why we need y_off = max_y_off - y_off, but it's
+                * definitely required (I guess it's also related to the "going
+                * backward" situation).
+                */
+               if (rotation & DRM_MODE_REFLECT_Y) {
+                       y_off = tile_h_mask - y_off;
+                       pitch0 = SCALER_PITCH0_TILE_LINE_DIR;
+               } else {
+                       pitch0 = 0;
+               }
 
                tiling = SCALER_CTL0_TILING_256B_OR_T;
-               pitch0 = (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) |
-                         VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) |
-                         VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) |
-                         VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R));
+               pitch0 |= (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) |
+                          VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) |
+                          VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) |
+                          VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R));
                vc4_state->offsets[0] += tiles_t * (tiles_w << tile_size_shift);
                vc4_state->offsets[0] += subtile_y << 8;
                vc4_state->offsets[0] += utile_y << 4;
@@ -595,31 +673,22 @@ 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;
 
-               /* Column-based NV12 or RGBA.
-                */
-               if (fb->format->num_planes > 1) {
-                       if (hvs_format != HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE) {
-                               DRM_DEBUG_KMS("SAND format only valid for NV12/21");
-                               return -EINVAL;
-                       }
-                       hvs_format = HVS_PIXEL_FORMAT_H264;
-               } else {
-                       if (base_format_mod == DRM_FORMAT_MOD_BROADCOM_SAND256) {
-                               DRM_DEBUG_KMS("SAND256 format only valid for H.264");
-                               return -EINVAL;
-                       }
-               }
+               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;
@@ -630,6 +699,23 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
                        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;
+
+               /* Adjust the base pointer to the first pixel to be scanned
+                * out.
+                */
+               for (i = 0; i < num_planes; i++) {
+                       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];
+               }
+
                pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT);
                break;
        }
@@ -643,6 +729,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
        /* Control word */
        vc4_dlist_write(vc4_state,
                        SCALER_CTL0_VALID |
+                       (rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) |
+                       (rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) |
                        VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) |
                        (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
                        (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
@@ -1050,8 +1138,6 @@ static bool vc4_format_mod_supported(struct drm_plane *plane,
                switch (fourcc_mod_broadcom_mod(modifier)) {
                case DRM_FORMAT_MOD_LINEAR:
                case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
-               case DRM_FORMAT_MOD_BROADCOM_SAND64:
-               case DRM_FORMAT_MOD_BROADCOM_SAND128:
                        return true;
                default:
                        return false;
@@ -1123,6 +1209,11 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
        drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
 
        drm_plane_create_alpha_property(plane);
+       drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
+                                          DRM_MODE_ROTATE_0 |
+                                          DRM_MODE_ROTATE_180 |
+                                          DRM_MODE_REFLECT_X |
+                                          DRM_MODE_REFLECT_Y);
 
        return plane;
 }