diff mbox series

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

Message ID 20191213155907.16581-6-andrzej.p@collabora.com (mailing list archive)
State New, archived
Headers show
Series AFBC support for Rockchip | expand

Commit Message

Andrzej Pietrasiewicz Dec. 13, 2019, 3:58 p.m. UTC
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(-)

Comments

Liviu Dudau Dec. 16, 2019, 5:19 p.m. UTC | #1
Hi Andrzej,

On Fri, Dec 13, 2019 at 04:58:36PM +0100, Andrzej Pietrasiewicz wrote:
> 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);
> +		}

What has this change to do with the rest of the patch? Also, I think this goes
against the idea that an AFBC-aware driver might return better data about the format
info than the drm_format_info() code.

As a bikeshed, I know it is useful for debugging to turn the oneliner into 3, but it
feels like not necessary here.

Best regards,
Liviu

>  
>  	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,
> -- 
> 2.17.1
>
Andrzej Pietrasiewicz Dec. 16, 2019, 6:41 p.m. UTC | #2
Hi Liviu,

W dniu 16.12.2019 o 18:19, Liviu Dudau pisze:
> Hi Andrzej,
> 
> On Fri, Dec 13, 2019 at 04:58:36PM +0100, Andrzej Pietrasiewicz wrote:
>> 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);
>> +		}
> 
> What has this change to do with the rest of the patch? Also, I think this goes
> against the idea that an AFBC-aware driver might return better data about the format
> info than the drm_format_info() code.
> 

The reason is the conclusion of my talk with danvet on irc:

https://people.freedesktop.org/~cbrill/dri-log/?channel=dri-devel&highlight_names=&date=2019-11-13&show_html=true

I followed his advice - if I understood him correctly, that is.

> As a bikeshed, I know it is useful for debugging to turn the oneliner into 3, but it
> feels like not necessary here.

80 chars per line. If kept in one line, the limit is exceeded
with an additional indentation level present.

Regards,

Andrzej
Liviu Dudau Dec. 17, 2019, 9:18 a.m. UTC | #3
On Mon, Dec 16, 2019 at 07:41:23PM +0100, Andrzej Pietrasiewicz wrote:
> Hi Liviu,
> 
> W dniu 16.12.2019 o 18:19, Liviu Dudau pisze:
> > Hi Andrzej,
> > 
> > On Fri, Dec 13, 2019 at 04:58:36PM +0100, Andrzej Pietrasiewicz wrote:
> > > 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);
> > > +		}
> > 
> > What has this change to do with the rest of the patch? Also, I think this goes
> > against the idea that an AFBC-aware driver might return better data about the format
> > info than the drm_format_info() code.
> > 
> 
> The reason is the conclusion of my talk with danvet on irc:
> 
> https://people.freedesktop.org/~cbrill/dri-log/?channel=dri-devel&highlight_names=&date=2019-11-13&show_html=true
> 
> I followed his advice - if I understood him correctly, that is.

Yeah, I don't necessarily agree with danvet here. I think a better approach is to
still let the driver have a say in getting the format info, but if the hook is not
present or if it returns NULL then apply the AFBC code before (or as an alternative to)
the rest of the generic code.

> 
> > As a bikeshed, I know it is useful for debugging to turn the oneliner into 3, but it
> > feels like not necessary here.
> 
> 80 chars per line. If kept in one line, the limit is exceeded
> with an additional indentation level present.

DRM subsystem has never enforced that and there are plenty of instances in the core DRM 
code where that rule gets ignored. We know that mode_config.funcs is never NULL and
that the get_format_info() hook is always populated, so we don't really gain anything
from splitting it into multiple lines.

Best regards,
Liviu

> 
> Regards,
> 
> Andrzej
James Qian Wang Feb. 17, 2020, 11:02 a.m. UTC | #4
Hi Andrzej:

On Fri, Dec 13, 2019 at 04:58:36PM +0100, Andrzej Pietrasiewicz wrote:
> 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);

After check the references in malidp, rockchip and komeda, seems this
afbc->alignment_header is dedicated for komeda only.

This is not true. Per afbc HW spec alignment is essential for
all afbc usage. according to the spec the requiremnt are:

  AFBC1.0/1.1: 64 byte alignment both for header and body buffer.
  AFBC1.2 (tiled header enabled): 4096 alignment.

So this alignement is not a vendor specific value, but afbc feature
requirement, can be determined by afbc modifier.
(malidp and komeda obeys this spec, not sure about Rockchip, but I
think it should be)

But you may see, komeda uses 1024 (not 64) for none-tiled-header afbc,
that's because GPU(MALI) changed this value to 1024 for bus
performance (sorry I don't know the detail), and komeda changed to
1024 to follow.

Back to display alignment, I think we can just follow the spec, use 64
for none-tiled-header, 4096 for tiled-header, but no need to caller to
pass a value.

> +
> +	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;

Seems we can remove this bpp if we set the block_size (4x4) for these afbc
only formats like DRM_FORMAT_YUV420_8BIT/10BIT in drm_format_info, and then
we can calculate the bpp like the pitch calculation, then no need
caller to specify bpp anymore, and vendor specific get_bpp() likes in malidp
and komeda also can be removed.

for this change maybe we can put it into a new series.

Thanks
James

>  };
>  
>  struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,
diff mbox series

Patch

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,