Skip to content

Commit

Permalink
drm/i915: Deal with NV12 CbCr plane AUX surface on SKL+
Browse files Browse the repository at this point in the history
With NV12 we have two color planes to deal with so we must compute the
surface and x/y offsets for the second plane as well.

What makes this a bit nasty is that the hardware expects the surface
offset to be specified as a distance from the main surface offset.
What's worse, the distance must be non-negative (no neat wraparound or
anything). So we must make sure that the main surface offset is always
less or equal to the AUX surface offset. We do that by computing the AUX
offset first and the main surface offset second. If the main surface
offset ends up being above the AUX offset, we just push it down as far
as is required while still maintaining the required alignment etc.

Fortunately the AUX offset only reuqires 4K alignment, so we don't need
to do any of the backwards searching for an acceptable offset that we
must do for the main surface. And X tiled + NV12 isn't a supported
combination anyway.

Note that this just computes aux surface offsets, we do not yet program
them into the actual hardware registers, and hence we can't yet expose
NV12.

v2: Rebase due to drm_plane_state src/dst rects
    s/TODO.../something else/ in the commit message/ (Daniel)

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1470821001-25272-12-git-send-email-ville.syrjala@linux.intel.com
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
  • Loading branch information
Ville Syrjälä committed Aug 11, 2016
1 parent b63a16f commit 8d97065
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 2 deletions.
62 changes: 60 additions & 2 deletions drivers/gpu/drm/i915/intel_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -2463,8 +2463,14 @@ u32 intel_compute_tile_offset(int *x, int *y,
const struct drm_i915_private *dev_priv = to_i915(state->base.plane->dev);
const struct drm_framebuffer *fb = state->base.fb;
unsigned int rotation = state->base.rotation;
u32 alignment = intel_surf_alignment(dev_priv, fb->modifier[plane]);
int pitch = intel_fb_pitch(fb, plane, rotation);
u32 alignment;

/* AUX_DIST needs only 4K alignment */
if (fb->pixel_format == DRM_FORMAT_NV12 && plane == 1)
alignment = 4096;
else
alignment = intel_surf_alignment(dev_priv, fb->modifier[plane]);

return _intel_compute_tile_offset(dev_priv, x, y, fb, plane, pitch,
rotation, alignment);
Expand Down Expand Up @@ -2895,7 +2901,7 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state)
int h = drm_rect_height(&plane_state->src) >> 16;
int max_width = skl_max_plane_width(fb, 0, rotation);
int max_height = 4096;
u32 alignment, offset;
u32 alignment, offset, aux_offset = plane_state->aux.offset;

if (w > max_width || h > max_height) {
DRM_DEBUG_KMS("requested Y/RGB source size %dx%d too big (limit %dx%d)\n",
Expand All @@ -2908,6 +2914,15 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state)

alignment = intel_surf_alignment(dev_priv, fb->modifier[0]);

/*
* AUX surface offset is specified as the distance from the
* main surface offset, and it must be non-negative. Make
* sure that is what we will get.
*/
if (offset > aux_offset)
offset = intel_adjust_tile_offset(&x, &y, plane_state, 0,
offset, aux_offset & ~(alignment - 1));

/*
* When using an X-tiled surface, the plane blows up
* if the x offset + width exceed the stride.
Expand Down Expand Up @@ -2935,6 +2950,35 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state)
return 0;
}

static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
{
const struct drm_framebuffer *fb = plane_state->base.fb;
unsigned int rotation = plane_state->base.rotation;
int max_width = skl_max_plane_width(fb, 1, rotation);
int max_height = 4096;
int x = plane_state->src.x1 >> 17;
int y = plane_state->src.y1 >> 17;
int w = drm_rect_width(&plane_state->src) >> 17;
int h = drm_rect_height(&plane_state->src) >> 17;
u32 offset;

intel_add_fb_offsets(&x, &y, plane_state, 1);
offset = intel_compute_tile_offset(&x, &y, plane_state, 1);

/* FIXME not quite sure how/if these apply to the chroma plane */
if (w > max_width || h > max_height) {
DRM_DEBUG_KMS("CbCr source size %dx%d too big (limit %dx%d)\n",
w, h, max_width, max_height);
return -EINVAL;
}

plane_state->aux.offset = offset;
plane_state->aux.x = x;
plane_state->aux.y = y;

return 0;
}

int skl_check_plane_surface(struct intel_plane_state *plane_state)
{
const struct drm_framebuffer *fb = plane_state->base.fb;
Expand All @@ -2946,6 +2990,20 @@ int skl_check_plane_surface(struct intel_plane_state *plane_state)
drm_rect_rotate(&plane_state->src,
fb->width, fb->height, BIT(DRM_ROTATE_270));

/*
* Handle the AUX surface first since
* the main surface setup depends on it.
*/
if (fb->pixel_format == DRM_FORMAT_NV12) {
ret = skl_check_nv12_aux_surface(plane_state);
if (ret)
return ret;
} else {
plane_state->aux.offset = ~0xfff;
plane_state->aux.x = 0;
plane_state->aux.y = 0;
}

ret = skl_check_main_surface(plane_state);
if (ret)
return ret;
Expand Down
4 changes: 4 additions & 0 deletions drivers/gpu/drm/i915/intel_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ struct intel_plane_state {
u32 offset;
int x, y;
} main;
struct {
u32 offset;
int x, y;
} aux;

/*
* scaler_id
Expand Down

0 comments on commit 8d97065

Please sign in to comment.