drm/i915/intel_fb: Pull FB plane functions from intel_display.c

Move the FB plane specific functions from intel_display.c to intel_fb.c.
There's more functions like this, but I leave moving those as well for a
follow up, and for now moving only the ones needed by the end of this
patchset (adding support for padding tile-rows in an FB GGTT view).

Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210325214808.2071517-11-imre.deak@intel.com
This commit is contained in:
Imre Deak 2021-03-25 23:47:53 +02:00
parent d7bdd1c8a9
commit 3cee626938
6 changed files with 840 additions and 831 deletions

View file

@ -11,6 +11,7 @@
#include "intel_atomic.h"
#include "intel_atomic_plane.h"
#include "intel_display_types.h"
#include "intel_fb.h"
#include "intel_sprite.h"
#include "i9xx_plane.h"

View file

@ -15,6 +15,7 @@
#include "intel_cursor.h"
#include "intel_display_types.h"
#include "intel_display.h"
#include "intel_fb.h"
#include "intel_frontbuffer.h"
#include "intel_pm.h"

View file

@ -856,19 +856,6 @@ void intel_disable_pipe(const struct intel_crtc_state *old_crtc_state)
intel_wait_for_pipe_off(old_crtc_state);
}
static unsigned int intel_tile_size(const struct drm_i915_private *dev_priv)
{
return IS_DISPLAY_VER(dev_priv, 2) ? 2048 : 4096;
}
static bool is_aux_plane(const struct drm_framebuffer *fb, int plane)
{
if (is_ccs_modifier(fb->modifier))
return is_ccs_plane(fb, plane);
return plane == 1;
}
bool
intel_format_info_is_yuv_semiplanar(const struct drm_format_info *info,
u64 modifier)
@ -877,13 +864,6 @@ intel_format_info_is_yuv_semiplanar(const struct drm_format_info *info,
info->num_planes == (is_ccs_modifier(modifier) ? 4 : 2);
}
static bool is_semiplanar_uv_plane(const struct drm_framebuffer *fb,
int color_plane)
{
return intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier) &&
color_plane == 1;
}
unsigned int
intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane)
{
@ -938,38 +918,6 @@ intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane)
}
}
unsigned int
intel_tile_height(const struct drm_framebuffer *fb, int color_plane)
{
if (is_gen12_ccs_plane(fb, color_plane))
return 1;
return intel_tile_size(to_i915(fb->dev)) /
intel_tile_width_bytes(fb, color_plane);
}
/* Return the tile dimensions in pixel units */
static void intel_tile_dims(const struct drm_framebuffer *fb, int color_plane,
unsigned int *tile_width,
unsigned int *tile_height)
{
unsigned int tile_width_bytes = intel_tile_width_bytes(fb, color_plane);
unsigned int cpp = fb->format->cpp[color_plane];
*tile_width = tile_width_bytes / cpp;
*tile_height = intel_tile_height(fb, color_plane);
}
static unsigned int intel_tile_row_size(const struct drm_framebuffer *fb,
int color_plane)
{
unsigned int tile_width, tile_height;
intel_tile_dims(fb, color_plane, &tile_width, &tile_height);
return fb->pitches[color_plane] * tile_height;
}
unsigned int
intel_fb_align_height(const struct drm_framebuffer *fb,
int color_plane, unsigned int height)
@ -1001,32 +949,6 @@ unsigned int intel_remapped_info_size(const struct intel_remapped_info *rem_info
return size;
}
static void
intel_fill_fb_ggtt_view(struct i915_ggtt_view *view,
const struct drm_framebuffer *fb,
unsigned int rotation)
{
memset(view, 0, sizeof(*view));
view->type = I915_GGTT_VIEW_NORMAL;
if (drm_rotation_90_or_270(rotation)) {
view->type = I915_GGTT_VIEW_ROTATED;
view->rotated = to_intel_framebuffer(fb)->rot_info;
}
}
static unsigned int intel_cursor_alignment(const struct drm_i915_private *dev_priv)
{
if (IS_I830(dev_priv))
return 16 * 1024;
else if (IS_I85X(dev_priv))
return 256;
else if (IS_I845G(dev_priv) || IS_I865G(dev_priv))
return 32;
else
return 4 * 1024;
}
static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_priv)
{
if (DISPLAY_VER(dev_priv) >= 9)
@ -1197,15 +1119,6 @@ void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags)
i915_vma_put(vma);
}
static int intel_fb_pitch(const struct drm_framebuffer *fb, int color_plane,
unsigned int rotation)
{
if (drm_rotation_90_or_270(rotation))
return to_intel_framebuffer(fb)->rotated[color_plane].pitch;
else
return fb->pitches[color_plane];
}
/*
* Convert the x/y offsets into a linear offset.
* Only valid with 0/180 degree rotation, which is fine since linear
@ -1237,224 +1150,6 @@ void intel_add_fb_offsets(int *x, int *y,
*y += state->color_plane[color_plane].y;
}
static u32 intel_adjust_tile_offset(int *x, int *y,
unsigned int tile_width,
unsigned int tile_height,
unsigned int tile_size,
unsigned int pitch_tiles,
u32 old_offset,
u32 new_offset)
{
unsigned int pitch_pixels = pitch_tiles * tile_width;
unsigned int tiles;
WARN_ON(old_offset & (tile_size - 1));
WARN_ON(new_offset & (tile_size - 1));
WARN_ON(new_offset > old_offset);
tiles = (old_offset - new_offset) / tile_size;
*y += tiles / pitch_tiles * tile_height;
*x += tiles % pitch_tiles * tile_width;
/* minimize x in case it got needlessly big */
*y += *x / pitch_pixels * tile_height;
*x %= pitch_pixels;
return new_offset;
}
static u32 intel_adjust_aligned_offset(int *x, int *y,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation,
unsigned int pitch,
u32 old_offset, u32 new_offset)
{
struct drm_i915_private *dev_priv = to_i915(fb->dev);
unsigned int cpp = fb->format->cpp[color_plane];
drm_WARN_ON(&dev_priv->drm, new_offset > old_offset);
if (!is_surface_linear(fb, color_plane)) {
unsigned int tile_size, tile_width, tile_height;
unsigned int pitch_tiles;
tile_size = intel_tile_size(dev_priv);
intel_tile_dims(fb, color_plane, &tile_width, &tile_height);
if (drm_rotation_90_or_270(rotation)) {
pitch_tiles = pitch / tile_height;
swap(tile_width, tile_height);
} else {
pitch_tiles = pitch / (tile_width * cpp);
}
intel_adjust_tile_offset(x, y, tile_width, tile_height,
tile_size, pitch_tiles,
old_offset, new_offset);
} else {
old_offset += *y * pitch + *x * cpp;
*y = (old_offset - new_offset) / pitch;
*x = ((old_offset - new_offset) - *y * pitch) / cpp;
}
return new_offset;
}
/*
* Adjust the tile offset by moving the difference into
* the x/y offsets.
*/
u32 intel_plane_adjust_aligned_offset(int *x, int *y,
const struct intel_plane_state *state,
int color_plane,
u32 old_offset, u32 new_offset)
{
return intel_adjust_aligned_offset(x, y, state->hw.fb, color_plane,
state->hw.rotation,
state->color_plane[color_plane].stride,
old_offset, new_offset);
}
/*
* Computes the aligned offset to the base tile and adjusts
* x, y. bytes per pixel is assumed to be a power-of-two.
*
* In the 90/270 rotated case, x and y are assumed
* to be already rotated to match the rotated GTT view, and
* pitch is the tile_height aligned framebuffer height.
*
* This function is used when computing the derived information
* under intel_framebuffer, so using any of that information
* here is not allowed. Anything under drm_framebuffer can be
* used. This is why the user has to pass in the pitch since it
* is specified in the rotated orientation.
*/
static u32 intel_compute_aligned_offset(struct drm_i915_private *dev_priv,
int *x, int *y,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int pitch,
unsigned int rotation,
u32 alignment)
{
unsigned int cpp = fb->format->cpp[color_plane];
u32 offset, offset_aligned;
if (!is_surface_linear(fb, color_plane)) {
unsigned int tile_size, tile_width, tile_height;
unsigned int tile_rows, tiles, pitch_tiles;
tile_size = intel_tile_size(dev_priv);
intel_tile_dims(fb, color_plane, &tile_width, &tile_height);
if (drm_rotation_90_or_270(rotation)) {
pitch_tiles = pitch / tile_height;
swap(tile_width, tile_height);
} else {
pitch_tiles = pitch / (tile_width * cpp);
}
tile_rows = *y / tile_height;
*y %= tile_height;
tiles = *x / tile_width;
*x %= tile_width;
offset = (tile_rows * pitch_tiles + tiles) * tile_size;
offset_aligned = offset;
if (alignment)
offset_aligned = rounddown(offset_aligned, alignment);
intel_adjust_tile_offset(x, y, tile_width, tile_height,
tile_size, pitch_tiles,
offset, offset_aligned);
} else {
offset = *y * pitch + *x * cpp;
offset_aligned = offset;
if (alignment) {
offset_aligned = rounddown(offset_aligned, alignment);
*y = (offset % alignment) / pitch;
*x = ((offset % alignment) - *y * pitch) / cpp;
} else {
*y = *x = 0;
}
}
return offset_aligned;
}
u32 intel_plane_compute_aligned_offset(int *x, int *y,
const struct intel_plane_state *state,
int color_plane)
{
struct intel_plane *intel_plane = to_intel_plane(state->uapi.plane);
struct drm_i915_private *dev_priv = to_i915(intel_plane->base.dev);
const struct drm_framebuffer *fb = state->hw.fb;
unsigned int rotation = state->hw.rotation;
int pitch = state->color_plane[color_plane].stride;
u32 alignment;
if (intel_plane->id == PLANE_CURSOR)
alignment = intel_cursor_alignment(dev_priv);
else
alignment = intel_surf_alignment(fb, color_plane);
return intel_compute_aligned_offset(dev_priv, x, y, fb, color_plane,
pitch, rotation, alignment);
}
/* Convert the fb->offset[] into x/y offsets */
static int intel_fb_offset_to_xy(int *x, int *y,
const struct drm_framebuffer *fb,
int color_plane)
{
struct drm_i915_private *dev_priv = to_i915(fb->dev);
unsigned int height;
u32 alignment;
if (DISPLAY_VER(dev_priv) >= 12 &&
is_semiplanar_uv_plane(fb, color_plane))
alignment = intel_tile_row_size(fb, color_plane);
else if (fb->modifier != DRM_FORMAT_MOD_LINEAR)
alignment = intel_tile_size(dev_priv);
else
alignment = 0;
if (alignment != 0 && fb->offsets[color_plane] % alignment) {
drm_dbg_kms(&dev_priv->drm,
"Misaligned offset 0x%08x for color plane %d\n",
fb->offsets[color_plane], color_plane);
return -EINVAL;
}
height = drm_framebuffer_plane_height(fb->height, fb, color_plane);
height = ALIGN(height, intel_tile_height(fb, color_plane));
/* Catch potential overflows early */
if (add_overflows_t(u32, mul_u32_u32(height, fb->pitches[color_plane]),
fb->offsets[color_plane])) {
drm_dbg_kms(&dev_priv->drm,
"Bad offset 0x%08x or pitch %d for color plane %d\n",
fb->offsets[color_plane], fb->pitches[color_plane],
color_plane);
return -ERANGE;
}
*x = 0;
*y = 0;
intel_adjust_aligned_offset(x, y,
fb, color_plane, DRM_MODE_ROTATE_0,
fb->pitches[color_plane],
fb->offsets[color_plane], 0);
return 0;
}
static unsigned int intel_fb_modifier_to_tiling(u64 fb_modifier)
{
switch (fb_modifier) {
@ -1690,519 +1385,6 @@ intel_fb_stride_alignment(const struct drm_framebuffer *fb, int color_plane)
return tile_width;
}
bool intel_plane_can_remap(const 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 i;
/* We don't want to deal with remapping with cursors */
if (plane->id == PLANE_CURSOR)
return false;
/*
* The display engine limits already match/exceed the
* render engine limits, so not much point in remapping.
* Would also need to deal with the fence POT alignment
* and gen2 2KiB GTT tile size.
*/
if (DISPLAY_VER(dev_priv) < 4)
return false;
/*
* The new CCS hash mode isn't compatible with remapping as
* the virtual address of the pages affects the compressed data.
*/
if (is_ccs_modifier(fb->modifier))
return false;
/* Linear needs a page aligned stride for remapping */
if (fb->modifier == DRM_FORMAT_MOD_LINEAR) {
unsigned int alignment = intel_tile_size(dev_priv) - 1;
for (i = 0; i < fb->format->num_planes; i++) {
if (fb->pitches[i] & alignment)
return false;
}
}
return true;
}
static bool intel_plane_needs_remap(const struct intel_plane_state *plane_state)
{
struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
const struct drm_framebuffer *fb = plane_state->hw.fb;
unsigned int rotation = plane_state->hw.rotation;
u32 stride, max_stride;
/*
* No remapping for invisible planes since we don't have
* an actual source viewport to remap.
*/
if (!plane_state->uapi.visible)
return false;
if (!intel_plane_can_remap(plane_state))
return false;
/*
* FIXME: aux plane limits on gen9+ are
* unclear in Bspec, for now no checking.
*/
stride = intel_fb_pitch(fb, 0, rotation);
max_stride = plane->max_stride(plane, fb->format->format,
fb->modifier, rotation);
return stride > max_stride;
}
void
intel_fb_plane_get_subsampling(int *hsub, int *vsub,
const struct drm_framebuffer *fb,
int color_plane)
{
int main_plane;
if (color_plane == 0) {
*hsub = 1;
*vsub = 1;
return;
}
/*
* TODO: Deduct the subsampling from the char block for all CCS
* formats and planes.
*/
if (!is_gen12_ccs_plane(fb, color_plane)) {
*hsub = fb->format->hsub;
*vsub = fb->format->vsub;
return;
}
main_plane = skl_ccs_to_main_plane(fb, color_plane);
*hsub = drm_format_info_block_width(fb->format, color_plane) /
drm_format_info_block_width(fb->format, main_plane);
/*
* The min stride check in the core framebuffer_check() function
* assumes that format->hsub applies to every plane except for the
* first plane. That's incorrect for the CCS AUX plane of the first
* plane, but for the above check to pass we must define the block
* width with that subsampling applied to it. Adjust the width here
* accordingly, so we can calculate the actual subsampling factor.
*/
if (main_plane == 0)
*hsub *= fb->format->hsub;
*vsub = 32;
}
static int
intel_fb_check_ccs_xy(struct drm_framebuffer *fb, int ccs_plane, int x, int y)
{
struct drm_i915_private *i915 = to_i915(fb->dev);
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
int main_plane;
int hsub, vsub;
int tile_width, tile_height;
int ccs_x, ccs_y;
int main_x, main_y;
if (!is_ccs_plane(fb, ccs_plane) || is_gen12_ccs_cc_plane(fb, ccs_plane))
return 0;
intel_tile_dims(fb, ccs_plane, &tile_width, &tile_height);
intel_fb_plane_get_subsampling(&hsub, &vsub, fb, ccs_plane);
tile_width *= hsub;
tile_height *= vsub;
ccs_x = (x * hsub) % tile_width;
ccs_y = (y * vsub) % tile_height;
main_plane = skl_ccs_to_main_plane(fb, ccs_plane);
main_x = intel_fb->normal[main_plane].x % tile_width;
main_y = intel_fb->normal[main_plane].y % tile_height;
/*
* 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_dbg_kms(&i915->drm,
"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[main_plane].x,
intel_fb->normal[main_plane].y,
x, y);
return -EINVAL;
}
return 0;
}
static void
intel_fb_plane_dims(int *w, int *h, struct drm_framebuffer *fb, int color_plane)
{
int main_plane = is_ccs_plane(fb, color_plane) ?
skl_ccs_to_main_plane(fb, color_plane) : 0;
int main_hsub, main_vsub;
int hsub, vsub;
intel_fb_plane_get_subsampling(&main_hsub, &main_vsub, fb, main_plane);
intel_fb_plane_get_subsampling(&hsub, &vsub, fb, color_plane);
*w = fb->width / main_hsub / hsub;
*h = fb->height / main_vsub / vsub;
}
/*
* Setup the rotated view for an FB plane and return the size the GTT mapping
* requires for this view.
*/
static u32
setup_fb_rotation(int plane, const struct intel_remapped_plane_info *plane_info,
u32 gtt_offset_rotated, int x, int y,
unsigned int width, unsigned int height,
unsigned int tile_size,
unsigned int tile_width, unsigned int tile_height,
struct drm_framebuffer *fb)
{
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct intel_rotation_info *rot_info = &intel_fb->rot_info;
unsigned int pitch_tiles;
struct drm_rect r;
/* Y or Yf modifiers required for 90/270 rotation */
if (fb->modifier != I915_FORMAT_MOD_Y_TILED &&
fb->modifier != I915_FORMAT_MOD_Yf_TILED)
return 0;
if (drm_WARN_ON(fb->dev, plane >= ARRAY_SIZE(rot_info->plane)))
return 0;
rot_info->plane[plane] = *plane_info;
intel_fb->rotated[plane].pitch = plane_info->height * tile_height;
/* rotate the x/y offsets to match the GTT view */
drm_rect_init(&r, x, y, width, height);
drm_rect_rotate(&r,
plane_info->width * tile_width,
plane_info->height * tile_height,
DRM_MODE_ROTATE_270);
x = r.x1;
y = r.y1;
/* rotate the tile dimensions to match the GTT view */
pitch_tiles = intel_fb->rotated[plane].pitch / tile_height;
swap(tile_width, tile_height);
/*
* We only keep the x/y offsets, so push all of the
* gtt offset into the x/y offsets.
*/
intel_adjust_tile_offset(&x, &y,
tile_width, tile_height,
tile_size, pitch_tiles,
gtt_offset_rotated * tile_size, 0);
/*
* First pixel of the framebuffer from
* the start of the rotated gtt mapping.
*/
intel_fb->rotated[plane].x = x;
intel_fb->rotated[plane].y = y;
return plane_info->width * plane_info->height;
}
static int
intel_fill_fb_info(struct drm_i915_private *dev_priv,
struct drm_framebuffer *fb)
{
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj = intel_fb_obj(fb);
u32 gtt_offset_rotated = 0;
unsigned int max_size = 0;
int i, num_planes = fb->format->num_planes;
unsigned int tile_size = intel_tile_size(dev_priv);
for (i = 0; i < num_planes; i++) {
unsigned int width, height;
unsigned int cpp, size;
u32 offset;
int x, y;
int ret;
/*
* Plane 2 of Render Compression with Clear Color fb modifier
* is consumed by the driver and not passed to DE. Skip the
* arithmetic related to alignment and offset calculation.
*/
if (is_gen12_ccs_cc_plane(fb, i)) {
if (IS_ALIGNED(fb->offsets[i], PAGE_SIZE))
continue;
else
return -EINVAL;
}
cpp = fb->format->cpp[i];
intel_fb_plane_dims(&width, &height, fb, i);
ret = intel_fb_offset_to_xy(&x, &y, fb, i);
if (ret) {
drm_dbg_kms(&dev_priv->drm,
"bad fb plane %d offset: 0x%x\n",
i, fb->offsets[i]);
return ret;
}
ret = intel_fb_check_ccs_xy(fb, i, x, y);
if (ret)
return ret;
/*
* The fence (if used) is aligned to the start of the object
* so having the framebuffer wrap around across the edge of the
* fenced region doesn't really work. We have no API to configure
* the fence start offset within the object (nor could we probably
* on gen2/3). So it's just easier if we just require that the
* fb layout agrees with the fence layout. We already check that the
* fb stride matches the fence stride elsewhere.
*/
if (i == 0 && i915_gem_object_is_tiled(obj) &&
(x + width) * cpp > fb->pitches[i]) {
drm_dbg_kms(&dev_priv->drm,
"bad fb plane %d offset: 0x%x\n",
i, fb->offsets[i]);
return -EINVAL;
}
/*
* First pixel of the framebuffer from
* the start of the normal gtt mapping.
*/
intel_fb->normal[i].x = x;
intel_fb->normal[i].y = y;
offset = intel_compute_aligned_offset(dev_priv, &x, &y, fb, i,
fb->pitches[i],
DRM_MODE_ROTATE_0,
tile_size);
offset /= tile_size;
if (!is_surface_linear(fb, i)) {
struct intel_remapped_plane_info plane_info;
unsigned int tile_width, tile_height;
intel_tile_dims(fb, i, &tile_width, &tile_height);
plane_info.offset = offset;
plane_info.stride = DIV_ROUND_UP(fb->pitches[i],
tile_width * cpp);
plane_info.width = DIV_ROUND_UP(x + width, tile_width);
plane_info.height = DIV_ROUND_UP(y + height,
tile_height);
/* how many tiles does this plane need */
size = plane_info.stride * plane_info.height;
/*
* If the plane isn't horizontally tile aligned,
* we need one more tile.
*/
if (x != 0)
size++;
gtt_offset_rotated +=
setup_fb_rotation(i, &plane_info,
gtt_offset_rotated,
x, y, width, height,
tile_size,
tile_width, tile_height,
fb);
} else {
size = DIV_ROUND_UP((y + height) * fb->pitches[i] +
x * cpp, tile_size);
}
/* how many tiles in total needed in the bo */
max_size = max(max_size, offset + size);
}
if (mul_u32_u32(max_size, tile_size) > obj->base.size) {
drm_dbg_kms(&dev_priv->drm,
"fb too big for bo (need %llu bytes, have %zu bytes)\n",
mul_u32_u32(max_size, tile_size), obj->base.size);
return -EINVAL;
}
return 0;
}
static void
intel_plane_remap_gtt(struct intel_plane_state *plane_state)
{
struct drm_i915_private *dev_priv =
to_i915(plane_state->uapi.plane->dev);
struct drm_framebuffer *fb = plane_state->hw.fb;
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct intel_rotation_info *info = &plane_state->view.rotated;
unsigned int rotation = plane_state->hw.rotation;
int i, num_planes = fb->format->num_planes;
unsigned int tile_size = intel_tile_size(dev_priv);
unsigned int src_x, src_y;
unsigned int src_w, src_h;
u32 gtt_offset = 0;
memset(&plane_state->view, 0, sizeof(plane_state->view));
plane_state->view.type = drm_rotation_90_or_270(rotation) ?
I915_GGTT_VIEW_ROTATED : I915_GGTT_VIEW_REMAPPED;
src_x = plane_state->uapi.src.x1 >> 16;
src_y = plane_state->uapi.src.y1 >> 16;
src_w = drm_rect_width(&plane_state->uapi.src) >> 16;
src_h = drm_rect_height(&plane_state->uapi.src) >> 16;
drm_WARN_ON(&dev_priv->drm, is_ccs_modifier(fb->modifier));
/* Make src coordinates relative to the viewport */
drm_rect_translate(&plane_state->uapi.src,
-(src_x << 16), -(src_y << 16));
/* Rotate src coordinates to match rotated GTT view */
if (drm_rotation_90_or_270(rotation))
drm_rect_rotate(&plane_state->uapi.src,
src_w << 16, src_h << 16,
DRM_MODE_ROTATE_270);
for (i = 0; i < num_planes; i++) {
unsigned int hsub = i ? fb->format->hsub : 1;
unsigned int vsub = i ? fb->format->vsub : 1;
unsigned int cpp = fb->format->cpp[i];
unsigned int tile_width, tile_height;
unsigned int width, height;
unsigned int pitch_tiles;
unsigned int x, y;
u32 offset;
intel_tile_dims(fb, i, &tile_width, &tile_height);
x = src_x / hsub;
y = src_y / vsub;
width = src_w / hsub;
height = src_h / vsub;
/*
* First pixel of the src viewport from the
* start of the normal gtt mapping.
*/
x += intel_fb->normal[i].x;
y += intel_fb->normal[i].y;
offset = intel_compute_aligned_offset(dev_priv, &x, &y,
fb, i, fb->pitches[i],
DRM_MODE_ROTATE_0, tile_size);
offset /= tile_size;
drm_WARN_ON(&dev_priv->drm, i >= ARRAY_SIZE(info->plane));
info->plane[i].offset = offset;
info->plane[i].stride = DIV_ROUND_UP(fb->pitches[i],
tile_width * cpp);
info->plane[i].width = DIV_ROUND_UP(x + width, tile_width);
info->plane[i].height = DIV_ROUND_UP(y + height, tile_height);
if (drm_rotation_90_or_270(rotation)) {
struct drm_rect r;
/* rotate the x/y offsets to match the GTT view */
drm_rect_init(&r, x, y, width, height);
drm_rect_rotate(&r,
info->plane[i].width * tile_width,
info->plane[i].height * tile_height,
DRM_MODE_ROTATE_270);
x = r.x1;
y = r.y1;
pitch_tiles = info->plane[i].height;
plane_state->color_plane[i].stride = pitch_tiles * tile_height;
/* rotate the tile dimensions to match the GTT view */
swap(tile_width, tile_height);
} else {
pitch_tiles = info->plane[i].width;
plane_state->color_plane[i].stride = pitch_tiles * tile_width * cpp;
}
/*
* We only keep the x/y offsets, so push all of the
* gtt offset into the x/y offsets.
*/
intel_adjust_tile_offset(&x, &y,
tile_width, tile_height,
tile_size, pitch_tiles,
gtt_offset * tile_size, 0);
gtt_offset += info->plane[i].width * info->plane[i].height;
plane_state->color_plane[i].offset = 0;
plane_state->color_plane[i].x = x;
plane_state->color_plane[i].y = y;
}
}
int
intel_plane_compute_gtt(struct intel_plane_state *plane_state)
{
const struct intel_framebuffer *fb =
to_intel_framebuffer(plane_state->hw.fb);
unsigned int rotation = plane_state->hw.rotation;
int i, num_planes;
if (!fb)
return 0;
num_planes = fb->base.format->num_planes;
if (intel_plane_needs_remap(plane_state)) {
intel_plane_remap_gtt(plane_state);
/*
* Sometimes even remapping can't overcome
* the stride limitations :( Can happen with
* big plane sizes and suitably misaligned
* offsets.
*/
return intel_plane_check_stride(plane_state);
}
intel_fill_fb_ggtt_view(&plane_state->view, &fb->base, rotation);
for (i = 0; i < num_planes; i++) {
plane_state->color_plane[i].stride = intel_fb_pitch(&fb->base, i, rotation);
plane_state->color_plane[i].offset = 0;
if (drm_rotation_90_or_270(rotation)) {
plane_state->color_plane[i].x = fb->rotated[i].x;
plane_state->color_plane[i].y = fb->rotated[i].y;
} else {
plane_state->color_plane[i].x = fb->normal[i].x;
plane_state->color_plane[i].y = fb->normal[i].y;
}
}
/* Rotate src coordinates to match rotated GTT view */
if (drm_rotation_90_or_270(rotation))
drm_rect_rotate(&plane_state->uapi.src,
fb->base.width << 16, fb->base.height << 16,
DRM_MODE_ROTATE_270);
return intel_plane_check_stride(plane_state);
}
static struct i915_vma *
initial_plane_vma(struct drm_i915_private *i915,
struct intel_initial_plane_config *plane_config)

View file

@ -514,7 +514,6 @@ void intel_link_compute_m_n(u16 bpp, int nlanes,
void lpt_disable_clkout_dp(struct drm_i915_private *dev_priv);
u32 intel_plane_fb_max_stride(struct drm_i915_private *dev_priv,
u32 pixel_format, u64 modifier);
bool intel_plane_can_remap(const struct intel_plane_state *plane_state);
enum drm_mode_status
intel_mode_valid_max_plane_size(struct drm_i915_private *dev_priv,
const struct drm_display_mode *mode,
@ -626,10 +625,6 @@ bool
intel_format_info_is_yuv_semiplanar(const struct drm_format_info *info,
u64 modifier);
int intel_plane_compute_gtt(struct intel_plane_state *plane_state);
u32 intel_plane_compute_aligned_offset(int *x, int *y,
const struct intel_plane_state *state,
int color_plane);
int intel_plane_pin_fb(struct intel_plane_state *plane_state);
void intel_plane_unpin_fb(struct intel_plane_state *old_plane_state);
struct intel_encoder *
@ -638,15 +633,7 @@ intel_get_crtc_new_encoder(const struct intel_atomic_state *state,
unsigned int intel_surf_alignment(const struct drm_framebuffer *fb,
int color_plane);
void intel_fb_plane_get_subsampling(int *hsub, int *vsub,
const struct drm_framebuffer *fb,
int color_plane);
u32 intel_plane_adjust_aligned_offset(int *x, int *y,
const struct intel_plane_state *state,
int color_plane,
u32 old_offset, u32 new_offset);
unsigned int intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane);
unsigned int intel_tile_height(const struct drm_framebuffer *fb, int color_plane);
void intel_display_driver_register(struct drm_i915_private *i915);
void intel_display_driver_unregister(struct drm_i915_private *i915);

View file

@ -5,6 +5,7 @@
#include <drm/drm_framebuffer.h>
#include "intel_display.h"
#include "intel_display_types.h"
#include "intel_fb.h"
@ -27,6 +28,20 @@ bool is_gen12_ccs_cc_plane(const struct drm_framebuffer *fb, int plane)
plane == 2;
}
bool is_aux_plane(const struct drm_framebuffer *fb, int plane)
{
if (is_ccs_modifier(fb->modifier))
return is_ccs_plane(fb, plane);
return plane == 1;
}
bool is_semiplanar_uv_plane(const struct drm_framebuffer *fb, int color_plane)
{
return intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier) &&
color_plane == 1;
}
bool is_surface_linear(const struct drm_framebuffer *fb, int color_plane)
{
return fb->modifier == DRM_FORMAT_MOD_LINEAR ||
@ -65,6 +80,750 @@ int skl_main_to_aux_plane(const struct drm_framebuffer *fb, int main_plane)
return 0;
}
unsigned int intel_tile_size(const struct drm_i915_private *dev_priv)
{
return IS_DISPLAY_VER(dev_priv, 2) ? 2048 : 4096;
}
unsigned int intel_tile_height(const struct drm_framebuffer *fb, int color_plane)
{
if (is_gen12_ccs_plane(fb, color_plane))
return 1;
return intel_tile_size(to_i915(fb->dev)) /
intel_tile_width_bytes(fb, color_plane);
}
/* Return the tile dimensions in pixel units */
static void intel_tile_dims(const struct drm_framebuffer *fb, int color_plane,
unsigned int *tile_width,
unsigned int *tile_height)
{
unsigned int tile_width_bytes = intel_tile_width_bytes(fb, color_plane);
unsigned int cpp = fb->format->cpp[color_plane];
*tile_width = tile_width_bytes / cpp;
*tile_height = intel_tile_height(fb, color_plane);
}
unsigned int intel_tile_row_size(const struct drm_framebuffer *fb, int color_plane)
{
unsigned int tile_width, tile_height;
intel_tile_dims(fb, color_plane, &tile_width, &tile_height);
return fb->pitches[color_plane] * tile_height;
}
unsigned int intel_cursor_alignment(const struct drm_i915_private *dev_priv)
{
if (IS_I830(dev_priv))
return 16 * 1024;
else if (IS_I85X(dev_priv))
return 256;
else if (IS_I845G(dev_priv) || IS_I865G(dev_priv))
return 32;
else
return 4 * 1024;
}
void intel_fb_plane_get_subsampling(int *hsub, int *vsub,
const struct drm_framebuffer *fb,
int color_plane)
{
int main_plane;
if (color_plane == 0) {
*hsub = 1;
*vsub = 1;
return;
}
/*
* TODO: Deduct the subsampling from the char block for all CCS
* formats and planes.
*/
if (!is_gen12_ccs_plane(fb, color_plane)) {
*hsub = fb->format->hsub;
*vsub = fb->format->vsub;
return;
}
main_plane = skl_ccs_to_main_plane(fb, color_plane);
*hsub = drm_format_info_block_width(fb->format, color_plane) /
drm_format_info_block_width(fb->format, main_plane);
/*
* The min stride check in the core framebuffer_check() function
* assumes that format->hsub applies to every plane except for the
* first plane. That's incorrect for the CCS AUX plane of the first
* plane, but for the above check to pass we must define the block
* width with that subsampling applied to it. Adjust the width here
* accordingly, so we can calculate the actual subsampling factor.
*/
if (main_plane == 0)
*hsub *= fb->format->hsub;
*vsub = 32;
}
static void intel_fb_plane_dims(int *w, int *h, struct drm_framebuffer *fb, int color_plane)
{
int main_plane = is_ccs_plane(fb, color_plane) ?
skl_ccs_to_main_plane(fb, color_plane) : 0;
int main_hsub, main_vsub;
int hsub, vsub;
intel_fb_plane_get_subsampling(&main_hsub, &main_vsub, fb, main_plane);
intel_fb_plane_get_subsampling(&hsub, &vsub, fb, color_plane);
*w = fb->width / main_hsub / hsub;
*h = fb->height / main_vsub / vsub;
}
static u32 intel_adjust_tile_offset(int *x, int *y,
unsigned int tile_width,
unsigned int tile_height,
unsigned int tile_size,
unsigned int pitch_tiles,
u32 old_offset,
u32 new_offset)
{
unsigned int pitch_pixels = pitch_tiles * tile_width;
unsigned int tiles;
WARN_ON(old_offset & (tile_size - 1));
WARN_ON(new_offset & (tile_size - 1));
WARN_ON(new_offset > old_offset);
tiles = (old_offset - new_offset) / tile_size;
*y += tiles / pitch_tiles * tile_height;
*x += tiles % pitch_tiles * tile_width;
/* minimize x in case it got needlessly big */
*y += *x / pitch_pixels * tile_height;
*x %= pitch_pixels;
return new_offset;
}
static u32 intel_adjust_aligned_offset(int *x, int *y,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation,
unsigned int pitch,
u32 old_offset, u32 new_offset)
{
struct drm_i915_private *dev_priv = to_i915(fb->dev);
unsigned int cpp = fb->format->cpp[color_plane];
drm_WARN_ON(&dev_priv->drm, new_offset > old_offset);
if (!is_surface_linear(fb, color_plane)) {
unsigned int tile_size, tile_width, tile_height;
unsigned int pitch_tiles;
tile_size = intel_tile_size(dev_priv);
intel_tile_dims(fb, color_plane, &tile_width, &tile_height);
if (drm_rotation_90_or_270(rotation)) {
pitch_tiles = pitch / tile_height;
swap(tile_width, tile_height);
} else {
pitch_tiles = pitch / (tile_width * cpp);
}
intel_adjust_tile_offset(x, y, tile_width, tile_height,
tile_size, pitch_tiles,
old_offset, new_offset);
} else {
old_offset += *y * pitch + *x * cpp;
*y = (old_offset - new_offset) / pitch;
*x = ((old_offset - new_offset) - *y * pitch) / cpp;
}
return new_offset;
}
/*
* Adjust the tile offset by moving the difference into
* the x/y offsets.
*/
u32 intel_plane_adjust_aligned_offset(int *x, int *y,
const struct intel_plane_state *state,
int color_plane,
u32 old_offset, u32 new_offset)
{
return intel_adjust_aligned_offset(x, y, state->hw.fb, color_plane,
state->hw.rotation,
state->color_plane[color_plane].stride,
old_offset, new_offset);
}
/*
* Computes the aligned offset to the base tile and adjusts
* x, y. bytes per pixel is assumed to be a power-of-two.
*
* In the 90/270 rotated case, x and y are assumed
* to be already rotated to match the rotated GTT view, and
* pitch is the tile_height aligned framebuffer height.
*
* This function is used when computing the derived information
* under intel_framebuffer, so using any of that information
* here is not allowed. Anything under drm_framebuffer can be
* used. This is why the user has to pass in the pitch since it
* is specified in the rotated orientation.
*/
static u32 intel_compute_aligned_offset(struct drm_i915_private *dev_priv,
int *x, int *y,
const struct drm_framebuffer *fb,
int color_plane,
unsigned int pitch,
unsigned int rotation,
u32 alignment)
{
unsigned int cpp = fb->format->cpp[color_plane];
u32 offset, offset_aligned;
if (!is_surface_linear(fb, color_plane)) {
unsigned int tile_size, tile_width, tile_height;
unsigned int tile_rows, tiles, pitch_tiles;
tile_size = intel_tile_size(dev_priv);
intel_tile_dims(fb, color_plane, &tile_width, &tile_height);
if (drm_rotation_90_or_270(rotation)) {
pitch_tiles = pitch / tile_height;
swap(tile_width, tile_height);
} else {
pitch_tiles = pitch / (tile_width * cpp);
}
tile_rows = *y / tile_height;
*y %= tile_height;
tiles = *x / tile_width;
*x %= tile_width;
offset = (tile_rows * pitch_tiles + tiles) * tile_size;
offset_aligned = offset;
if (alignment)
offset_aligned = rounddown(offset_aligned, alignment);
intel_adjust_tile_offset(x, y, tile_width, tile_height,
tile_size, pitch_tiles,
offset, offset_aligned);
} else {
offset = *y * pitch + *x * cpp;
offset_aligned = offset;
if (alignment) {
offset_aligned = rounddown(offset_aligned, alignment);
*y = (offset % alignment) / pitch;
*x = ((offset % alignment) - *y * pitch) / cpp;
} else {
*y = *x = 0;
}
}
return offset_aligned;
}
u32 intel_plane_compute_aligned_offset(int *x, int *y,
const struct intel_plane_state *state,
int color_plane)
{
struct intel_plane *intel_plane = to_intel_plane(state->uapi.plane);
struct drm_i915_private *dev_priv = to_i915(intel_plane->base.dev);
const struct drm_framebuffer *fb = state->hw.fb;
unsigned int rotation = state->hw.rotation;
int pitch = state->color_plane[color_plane].stride;
u32 alignment;
if (intel_plane->id == PLANE_CURSOR)
alignment = intel_cursor_alignment(dev_priv);
else
alignment = intel_surf_alignment(fb, color_plane);
return intel_compute_aligned_offset(dev_priv, x, y, fb, color_plane,
pitch, rotation, alignment);
}
/* Convert the fb->offset[] into x/y offsets */
static int intel_fb_offset_to_xy(int *x, int *y,
const struct drm_framebuffer *fb,
int color_plane)
{
struct drm_i915_private *dev_priv = to_i915(fb->dev);
unsigned int height;
u32 alignment;
if (DISPLAY_VER(dev_priv) >= 12 &&
is_semiplanar_uv_plane(fb, color_plane))
alignment = intel_tile_row_size(fb, color_plane);
else if (fb->modifier != DRM_FORMAT_MOD_LINEAR)
alignment = intel_tile_size(dev_priv);
else
alignment = 0;
if (alignment != 0 && fb->offsets[color_plane] % alignment) {
drm_dbg_kms(&dev_priv->drm,
"Misaligned offset 0x%08x for color plane %d\n",
fb->offsets[color_plane], color_plane);
return -EINVAL;
}
height = drm_framebuffer_plane_height(fb->height, fb, color_plane);
height = ALIGN(height, intel_tile_height(fb, color_plane));
/* Catch potential overflows early */
if (add_overflows_t(u32, mul_u32_u32(height, fb->pitches[color_plane]),
fb->offsets[color_plane])) {
drm_dbg_kms(&dev_priv->drm,
"Bad offset 0x%08x or pitch %d for color plane %d\n",
fb->offsets[color_plane], fb->pitches[color_plane],
color_plane);
return -ERANGE;
}
*x = 0;
*y = 0;
intel_adjust_aligned_offset(x, y,
fb, color_plane, DRM_MODE_ROTATE_0,
fb->pitches[color_plane],
fb->offsets[color_plane], 0);
return 0;
}
static int intel_fb_check_ccs_xy(struct drm_framebuffer *fb, int ccs_plane, int x, int y)
{
struct drm_i915_private *i915 = to_i915(fb->dev);
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
int main_plane;
int hsub, vsub;
int tile_width, tile_height;
int ccs_x, ccs_y;
int main_x, main_y;
if (!is_ccs_plane(fb, ccs_plane) || is_gen12_ccs_cc_plane(fb, ccs_plane))
return 0;
intel_tile_dims(fb, ccs_plane, &tile_width, &tile_height);
intel_fb_plane_get_subsampling(&hsub, &vsub, fb, ccs_plane);
tile_width *= hsub;
tile_height *= vsub;
ccs_x = (x * hsub) % tile_width;
ccs_y = (y * vsub) % tile_height;
main_plane = skl_ccs_to_main_plane(fb, ccs_plane);
main_x = intel_fb->normal[main_plane].x % tile_width;
main_y = intel_fb->normal[main_plane].y % tile_height;
/*
* 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_dbg_kms(&i915->drm,
"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[main_plane].x,
intel_fb->normal[main_plane].y,
x, y);
return -EINVAL;
}
return 0;
}
static bool intel_plane_can_remap(const 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 i;
/* We don't want to deal with remapping with cursors */
if (plane->id == PLANE_CURSOR)
return false;
/*
* The display engine limits already match/exceed the
* render engine limits, so not much point in remapping.
* Would also need to deal with the fence POT alignment
* and gen2 2KiB GTT tile size.
*/
if (DISPLAY_VER(dev_priv) < 4)
return false;
/*
* The new CCS hash mode isn't compatible with remapping as
* the virtual address of the pages affects the compressed data.
*/
if (is_ccs_modifier(fb->modifier))
return false;
/* Linear needs a page aligned stride for remapping */
if (fb->modifier == DRM_FORMAT_MOD_LINEAR) {
unsigned int alignment = intel_tile_size(dev_priv) - 1;
for (i = 0; i < fb->format->num_planes; i++) {
if (fb->pitches[i] & alignment)
return false;
}
}
return true;
}
int intel_fb_pitch(const struct drm_framebuffer *fb, int color_plane, unsigned int rotation)
{
if (drm_rotation_90_or_270(rotation))
return to_intel_framebuffer(fb)->rotated[color_plane].pitch;
else
return fb->pitches[color_plane];
}
static bool intel_plane_needs_remap(const struct intel_plane_state *plane_state)
{
struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
const struct drm_framebuffer *fb = plane_state->hw.fb;
unsigned int rotation = plane_state->hw.rotation;
u32 stride, max_stride;
/*
* No remapping for invisible planes since we don't have
* an actual source viewport to remap.
*/
if (!plane_state->uapi.visible)
return false;
if (!intel_plane_can_remap(plane_state))
return false;
/*
* FIXME: aux plane limits on gen9+ are
* unclear in Bspec, for now no checking.
*/
stride = intel_fb_pitch(fb, 0, rotation);
max_stride = plane->max_stride(plane, fb->format->format,
fb->modifier, rotation);
return stride > max_stride;
}
/*
* Setup the rotated view for an FB plane and return the size the GTT mapping
* requires for this view.
*/
static u32 setup_fb_rotation(int plane, const struct intel_remapped_plane_info *plane_info,
u32 gtt_offset_rotated, int x, int y,
unsigned int width, unsigned int height,
unsigned int tile_size,
unsigned int tile_width, unsigned int tile_height,
struct drm_framebuffer *fb)
{
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct intel_rotation_info *rot_info = &intel_fb->rot_info;
unsigned int pitch_tiles;
struct drm_rect r;
/* Y or Yf modifiers required for 90/270 rotation */
if (fb->modifier != I915_FORMAT_MOD_Y_TILED &&
fb->modifier != I915_FORMAT_MOD_Yf_TILED)
return 0;
if (drm_WARN_ON(fb->dev, plane >= ARRAY_SIZE(rot_info->plane)))
return 0;
rot_info->plane[plane] = *plane_info;
intel_fb->rotated[plane].pitch = plane_info->height * tile_height;
/* rotate the x/y offsets to match the GTT view */
drm_rect_init(&r, x, y, width, height);
drm_rect_rotate(&r,
plane_info->width * tile_width,
plane_info->height * tile_height,
DRM_MODE_ROTATE_270);
x = r.x1;
y = r.y1;
/* rotate the tile dimensions to match the GTT view */
pitch_tiles = intel_fb->rotated[plane].pitch / tile_height;
swap(tile_width, tile_height);
/*
* We only keep the x/y offsets, so push all of the
* gtt offset into the x/y offsets.
*/
intel_adjust_tile_offset(&x, &y,
tile_width, tile_height,
tile_size, pitch_tiles,
gtt_offset_rotated * tile_size, 0);
/*
* First pixel of the framebuffer from
* the start of the rotated gtt mapping.
*/
intel_fb->rotated[plane].x = x;
intel_fb->rotated[plane].y = y;
return plane_info->width * plane_info->height;
}
int intel_fill_fb_info(struct drm_i915_private *dev_priv, struct drm_framebuffer *fb)
{
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj = intel_fb_obj(fb);
u32 gtt_offset_rotated = 0;
unsigned int max_size = 0;
int i, num_planes = fb->format->num_planes;
unsigned int tile_size = intel_tile_size(dev_priv);
for (i = 0; i < num_planes; i++) {
unsigned int width, height;
unsigned int cpp, size;
u32 offset;
int x, y;
int ret;
/*
* Plane 2 of Render Compression with Clear Color fb modifier
* is consumed by the driver and not passed to DE. Skip the
* arithmetic related to alignment and offset calculation.
*/
if (is_gen12_ccs_cc_plane(fb, i)) {
if (IS_ALIGNED(fb->offsets[i], PAGE_SIZE))
continue;
else
return -EINVAL;
}
cpp = fb->format->cpp[i];
intel_fb_plane_dims(&width, &height, fb, i);
ret = intel_fb_offset_to_xy(&x, &y, fb, i);
if (ret) {
drm_dbg_kms(&dev_priv->drm,
"bad fb plane %d offset: 0x%x\n",
i, fb->offsets[i]);
return ret;
}
ret = intel_fb_check_ccs_xy(fb, i, x, y);
if (ret)
return ret;
/*
* The fence (if used) is aligned to the start of the object
* so having the framebuffer wrap around across the edge of the
* fenced region doesn't really work. We have no API to configure
* the fence start offset within the object (nor could we probably
* on gen2/3). So it's just easier if we just require that the
* fb layout agrees with the fence layout. We already check that the
* fb stride matches the fence stride elsewhere.
*/
if (i == 0 && i915_gem_object_is_tiled(obj) &&
(x + width) * cpp > fb->pitches[i]) {
drm_dbg_kms(&dev_priv->drm,
"bad fb plane %d offset: 0x%x\n",
i, fb->offsets[i]);
return -EINVAL;
}
/*
* First pixel of the framebuffer from
* the start of the normal gtt mapping.
*/
intel_fb->normal[i].x = x;
intel_fb->normal[i].y = y;
offset = intel_compute_aligned_offset(dev_priv, &x, &y, fb, i,
fb->pitches[i],
DRM_MODE_ROTATE_0,
tile_size);
offset /= tile_size;
if (!is_surface_linear(fb, i)) {
struct intel_remapped_plane_info plane_info;
unsigned int tile_width, tile_height;
intel_tile_dims(fb, i, &tile_width, &tile_height);
plane_info.offset = offset;
plane_info.stride = DIV_ROUND_UP(fb->pitches[i],
tile_width * cpp);
plane_info.width = DIV_ROUND_UP(x + width, tile_width);
plane_info.height = DIV_ROUND_UP(y + height,
tile_height);
/* how many tiles does this plane need */
size = plane_info.stride * plane_info.height;
/*
* If the plane isn't horizontally tile aligned,
* we need one more tile.
*/
if (x != 0)
size++;
gtt_offset_rotated +=
setup_fb_rotation(i, &plane_info,
gtt_offset_rotated,
x, y, width, height,
tile_size,
tile_width, tile_height,
fb);
} else {
size = DIV_ROUND_UP((y + height) * fb->pitches[i] +
x * cpp, tile_size);
}
/* how many tiles in total needed in the bo */
max_size = max(max_size, offset + size);
}
if (mul_u32_u32(max_size, tile_size) > obj->base.size) {
drm_dbg_kms(&dev_priv->drm,
"fb too big for bo (need %llu bytes, have %zu bytes)\n",
mul_u32_u32(max_size, tile_size), obj->base.size);
return -EINVAL;
}
return 0;
}
static void intel_plane_remap_gtt(struct intel_plane_state *plane_state)
{
struct drm_i915_private *dev_priv =
to_i915(plane_state->uapi.plane->dev);
struct drm_framebuffer *fb = plane_state->hw.fb;
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct intel_rotation_info *info = &plane_state->view.rotated;
unsigned int rotation = plane_state->hw.rotation;
int i, num_planes = fb->format->num_planes;
unsigned int tile_size = intel_tile_size(dev_priv);
unsigned int src_x, src_y;
unsigned int src_w, src_h;
u32 gtt_offset = 0;
memset(&plane_state->view, 0, sizeof(plane_state->view));
plane_state->view.type = drm_rotation_90_or_270(rotation) ?
I915_GGTT_VIEW_ROTATED : I915_GGTT_VIEW_REMAPPED;
src_x = plane_state->uapi.src.x1 >> 16;
src_y = plane_state->uapi.src.y1 >> 16;
src_w = drm_rect_width(&plane_state->uapi.src) >> 16;
src_h = drm_rect_height(&plane_state->uapi.src) >> 16;
drm_WARN_ON(&dev_priv->drm, is_ccs_modifier(fb->modifier));
/* Make src coordinates relative to the viewport */
drm_rect_translate(&plane_state->uapi.src,
-(src_x << 16), -(src_y << 16));
/* Rotate src coordinates to match rotated GTT view */
if (drm_rotation_90_or_270(rotation))
drm_rect_rotate(&plane_state->uapi.src,
src_w << 16, src_h << 16,
DRM_MODE_ROTATE_270);
for (i = 0; i < num_planes; i++) {
unsigned int hsub = i ? fb->format->hsub : 1;
unsigned int vsub = i ? fb->format->vsub : 1;
unsigned int cpp = fb->format->cpp[i];
unsigned int tile_width, tile_height;
unsigned int width, height;
unsigned int pitch_tiles;
unsigned int x, y;
u32 offset;
intel_tile_dims(fb, i, &tile_width, &tile_height);
x = src_x / hsub;
y = src_y / vsub;
width = src_w / hsub;
height = src_h / vsub;
/*
* First pixel of the src viewport from the
* start of the normal gtt mapping.
*/
x += intel_fb->normal[i].x;
y += intel_fb->normal[i].y;
offset = intel_compute_aligned_offset(dev_priv, &x, &y,
fb, i, fb->pitches[i],
DRM_MODE_ROTATE_0, tile_size);
offset /= tile_size;
drm_WARN_ON(&dev_priv->drm, i >= ARRAY_SIZE(info->plane));
info->plane[i].offset = offset;
info->plane[i].stride = DIV_ROUND_UP(fb->pitches[i],
tile_width * cpp);
info->plane[i].width = DIV_ROUND_UP(x + width, tile_width);
info->plane[i].height = DIV_ROUND_UP(y + height, tile_height);
if (drm_rotation_90_or_270(rotation)) {
struct drm_rect r;
/* rotate the x/y offsets to match the GTT view */
drm_rect_init(&r, x, y, width, height);
drm_rect_rotate(&r,
info->plane[i].width * tile_width,
info->plane[i].height * tile_height,
DRM_MODE_ROTATE_270);
x = r.x1;
y = r.y1;
pitch_tiles = info->plane[i].height;
plane_state->color_plane[i].stride = pitch_tiles * tile_height;
/* rotate the tile dimensions to match the GTT view */
swap(tile_width, tile_height);
} else {
pitch_tiles = info->plane[i].width;
plane_state->color_plane[i].stride = pitch_tiles * tile_width * cpp;
}
/*
* We only keep the x/y offsets, so push all of the
* gtt offset into the x/y offsets.
*/
intel_adjust_tile_offset(&x, &y,
tile_width, tile_height,
tile_size, pitch_tiles,
gtt_offset * tile_size, 0);
gtt_offset += info->plane[i].width * info->plane[i].height;
plane_state->color_plane[i].offset = 0;
plane_state->color_plane[i].x = x;
plane_state->color_plane[i].y = y;
}
}
void intel_fill_fb_ggtt_view(struct i915_ggtt_view *view,
const struct drm_framebuffer *fb,
unsigned int rotation)
{
memset(view, 0, sizeof(*view));
view->type = I915_GGTT_VIEW_NORMAL;
if (drm_rotation_90_or_270(rotation)) {
view->type = I915_GGTT_VIEW_ROTATED;
view->rotated = to_intel_framebuffer(fb)->rot_info;
}
}
int intel_plane_check_stride(const struct intel_plane_state *plane_state)
{
struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
@ -96,3 +855,51 @@ int intel_plane_check_stride(const struct intel_plane_state *plane_state)
return 0;
}
int intel_plane_compute_gtt(struct intel_plane_state *plane_state)
{
const struct intel_framebuffer *fb =
to_intel_framebuffer(plane_state->hw.fb);
unsigned int rotation = plane_state->hw.rotation;
int i, num_planes;
if (!fb)
return 0;
num_planes = fb->base.format->num_planes;
if (intel_plane_needs_remap(plane_state)) {
intel_plane_remap_gtt(plane_state);
/*
* Sometimes even remapping can't overcome
* the stride limitations :( Can happen with
* big plane sizes and suitably misaligned
* offsets.
*/
return intel_plane_check_stride(plane_state);
}
intel_fill_fb_ggtt_view(&plane_state->view, &fb->base, rotation);
for (i = 0; i < num_planes; i++) {
plane_state->color_plane[i].stride = intel_fb_pitch(&fb->base, i, rotation);
plane_state->color_plane[i].offset = 0;
if (drm_rotation_90_or_270(rotation)) {
plane_state->color_plane[i].x = fb->rotated[i].x;
plane_state->color_plane[i].y = fb->rotated[i].y;
} else {
plane_state->color_plane[i].x = fb->normal[i].x;
plane_state->color_plane[i].y = fb->normal[i].y;
}
}
/* Rotate src coordinates to match rotated GTT view */
if (drm_rotation_90_or_270(rotation))
drm_rect_rotate(&plane_state->uapi.src,
fb->base.width << 16, fb->base.height << 16,
DRM_MODE_ROTATE_270);
return intel_plane_check_stride(plane_state);
}

View file

@ -10,11 +10,17 @@
struct drm_framebuffer;
struct drm_i915_private;
struct i915_ggtt_view;
struct intel_plane_state;
bool is_ccs_plane(const struct drm_framebuffer *fb, int plane);
bool is_gen12_ccs_plane(const struct drm_framebuffer *fb, int plane);
bool is_gen12_ccs_cc_plane(const struct drm_framebuffer *fb, int plane);
bool is_aux_plane(const struct drm_framebuffer *fb, int plane);
bool is_semiplanar_uv_plane(const struct drm_framebuffer *fb, int color_plane);
bool is_surface_linear(const struct drm_framebuffer *fb, int color_plane);
@ -24,4 +30,29 @@ int skl_main_to_aux_plane(const struct drm_framebuffer *fb, int main_plane);
int intel_plane_check_stride(const struct intel_plane_state *plane_state);
unsigned int intel_tile_size(const struct drm_i915_private *dev_priv);
unsigned int intel_tile_height(const struct drm_framebuffer *fb, int color_plane);
unsigned int intel_tile_row_size(const struct drm_framebuffer *fb, int color_plane);
unsigned int intel_cursor_alignment(const struct drm_i915_private *dev_priv);
void intel_fb_plane_get_subsampling(int *hsub, int *vsub,
const struct drm_framebuffer *fb,
int color_plane);
u32 intel_plane_adjust_aligned_offset(int *x, int *y,
const struct intel_plane_state *state,
int color_plane,
u32 old_offset, u32 new_offset);
u32 intel_plane_compute_aligned_offset(int *x, int *y,
const struct intel_plane_state *state,
int color_plane);
int intel_fb_pitch(const struct drm_framebuffer *fb, int color_plane, unsigned int rotation);
int intel_fill_fb_info(struct drm_i915_private *dev_priv, struct drm_framebuffer *fb);
void intel_fill_fb_ggtt_view(struct i915_ggtt_view *view, const struct drm_framebuffer *fb,
unsigned int rotation);
int intel_plane_compute_gtt(struct intel_plane_state *plane_state);
#endif /* __INTEL_FB_H__ */