diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_atomic_plane.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_atomic_plane.c | 352 |
1 files changed, 337 insertions, 15 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c index 612e9b0ec14a..124cd9ddba0b 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c @@ -52,6 +52,7 @@ #include "intel_fb.h" #include "intel_fb_pin.h" #include "skl_scaler.h" +#include "skl_universal_plane.h" #include "skl_watermark.h" static void intel_plane_state_reset(struct intel_plane_state *plane_state, @@ -93,6 +94,19 @@ void intel_plane_free(struct intel_plane *plane) } /** + * intel_plane_destroy - destroy a plane + * @plane: plane to destroy + * + * Common destruction function for all types of planes (primary, cursor, + * sprite). + */ +void intel_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); + kfree(to_intel_plane(plane)); +} + +/** * intel_plane_duplicate_state - duplicate plane state * @plane: drm plane * @@ -156,6 +170,11 @@ bool intel_plane_needs_physical(struct intel_plane *plane) DISPLAY_INFO(i915)->cursor_needs_physical; } +bool intel_plane_can_async_flip(struct intel_plane *plane, u64 modifier) +{ + return plane->can_async_flip && plane->can_async_flip(modifier); +} + unsigned int intel_adjusted_rate(const struct drm_rect *src, const struct drm_rect *dst, unsigned int rate) @@ -663,7 +682,7 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_ old_plane_state, new_plane_state); } -static struct intel_plane * +struct intel_plane * intel_crtc_get_plane(struct intel_crtc *crtc, enum plane_id plane_id) { struct drm_i915_private *i915 = to_i915(crtc->base.dev); @@ -767,7 +786,7 @@ void intel_plane_update_noarm(struct intel_dsb *dsb, { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - trace_intel_plane_update_noarm(plane, crtc); + trace_intel_plane_update_noarm(plane_state, crtc); if (plane->update_noarm) plane->update_noarm(dsb, plane, crtc_state, plane_state); @@ -797,7 +816,7 @@ void intel_plane_update_arm(struct intel_dsb *dsb, return; } - trace_intel_plane_update_arm(plane, crtc); + trace_intel_plane_update_arm(plane_state, crtc); plane->update_arm(dsb, plane, crtc_state, plane_state); } @@ -836,7 +855,7 @@ void intel_crtc_planes_update_noarm(struct intel_dsb *dsb, /* TODO: for mailbox updates this should be skipped */ if (new_plane_state->uapi.visible || - new_plane_state->planar_slave) + new_plane_state->is_y_plane) intel_plane_update_noarm(dsb, plane, new_crtc_state, new_plane_state); } @@ -869,7 +888,7 @@ static void skl_crtc_planes_update_arm(struct intel_dsb *dsb, * would have to be called here as well. */ if (new_plane_state->uapi.visible || - new_plane_state->planar_slave) + new_plane_state->is_y_plane) intel_plane_update_arm(dsb, plane, new_crtc_state, new_plane_state); else intel_plane_disable_arm(dsb, plane, new_crtc_state); @@ -921,6 +940,7 @@ int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state, bool can_position) { struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev); + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); struct drm_framebuffer *fb = plane_state->hw.fb; struct drm_rect *src = &plane_state->uapi.src; struct drm_rect *dst = &plane_state->uapi.dst; @@ -939,9 +959,10 @@ int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state, hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale); vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale); if (hscale < 0 || vscale < 0) { - drm_dbg_kms(&i915->drm, "Invalid scaling of plane\n"); - drm_rect_debug_print("src: ", src, true); - drm_rect_debug_print("dst: ", dst, false); + drm_dbg_kms(&i915->drm, + "[PLANE:%d:%s] invalid scaling "DRM_RECT_FP_FMT " -> " DRM_RECT_FMT "\n", + plane->base.base.id, plane->base.name, + DRM_RECT_FP_ARG(src), DRM_RECT_ARG(dst)); return -ERANGE; } @@ -955,9 +976,10 @@ int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state, if (!can_position && plane_state->uapi.visible && !drm_rect_equals(dst, clip)) { - drm_dbg_kms(&i915->drm, "Plane must cover entire CRTC\n"); - drm_rect_debug_print("dst: ", dst, false); - drm_rect_debug_print("clip: ", clip, false); + drm_dbg_kms(&i915->drm, + "[PLANE:%d:%s] plane (" DRM_RECT_FMT ") must cover entire CRTC (" DRM_RECT_FMT ")\n", + plane->base.base.id, plane->base.name, + DRM_RECT_ARG(dst), DRM_RECT_ARG(clip)); return -EINVAL; } @@ -970,6 +992,7 @@ int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state, int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state) { struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev); + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); const struct drm_framebuffer *fb = plane_state->hw.fb; struct drm_rect *src = &plane_state->uapi.src; u32 src_x, src_y, src_w, src_h, hsub, vsub; @@ -1025,13 +1048,17 @@ int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state) hsub = vsub = max(hsub, vsub); if (src_x % hsub || src_w % hsub) { - drm_dbg_kms(&i915->drm, "src x/w (%u, %u) must be a multiple of %u (rotated: %s)\n", + drm_dbg_kms(&i915->drm, + "[PLANE:%d:%s] src x/w (%u, %u) must be a multiple of %u (rotated: %s)\n", + plane->base.base.id, plane->base.name, src_x, src_w, hsub, str_yes_no(rotated)); return -EINVAL; } if (src_y % vsub || src_h % vsub) { - drm_dbg_kms(&i915->drm, "src y/h (%u, %u) must be a multiple of %u (rotated: %s)\n", + drm_dbg_kms(&i915->drm, + "[PLANE:%d:%s] src y/h (%u, %u) must be a multiple of %u (rotated: %s)\n", + plane->base.base.id, plane->base.name, src_y, src_h, vsub, str_yes_no(rotated)); return -EINVAL; } @@ -1119,7 +1146,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane, * This should only fail upon a hung GPU, in which case we * can safely continue. */ - if (new_crtc_state && intel_crtc_needs_modeset(new_crtc_state)) { + if (intel_crtc_needs_modeset(new_crtc_state)) { ret = add_dma_resv_fences(old_obj->resv, &new_plane_state->uapi); if (ret < 0) @@ -1130,7 +1157,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane, if (!obj) return 0; - ret = intel_plane_pin_fb(new_plane_state); + ret = intel_plane_pin_fb(new_plane_state, old_plane_state); if (ret) return ret; @@ -1210,3 +1237,298 @@ void intel_plane_init_cursor_vblank_work(struct intel_plane_state *old_plane_sta drm_vblank_work_init(&old_plane_state->unpin_work, old_plane_state->uapi.crtc, intel_cursor_unpin_work); } + +static void link_nv12_planes(struct intel_crtc_state *crtc_state, + struct intel_plane_state *uv_plane_state, + struct intel_plane_state *y_plane_state) +{ + struct intel_display *display = to_intel_display(uv_plane_state); + struct intel_plane *uv_plane = to_intel_plane(uv_plane_state->uapi.plane); + struct intel_plane *y_plane = to_intel_plane(y_plane_state->uapi.plane); + + drm_dbg_kms(display->drm, "UV plane [PLANE:%d:%s] using Y plane [PLANE:%d:%s]\n", + uv_plane->base.base.id, uv_plane->base.name, + y_plane->base.base.id, y_plane->base.name); + + uv_plane_state->planar_linked_plane = y_plane; + + y_plane_state->is_y_plane = true; + y_plane_state->planar_linked_plane = uv_plane; + + crtc_state->enabled_planes |= BIT(y_plane->id); + crtc_state->active_planes |= BIT(y_plane->id); + crtc_state->update_planes |= BIT(y_plane->id); + + crtc_state->data_rate[y_plane->id] = crtc_state->data_rate_y[uv_plane->id]; + crtc_state->rel_data_rate[y_plane->id] = crtc_state->rel_data_rate_y[uv_plane->id]; + + /* Copy parameters to Y plane */ + intel_plane_copy_hw_state(y_plane_state, uv_plane_state); + y_plane_state->uapi.src = uv_plane_state->uapi.src; + y_plane_state->uapi.dst = uv_plane_state->uapi.dst; + + y_plane_state->ctl = uv_plane_state->ctl; + y_plane_state->color_ctl = uv_plane_state->color_ctl; + y_plane_state->view = uv_plane_state->view; + y_plane_state->decrypt = uv_plane_state->decrypt; + + icl_link_nv12_planes(uv_plane_state, y_plane_state); +} + +static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, + struct intel_plane_state *plane_state) +{ + struct intel_display *display = to_intel_display(plane_state); + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + + plane_state->planar_linked_plane = NULL; + + if (!plane_state->is_y_plane) + return; + + drm_WARN_ON(display->drm, plane_state->uapi.visible); + + plane_state->is_y_plane = false; + + crtc_state->enabled_planes &= ~BIT(plane->id); + crtc_state->active_planes &= ~BIT(plane->id); + crtc_state->update_planes |= BIT(plane->id); + crtc_state->data_rate[plane->id] = 0; + crtc_state->rel_data_rate[plane->id] = 0; +} + +static int icl_check_nv12_planes(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + struct intel_display *display = to_intel_display(state); + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + struct intel_plane_state *plane_state; + struct intel_plane *plane; + int i; + + if (DISPLAY_VER(dev_priv) < 11) + return 0; + + /* + * Destroy all old plane links and make the Y plane invisible + * in the crtc_state->active_planes mask. + */ + for_each_new_intel_plane_in_state(state, plane, plane_state, i) { + if (plane->pipe != crtc->pipe) + continue; + + if (plane_state->planar_linked_plane) + unlink_nv12_plane(crtc_state, plane_state); + } + + if (!crtc_state->nv12_planes) + return 0; + + for_each_new_intel_plane_in_state(state, plane, plane_state, i) { + struct intel_plane_state *y_plane_state = NULL; + struct intel_plane *y_plane; + + if (plane->pipe != crtc->pipe) + continue; + + if ((crtc_state->nv12_planes & BIT(plane->id)) == 0) + continue; + + for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, y_plane) { + if (!icl_is_nv12_y_plane(display, y_plane->id)) + continue; + + if (crtc_state->active_planes & BIT(y_plane->id)) + continue; + + y_plane_state = intel_atomic_get_plane_state(state, y_plane); + if (IS_ERR(y_plane_state)) + return PTR_ERR(y_plane_state); + + break; + } + + if (!y_plane_state) { + drm_dbg_kms(&dev_priv->drm, + "[CRTC:%d:%s] need %d free Y planes for planar YUV\n", + crtc->base.base.id, crtc->base.name, + hweight8(crtc_state->nv12_planes)); + return -EINVAL; + } + + link_nv12_planes(crtc_state, plane_state, y_plane_state); + } + + return 0; +} + +static int intel_crtc_add_planes_to_state(struct intel_atomic_state *state, + struct intel_crtc *crtc, + u8 plane_ids_mask) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_plane *plane; + + for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) { + struct intel_plane_state *plane_state; + + if ((plane_ids_mask & BIT(plane->id)) == 0) + continue; + + plane_state = intel_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + } + + return 0; +} + +int intel_atomic_add_affected_planes(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + const struct intel_crtc_state *old_crtc_state = + intel_atomic_get_old_crtc_state(state, crtc); + const struct intel_crtc_state *new_crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + + return intel_crtc_add_planes_to_state(state, crtc, + old_crtc_state->enabled_planes | + new_crtc_state->enabled_planes); +} + +static bool active_planes_affects_min_cdclk(struct drm_i915_private *dev_priv) +{ + /* See {hsw,vlv,ivb}_plane_ratio() */ + return IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv) || + IS_CHERRYVIEW(dev_priv) || IS_VALLEYVIEW(dev_priv) || + IS_IVYBRIDGE(dev_priv); +} + +static u8 intel_joiner_affected_planes(struct intel_atomic_state *state, + u8 joined_pipes) +{ + const struct intel_plane_state *plane_state; + struct intel_plane *plane; + u8 affected_planes = 0; + int i; + + for_each_new_intel_plane_in_state(state, plane, plane_state, i) { + struct intel_plane *linked = plane_state->planar_linked_plane; + + if ((joined_pipes & BIT(plane->pipe)) == 0) + continue; + + affected_planes |= BIT(plane->id); + if (linked) + affected_planes |= BIT(linked->id); + } + + return affected_planes; +} + +static int intel_joiner_add_affected_planes(struct intel_atomic_state *state, + u8 joined_pipes) +{ + u8 prev_affected_planes, affected_planes = 0; + + /* + * We want all the joined pipes to have the same + * set of planes in the atomic state, to make sure + * state copying always works correctly, and the + * UV<->Y plane linkage is always up to date. + * Keep pulling planes in until we've determined + * the full set of affected planes. A bit complicated + * on account of each pipe being capable of selecting + * their own Y planes independently of the other pipes, + * and the selection being done from the set of + * inactive planes. + */ + do { + struct intel_crtc *crtc; + + for_each_intel_crtc_in_pipe_mask(state->base.dev, crtc, joined_pipes) { + int ret; + + ret = intel_crtc_add_planes_to_state(state, crtc, affected_planes); + if (ret) + return ret; + } + + prev_affected_planes = affected_planes; + affected_planes = intel_joiner_affected_planes(state, joined_pipes); + } while (affected_planes != prev_affected_planes); + + return 0; +} + +static int intel_add_affected_planes(struct intel_atomic_state *state) +{ + const struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; + int i; + + for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { + int ret; + + ret = intel_joiner_add_affected_planes(state, intel_crtc_joined_pipe_mask(crtc_state)); + if (ret) + return ret; + } + + return 0; +} + +int intel_atomic_check_planes(struct intel_atomic_state *state) +{ + struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct intel_crtc_state *old_crtc_state, *new_crtc_state; + struct intel_plane_state __maybe_unused *plane_state; + struct intel_plane *plane; + struct intel_crtc *crtc; + int i, ret; + + ret = intel_add_affected_planes(state); + if (ret) + return ret; + + for_each_new_intel_plane_in_state(state, plane, plane_state, i) { + ret = intel_plane_atomic_check(state, plane); + if (ret) { + drm_dbg_atomic(&dev_priv->drm, + "[PLANE:%d:%s] atomic driver check failed\n", + plane->base.base.id, plane->base.name); + return ret; + } + } + + for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, + new_crtc_state, i) { + u8 old_active_planes, new_active_planes; + + ret = icl_check_nv12_planes(state, crtc); + if (ret) + return ret; + + /* + * On some platforms the number of active planes affects + * the planes' minimum cdclk calculation. Add such planes + * to the state before we compute the minimum cdclk. + */ + if (!active_planes_affects_min_cdclk(dev_priv)) + continue; + + old_active_planes = old_crtc_state->active_planes & ~BIT(PLANE_CURSOR); + new_active_planes = new_crtc_state->active_planes & ~BIT(PLANE_CURSOR); + + if (hweight8(old_active_planes) == hweight8(new_active_planes)) + continue; + + ret = intel_crtc_add_planes_to_state(state, crtc, new_active_planes); + if (ret) + return ret; + } + + return 0; +} |
