From: Benjamin Gaignard <benjamin.gaignard@collabora.com>
To: Nicolas Dufresne <nicolas@ndufresne.ca>,
mchehab@kernel.org, corbet@lwn.net, hverkuil+cisco@kernel.org,
laurent.pinchart@ideasonboard.com, sakari.ailus@linux.intel.com
Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org,
kernel@collabora.com
Subject: Re: [RFC] media: Add AFBC pixel formats
Date: Thu, 16 Apr 2026 18:29:05 +0200 [thread overview]
Message-ID: <a108f9f2-e274-4d81-8101-900fa629b4fb@collabora.com> (raw)
In-Reply-To: <0407249c6dcceec5419af3870c7bb4defb230e5a.camel@ndufresne.ca>
Le 16/04/2026 à 17:23, Nicolas Dufresne a écrit :
> Le jeudi 16 avril 2026 à 14:41 +0200, Benjamin Gaignard a écrit :
>> Add 8-bit and 10-bit YUV420 Arm Frame Buffer Compression (AFBC)
>> pixel formats.
>>
>> AFBC stride and image size computation needed to be done by
>> specific helpers functions which are also exported to be used
>> by drivers.
>>
>> Add documentation for each of the formats.
>>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>
>> ---
>> .../userspace-api/media/v4l/pixfmt-afbc.rst | 64 +++++++++++
>> .../userspace-api/media/v4l/pixfmt.rst | 1 +
>> drivers/media/v4l2-core/v4l2-common.c | 104 ++++++++++++++++++
>> drivers/media/v4l2-core/v4l2-ioctl.c | 4 +
>> include/media/v4l2-common.h | 4 +
>> include/uapi/linux/videodev2.h | 6 +
>> 6 files changed, 183 insertions(+)
>> create mode 100644 Documentation/userspace-api/media/v4l/pixfmt-afbc.rst
>>
>> diff --git a/Documentation/userspace-api/media/v4l/pixfmt-afbc.rst b/Documentation/userspace-api/media/v4l/pixfmt-afbc.rst
>> new file mode 100644
>> index 000000000000..2867e5d45810
>> --- /dev/null
>> +++ b/Documentation/userspace-api/media/v4l/pixfmt-afbc.rst
>> @@ -0,0 +1,64 @@
>> +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
>> +
>> +.. afbc:
>> +
>> +*******************************************
>> +ARM Frame Buffer Compression formats (AFBC)
>> +*******************************************
>> +
>> +The AFBC format is a lossless compression format which can support
>> +up to four components. It could compress 8 bits to 64 bits per pixel.
>> +The internal superblock size could be:
>> +
>> +- 16x16 pixels
>> +
>> +- 32x8 pixels
>> +
>> +- 64x4 pixels.
>> +
>> +The memory layout is composed of a header block followed by payload data.
> Because these format are so tied with the Graphics, I would avoid past mistakes
> with QC formats, and up-front document their matching DRM format and modifiers.
sure, I can add a column with the matching DRM format + modifier
>
> While its documented in ./Documentation/userspace-api/dma-buf-alloc-exchange.rst
> I would still document a bit the way we calculate a stride for this formats, or
> cross-reference that documentation, but being explicit for this specific format
> may avoid a lot of issues.
I don't see that in dma-buf-alloc-exchange.rst if you a place where it is
documented I can add a link to it.
>> +AFBC Formats
>> +============
>> +
>> +.. tabularcolumns:: |p{5.2cm}|p{1.0cm}|p{1.5cm}|p{1.9cm}|p{1.2cm}|p{1.8cm}|
>> +
>> +.. flat-table:: Overview of AFBC formats
>> + :header-rows: 1
>> + :stub-columns: 0
>> +
>> + * - Identifier
>> + - Code
>> + - Colorspace
>> + - Bits per component
>> + - Superblock size
>> + - Compression parameters
>> + * - V4L2_PIX_FMT_AFBC_YUV420_16x16
>> + - 'A168'
>> + - YUV420
>> + - 8 bits
>> + - 16x16
>> + - Sparse, Split
> From my accumulate knowledge, RK35xx RKVDEC produce non-split, and this one is
> split. To make it easy for naming in the future, I'd rename this one.
>
> V4L2_PIX_FMT_AFBC_YUV420_16x16_SPLIT
>
> Or
>
> V4L2_PIX_FMT_AFBC_YUV420_16x16_S
> V4L2_PIX_FMT_AFBC_YUV420_16x16S
>
> Other ideas, but it would be really hard to come up with a name for "not slip".
> While non-sparse is very special, and not commonly used.
Let's go for SPLIT it is more readable.
>
>> + * - V4L2_PIX_FMT_AFBC_YUV420_32x8
>> + - 'A328'
>> + - YUV420
>> + - 8 bits
>> + - 32x8
>> + - Sparse
>> + * - V4L2_PIX_FMT_AFBC_YUV420_16x16_10
>> + - 'A16a'
>> + - YUV420
>> + - 10 bits
>> + - 16x16
>> + - Sparse, Split
>> + * - V4L2_PIX_FMT_AFBC_YUV420_32x8_10
>> + - 'A32a'
>> + - YUV420
>> + - 10 bits
>> + - 32x8
>> + - Sparse
>> +
>> +.. _V4L2-PIX-FMT-AFBC-YUV420-16x16:
>> +.. _V4L2-PIX-FMT-AFBC-YUV420-32x8:
>> +.. _V4L2-PIX-FMT-AFBC-YUV420-16x16-10:
>> +.. _V4L2-PIX-FMT-AFBC-YUV420-32x8-10:
>> diff --git a/Documentation/userspace-api/media/v4l/pixfmt.rst b/Documentation/userspace-api/media/v4l/pixfmt.rst
>> index 71b29267488f..c6728b91b74f 100644
>> --- a/Documentation/userspace-api/media/v4l/pixfmt.rst
>> +++ b/Documentation/userspace-api/media/v4l/pixfmt.rst
>> @@ -26,6 +26,7 @@ see also :ref:`VIDIOC_G_FBUF <VIDIOC_G_FBUF>`.)
>> pixfmt-indexed
>> pixfmt-rgb
>> pixfmt-bayer
>> + pixfmt-afbc
>> yuv-formats
>> hsv-formats
>> depth-formats
>> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
>> index 554c591e1113..9187cb18a4ef 100644
>> --- a/drivers/media/v4l2-core/v4l2-common.c
>> +++ b/drivers/media/v4l2-core/v4l2-common.c
>> @@ -332,6 +332,16 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
>> { .format = V4L2_PIX_FMT_NV12MT_16X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2,
>> .block_w = { 16, 8, 0, 0 }, .block_h = { 16, 8, 0, 0 }},
>>
>> + /* AFBC formats */
>> + { .format = V4L2_PIX_FMT_AFBC_YUV420_16x16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1,
>> + .block_w = { 16, 0, 0, 0 }, .block_h = { 16, 0, 0, 0 }},
>> + { .format = V4L2_PIX_FMT_AFBC_YUV420_32x8, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1,
>> + .block_w = { 32, 0, 0, 0 }, .block_h = { 8, 0, 0, 0 }},
>> + { .format = V4L2_PIX_FMT_AFBC_YUV420_16x16_10, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 15, 0, 0, 0 }, .bpp_div = { 8, 1, 1, 1 }, .hdiv = 1, .vdiv = 1,
>> + .block_w = { 16, 0, 0, 0 }, .block_h = { 16, 0, 0, 0 }},
>> + { .format = V4L2_PIX_FMT_AFBC_YUV420_32x8_10, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 15, 0, 0, 0 }, .bpp_div = { 8, 1, 1, 1 }, .hdiv = 1, .vdiv = 1,
>> + .block_w = { 32, 0, 0, 0 }, .block_h = { 8, 0, 0, 0 }},
>> +
>> /* Bayer RGB formats */
>> { .format = V4L2_PIX_FMT_SBGGR8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
>> { .format = V4L2_PIX_FMT_SGBRG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
>> @@ -448,6 +458,97 @@ void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
>> }
>> EXPORT_SYMBOL_GPL(v4l2_apply_frmsize_constraints);
>>
>> +size_t v4l2_pixfmt_afbc_header_size(int fourcc, int width, int height)
>> +{
>> + int width_in_block, height_in_block;
>> +
>> + if (!v4l2_is_format_afbc(fourcc))
>> + return 0;
>> +
>> + switch (fourcc) {
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16:
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16_10:
>> + width_in_block = ALIGN(width, 16) >> 4;
>> + height_in_block = ALIGN(height, 16) >> 4;
>> + break;
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8:
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8_10:
>> + width_in_block = ALIGN(width, 32) >> 5;
>> + height_in_block = ALIGN(height, 8) >> 3;
>> + break;
>> + }
>> +
>> + return ALIGN(width_in_block * 16 * height_in_block, 128);
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_pixfmt_afbc_header_size);
>> +
>> +size_t v4l2_pixfmt_afbc_payload_size(int fourcc, int width, int height)
>> +{
>> + int width_in_block, height_in_block, block_payload_size;
>> +
>> + if (!v4l2_is_format_afbc(fourcc))
>> + return 0;
>> +
>> + switch (fourcc) {
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16:
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16_10:
>> + width_in_block = ALIGN(width, 16) >> 4;
>> + height_in_block = ALIGN(height, 16) >> 4;
>> + break;
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8:
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8_10:
>> + width_in_block = ALIGN(width, 32) >> 5;
>> + height_in_block = ALIGN(height, 8) >> 3;
>> + break;
>> + }
>> +
>> + switch (fourcc) {
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16:
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8:
>> + block_payload_size = 384;
>> + break;
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16_10:
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8_10:
>> + block_payload_size = 512;
>> + break;
>> + }
>> +
>> + return ALIGN(block_payload_size * width_in_block * height_in_block, 128);
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_pixfmt_afbc_payload_size);
>> +
>> +static int v4l2_fill_pixfmt_afbc(struct v4l2_pix_format_mplane *pixfmt,
>> + const struct v4l2_format_info *info)
>> +{
>> + struct v4l2_plane_pix_format *plane = &pixfmt->plane_fmt[0];
>> + unsigned int width = pixfmt->width;
>> + unsigned int height = pixfmt->height;
>> + unsigned int aligned_width = ALIGN(width, v4l2_format_block_width(info, 0));
>> + unsigned int stride = DIV_ROUND_UP(aligned_width, info->hdiv) *
>> + info->bpp[0] / info->bpp_div[0];
>> + size_t header_size = v4l2_pixfmt_afbc_header_size(info->format, width, height);
>> + size_t payload_size = v4l2_pixfmt_afbc_payload_size(info->format, width, height);
>> +
>> + plane->bytesperline = stride;
>> + plane->sizeimage = header_size + payload_size;
> I want to give some more thought on this, its literally by-pass
> v4l2_apply_frmsize_constraints(), and that I don't like much.
>
>> +
>> + return 0;
>> +}
>> +
>> +bool v4l2_is_format_afbc(int fourcc)
>> +{
>> + switch (fourcc) {
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16:
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8:
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16_10:
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8_10:
>> + return true;
>> + }
>> +
>> + return false;
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_is_format_afbc);
> Does that really need to be exported ?
Yes it is useful to not duplicate it in all drivers.
>
>> +
>> int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
>> u32 pixelformat, u32 width, u32 height)
>> {
>> @@ -464,6 +565,9 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
>> pixfmt->pixelformat = pixelformat;
>> pixfmt->num_planes = info->mem_planes;
>>
>> + if (v4l2_is_format_afbc(info->format))
>> + return v4l2_fill_pixfmt_afbc(pixfmt, info);
>> +
>> if (info->mem_planes == 1) {
>> plane = &pixfmt->plane_fmt[0];
>> plane->bytesperline = v4l2_format_plane_stride(info, 0, width);
>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index a2b650f4ec3c..016d4244c9ee 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -1387,6 +1387,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
>> case V4L2_PIX_FMT_YVU422M: descr = "Planar YVU 4:2:2 (N-C)"; break;
>> case V4L2_PIX_FMT_YUV444M: descr = "Planar YUV 4:4:4 (N-C)"; break;
>> case V4L2_PIX_FMT_YVU444M: descr = "Planar YVU 4:4:4 (N-C)"; break;
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16: descr = "AFBC 8-bit YUV420 16x16"; break;
> Missing "split" in the descr, which will cause confusion in the future.
Ok, just keep in mind that description field is limited to 32 bytes.
Benjamin
>
> Nicolas
>
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8: descr = "AFBC 8-bit YUV420 32x8"; break;
>> + case V4L2_PIX_FMT_AFBC_YUV420_16x16_10: descr = "AFBC 10-bit YUV420 16x16"; break;
>> + case V4L2_PIX_FMT_AFBC_YUV420_32x8_10: descr = "AFBC 10-bit YUV420 32x8"; break;
>> case V4L2_PIX_FMT_SBGGR8: descr = "8-bit Bayer BGBG/GRGR"; break;
>> case V4L2_PIX_FMT_SGBRG8: descr = "8-bit Bayer GBGB/RGRG"; break;
>> case V4L2_PIX_FMT_SGRBG8: descr = "8-bit Bayer GRGR/BGBG"; break;
>> diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
>> index f8b1faced79c..d1a75e6a7d4c 100644
>> --- a/include/media/v4l2-common.h
>> +++ b/include/media/v4l2-common.h
>> @@ -549,6 +549,10 @@ static inline bool v4l2_is_format_bayer(const struct v4l2_format_info *f)
>> return f && f->pixel_enc == V4L2_PIXEL_ENC_BAYER;
>> }
>>
>> +bool v4l2_is_format_afbc(int fourcc);
>> +size_t v4l2_pixfmt_afbc_header_size(int fourcc, int width, int height);
>> +size_t v4l2_pixfmt_afbc_payload_size(int fourcc, int width, int height);
>> +
>> const struct v4l2_format_info *v4l2_format_info(u32 format);
>> void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
>> const struct v4l2_frmsize_stepwise *frmsize);
>> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
>> index eda4492e40dc..88fafadbe13c 100644
>> --- a/include/uapi/linux/videodev2.h
>> +++ b/include/uapi/linux/videodev2.h
>> @@ -698,6 +698,12 @@ struct v4l2_pix_format {
>> #define V4L2_PIX_FMT_NV12M_8L128 v4l2_fourcc('N', 'A', '1', '2') /* Y/CbCr 4:2:0 8x128 tiles */
>> #define V4L2_PIX_FMT_NV12M_10BE_8L128 v4l2_fourcc_be('N', 'T', '1', '2') /* Y/CbCr 4:2:0 10-bit 8x128 tiles */
>>
>> +/* AFBC formats */
>> +#define V4L2_PIX_FMT_AFBC_YUV420_16x16 v4l2_fourcc('A', '1', '6', '8') /* AFBC containing 8-bit YUV420 in 16x16 blocks, sparse, split */
>> +#define V4L2_PIX_FMT_AFBC_YUV420_32x8 v4l2_fourcc('A', '3', '2', '8') /* AFBC containing 8-bit YUV420 in 32x8 blocks, sparse */
>> +#define V4L2_PIX_FMT_AFBC_YUV420_16x16_10 v4l2_fourcc('A', '1', '6', 'a') /* AFBC containing 10-bit YUV420 in 16x16 blocks, sparse, split */
>> +#define V4L2_PIX_FMT_AFBC_YUV420_32x8_10 v4l2_fourcc('A', '3', '2', 'a') /* AFBC containing 10-bit YUV420 in 32x8 blocks, sparse */
>> +
>> /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
>> #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */
>> #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */
next prev parent reply other threads:[~2026-04-16 16:29 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-16 12:41 [RFC] media: Add AFBC pixel formats Benjamin Gaignard
2026-04-16 15:23 ` Nicolas Dufresne
2026-04-16 16:29 ` Benjamin Gaignard [this message]
2026-04-16 16:51 ` Nicolas Dufresne
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=a108f9f2-e274-4d81-8101-900fa629b4fb@collabora.com \
--to=benjamin.gaignard@collabora.com \
--cc=corbet@lwn.net \
--cc=hverkuil+cisco@kernel.org \
--cc=kernel@collabora.com \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@kernel.org \
--cc=nicolas@ndufresne.ca \
--cc=sakari.ailus@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox