[PATCHv4 05/36] drm/gem-fb-helper: Add generic afbc size checks

Andrzej Pietrasiewicz
2019-12-13T21:19:58Z

Extend the size-checking special function to handle afbc.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
---
drivers/gpu/drm/drm_fourcc.c | 10 +++-
drivers/gpu/drm/drm_framebuffer.c | 3 +
drivers/gpu/drm/drm_gem_framebuffer_helper.c | 60 ++++++++++++++++++--
include/drm/drm_gem_framebuffer_helper.h | 16 ++++++
4 files changed, 82 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
index d14dd7c86020..9ac2175c5bee 100644
--- a/drivers/gpu/drm/drm_fourcc.c
+++ b/drivers/gpu/drm/drm_fourcc.c
@@ -323,8 +323,14 @@ drm_get_format_info(struct drm_device *dev,
{
const struct drm_format_info *info = NULL;

- if (dev->mode_config.funcs->get_format_info)
- info = dev->mode_config.funcs->get_format_info(mode_cmd);
+ /* bypass driver callback if afbc */
+ if (!drm_is_afbc(mode_cmd->modifier[0]))
+ if (dev->mode_config.funcs->get_format_info) {
+ const struct drm_mode_config_funcs *funcs;
+
+ funcs = dev->mode_config.funcs;
+ info = funcs->get_format_info(mode_cmd);
+ }

if (!info)
info = drm_format_info(mode_cmd->pixel_format);
diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index 57564318ceea..33b741cc73e8 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -204,6 +204,9 @@ static int framebuffer_check(struct drm_device *dev,
unsigned int block_size = info->char_per_block[i];
u64 min_pitch = drm_format_info_min_pitch(info, i, width);

+ if (drm_is_afbc(r->modifier[i]))
+ block_size = 0;
+
if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) {
DRM_DEBUG_KMS("Format requires non-linear modifier for plane %d\n", i);
return -EINVAL;
diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c
index 4201dc1f32a5..e20f4d00b0a5 100644
--- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c
+++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c
@@ -21,6 +21,11 @@
#include <drm/drm_modeset_helper.h>
#include <drm/drm_simple_kms_helper.h>

+#define AFBC_HEADER_SIZE 16
+#define AFBC_TH_LAYOUT_ALIGNMENT 8
+#define AFBC_SUPERBLOCK_PIXELS 256
+#define AFBC_SUPERBLOCK_ALIGNMENT 128
+
/**
* DOC: overview
*
@@ -200,6 +205,40 @@ int drm_gem_fb_lookup(struct drm_device *dev,
}
EXPORT_SYMBOL_GPL(drm_gem_fb_lookup);

+static int drm_gem_afbc_min_size(struct drm_device *dev,
+ const struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_afbc *afbc)
+{
+ u32 n_blocks;
+
+ if (!drm_afbc_get_superblock_wh(mode_cmd->modifier[0],
+ &afbc->block_width,
+ &afbc->block_height)) {
+ return -EINVAL;
+ }
+
+ /* tiled header afbc */
+ if (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_TILED) {
+ afbc->block_width *= AFBC_TH_LAYOUT_ALIGNMENT;
+ afbc->block_height *= AFBC_TH_LAYOUT_ALIGNMENT;
+ }
+
+ afbc->aligned_width = ALIGN(mode_cmd->width, afbc->block_width);
+ afbc->aligned_height = ALIGN(mode_cmd->height, afbc->block_height);
+ afbc->offset = mode_cmd->offsets[0];
+
+ n_blocks = (afbc->aligned_width * afbc->aligned_height)
+ / AFBC_SUPERBLOCK_PIXELS;
+ afbc->offset_payload = ALIGN(n_blocks * AFBC_HEADER_SIZE,
+ afbc->alignment_header);
+
+ afbc->afbc_size = afbc->offset_payload + n_blocks *
+ ALIGN(afbc->bpp * AFBC_SUPERBLOCK_PIXELS / 8,
+ AFBC_SUPERBLOCK_ALIGNMENT);
+
+ return 0;
+}
+
/**
* drm_gem_fb_size_check_special() - Helper function for use in
* &drm_mode_config_funcs.fb_create
@@ -218,6 +257,7 @@ int drm_gem_fb_size_check_special(struct drm_device *dev,
const struct drm_size_check *check,
struct drm_gem_object **objs)
{
+#define CHECK_HAS(field) (check && check->field)
const struct drm_format_info *info;
int i;

@@ -231,24 +271,34 @@ int drm_gem_fb_size_check_special(struct drm_device *dev,
unsigned int min_size;
u32 pitch = mode_cmd->pitches[i];

- if (check && check->use_pitch_multiplier)
+ if (CHECK_HAS(use_pitch_multiplier))
if ((pitch * check->pitch_multiplier[i]) %
check->pitch_modulo)
return -EINVAL;

- if (check && check->use_min_size)
+ if (CHECK_HAS(use_min_size)) {
min_size = check->min_size[i];
- else
+ } else if (CHECK_HAS(data) &&
+ drm_is_afbc(mode_cmd->modifier[0])) {
+ struct drm_afbc *afbc;
+ int ret;
+
+ afbc = check->data;
+ ret = drm_gem_afbc_min_size(dev, mode_cmd, afbc);
+ if (ret < 0)
+ return ret;
+ min_size = ret;
+ } else {
min_size = (height - 1) * pitch
+ drm_format_info_min_pitch(info, i, width)
+ mode_cmd->offsets[i];
-
+ }
if (objs[i]->size < min_size)
return -EINVAL;
}

return 0;
-
+#undef CHECK_HAS
}
EXPORT_SYMBOL_GPL(drm_gem_fb_size_check_special);

diff --git a/include/drm/drm_gem_framebuffer_helper.h b/include/drm/drm_gem_framebuffer_helper.h
index 74304a268694..3d6015194b3c 100644
--- a/include/drm/drm_gem_framebuffer_helper.h
+++ b/include/drm/drm_gem_framebuffer_helper.h
@@ -22,6 +22,22 @@ struct drm_size_check {
u32 pitch_multiplier[4];
u32 pitch_modulo;
bool use_pitch_multiplier;
+ void *data;
+};
+
+/**
+ * struct drm_afbc - AFBC-specific data.
+ */
+struct drm_afbc {
+ u32 block_width;
+ u32 block_height;
+ u32 aligned_width;
+ u32 aligned_height;
+ u32 offset;
+ u32 alignment_header;
+ u32 afbc_size;
+ u32 offset_payload;
+ u32 bpp;
};

struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,