// SPDX-License-Identifier: MIT /* * Copyright © 2020 Intel Corporation */ #include #include #include #include #include "i915_reg.h" #include "i9xx_plane.h" #include "i9xx_plane_regs.h" #include "intel_atomic.h" #include "intel_atomic_plane.h" #include "intel_de.h" #include "intel_display_irq.h" #include "intel_display_types.h" #include "intel_fb.h" #include "intel_fbc.h" #include "intel_frontbuffer.h" #include "intel_sprite.h" /* Primary plane formats for gen <= 3 */ static const u32 i8xx_primary_formats[] = { DRM_FORMAT_C8, DRM_FORMAT_XRGB1555, DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, }; /* Primary plane formats for ivb (no fp16 due to hw issue) */ static const u32 ivb_primary_formats[] = { DRM_FORMAT_C8, DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010, }; /* Primary plane formats for gen >= 4, except ivb */ static const u32 i965_primary_formats[] = { DRM_FORMAT_C8, DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_XBGR16161616F, }; /* Primary plane formats for vlv/chv */ static const u32 vlv_primary_formats[] = { DRM_FORMAT_C8, DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010, DRM_FORMAT_XBGR16161616F, }; static bool i8xx_plane_format_mod_supported(struct drm_plane *_plane, u32 format, u64 modifier) { if (!intel_fb_plane_supports_modifier(to_intel_plane(_plane), modifier)) return false; switch (format) { case DRM_FORMAT_C8: case DRM_FORMAT_RGB565: case DRM_FORMAT_XRGB1555: case DRM_FORMAT_XRGB8888: return modifier == DRM_FORMAT_MOD_LINEAR || modifier == I915_FORMAT_MOD_X_TILED; default: return false; } } static bool i965_plane_format_mod_supported(struct drm_plane *_plane, u32 format, u64 modifier) { if (!intel_fb_plane_supports_modifier(to_intel_plane(_plane), modifier)) return false; switch (format) { case DRM_FORMAT_C8: case DRM_FORMAT_RGB565: case DRM_FORMAT_XRGB8888: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ARGB8888: case DRM_FORMAT_ABGR8888: case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_ABGR2101010: case DRM_FORMAT_XBGR16161616F: return modifier == DRM_FORMAT_MOD_LINEAR || modifier == I915_FORMAT_MOD_X_TILED; default: return false; } } static bool i9xx_plane_has_fbc(struct drm_i915_private *dev_priv, enum i9xx_plane_id i9xx_plane) { if (!HAS_FBC(dev_priv)) return false; if (IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv)) return i9xx_plane == PLANE_A; /* tied to pipe A */ else if (IS_IVYBRIDGE(dev_priv)) return i9xx_plane == PLANE_A || i9xx_plane == PLANE_B || i9xx_plane == PLANE_C; else if (DISPLAY_VER(dev_priv) >= 4) return i9xx_plane == PLANE_A || i9xx_plane == PLANE_B; else return i9xx_plane == PLANE_A; } static struct intel_fbc *i9xx_plane_fbc(struct drm_i915_private *dev_priv, enum i9xx_plane_id i9xx_plane) { if (i9xx_plane_has_fbc(dev_priv, i9xx_plane)) return dev_priv->display.fbc[INTEL_FBC_A]; else return NULL; } static bool i9xx_plane_has_windowing(struct intel_plane *plane) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; if (IS_CHERRYVIEW(dev_priv)) return i9xx_plane == PLANE_B; else if (DISPLAY_VER(dev_priv) >= 5 || IS_G4X(dev_priv)) return false; else if (DISPLAY_VER(dev_priv) == 4) return i9xx_plane == PLANE_C; else return i9xx_plane == PLANE_B || i9xx_plane == PLANE_C; } static u32 i9xx_plane_ctl(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { struct drm_i915_private *dev_priv = to_i915(plane_state->uapi.plane->dev); const struct drm_framebuffer *fb = plane_state->hw.fb; unsigned int rotation = plane_state->hw.rotation; u32 dspcntr; dspcntr = DISP_ENABLE; if (IS_G4X(dev_priv) || IS_IRONLAKE(dev_priv) || IS_SANDYBRIDGE(dev_priv) || IS_IVYBRIDGE(dev_priv)) dspcntr |= DISP_TRICKLE_FEED_DISABLE; switch (fb->format->format) { case DRM_FORMAT_C8: dspcntr |= DISP_FORMAT_8BPP; break; case DRM_FORMAT_XRGB1555: dspcntr |= DISP_FORMAT_BGRX555; break; case DRM_FORMAT_ARGB1555: dspcntr |= DISP_FORMAT_BGRA555; break; case DRM_FORMAT_RGB565: dspcntr |= DISP_FORMAT_BGRX565; break; case DRM_FORMAT_XRGB8888: dspcntr |= DISP_FORMAT_BGRX888; break; case DRM_FORMAT_XBGR8888: dspcntr |= DISP_FORMAT_RGBX888; break; case DRM_FORMAT_ARGB8888: dspcntr |= DISP_FORMAT_BGRA888; break; case DRM_FORMAT_ABGR8888: dspcntr |= DISP_FORMAT_RGBA888; break; case DRM_FORMAT_XRGB2101010: dspcntr |= DISP_FORMAT_BGRX101010; break; case DRM_FORMAT_XBGR2101010: dspcntr |= DISP_FORMAT_RGBX101010; break; case DRM_FORMAT_ARGB2101010: dspcntr |= DISP_FORMAT_BGRA101010; break; case DRM_FORMAT_ABGR2101010: dspcntr |= DISP_FORMAT_RGBA101010; break; case DRM_FORMAT_XBGR16161616F: dspcntr |= DISP_FORMAT_RGBX161616; break; default: MISSING_CASE(fb->format->format); return 0; } if (DISPLAY_VER(dev_priv) >= 4 && fb->modifier == I915_FORMAT_MOD_X_TILED) dspcntr |= DISP_TILED; if (rotation & DRM_MODE_ROTATE_180) dspcntr |= DISP_ROTATE_180; if (rotation & DRM_MODE_REFLECT_X) dspcntr |= DISP_MIRROR; return dspcntr; } int i9xx_check_plane_surface(struct intel_plane_state *plane_state) { struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); struct drm_i915_private *dev_priv = to_i915(plane->base.dev); const struct drm_framebuffer *fb = plane_state->hw.fb; int src_x, src_y, src_w; u32 offset; int ret; ret = intel_plane_compute_gtt(plane_state); if (ret) return ret; if (!plane_state->uapi.visible) return 0; src_w = drm_rect_width(&plane_state->uapi.src) >> 16; src_x = plane_state->uapi.src.x1 >> 16; src_y = plane_state->uapi.src.y1 >> 16; /* Undocumented hardware limit on i965/g4x/vlv/chv */ if (HAS_GMCH(dev_priv) && fb->format->cpp[0] == 8 && src_w > 2048) return -EINVAL; intel_add_fb_offsets(&src_x, &src_y, plane_state, 0); if (DISPLAY_VER(dev_priv) >= 4) offset = intel_plane_compute_aligned_offset(&src_x, &src_y, plane_state, 0); else offset = 0; /* * When using an X-tiled surface the plane starts to * misbehave if the x offset + width exceeds the stride. * hsw/bdw: underrun galore * ilk/snb/ivb: wrap to the next tile row mid scanout * i965/g4x: so far appear immune to this * vlv/chv: TODO check * * Linear surfaces seem to work just fine, even on hsw/bdw * despite them not using the linear offset anymore. */ if (DISPLAY_VER(dev_priv) >= 4 && fb->modifier == I915_FORMAT_MOD_X_TILED) { unsigned int alignment = plane->min_alignment(plane, fb, 0); int cpp = fb->format->cpp[0]; while ((src_x + src_w) * cpp > plane_state->view.color_plane[0].mapping_stride) { if (offset == 0) { drm_dbg_kms(&dev_priv->drm, "Unable to find suitable display surface offset due to X-tiling\n"); return -EINVAL; } offset = intel_plane_adjust_aligned_offset(&src_x, &src_y, plane_state, 0, offset, offset - alignment); } } /* * Put the final coordinates back so that the src * coordinate checks will see the right values. */ drm_rect_translate_to(&plane_state->uapi.src, src_x << 16, src_y << 16); /* HSW/BDW do this automagically in hardware */ if (!IS_HASWELL(dev_priv) && !IS_BROADWELL(dev_priv)) { unsigned int rotation = plane_state->hw.rotation; int src_w = drm_rect_width(&plane_state->uapi.src) >> 16; int src_h = drm_rect_height(&plane_state->uapi.src) >> 16; if (rotation & DRM_MODE_ROTATE_180) { src_x += src_w - 1; src_y += src_h - 1; } else if (rotation & DRM_MODE_REFLECT_X) { src_x += src_w - 1; } } if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { drm_WARN_ON(&dev_priv->drm, src_x > 8191 || src_y > 4095); } else if (DISPLAY_VER(dev_priv) >= 4 && fb->modifier == I915_FORMAT_MOD_X_TILED) { drm_WARN_ON(&dev_priv->drm, src_x > 4095 || src_y > 4095); } plane_state->view.color_plane[0].offset = offset; plane_state->view.color_plane[0].x = src_x; plane_state->view.color_plane[0].y = src_y; return 0; } static int i9xx_plane_check(struct intel_crtc_state *crtc_state, struct intel_plane_state *plane_state) { struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); int ret; ret = chv_plane_check_rotation(plane_state); if (ret) return ret; ret = intel_atomic_plane_check_clipping(plane_state, crtc_state, DRM_PLANE_NO_SCALING, DRM_PLANE_NO_SCALING, i9xx_plane_has_windowing(plane)); if (ret) return ret; ret = i9xx_check_plane_surface(plane_state); if (ret) return ret; if (!plane_state->uapi.visible) return 0; ret = intel_plane_check_src_coordinates(plane_state); if (ret) return ret; plane_state->ctl = i9xx_plane_ctl(crtc_state, plane_state); return 0; } static u32 i9xx_plane_ctl_crtc(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); u32 dspcntr = 0; if (crtc_state->gamma_enable) dspcntr |= DISP_PIPE_GAMMA_ENABLE; if (crtc_state->csc_enable) dspcntr |= DISP_PIPE_CSC_ENABLE; if (DISPLAY_VER(dev_priv) < 5) dspcntr |= DISP_PIPE_SEL(crtc->pipe); return dspcntr; } static void i9xx_plane_ratio(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, unsigned int *num, unsigned int *den) { const struct drm_framebuffer *fb = plane_state->hw.fb; unsigned int cpp = fb->format->cpp[0]; /* * g4x bspec says 64bpp pixel rate can't exceed 80% * of cdclk when the sprite plane is enabled on the * same pipe. ilk/snb bspec says 64bpp pixel rate is * never allowed to exceed 80% of cdclk. Let's just go * with the ilk/snb limit always. */ if (cpp == 8) { *num = 10; *den = 8; } else { *num = 1; *den = 1; } } static int i9xx_plane_min_cdclk(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { unsigned int pixel_rate; unsigned int num, den; /* * Note that crtc_state->pixel_rate accounts for both * horizontal and vertical panel fitter downscaling factors. * Pre-HSW bspec tells us to only consider the horizontal * downscaling factor here. We ignore that and just consider * both for simplicity. */ pixel_rate = crtc_state->pixel_rate; i9xx_plane_ratio(crtc_state, plane_state, &num, &den); /* two pixels per clock with double wide pipe */ if (crtc_state->double_wide) den *= 2; return DIV_ROUND_UP(pixel_rate * num, den); } static void i9xx_plane_update_noarm(struct intel_dsb *dsb, struct intel_plane *plane, const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; intel_de_write_fw(dev_priv, DSPSTRIDE(dev_priv, i9xx_plane), plane_state->view.color_plane[0].mapping_stride); if (DISPLAY_VER(dev_priv) < 4) { int crtc_x = plane_state->uapi.dst.x1; int crtc_y = plane_state->uapi.dst.y1; int crtc_w = drm_rect_width(&plane_state->uapi.dst); int crtc_h = drm_rect_height(&plane_state->uapi.dst); /* * PLANE_A doesn't actually have a full window * generator but let's assume we still need to * program whatever is there. */ intel_de_write_fw(dev_priv, DSPPOS(dev_priv, i9xx_plane), DISP_POS_Y(crtc_y) | DISP_POS_X(crtc_x)); intel_de_write_fw(dev_priv, DSPSIZE(dev_priv, i9xx_plane), DISP_HEIGHT(crtc_h - 1) | DISP_WIDTH(crtc_w - 1)); } } static void i9xx_plane_update_arm(struct intel_dsb *dsb, struct intel_plane *plane, const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; int x = plane_state->view.color_plane[0].x; int y = plane_state->view.color_plane[0].y; u32 dspcntr, dspaddr_offset, linear_offset; dspcntr = plane_state->ctl | i9xx_plane_ctl_crtc(crtc_state); /* see intel_plane_atomic_calc_changes() */ if (plane->need_async_flip_toggle_wa && crtc_state->async_flip_planes & BIT(plane->id)) dspcntr |= DISP_ASYNC_FLIP; linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0); if (DISPLAY_VER(dev_priv) >= 4) dspaddr_offset = plane_state->view.color_plane[0].offset; else dspaddr_offset = linear_offset; if (IS_CHERRYVIEW(dev_priv) && i9xx_plane == PLANE_B) { int crtc_x = plane_state->uapi.dst.x1; int crtc_y = plane_state->uapi.dst.y1; int crtc_w = drm_rect_width(&plane_state->uapi.dst); int crtc_h = drm_rect_height(&plane_state->uapi.dst); intel_de_write_fw(dev_priv, PRIMPOS(dev_priv, i9xx_plane), PRIM_POS_Y(crtc_y) | PRIM_POS_X(crtc_x)); intel_de_write_fw(dev_priv, PRIMSIZE(dev_priv, i9xx_plane), PRIM_HEIGHT(crtc_h - 1) | PRIM_WIDTH(crtc_w - 1)); intel_de_write_fw(dev_priv, PRIMCNSTALPHA(dev_priv, i9xx_plane), 0); } if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { intel_de_write_fw(dev_priv, DSPOFFSET(dev_priv, i9xx_plane), DISP_OFFSET_Y(y) | DISP_OFFSET_X(x)); } else if (DISPLAY_VER(dev_priv) >= 4) { intel_de_write_fw(dev_priv, DSPLINOFF(dev_priv, i9xx_plane), linear_offset); intel_de_write_fw(dev_priv, DSPTILEOFF(dev_priv, i9xx_plane), DISP_OFFSET_Y(y) | DISP_OFFSET_X(x)); } /* * The control register self-arms if the plane was previously * disabled. Try to make the plane enable atomic by writing * the control register just before the surface register. */ intel_de_write_fw(dev_priv, DSPCNTR(dev_priv, i9xx_plane), dspcntr); if (DISPLAY_VER(dev_priv) >= 4) intel_de_write_fw(dev_priv, DSPSURF(dev_priv, i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); else intel_de_write_fw(dev_priv, DSPADDR(dev_priv, i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); } static void i830_plane_update_arm(struct intel_dsb *dsb, struct intel_plane *plane, const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { /* * On i830/i845 all registers are self-arming [ALM040]. * * Additional breakage on i830 causes register reads to return * the last latched value instead of the last written value [ALM026]. */ i9xx_plane_update_noarm(dsb, plane, crtc_state, plane_state); i9xx_plane_update_arm(dsb, plane, crtc_state, plane_state); } static void i9xx_plane_disable_arm(struct intel_dsb *dsb, struct intel_plane *plane, const struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; u32 dspcntr; /* * DSPCNTR pipe gamma enable on g4x+ and pipe csc * enable on ilk+ affect the pipe bottom color as * well, so we must configure them even if the plane * is disabled. * * On pre-g4x there is no way to gamma correct the * pipe bottom color but we'll keep on doing this * anyway so that the crtc state readout works correctly. */ dspcntr = i9xx_plane_ctl_crtc(crtc_state); intel_de_write_fw(dev_priv, DSPCNTR(dev_priv, i9xx_plane), dspcntr); if (DISPLAY_VER(dev_priv) >= 4) intel_de_write_fw(dev_priv, DSPSURF(dev_priv, i9xx_plane), 0); else intel_de_write_fw(dev_priv, DSPADDR(dev_priv, i9xx_plane), 0); } static void g4x_primary_async_flip(struct intel_dsb *dsb, struct intel_plane *plane, const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, bool async_flip) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); u32 dspcntr = plane_state->ctl | i9xx_plane_ctl_crtc(crtc_state); u32 dspaddr_offset = plane_state->view.color_plane[0].offset; enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; if (async_flip) dspcntr |= DISP_ASYNC_FLIP; intel_de_write_fw(dev_priv, DSPCNTR(dev_priv, i9xx_plane), dspcntr); intel_de_write_fw(dev_priv, DSPSURF(dev_priv, i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); } static void vlv_primary_async_flip(struct intel_dsb *dsb, struct intel_plane *plane, const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, bool async_flip) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); u32 dspaddr_offset = plane_state->view.color_plane[0].offset; enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; intel_de_write_fw(dev_priv, DSPADDR_VLV(dev_priv, i9xx_plane), intel_plane_ggtt_offset(plane_state) + dspaddr_offset); } static void bdw_primary_enable_flip_done(struct intel_plane *plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); enum pipe pipe = plane->pipe; spin_lock_irq(&i915->irq_lock); bdw_enable_pipe_irq(i915, pipe, GEN8_PIPE_PRIMARY_FLIP_DONE); spin_unlock_irq(&i915->irq_lock); } static void bdw_primary_disable_flip_done(struct intel_plane *plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); enum pipe pipe = plane->pipe; spin_lock_irq(&i915->irq_lock); bdw_disable_pipe_irq(i915, pipe, GEN8_PIPE_PRIMARY_FLIP_DONE); spin_unlock_irq(&i915->irq_lock); } static void ivb_primary_enable_flip_done(struct intel_plane *plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); spin_lock_irq(&i915->irq_lock); ilk_enable_display_irq(i915, DE_PLANE_FLIP_DONE_IVB(plane->i9xx_plane)); spin_unlock_irq(&i915->irq_lock); } static void ivb_primary_disable_flip_done(struct intel_plane *plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); spin_lock_irq(&i915->irq_lock); ilk_disable_display_irq(i915, DE_PLANE_FLIP_DONE_IVB(plane->i9xx_plane)); spin_unlock_irq(&i915->irq_lock); } static void ilk_primary_enable_flip_done(struct intel_plane *plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); spin_lock_irq(&i915->irq_lock); ilk_enable_display_irq(i915, DE_PLANE_FLIP_DONE(plane->i9xx_plane)); spin_unlock_irq(&i915->irq_lock); } static void ilk_primary_disable_flip_done(struct intel_plane *plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); spin_lock_irq(&i915->irq_lock); ilk_disable_display_irq(i915, DE_PLANE_FLIP_DONE(plane->i9xx_plane)); spin_unlock_irq(&i915->irq_lock); } static void vlv_primary_enable_flip_done(struct intel_plane *plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); enum pipe pipe = plane->pipe; spin_lock_irq(&i915->irq_lock); i915_enable_pipestat(i915, pipe, PLANE_FLIP_DONE_INT_STATUS_VLV); spin_unlock_irq(&i915->irq_lock); } static void vlv_primary_disable_flip_done(struct intel_plane *plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); enum pipe pipe = plane->pipe; spin_lock_irq(&i915->irq_lock); i915_disable_pipestat(i915, pipe, PLANE_FLIP_DONE_INT_STATUS_VLV); spin_unlock_irq(&i915->irq_lock); } static bool i9xx_plane_get_hw_state(struct intel_plane *plane, enum pipe *pipe) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); enum intel_display_power_domain power_domain; enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; intel_wakeref_t wakeref; bool ret; u32 val; /* * Not 100% correct for planes that can move between pipes, * but that's only the case for gen2-4 which don't have any * display power wells. */ power_domain = POWER_DOMAIN_PIPE(plane->pipe); wakeref = intel_display_power_get_if_enabled(dev_priv, power_domain); if (!wakeref) return false; val = intel_de_read(dev_priv, DSPCNTR(dev_priv, i9xx_plane)); ret = val & DISP_ENABLE; if (DISPLAY_VER(dev_priv) >= 5) *pipe = plane->pipe; else *pipe = REG_FIELD_GET(DISP_PIPE_SEL_MASK, val); intel_display_power_put(dev_priv, power_domain, wakeref); return ret; } static unsigned int hsw_primary_max_stride(struct intel_plane *plane, u32 pixel_format, u64 modifier, unsigned int rotation) { const struct drm_format_info *info = drm_format_info(pixel_format); int cpp = info->cpp[0]; /* Limit to 8k pixels to guarantee OFFSET.x doesn't get too big. */ return min(8192 * cpp, 32 * 1024); } static unsigned int ilk_primary_max_stride(struct intel_plane *plane, u32 pixel_format, u64 modifier, unsigned int rotation) { const struct drm_format_info *info = drm_format_info(pixel_format); int cpp = info->cpp[0]; /* Limit to 4k pixels to guarantee TILEOFF.x doesn't get too big. */ if (modifier == I915_FORMAT_MOD_X_TILED) return min(4096 * cpp, 32 * 1024); else return 32 * 1024; } unsigned int i965_plane_max_stride(struct intel_plane *plane, u32 pixel_format, u64 modifier, unsigned int rotation) { const struct drm_format_info *info = drm_format_info(pixel_format); int cpp = info->cpp[0]; /* Limit to 4k pixels to guarantee TILEOFF.x doesn't get too big. */ if (modifier == I915_FORMAT_MOD_X_TILED) return min(4096 * cpp, 16 * 1024); else return 32 * 1024; } static unsigned int i915_plane_max_stride(struct intel_plane *plane, u32 pixel_format, u64 modifier, unsigned int rotation) { if (modifier == I915_FORMAT_MOD_X_TILED) return 8 * 1024; else return 16 * 1024; } static unsigned int i8xx_plane_max_stride(struct intel_plane *plane, u32 pixel_format, u64 modifier, unsigned int rotation) { if (plane->i9xx_plane == PLANE_C) return 4 * 1024; else return 8 * 1024; } static unsigned int vlv_primary_min_alignment(struct intel_plane *plane, const struct drm_framebuffer *fb, int color_plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); switch (fb->modifier) { case I915_FORMAT_MOD_X_TILED: if (HAS_ASYNC_FLIPS(i915)) return 256 * 1024; return 4 * 1024; case DRM_FORMAT_MOD_LINEAR: return 128 * 1024; default: MISSING_CASE(fb->modifier); return 0; } } static unsigned int g4x_primary_min_alignment(struct intel_plane *plane, const struct drm_framebuffer *fb, int color_plane) { struct drm_i915_private *i915 = to_i915(plane->base.dev); switch (fb->modifier) { case I915_FORMAT_MOD_X_TILED: if (HAS_ASYNC_FLIPS(i915)) return 256 * 1024; return 4 * 1024; case DRM_FORMAT_MOD_LINEAR: return 4 * 1024; default: MISSING_CASE(fb->modifier); return 0; } } static unsigned int i965_plane_min_alignment(struct intel_plane *plane, const struct drm_framebuffer *fb, int color_plane) { switch (fb->modifier) { case I915_FORMAT_MOD_X_TILED: return 4 * 1024; case DRM_FORMAT_MOD_LINEAR: return 128 * 1024; default: MISSING_CASE(fb->modifier); return 0; } } static unsigned int i9xx_plane_min_alignment(struct intel_plane *plane, const struct drm_framebuffer *fb, int color_plane) { return 0; } static const struct drm_plane_funcs i965_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = intel_plane_destroy, .atomic_duplicate_state = intel_plane_duplicate_state, .atomic_destroy_state = intel_plane_destroy_state, .format_mod_supported = i965_plane_format_mod_supported, }; static const struct drm_plane_funcs i8xx_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = intel_plane_destroy, .atomic_duplicate_state = intel_plane_duplicate_state, .atomic_destroy_state = intel_plane_destroy_state, .format_mod_supported = i8xx_plane_format_mod_supported, }; struct intel_plane * intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) { struct intel_plane *plane; const struct drm_plane_funcs *plane_funcs; unsigned int supported_rotations; const u64 *modifiers; const u32 *formats; int num_formats; int ret, zpos; plane = intel_plane_alloc(); if (IS_ERR(plane)) return plane; plane->pipe = pipe; /* * On gen2/3 only plane A can do FBC, but the panel fitter and LVDS * port is hooked to pipe B. Hence we want plane A feeding pipe B. */ if (HAS_FBC(dev_priv) && DISPLAY_VER(dev_priv) < 4 && INTEL_NUM_PIPES(dev_priv) == 2) plane->i9xx_plane = (enum i9xx_plane_id) !pipe; else plane->i9xx_plane = (enum i9xx_plane_id) pipe; plane->id = PLANE_PRIMARY; plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane->id); intel_fbc_add_plane(i9xx_plane_fbc(dev_priv, plane->i9xx_plane), plane); if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { formats = vlv_primary_formats; num_formats = ARRAY_SIZE(vlv_primary_formats); } else if (DISPLAY_VER(dev_priv) >= 4) { /* * WaFP16GammaEnabling:ivb * "Workaround : When using the 64-bit format, the plane * output on each color channel has one quarter amplitude. * It can be brought up to full amplitude by using pipe * gamma correction or pipe color space conversion to * multiply the plane output by four." * * There is no dedicated plane gamma for the primary plane, * and using the pipe gamma/csc could conflict with other * planes, so we choose not to expose fp16 on IVB primary * planes. HSW primary planes no longer have this problem. */ if (IS_IVYBRIDGE(dev_priv)) { formats = ivb_primary_formats; num_formats = ARRAY_SIZE(ivb_primary_formats); } else { formats = i965_primary_formats; num_formats = ARRAY_SIZE(i965_primary_formats); } } else { formats = i8xx_primary_formats; num_formats = ARRAY_SIZE(i8xx_primary_formats); } if (DISPLAY_VER(dev_priv) >= 4) plane_funcs = &i965_plane_funcs; else plane_funcs = &i8xx_plane_funcs; if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) plane->min_cdclk = vlv_plane_min_cdclk; else if (IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv)) plane->min_cdclk = hsw_plane_min_cdclk; else if (IS_IVYBRIDGE(dev_priv)) plane->min_cdclk = ivb_plane_min_cdclk; else plane->min_cdclk = i9xx_plane_min_cdclk; if (HAS_GMCH(dev_priv)) { if (DISPLAY_VER(dev_priv) >= 4) plane->max_stride = i965_plane_max_stride; else if (DISPLAY_VER(dev_priv) == 3) plane->max_stride = i915_plane_max_stride; else plane->max_stride = i8xx_plane_max_stride; } else { if (IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv)) plane->max_stride = hsw_primary_max_stride; else plane->max_stride = ilk_primary_max_stride; } if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) plane->min_alignment = vlv_primary_min_alignment; else if (DISPLAY_VER(dev_priv) >= 5 || IS_G4X(dev_priv)) plane->min_alignment = g4x_primary_min_alignment; else if (DISPLAY_VER(dev_priv) == 4) plane->min_alignment = i965_plane_min_alignment; else plane->min_alignment = i9xx_plane_min_alignment; if (IS_I830(dev_priv) || IS_I845G(dev_priv)) { plane->update_arm = i830_plane_update_arm; } else { plane->update_noarm = i9xx_plane_update_noarm; plane->update_arm = i9xx_plane_update_arm; } plane->disable_arm = i9xx_plane_disable_arm; plane->get_hw_state = i9xx_plane_get_hw_state; plane->check_plane = i9xx_plane_check; if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { plane->async_flip = vlv_primary_async_flip; plane->enable_flip_done = vlv_primary_enable_flip_done; plane->disable_flip_done = vlv_primary_disable_flip_done; } else if (IS_BROADWELL(dev_priv)) { plane->need_async_flip_toggle_wa = true; plane->async_flip = g4x_primary_async_flip; plane->enable_flip_done = bdw_primary_enable_flip_done; plane->disable_flip_done = bdw_primary_disable_flip_done; } else if (DISPLAY_VER(dev_priv) >= 7) { plane->async_flip = g4x_primary_async_flip; plane->enable_flip_done = ivb_primary_enable_flip_done; plane->disable_flip_done = ivb_primary_disable_flip_done; } else if (DISPLAY_VER(dev_priv) >= 5) { plane->async_flip = g4x_primary_async_flip; plane->enable_flip_done = ilk_primary_enable_flip_done; plane->disable_flip_done = ilk_primary_disable_flip_done; } modifiers = intel_fb_plane_get_modifiers(dev_priv, INTEL_PLANE_CAP_TILING_X); if (DISPLAY_VER(dev_priv) >= 5 || IS_G4X(dev_priv)) ret = drm_universal_plane_init(&dev_priv->drm, &plane->base, 0, plane_funcs, formats, num_formats, modifiers, DRM_PLANE_TYPE_PRIMARY, "primary %c", pipe_name(pipe)); else ret = drm_universal_plane_init(&dev_priv->drm, &plane->base, 0, plane_funcs, formats, num_formats, modifiers, DRM_PLANE_TYPE_PRIMARY, "plane %c", plane_name(plane->i9xx_plane)); kfree(modifiers); if (ret) goto fail; if (IS_CHERRYVIEW(dev_priv) && pipe == PIPE_B) { supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 | DRM_MODE_REFLECT_X; } else if (DISPLAY_VER(dev_priv) >= 4) { supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180; } else { supported_rotations = DRM_MODE_ROTATE_0; } if (DISPLAY_VER(dev_priv) >= 4) drm_plane_create_rotation_property(&plane->base, DRM_MODE_ROTATE_0, supported_rotations); zpos = 0; drm_plane_create_zpos_immutable_property(&plane->base, zpos); intel_plane_helper_add(plane); return plane; fail: intel_plane_free(plane); return ERR_PTR(ret); } static int i9xx_format_to_fourcc(int format) { switch (format) { case DISP_FORMAT_8BPP: return DRM_FORMAT_C8; case DISP_FORMAT_BGRA555: return DRM_FORMAT_ARGB1555; case DISP_FORMAT_BGRX555: return DRM_FORMAT_XRGB1555; case DISP_FORMAT_BGRX565: return DRM_FORMAT_RGB565; default: case DISP_FORMAT_BGRX888: return DRM_FORMAT_XRGB8888; case DISP_FORMAT_RGBX888: return DRM_FORMAT_XBGR8888; case DISP_FORMAT_BGRA888: return DRM_FORMAT_ARGB8888; case DISP_FORMAT_RGBA888: return DRM_FORMAT_ABGR8888; case DISP_FORMAT_BGRX101010: return DRM_FORMAT_XRGB2101010; case DISP_FORMAT_RGBX101010: return DRM_FORMAT_XBGR2101010; case DISP_FORMAT_BGRA101010: return DRM_FORMAT_ARGB2101010; case DISP_FORMAT_RGBA101010: return DRM_FORMAT_ABGR2101010; case DISP_FORMAT_RGBX161616: return DRM_FORMAT_XBGR16161616F; } } void i9xx_get_initial_plane_config(struct intel_crtc *crtc, struct intel_initial_plane_config *plane_config) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_plane *plane = to_intel_plane(crtc->base.primary); enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; enum pipe pipe; u32 val, base, offset; int fourcc, pixel_format; unsigned int aligned_height; struct drm_framebuffer *fb; struct intel_framebuffer *intel_fb; if (!plane->get_hw_state(plane, &pipe)) return; drm_WARN_ON(dev, pipe != crtc->pipe); intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); if (!intel_fb) { drm_dbg_kms(&dev_priv->drm, "failed to alloc fb\n"); return; } fb = &intel_fb->base; fb->dev = dev; val = intel_de_read(dev_priv, DSPCNTR(dev_priv, i9xx_plane)); if (DISPLAY_VER(dev_priv) >= 4) { if (val & DISP_TILED) { plane_config->tiling = I915_TILING_X; fb->modifier = I915_FORMAT_MOD_X_TILED; } if (val & DISP_ROTATE_180) plane_config->rotation = DRM_MODE_ROTATE_180; } if (IS_CHERRYVIEW(dev_priv) && pipe == PIPE_B && val & DISP_MIRROR) plane_config->rotation |= DRM_MODE_REFLECT_X; pixel_format = val & DISP_FORMAT_MASK; fourcc = i9xx_format_to_fourcc(pixel_format); fb->format = drm_format_info(fourcc); if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { offset = intel_de_read(dev_priv, DSPOFFSET(dev_priv, i9xx_plane)); base = intel_de_read(dev_priv, DSPSURF(dev_priv, i9xx_plane)) & DISP_ADDR_MASK; } else if (DISPLAY_VER(dev_priv) >= 4) { if (plane_config->tiling) offset = intel_de_read(dev_priv, DSPTILEOFF(dev_priv, i9xx_plane)); else offset = intel_de_read(dev_priv, DSPLINOFF(dev_priv, i9xx_plane)); base = intel_de_read(dev_priv, DSPSURF(dev_priv, i9xx_plane)) & DISP_ADDR_MASK; } else { offset = 0; base = intel_de_read(dev_priv, DSPADDR(dev_priv, i9xx_plane)); } plane_config->base = base; drm_WARN_ON(&dev_priv->drm, offset != 0); val = intel_de_read(dev_priv, PIPESRC(dev_priv, pipe)); fb->width = REG_FIELD_GET(PIPESRC_WIDTH_MASK, val) + 1; fb->height = REG_FIELD_GET(PIPESRC_HEIGHT_MASK, val) + 1; val = intel_de_read(dev_priv, DSPSTRIDE(dev_priv, i9xx_plane)); fb->pitches[0] = val & 0xffffffc0; aligned_height = intel_fb_align_height(fb, 0, fb->height); plane_config->size = fb->pitches[0] * aligned_height; drm_dbg_kms(&dev_priv->drm, "%s/%s with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n", crtc->base.name, plane->base.name, fb->width, fb->height, fb->format->cpp[0] * 8, base, fb->pitches[0], plane_config->size); plane_config->fb = intel_fb; } bool i9xx_fixup_initial_plane_config(struct intel_crtc *crtc, const struct intel_initial_plane_config *plane_config) { struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct intel_plane *plane = to_intel_plane(crtc->base.primary); const struct intel_plane_state *plane_state = to_intel_plane_state(plane->base.state); enum i9xx_plane_id i9xx_plane = plane->i9xx_plane; u32 base; if (!plane_state->uapi.visible) return false; base = intel_plane_ggtt_offset(plane_state); /* * We may have moved the surface to a different * part of ggtt, make the plane aware of that. */ if (plane_config->base == base) return false; if (DISPLAY_VER(dev_priv) >= 4) intel_de_write(dev_priv, DSPSURF(dev_priv, i9xx_plane), base); else intel_de_write(dev_priv, DSPADDR(dev_priv, i9xx_plane), base); return true; }