#define _PLANE_KEYMSK_2_A                      0x70298
 #define _PLANE_KEYMAX_1_A                      0x701a0
 #define _PLANE_KEYMAX_2_A                      0x702a0
+#define _PLANE_AUX_DIST_1_A                    0x701c0
+#define _PLANE_AUX_DIST_2_A                    0x702c0
+#define _PLANE_AUX_OFFSET_1_A                  0x701c4
+#define _PLANE_AUX_OFFSET_2_A                  0x702c4
 #define _PLANE_COLOR_CTL_1_A                   0x701CC /* GLK+ */
 #define _PLANE_COLOR_CTL_2_A                   0x702CC /* GLK+ */
 #define _PLANE_COLOR_CTL_3_A                   0x703CC /* GLK+ */
 #define PLANE_NV12_BUF_CFG(pipe, plane)        \
        _MMIO_PLANE(plane, _PLANE_NV12_BUF_CFG_1(pipe), _PLANE_NV12_BUF_CFG_2(pipe))
 
+#define _PLANE_AUX_DIST_1_B            0x711c0
+#define _PLANE_AUX_DIST_2_B            0x712c0
+#define _PLANE_AUX_DIST_1(pipe) \
+                       _PIPE(pipe, _PLANE_AUX_DIST_1_A, _PLANE_AUX_DIST_1_B)
+#define _PLANE_AUX_DIST_2(pipe) \
+                       _PIPE(pipe, _PLANE_AUX_DIST_2_A, _PLANE_AUX_DIST_2_B)
+#define PLANE_AUX_DIST(pipe, plane)     \
+       _MMIO_PLANE(plane, _PLANE_AUX_DIST_1(pipe), _PLANE_AUX_DIST_2(pipe))
+
+#define _PLANE_AUX_OFFSET_1_B          0x711c4
+#define _PLANE_AUX_OFFSET_2_B          0x712c4
+#define _PLANE_AUX_OFFSET_1(pipe)       \
+               _PIPE(pipe, _PLANE_AUX_OFFSET_1_A, _PLANE_AUX_OFFSET_1_B)
+#define _PLANE_AUX_OFFSET_2(pipe)       \
+               _PIPE(pipe, _PLANE_AUX_OFFSET_2_A, _PLANE_AUX_OFFSET_2_B)
+#define PLANE_AUX_OFFSET(pipe, plane)   \
+       _MMIO_PLANE(plane, _PLANE_AUX_OFFSET_1(pipe), _PLANE_AUX_OFFSET_2(pipe))
+
 #define _PLANE_COLOR_CTL_1_B                   0x711CC
 #define _PLANE_COLOR_CTL_2_B                   0x712CC
 #define _PLANE_COLOR_CTL_3_B                   0x713CC
 # define CHICKEN3_DGMG_DONE_FIX_DISABLE                (1 << 2)
 
 #define CHICKEN_PAR1_1         _MMIO(0x42080)
+#define  SKL_RC_HASH_OUTSIDE   (1 << 15)
 #define  DPA_MASK_VBLANK_SRD   (1 << 15)
 #define  FORCE_ARB_IDLE_PLANES (1 << 14)
 #define  SKL_EDP_PSR_FIX_RDWRAP        (1 << 3)
 
                        return 128;
                else
                        return 512;
+       case I915_FORMAT_MOD_Y_TILED_CCS:
+               if (plane == 1)
+                       return 128;
+               /* fall through */
        case I915_FORMAT_MOD_Y_TILED:
                if (IS_GEN2(dev_priv) || HAS_128_BYTE_Y_TILING(dev_priv))
                        return 128;
                else
                        return 512;
+       case I915_FORMAT_MOD_Yf_TILED_CCS:
+               if (plane == 1)
+                       return 128;
+               /* fall through */
        case I915_FORMAT_MOD_Yf_TILED:
                switch (cpp) {
                case 1:
        struct drm_i915_private *dev_priv = to_i915(fb->dev);
 
        /* AUX_DIST needs only 4K alignment */
-       if (fb->format->format == DRM_FORMAT_NV12 && plane == 1)
+       if (plane == 1)
                return 4096;
 
        switch (fb->modifier) {
                if (INTEL_GEN(dev_priv) >= 9)
                        return 256 * 1024;
                return 0;
+       case I915_FORMAT_MOD_Y_TILED_CCS:
+       case I915_FORMAT_MOD_Yf_TILED_CCS:
        case I915_FORMAT_MOD_Y_TILED:
        case I915_FORMAT_MOD_Yf_TILED:
                return 1 * 1024 * 1024;
        case I915_FORMAT_MOD_X_TILED:
                return I915_TILING_X;
        case I915_FORMAT_MOD_Y_TILED:
+       case I915_FORMAT_MOD_Y_TILED_CCS:
                return I915_TILING_Y;
        default:
                return I915_TILING_NONE;
 
                intel_fb_offset_to_xy(&x, &y, fb, i);
 
+               if ((fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
+                    fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS) && i == 1) {
+                       int hsub = fb->format->hsub;
+                       int vsub = fb->format->vsub;
+                       int tile_width, tile_height;
+                       int main_x, main_y;
+                       int ccs_x, ccs_y;
+
+                       intel_tile_dims(fb, i, &tile_width, &tile_height);
+
+                       ccs_x = (x * hsub) % (tile_width * hsub);
+                       ccs_y = (y * vsub) % (tile_height * vsub);
+                       main_x = intel_fb->normal[0].x % (tile_width * hsub);
+                       main_y = intel_fb->normal[0].y % (tile_height * vsub);
+
+                       /*
+                        * CCS doesn't have its own x/y offset register, so the intra CCS tile
+                        * x/y offsets must match between CCS and the main surface.
+                        */
+                       if (main_x != ccs_x || main_y != ccs_y) {
+                               DRM_DEBUG_KMS("Bad CCS x/y (main %d,%d ccs %d,%d) full (main %d,%d ccs %d,%d)\n",
+                                             main_x, main_y,
+                                             ccs_x, ccs_y,
+                                             intel_fb->normal[0].x,
+                                             intel_fb->normal[0].y,
+                                             x, y);
+                               return -EINVAL;
+                       }
+               }
+
                /*
                 * The fence (if used) is aligned to the start of the object
                 * so having the framebuffer wrap around across the edge of the
                        break;
                }
                break;
+       case I915_FORMAT_MOD_Y_TILED_CCS:
+       case I915_FORMAT_MOD_Yf_TILED_CCS:
+               /* FIXME AUX plane? */
        case I915_FORMAT_MOD_Y_TILED:
        case I915_FORMAT_MOD_Yf_TILED:
                switch (cpp) {
        return 2048;
 }
 
+static bool skl_check_main_ccs_coordinates(struct intel_plane_state *plane_state,
+                                          int main_x, int main_y, u32 main_offset)
+{
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       int hsub = fb->format->hsub;
+       int vsub = fb->format->vsub;
+       int aux_x = plane_state->aux.x;
+       int aux_y = plane_state->aux.y;
+       u32 aux_offset = plane_state->aux.offset;
+       u32 alignment = intel_surf_alignment(fb, 1);
+
+       while (aux_offset >= main_offset && aux_y <= main_y) {
+               int x, y;
+
+               if (aux_x == main_x && aux_y == main_y)
+                       break;
+
+               if (aux_offset == 0)
+                       break;
+
+               x = aux_x / hsub;
+               y = aux_y / vsub;
+               aux_offset = intel_adjust_tile_offset(&x, &y, plane_state, 1,
+                                                     aux_offset, aux_offset - alignment);
+               aux_x = x * hsub + aux_x % hsub;
+               aux_y = y * vsub + aux_y % vsub;
+       }
+
+       if (aux_x != main_x || aux_y != main_y)
+               return false;
+
+       plane_state->aux.offset = aux_offset;
+       plane_state->aux.x = aux_x;
+       plane_state->aux.y = aux_y;
+
+       return true;
+}
+
 static int skl_check_main_surface(struct intel_plane_state *plane_state)
 {
        const struct drm_framebuffer *fb = plane_state->base.fb;
 
                while ((x + w) * cpp > fb->pitches[0]) {
                        if (offset == 0) {
-                               DRM_DEBUG_KMS("Unable to find suitable display surface offset\n");
+                               DRM_DEBUG_KMS("Unable to find suitable display surface offset due to X-tiling\n");
                                return -EINVAL;
                        }
 
                }
        }
 
+       /*
+        * CCS AUX surface doesn't have its own x/y offsets, we must make sure
+        * they match with the main surface x/y offsets.
+        */
+       if (fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
+           fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS) {
+               while (!skl_check_main_ccs_coordinates(plane_state, x, y, offset)) {
+                       if (offset == 0)
+                               break;
+
+                       offset = intel_adjust_tile_offset(&x, &y, plane_state, 0,
+                                                         offset, offset - alignment);
+               }
+
+               if (x != plane_state->aux.x || y != plane_state->aux.y) {
+                       DRM_DEBUG_KMS("Unable to find suitable display surface offset due to CCS\n");
+                       return -EINVAL;
+               }
+       }
+
        plane_state->main.offset = offset;
        plane_state->main.x = x;
        plane_state->main.y = y;
        return 0;
 }
 
+static int skl_check_ccs_aux_surface(struct intel_plane_state *plane_state)
+{
+       struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
+       struct intel_crtc *crtc = to_intel_crtc(plane_state->base.crtc);
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       int src_x = plane_state->base.src.x1 >> 16;
+       int src_y = plane_state->base.src.y1 >> 16;
+       int hsub = fb->format->hsub;
+       int vsub = fb->format->vsub;
+       int x = src_x / hsub;
+       int y = src_y / vsub;
+       u32 offset;
+
+       switch (plane->id) {
+       case PLANE_PRIMARY:
+       case PLANE_SPRITE0:
+               break;
+       default:
+               DRM_DEBUG_KMS("RC support only on plane 1 and 2\n");
+               return -EINVAL;
+       }
+
+       if (crtc->pipe == PIPE_C) {
+               DRM_DEBUG_KMS("No RC support on pipe C\n");
+               return -EINVAL;
+       }
+
+       if (plane_state->base.rotation & ~(DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180)) {
+               DRM_DEBUG_KMS("RC support only with 0/180 degree rotation %x\n",
+                             plane_state->base.rotation);
+               return -EINVAL;
+       }
+
+       intel_add_fb_offsets(&x, &y, plane_state, 1);
+       offset = intel_compute_tile_offset(&x, &y, plane_state, 1);
+
+       plane_state->aux.offset = offset;
+       plane_state->aux.x = x * hsub + src_x % hsub;
+       plane_state->aux.y = y * vsub + src_y % vsub;
+
+       return 0;
+}
+
 int skl_check_plane_surface(struct intel_plane_state *plane_state)
 {
        const struct drm_framebuffer *fb = plane_state->base.fb;
                ret = skl_check_nv12_aux_surface(plane_state);
                if (ret)
                        return ret;
+       } else if (fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
+                  fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS) {
+               ret = skl_check_ccs_aux_surface(plane_state);
+               if (ret)
+                       return ret;
        } else {
                plane_state->aux.offset = ~0xfff;
                plane_state->aux.x = 0;
                return PLANE_CTL_TILED_X;
        case I915_FORMAT_MOD_Y_TILED:
                return PLANE_CTL_TILED_Y;
+       case I915_FORMAT_MOD_Y_TILED_CCS:
+               return PLANE_CTL_TILED_Y | PLANE_CTL_DECOMPRESSION_ENABLE;
        case I915_FORMAT_MOD_Yf_TILED:
                return PLANE_CTL_TILED_YF;
+       case I915_FORMAT_MOD_Yf_TILED_CCS:
+               return PLANE_CTL_TILED_YF | PLANE_CTL_DECOMPRESSION_ENABLE;
        default:
                MISSING_CASE(fb_modifier);
        }
        u32 plane_ctl = plane_state->ctl;
        unsigned int rotation = plane_state->base.rotation;
        u32 stride = skl_plane_stride(fb, 0, rotation);
+       u32 aux_stride = skl_plane_stride(fb, 1, rotation);
        u32 surf_addr = plane_state->main.offset;
        int scaler_id = plane_state->scaler_id;
        int src_x = plane_state->main.x;
        I915_WRITE_FW(PLANE_OFFSET(pipe, plane_id), (src_y << 16) | src_x);
        I915_WRITE_FW(PLANE_STRIDE(pipe, plane_id), stride);
        I915_WRITE_FW(PLANE_SIZE(pipe, plane_id), (src_h << 16) | src_w);
+       I915_WRITE_FW(PLANE_AUX_DIST(pipe, plane_id),
+                     (plane_state->aux.offset - surf_addr) | aux_stride);
+       I915_WRITE_FW(PLANE_AUX_OFFSET(pipe, plane_id),
+                     (plane_state->aux.y << 16) | plane_state->aux.x);
 
        if (scaler_id >= 0) {
                uint32_t ps_ctrl = 0;
                fb->modifier = I915_FORMAT_MOD_X_TILED;
                break;
        case PLANE_CTL_TILED_Y:
-               fb->modifier = I915_FORMAT_MOD_Y_TILED;
+               if (val & PLANE_CTL_DECOMPRESSION_ENABLE)
+                       fb->modifier = I915_FORMAT_MOD_Y_TILED_CCS;
+               else
+                       fb->modifier = I915_FORMAT_MOD_Y_TILED;
                break;
        case PLANE_CTL_TILED_YF:
-               fb->modifier = I915_FORMAT_MOD_Yf_TILED;
+               if (val & PLANE_CTL_DECOMPRESSION_ENABLE)
+                       fb->modifier = I915_FORMAT_MOD_Yf_TILED_CCS;
+               else
+                       fb->modifier = I915_FORMAT_MOD_Yf_TILED;
                break;
        default:
                MISSING_CASE(tiling);
                                  struct drm_mode_fb_cmd2 *mode_cmd)
 {
        struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
+       struct drm_framebuffer *fb = &intel_fb->base;
        struct drm_format_name_buf format_name;
-       u32 pitch_limit, stride_alignment;
+       u32 pitch_limit;
        unsigned int tiling, stride;
        int ret = -EINVAL;
+       int i;
 
        i915_gem_object_lock(obj);
        obj->framebuffer_references++;
 
        /* Passed in modifier sanity checking. */
        switch (mode_cmd->modifier[0]) {
+       case I915_FORMAT_MOD_Y_TILED_CCS:
+       case I915_FORMAT_MOD_Yf_TILED_CCS:
+               switch (mode_cmd->pixel_format) {
+               case DRM_FORMAT_XBGR8888:
+               case DRM_FORMAT_ABGR8888:
+               case DRM_FORMAT_XRGB8888:
+               case DRM_FORMAT_ARGB8888:
+                       break;
+               default:
+                       DRM_DEBUG_KMS("RC supported only with RGB8888 formats\n");
+                       goto err;
+               }
+               /* fall through */
        case I915_FORMAT_MOD_Y_TILED:
        case I915_FORMAT_MOD_Yf_TILED:
                if (INTEL_GEN(dev_priv) < 9) {
        if (mode_cmd->offsets[0] != 0)
                goto err;
 
-       drm_helper_mode_fill_fb_struct(&dev_priv->drm,
-                                      &intel_fb->base, mode_cmd);
+       drm_helper_mode_fill_fb_struct(&dev_priv->drm, fb, mode_cmd);
 
-       stride_alignment = intel_fb_stride_alignment(&intel_fb->base, 0);
-       if (mode_cmd->pitches[0] & (stride_alignment - 1)) {
-               DRM_DEBUG_KMS("pitch (%d) must be at least %u byte aligned\n",
-                             mode_cmd->pitches[0], stride_alignment);
-               goto err;
+       for (i = 0; i < fb->format->num_planes; i++) {
+               u32 stride_alignment;
+
+               if (mode_cmd->handles[i] != mode_cmd->handles[0]) {
+                       DRM_DEBUG_KMS("bad plane %d handle\n", i);
+                       return -EINVAL;
+               }
+
+               stride_alignment = intel_fb_stride_alignment(fb, i);
+
+               /*
+                * Display WA #0531: skl,bxt,kbl,glk
+                *
+                * Render decompression and plane width > 3840
+                * combined with horizontal panning requires the
+                * plane stride to be a multiple of 4. We'll just
+                * require the entire fb to accommodate that to avoid
+                * potential runtime errors at plane configuration time.
+                */
+               if (IS_GEN9(dev_priv) && i == 0 && fb->width > 3840 &&
+                   (fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
+                    fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS))
+                       stride_alignment *= 4;
+
+               if (fb->pitches[i] & (stride_alignment - 1)) {
+                       DRM_DEBUG_KMS("plane %d pitch (%d) must be at least %u byte aligned\n",
+                                     i, fb->pitches[i], stride_alignment);
+                       goto err;
+               }
        }
 
        intel_fb->obj = obj;
 
-       ret = intel_fill_fb_info(dev_priv, &intel_fb->base);
+       ret = intel_fill_fb_info(dev_priv, fb);
        if (ret)
                goto err;
 
-       ret = drm_framebuffer_init(obj->base.dev,
-                                  &intel_fb->base,
-                                  &intel_fb_funcs);
+       ret = drm_framebuffer_init(&dev_priv->drm, fb, &intel_fb_funcs);
        if (ret) {
                DRM_ERROR("framebuffer init failed %d\n", ret);
                goto err;