diff mbox series

[06/20] lib: Add video format information library

Message ID c17a896150f0b793cfa8581d18969cd7b3e93bf2.1555487650.git-series.maxime.ripard@bootlin.com (mailing list archive)
State New, archived
Headers show
Series drm: Split out the formats API and move it to a common place | expand

Commit Message

Maxime Ripard April 17, 2019, 7:54 a.m. UTC
Move the DRM formats API to turn this into a more generic image formats API
to be able to leverage it into some other places of the kernel, such as
v4l2 drivers.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 include/linux/image-formats.h | 387 +++++++++++++++++++++-
 lib/Kconfig                   |   7 +-
 lib/Makefile                  |   3 +-
 lib/image-formats-selftests.c | 325 +++++++++++++++++-
 lib/image-formats.c           | 655 +++++++++++++++++++++++++++++++++++-
 5 files changed, 1377 insertions(+)
 create mode 100644 include/linux/image-formats.h
 create mode 100644 lib/image-formats-selftests.c
 create mode 100644 lib/image-formats.c

Comments

Paul Kocialkowski April 17, 2019, 12:34 p.m. UTC | #1
Hi,

On Wed, 2019-04-17 at 09:54 +0200, Maxime Ripard wrote:
> Move the DRM formats API to turn this into a more generic image formats API
> to be able to leverage it into some other places of the kernel, such as
> v4l2 drivers.

See a few comments below.

> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  include/linux/image-formats.h | 387 +++++++++++++++++++++-
>  lib/Kconfig                   |   7 +-
>  lib/Makefile                  |   3 +-
>  lib/image-formats-selftests.c | 325 +++++++++++++++++-
>  lib/image-formats.c           | 655 +++++++++++++++++++++++++++++++++++-
>  5 files changed, 1377 insertions(+)
>  create mode 100644 include/linux/image-formats.h
>  create mode 100644 lib/image-formats-selftests.c
>  create mode 100644 lib/image-formats.c
> 
> diff --git a/include/linux/image-formats.h b/include/linux/image-formats.h
> new file mode 100644
> index 000000000000..ec43d9f9a527
> --- /dev/null
> +++ b/include/linux/image-formats.h
> @@ -0,0 +1,387 @@
> +/*
> + * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> + * Copyright (c) 2019 Maxime Ripard <maxime.ripard@bootlin.com>
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission.  The copyright holders make no representations
> + * about the suitability of this software for any purpose.  It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> + * OF THIS SOFTWARE.
> + */
> +
> +#ifndef _IMAGE_FORMATS_H_
> +#define _IMAGE_FORMATS_H_
> +
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +
> +/**
> + * struct image_format_info - information about a image format
> + */
> +struct image_format_info {
> +	union {
> +		/**
> +		 * @drm_fmt:
> +		 *
> +		 * DRM 4CC format identifier (DRM_FORMAT_*)
> +		 */
> +		u32 drm_fmt;

Could we call this one format_drm for consistency with the one below?

> +
> +		/**
> +		 * @format:
> +		 *
> +		 * DRM 4CC format identifier (DRM_FORMAT_*). Kept
> +		 * around for compatibility reasons with the current
> +		 * DRM drivers.
> +		 */
> +		u32 format;
> +	};
> +
> +	/**
> +	 * @depth:
> +	 *
> +	 * Color depth (number of bits per pixel excluding padding bits),
> +	 * valid for a subset of RGB formats only. This is a legacy field, do
> +	 * not use in new code and set to 0 for new formats.
> +	 */
> +	u8 depth;
> +
> +	/** @num_planes: Number of color planes (1 to 3) */
> +	u8 num_planes;
> +
> +	union {
> +		/**
> +		 * @cpp:
> +		 *
> +		 * Number of bytes per pixel (per plane), this is aliased with
> +		 * @char_per_block. It is deprecated in favour of using the
> +		 * triplet @char_per_block, @block_w, @block_h for better
> +		 * describing the pixel format.
> +		 */
> +		u8 cpp[3];
> +
> +		/**
> +		 * @char_per_block:
> +		 *
> +		 * Number of bytes per block (per plane), where blocks are
> +		 * defined as a rectangle of pixels which are stored next to
> +		 * each other in a byte aligned memory region. Together with
> +		 * @block_w and @block_h this is used to properly describe tiles
> +		 * in tiled formats or to describe groups of pixels in packed
> +		 * formats for which the memory needed for a single pixel is not
> +		 * byte aligned.
> +		 *
> +		 * @cpp has been kept for historical reasons because there are
> +		 * a lot of places in drivers where it's used. In drm core for
> +		 * generic code paths the preferred way is to use
> +		 * @char_per_block, image_format_info_block_width() and
> +		 * image_format_info_block_height() which allows handling both
> +		 * block and non-block formats in the same way.
> +		 *
> +		 * For formats that are intended to be used only with non-linear
> +		 * modifiers both @cpp and @char_per_block must be 0 in the
> +		 * generic format table. Drivers could supply accurate
> +		 * information from their drm_mode_config.get_format_info hook
> +		 * if they want the core to be validating the pitch.
> +		 */
> +		u8 char_per_block[3];
> +	};
> +
> +	/**
> +	 * @block_w:
> +	 *
> +	 * Block width in pixels, this is intended to be accessed through
> +	 * image_format_info_block_width()
> +	 */
> +	u8 block_w[3];
> +
> +	/**
> +	 * @block_h:
> +	 *
> +	 * Block height in pixels, this is intended to be accessed through
> +	 * image_format_info_block_height()
> +	 */
> +	u8 block_h[3];
> +
> +	/** @hsub: Horizontal chroma subsampling factor */
> +	u8 hsub;
> +	/** @vsub: Vertical chroma subsampling factor */
> +	u8 vsub;
> +
> +	/** @has_alpha: Does the format embeds an alpha component? */
> +	bool has_alpha;
> +
> +	/** @is_yuv: Is it a YUV format? */
> +	bool is_yuv;
> +};
> +
> +/**
> + * image_format_info_is_yuv_packed - check that the format info matches a YUV
> + * format with data laid in a single plane
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a packed YUV format.
> + */
> +static inline bool
> +image_format_info_is_yuv_packed(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->num_planes == 1;
> +}
> +
> +/**
> + * image_format_info_is_yuv_semiplanar - check that the format info matches a YUV
> + * format with data laid in two planes (luminance and chrominance)
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a semiplanar YUV format.
> + */
> +static inline bool
> +image_format_info_is_yuv_semiplanar(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->num_planes == 2;
> +}
> +
> +/**
> + * image_format_info_is_yuv_planar - check that the format info matches a YUV
> + * format with data laid in three planes (one for each YUV component)
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a planar YUV format.
> + */
> +static inline bool
> +image_format_info_is_yuv_planar(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->num_planes == 3;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_410 - check that the format info matches a
> + * YUV format with 4:1:0 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:1:0
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_410(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 4 && info->vsub == 4;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_411 - check that the format info matches a
> + * YUV format with 4:1:1 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:1:1
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_411(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 4 && info->vsub == 1;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_420 - check that the format info matches a
> + * YUV format with 4:2:0 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:2:0
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_420(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 2 && info->vsub == 2;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_422 - check that the format info matches a
> + * YUV format with 4:2:2 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:2:2
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_422(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 2 && info->vsub == 1;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_444 - check that the format info matches a
> + * YUV format with 4:4:4 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:4:4
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_444(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 1 && info->vsub == 1;
> +}
> +
> +/**
> + * image_format_info_plane_cpp - determine the bytes per pixel value
> + * @format: pixel format info
> + * @plane: plane index
> + *
> + * Returns:
> + * The bytes per pixel value for the specified plane.
> + */
> +static inline
> +int image_format_info_plane_cpp(const struct image_format_info *info, int plane)
> +{
> +	if (!info || plane >= info->num_planes)
> +		return 0;
> +
> +	return info->cpp[plane];
> +}
> +
> +/**
> + * image_format_info_plane_width - width of the plane given the first plane
> + * @format: pixel format info
> + * @width: width of the first plane
> + * @plane: plane index
> + *
> + * Returns:
> + * The width of @plane, given that the width of the first plane is @width.
> + */
> +static inline
> +int image_format_info_plane_width(const struct image_format_info *info, int width,
> +				  int plane)
> +{
> +	if (!info || plane >= info->num_planes)
> +		return 0;
> +
> +	if (plane == 0)
> +		return width;
> +
> +	return width / info->hsub;

I think you should DIV_ROUND_UP here.

> +}
> +
> +/**
> + * image_format_info_plane_height - height of the plane given the first plane
> + * @format: pixel format info
> + * @height: height of the first plane
> + * @plane: plane index
> + *
> + * Returns:
> + * The height of @plane, given that the height of the first plane is @height.
> + */
> +static inline
> +int image_format_info_plane_height(const struct image_format_info *info, int height,
> +				   int plane)
> +{
> +	if (!info || plane >= info->num_planes)
> +		return 0;
> +
> +	if (plane == 0)
> +		return height;
> +
> +	return height / info->vsub;

Ditto.

> +}
> +
> +/**
> + * image_format_info_block_width - width in pixels of block.
> + * @format: pointer to the image_format_info
> + * @plane: plane index
> + *
> + * Returns:
> + * The width in pixels of a block, depending on the plane index.
> + */
> +static inline
> +unsigned int image_format_info_block_width(const struct image_format_info *format,
> +					   int plane)
> +{
> +	if (!format)
> +		return 0;
> +
> +	if (plane < 0 || plane >= ARRAY_SIZE(format->block_w))
> +		return 0;
> +
> +	if (plane >= format->num_planes)
> +		return 0;
> +
> +	if (!format->block_w[plane])
> +		return 1;
> +
> +	return format->block_w[plane];
> +}
> +
> +/**
> + * image_format_info_block_height - height in pixels of a block
> + * @info: pointer to the image_format_info
> + * @plane: plane index
> + *
> + * Returns:
> + * The height in pixels of a block, depending on the plane index.
> + */
> +static inline
> +unsigned int image_format_info_block_height(const struct image_format_info *format,
> +					    int plane)
> +{
> +	if (!format)
> +		return 0;
> +
> +	if (plane < 0 || plane >= ARRAY_SIZE(format->block_w))
> +		return 0;
> +
> +	if (plane >= format->num_planes)
> +		return 0;
> +
> +	if (!format->block_h[plane])
> +		return 1;
> +
> +	return format->block_h[plane];
> +}
> +
> +/**
> + * image_format_info_min_pitch - computes the minimum required pitch in bytes
> + * @info: pixel format info
> + * @plane: plane index
> + * @buffer_width: buffer width in pixels
> + *
> + * Returns:
> + * The minimum required pitch in bytes for a buffer by taking into consideration
> + * the pixel format information and the buffer width.
> + */
> +static inline
> +uint64_t image_format_info_min_pitch(const struct image_format_info *info,
> +				     int plane, unsigned int buffer_width)
> +{
> +	if (!info || plane < 0 || plane >= info->num_planes)
> +		return 0;
> +
> +	return DIV_ROUND_UP_ULL((u64)buffer_width * info->char_per_block[plane],
> +			    image_format_info_block_width(info, plane) *
> +			    image_format_info_block_height(info, plane));

I'm not sure I understand how this works: char_per_block is 0 for
almost all formats and this doesn't take in account the cpp. Am I
missing something here?

Also, this might be a good occasion to discuss what meaning we want to
give "stride" and "pitch": should one be in bytes and the other in bit,
etc? I keep forgetting what each API expects.

Cheers,

Paul

> +}
> +
> +const struct image_format_info *__image_format_drm_lookup(u32 drm);
> +const struct image_format_info *image_format_drm_lookup(u32 drm);
> +
> +#endif /* _IMAGE_FORMATS_H_ */
> diff --git a/lib/Kconfig b/lib/Kconfig
> index fb453afff32e..9a0160d3123b 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -625,3 +625,10 @@ config GENERIC_LIB_UCMPDI2
>  
>  config OBJAGG
>  	tristate "objagg" if COMPILE_TEST
> +
> +config IMAGE_FORMATS
> +	bool
> +
> +config IMAGE_FORMATS_SELFTESTS
> +	tristate "Test image format functions"
> +	depends on IMAGE_FORMATS
> diff --git a/lib/Makefile b/lib/Makefile
> index 6996d2b9f401..203336b91248 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -280,3 +280,6 @@ obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
>  obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
>  obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
>  obj-$(CONFIG_OBJAGG) += objagg.o
> +
> +obj-$(CONFIG_IMAGE_FORMATS) += image-formats.o
> +obj-$(CONFIG_IMAGE_FORMATS_SELFTESTS) += image-formats-selftests.o
> diff --git a/lib/image-formats-selftests.c b/lib/image-formats-selftests.c
> new file mode 100644
> index 000000000000..d0f0011b535e
> --- /dev/null
> +++ b/lib/image-formats-selftests.c
> @@ -0,0 +1,325 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Test cases for the image_format functions
> + */
> +
> +#define pr_fmt(fmt) "image_format: " fmt
> +
> +#include <linux/errno.h>
> +#include <linux/image-formats.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +#include <drm/drm_fourcc.h>
> +
> +#define FAIL(test, msg, ...) \
> +	do { \
> +		if (test) { \
> +			pr_err("%s/%u: " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
> +			return -EINVAL; \
> +		} \
> +	} while (0)
> +
> +#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n")
> +
> +static int test_image_format_info_block_width(void)
> +{
> +	const struct image_format_info *info = NULL;
> +
> +	/* Test invalid arguments */
> +	FAIL_ON(image_format_info_block_width(info, 0) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 0);
> +
> +	/* Test 1 plane format */
> +	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_width(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +
> +	/* Test 2 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_NV12);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_width(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 2) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +
> +	/* Test 3 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_width(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 2) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 3) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +
> +	/* Test a tiled format */
> +	info = image_format_drm_lookup(DRM_FORMAT_X0L0);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_width(info, 0) != 2);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +
> +	return 0;
> +}
> +
> +static int test_image_format_info_block_height(void)
> +{
> +	const struct image_format_info *info = NULL;
> +
> +	/* Test invalid arguments */
> +	FAIL_ON(image_format_info_block_height(info, 0) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 0);
> +
> +	/* Test 1 plane format */
> +	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_height(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +
> +	/* Test 2 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_NV12);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_height(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 2) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +
> +	/* Test 3 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_height(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 2) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 3) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +
> +	/* Test a tiled format */
> +	info = image_format_drm_lookup(DRM_FORMAT_X0L0);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_height(info, 0) != 2);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +
> +	return 0;
> +}
> +
> +static int test_image_format_info_min_pitch(void)
> +{
> +	const struct image_format_info *info = NULL;
> +
> +	/* Test invalid arguments */
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	/* Test 1 plane 8 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_RGB332);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
> +			(uint64_t)(UINT_MAX - 1));
> +
> +	/* Test 1 plane 16 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 4);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1280);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 2048);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 3840);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 8192);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 1342);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX * 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
> +			(uint64_t)(UINT_MAX - 1) * 2);
> +
> +	/* Test 1 plane 24 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_RGB888);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 3);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 6);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 3072);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 5760);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 12288);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 2013);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX * 3);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
> +			(uint64_t)(UINT_MAX - 1) * 3);
> +
> +	/* Test 1 plane 32 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_ABGR8888);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 4);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 8);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 2560);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 7680);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 16384);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 2684);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX * 4);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
> +			(uint64_t)(UINT_MAX - 1) * 4);
> +
> +	/* Test 2 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_NV12);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 320) != 640);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 512) != 1024);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 960) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 2048) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 336) != 672);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) !=
> +			(uint64_t)UINT_MAX + 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
> +			(uint64_t)(UINT_MAX - 1));
> +	FAIL_ON(image_format_info_min_pitch(info, 1, (UINT_MAX - 1) /  2) !=
> +			(uint64_t)(UINT_MAX - 1));
> +
> +	/* Test 3 planes 8 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 3, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 320) != 320);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 320) != 320);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 512) != 512);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 512) != 512);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 960) != 960);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 960) != 960);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 2048) != 2048);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 2048) != 2048);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 336) != 336);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 336) != 336);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) !=
> +			(uint64_t)UINT_MAX / 2 + 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, UINT_MAX / 2 + 1) !=
> +			(uint64_t)UINT_MAX / 2 + 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1) / 2) !=
> +			(uint64_t)(UINT_MAX - 1) / 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2) !=
> +			(uint64_t)(UINT_MAX - 1) / 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, (UINT_MAX - 1) / 2) !=
> +			(uint64_t)(UINT_MAX - 1) / 2);
> +
> +	/* Test tiled format */
> +	info = image_format_drm_lookup(DRM_FORMAT_X0L2);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 4);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1280);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 2048);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 3840);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 8192);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 1342);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX * 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
> +			(uint64_t)(UINT_MAX - 1) * 2);
> +
> +	return 0;
> +}
> +
> +#define selftest(test)	{ .name = #test, .func = test, }
> +
> +static struct image_format_test {
> +	char	*name;
> +	int	(*func)(void);
> +} tests[] = {
> +	selftest(test_image_format_info_block_height),
> +	selftest(test_image_format_info_block_width),
> +	selftest(test_image_format_info_min_pitch),
> +};
> +
> +static int __init image_format_test_init(void)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(tests); i++) {
> +		struct image_format_test *test = &tests[i];
> +		int ret;
> +
> +		ret = test->func();
> +		if (ret) {
> +			pr_err("Failed test %s\n", test->name);
> +			return ret;
> +		}
> +	}
> +
> +	pr_info("All tests executed properly.\n");
> +	return 0;
> +}
> +module_init(image_format_test_init);
> +
> +MODULE_AUTHOR("Intel Corporation");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/lib/image-formats.c b/lib/image-formats.c
> new file mode 100644
> index 000000000000..1e52a7410222
> --- /dev/null
> +++ b/lib/image-formats.c
> @@ -0,0 +1,655 @@
> +/*
> + * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> + * Copyright (c) 2019 Maxime Ripard <maxime.ripard@bootlin.com>
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission.  The copyright holders make no representations
> + * about the suitability of this software for any purpose.  It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> + * OF THIS SOFTWARE.
> + */
> +
> +#include <linux/bug.h>
> +#include <linux/image-formats.h>
> +#include <linux/kernel.h>
> +#include <linux/math64.h>
> +
> +#include <uapi/drm/drm_fourcc.h>
> +
> +static const struct image_format_info formats[] = {
> +	{
> +		.drm_fmt = DRM_FORMAT_C8,
> +		.depth = 8,
> +		.num_planes = 1,
> +		.cpp = { 1, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB332,
> +		.depth = 8,
> +		.num_planes = 1,
> +		.cpp = { 1, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR233,
> +		.depth = 8,
> +		.num_planes = 1,
> +		.cpp = { 1, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ARGB4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ABGR4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBA4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRA4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB1555,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR1555,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX5551,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX5551,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ARGB1555,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ABGR1555,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBA5551,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRA5551,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB565,
> +		.depth = 16,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR565,
> +		.depth = 16,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 3, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 3, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB8888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR8888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX8888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX8888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB565_A8,
> +		.depth = 24,
> +		.num_planes = 2,
> +		.cpp = { 2, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR565_A8,
> +		.depth = 24,
> +		.num_planes = 2,
> +		.cpp = { 2, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB2101010,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR2101010,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX1010102,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX1010102,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ARGB2101010,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ABGR2101010,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBA1010102,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRA1010102,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ARGB8888,
> +		.depth = 32,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ABGR8888,
> +		.depth = 32,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBA8888,
> +		.depth = 32,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRA8888,
> +		.depth = 32,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 3, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 3, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB8888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 4, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR8888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 4, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX8888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 4, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX8888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 4, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV410,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 4,
> +		.vsub = 4,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU410,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 4,
> +		.vsub = 4,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV411,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 4,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU411,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 4,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV420,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU420,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV422,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU422,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV444,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU444,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV12,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV21,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV16,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV61,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV24,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV42,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUYV,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVYU,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_UYVY,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_VYUY,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XYUV8888,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_AYUV,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_Y0L0,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.char_per_block = { 8, 0, 0 },
> +		.block_w = { 2, 0, 0 },
> +		.block_h = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.has_alpha = true,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_X0L0,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.char_per_block = { 8, 0, 0 },
> +		.block_w = { 2, 0, 0 },
> +		.block_h = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_Y0L2,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.char_per_block = { 8, 0, 0 },
> +		.block_w = { 2, 0, 0 },
> +		.block_h = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.has_alpha = true,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_X0L2,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.char_per_block = { 8, 0, 0 },
> +		.block_w = { 2, 0, 0 },
> +		.block_h = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	},
> +};
> +
> +#define __image_format_lookup(_field, _fmt)			\
> +	({							\
> +		const struct image_format_info *format = NULL;	\
> +		unsigned i;					\
> +								\
> +		for (i = 0; i < ARRAY_SIZE(formats); i++)	\
> +			if (formats[i]._field == _fmt)		\
> +				format = &formats[i];		\
> +								\
> +		format;						\
> +	})
> +
> +/**
> + * __image_format_drm_lookup - query information for a given format
> + * @drm: DRM fourcc pixel format (DRM_FORMAT_*)
> + *
> + * The caller should only pass a supported pixel format to this function.
> + *
> + * Returns:
> + * The instance of struct image_format_info that describes the pixel format, or
> + * NULL if the format is unsupported.
> + */
> +const struct image_format_info *__image_format_drm_lookup(u32 drm)
> +{
> +	return __image_format_lookup(drm_fmt, drm);
> +}
> +EXPORT_SYMBOL(__image_format_drm_lookup);
> +
> +/**
> + * image_format_drm_lookup - query information for a given format
> + * @drm: DRM fourcc pixel format (DRM_FORMAT_*)
> + *
> + * The caller should only pass a supported pixel format to this function.
> + * Unsupported pixel formats will generate a warning in the kernel log.
> + *
> + * Returns:
> + * The instance of struct image_format_info that describes the pixel format, or
> + * NULL if the format is unsupported.
> + */
> +const struct image_format_info *image_format_drm_lookup(u32 drm)
> +{
> +	const struct image_format_info *format;
> +
> +	format = __image_format_drm_lookup(drm);
> +
> +	WARN_ON(!format);
> +	return format;
> +}
> +EXPORT_SYMBOL(image_format_drm_lookup);
Maxime Ripard April 17, 2019, 12:48 p.m. UTC | #2
Hi,

On Wed, Apr 17, 2019 at 02:34:54PM +0200, Paul Kocialkowski wrote:
> > +struct image_format_info {
> > +	union {
> > +		/**
> > +		 * @drm_fmt:
> > +		 *
> > +		 * DRM 4CC format identifier (DRM_FORMAT_*)
> > +		 */
> > +		u32 drm_fmt;
>
> Could we call this one format_drm for consistency with the one below?

The deprecated "format" field will go away at some point, so I'm not
sure the consistency is an argument there.

> > +/**
> > + * image_format_info_min_pitch - computes the minimum required pitch in bytes
> > + * @info: pixel format info
> > + * @plane: plane index
> > + * @buffer_width: buffer width in pixels
> > + *
> > + * Returns:
> > + * The minimum required pitch in bytes for a buffer by taking into consideration
> > + * the pixel format information and the buffer width.
> > + */
> > +static inline
> > +uint64_t image_format_info_min_pitch(const struct image_format_info *info,
> > +				     int plane, unsigned int buffer_width)
> > +{
> > +	if (!info || plane < 0 || plane >= info->num_planes)
> > +		return 0;
> > +
> > +	return DIV_ROUND_UP_ULL((u64)buffer_width * info->char_per_block[plane],
> > +			    image_format_info_block_width(info, plane) *
> > +			    image_format_info_block_height(info, plane));
>
> I'm not sure I understand how this works: char_per_block is 0 for
> almost all formats and this doesn't take in account the cpp. Am I
> missing something here?

I guess it doesn't. That's the DRM function here, without any
modification, but from a quick look at the current users of that
function, there's nobody that uses the value directly.

So you might be right there.

> Also, this might be a good occasion to discuss what meaning we want to
> give "stride" and "pitch": should one be in bytes and the other in bit,
> etc? I keep forgetting what each API expects.

pitch is documented as bytes, and the function computing the stride I
added did too. I'll remove the stride one.

Maxime

--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Paul Kocialkowski April 17, 2019, 2:03 p.m. UTC | #3
Hi,

On Wed, 2019-04-17 at 14:48 +0200, Maxime Ripard wrote:
> Hi,
> 
> On Wed, Apr 17, 2019 at 02:34:54PM +0200, Paul Kocialkowski wrote:
> > > +struct image_format_info {
> > > +	union {
> > > +		/**
> > > +		 * @drm_fmt:
> > > +		 *
> > > +		 * DRM 4CC format identifier (DRM_FORMAT_*)
> > > +		 */
> > > +		u32 drm_fmt;
> > 
> > Could we call this one format_drm for consistency with the one below?
> 
> The deprecated "format" field will go away at some point, so I'm not
> sure the consistency is an argument there.

Fair enough then. My point was mostly about format vs fmt, so one
option could be to use "compat_fmt" or such, but do whatever you feel
is right, I don't care too much about it.

> > > +/**
> > > + * image_format_info_min_pitch - computes the minimum required pitch in bytes
> > > + * @info: pixel format info
> > > + * @plane: plane index
> > > + * @buffer_width: buffer width in pixels
> > > + *
> > > + * Returns:
> > > + * The minimum required pitch in bytes for a buffer by taking into consideration
> > > + * the pixel format information and the buffer width.
> > > + */
> > > +static inline
> > > +uint64_t image_format_info_min_pitch(const struct image_format_info *info,
> > > +				     int plane, unsigned int buffer_width)
> > > +{
> > > +	if (!info || plane < 0 || plane >= info->num_planes)
> > > +		return 0;
> > > +
> > > +	return DIV_ROUND_UP_ULL((u64)buffer_width * info->char_per_block[plane],
> > > +			    image_format_info_block_width(info, plane) *
> > > +			    image_format_info_block_height(info, plane));
> > 
> > I'm not sure I understand how this works: char_per_block is 0 for
> > almost all formats and this doesn't take in account the cpp. Am I
> > missing something here?
> 
> I guess it doesn't. That's the DRM function here, without any
> modification, but from a quick look at the current users of that
> function, there's nobody that uses the value directly.
> 
> So you might be right there.
>
> > Also, this might be a good occasion to discuss what meaning we want to
> > give "stride" and "pitch": should one be in bytes and the other in bit,
> > etc? I keep forgetting what each API expects.
> 
> pitch is documented as bytes, and the function computing the stride I
> added did too. I'll remove the stride one.

Okay, it seems that I was just confused for no particular reason and
stride, pitch, bytesperline and linesize are all just synonyms. So feel
free to pick whichever you see fit :) 

Cheers,

Paul
Thomas Zimmermann April 23, 2019, 11:22 a.m. UTC | #4
Hi

Am 17.04.19 um 09:54 schrieb Maxime Ripard:
> Move the DRM formats API to turn this into a more generic image formats API
> to be able to leverage it into some other places of the kernel, such as
> v4l2 drivers.

Here's some nitpicking: 'image_format_*' appears slightly vague; it
could refer to a disk or ROM image as well. Something like
'pixel_format_*', 'pixbuf_format_*', etc. would clearly indicate graphics.

Best regards
Thomas

> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  include/linux/image-formats.h | 387 +++++++++++++++++++++-
>  lib/Kconfig                   |   7 +-
>  lib/Makefile                  |   3 +-
>  lib/image-formats-selftests.c | 325 +++++++++++++++++-
>  lib/image-formats.c           | 655 +++++++++++++++++++++++++++++++++++-
>  5 files changed, 1377 insertions(+)
>  create mode 100644 include/linux/image-formats.h
>  create mode 100644 lib/image-formats-selftests.c
>  create mode 100644 lib/image-formats.c
> 
> diff --git a/include/linux/image-formats.h b/include/linux/image-formats.h
> new file mode 100644
> index 000000000000..ec43d9f9a527
> --- /dev/null
> +++ b/include/linux/image-formats.h
> @@ -0,0 +1,387 @@
> +/*
> + * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> + * Copyright (c) 2019 Maxime Ripard <maxime.ripard@bootlin.com>
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission.  The copyright holders make no representations
> + * about the suitability of this software for any purpose.  It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> + * OF THIS SOFTWARE.
> + */
> +
> +#ifndef _IMAGE_FORMATS_H_
> +#define _IMAGE_FORMATS_H_
> +
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +
> +/**
> + * struct image_format_info - information about a image format
> + */
> +struct image_format_info {
> +	union {
> +		/**
> +		 * @drm_fmt:
> +		 *
> +		 * DRM 4CC format identifier (DRM_FORMAT_*)
> +		 */
> +		u32 drm_fmt;
> +
> +		/**
> +		 * @format:
> +		 *
> +		 * DRM 4CC format identifier (DRM_FORMAT_*). Kept
> +		 * around for compatibility reasons with the current
> +		 * DRM drivers.
> +		 */
> +		u32 format;
> +	};
> +
> +	/**
> +	 * @depth:
> +	 *
> +	 * Color depth (number of bits per pixel excluding padding bits),
> +	 * valid for a subset of RGB formats only. This is a legacy field, do
> +	 * not use in new code and set to 0 for new formats.
> +	 */
> +	u8 depth;
> +
> +	/** @num_planes: Number of color planes (1 to 3) */
> +	u8 num_planes;
> +
> +	union {
> +		/**
> +		 * @cpp:
> +		 *
> +		 * Number of bytes per pixel (per plane), this is aliased with
> +		 * @char_per_block. It is deprecated in favour of using the
> +		 * triplet @char_per_block, @block_w, @block_h for better
> +		 * describing the pixel format.
> +		 */
> +		u8 cpp[3];
> +
> +		/**
> +		 * @char_per_block:
> +		 *
> +		 * Number of bytes per block (per plane), where blocks are
> +		 * defined as a rectangle of pixels which are stored next to
> +		 * each other in a byte aligned memory region. Together with
> +		 * @block_w and @block_h this is used to properly describe tiles
> +		 * in tiled formats or to describe groups of pixels in packed
> +		 * formats for which the memory needed for a single pixel is not
> +		 * byte aligned.
> +		 *
> +		 * @cpp has been kept for historical reasons because there are
> +		 * a lot of places in drivers where it's used. In drm core for
> +		 * generic code paths the preferred way is to use
> +		 * @char_per_block, image_format_info_block_width() and
> +		 * image_format_info_block_height() which allows handling both
> +		 * block and non-block formats in the same way.
> +		 *
> +		 * For formats that are intended to be used only with non-linear
> +		 * modifiers both @cpp and @char_per_block must be 0 in the
> +		 * generic format table. Drivers could supply accurate
> +		 * information from their drm_mode_config.get_format_info hook
> +		 * if they want the core to be validating the pitch.
> +		 */
> +		u8 char_per_block[3];
> +	};
> +
> +	/**
> +	 * @block_w:
> +	 *
> +	 * Block width in pixels, this is intended to be accessed through
> +	 * image_format_info_block_width()
> +	 */
> +	u8 block_w[3];
> +
> +	/**
> +	 * @block_h:
> +	 *
> +	 * Block height in pixels, this is intended to be accessed through
> +	 * image_format_info_block_height()
> +	 */
> +	u8 block_h[3];
> +
> +	/** @hsub: Horizontal chroma subsampling factor */
> +	u8 hsub;
> +	/** @vsub: Vertical chroma subsampling factor */
> +	u8 vsub;
> +
> +	/** @has_alpha: Does the format embeds an alpha component? */
> +	bool has_alpha;
> +
> +	/** @is_yuv: Is it a YUV format? */
> +	bool is_yuv;
> +};
> +
> +/**
> + * image_format_info_is_yuv_packed - check that the format info matches a YUV
> + * format with data laid in a single plane
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a packed YUV format.
> + */
> +static inline bool
> +image_format_info_is_yuv_packed(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->num_planes == 1;
> +}
> +
> +/**
> + * image_format_info_is_yuv_semiplanar - check that the format info matches a YUV
> + * format with data laid in two planes (luminance and chrominance)
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a semiplanar YUV format.
> + */
> +static inline bool
> +image_format_info_is_yuv_semiplanar(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->num_planes == 2;
> +}
> +
> +/**
> + * image_format_info_is_yuv_planar - check that the format info matches a YUV
> + * format with data laid in three planes (one for each YUV component)
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a planar YUV format.
> + */
> +static inline bool
> +image_format_info_is_yuv_planar(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->num_planes == 3;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_410 - check that the format info matches a
> + * YUV format with 4:1:0 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:1:0
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_410(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 4 && info->vsub == 4;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_411 - check that the format info matches a
> + * YUV format with 4:1:1 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:1:1
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_411(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 4 && info->vsub == 1;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_420 - check that the format info matches a
> + * YUV format with 4:2:0 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:2:0
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_420(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 2 && info->vsub == 2;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_422 - check that the format info matches a
> + * YUV format with 4:2:2 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:2:2
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_422(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 2 && info->vsub == 1;
> +}
> +
> +/**
> + * image_format_info_is_yuv_sampling_444 - check that the format info matches a
> + * YUV format with 4:4:4 sub-sampling
> + * @info: format info
> + *
> + * Returns:
> + * A boolean indicating whether the format info matches a YUV format with 4:4:4
> + * sub-sampling.
> + */
> +static inline bool
> +image_format_info_is_yuv_sampling_444(const struct image_format_info *info)
> +{
> +	return info->is_yuv && info->hsub == 1 && info->vsub == 1;
> +}
> +
> +/**
> + * image_format_info_plane_cpp - determine the bytes per pixel value
> + * @format: pixel format info
> + * @plane: plane index
> + *
> + * Returns:
> + * The bytes per pixel value for the specified plane.
> + */
> +static inline
> +int image_format_info_plane_cpp(const struct image_format_info *info, int plane)
> +{
> +	if (!info || plane >= info->num_planes)
> +		return 0;
> +
> +	return info->cpp[plane];
> +}
> +
> +/**
> + * image_format_info_plane_width - width of the plane given the first plane
> + * @format: pixel format info
> + * @width: width of the first plane
> + * @plane: plane index
> + *
> + * Returns:
> + * The width of @plane, given that the width of the first plane is @width.
> + */
> +static inline
> +int image_format_info_plane_width(const struct image_format_info *info, int width,
> +				  int plane)
> +{
> +	if (!info || plane >= info->num_planes)
> +		return 0;
> +
> +	if (plane == 0)
> +		return width;
> +
> +	return width / info->hsub;
> +}
> +
> +/**
> + * image_format_info_plane_height - height of the plane given the first plane
> + * @format: pixel format info
> + * @height: height of the first plane
> + * @plane: plane index
> + *
> + * Returns:
> + * The height of @plane, given that the height of the first plane is @height.
> + */
> +static inline
> +int image_format_info_plane_height(const struct image_format_info *info, int height,
> +				   int plane)
> +{
> +	if (!info || plane >= info->num_planes)
> +		return 0;
> +
> +	if (plane == 0)
> +		return height;
> +
> +	return height / info->vsub;
> +}
> +
> +/**
> + * image_format_info_block_width - width in pixels of block.
> + * @format: pointer to the image_format_info
> + * @plane: plane index
> + *
> + * Returns:
> + * The width in pixels of a block, depending on the plane index.
> + */
> +static inline
> +unsigned int image_format_info_block_width(const struct image_format_info *format,
> +					   int plane)
> +{
> +	if (!format)
> +		return 0;
> +
> +	if (plane < 0 || plane >= ARRAY_SIZE(format->block_w))
> +		return 0;
> +
> +	if (plane >= format->num_planes)
> +		return 0;
> +
> +	if (!format->block_w[plane])
> +		return 1;
> +
> +	return format->block_w[plane];
> +}
> +
> +/**
> + * image_format_info_block_height - height in pixels of a block
> + * @info: pointer to the image_format_info
> + * @plane: plane index
> + *
> + * Returns:
> + * The height in pixels of a block, depending on the plane index.
> + */
> +static inline
> +unsigned int image_format_info_block_height(const struct image_format_info *format,
> +					    int plane)
> +{
> +	if (!format)
> +		return 0;
> +
> +	if (plane < 0 || plane >= ARRAY_SIZE(format->block_w))
> +		return 0;
> +
> +	if (plane >= format->num_planes)
> +		return 0;
> +
> +	if (!format->block_h[plane])
> +		return 1;
> +
> +	return format->block_h[plane];
> +}
> +
> +/**
> + * image_format_info_min_pitch - computes the minimum required pitch in bytes
> + * @info: pixel format info
> + * @plane: plane index
> + * @buffer_width: buffer width in pixels
> + *
> + * Returns:
> + * The minimum required pitch in bytes for a buffer by taking into consideration
> + * the pixel format information and the buffer width.
> + */
> +static inline
> +uint64_t image_format_info_min_pitch(const struct image_format_info *info,
> +				     int plane, unsigned int buffer_width)
> +{
> +	if (!info || plane < 0 || plane >= info->num_planes)
> +		return 0;
> +
> +	return DIV_ROUND_UP_ULL((u64)buffer_width * info->char_per_block[plane],
> +			    image_format_info_block_width(info, plane) *
> +			    image_format_info_block_height(info, plane));
> +}
> +
> +const struct image_format_info *__image_format_drm_lookup(u32 drm);
> +const struct image_format_info *image_format_drm_lookup(u32 drm);
> +
> +#endif /* _IMAGE_FORMATS_H_ */
> diff --git a/lib/Kconfig b/lib/Kconfig
> index fb453afff32e..9a0160d3123b 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -625,3 +625,10 @@ config GENERIC_LIB_UCMPDI2
>  
>  config OBJAGG
>  	tristate "objagg" if COMPILE_TEST
> +
> +config IMAGE_FORMATS
> +	bool
> +
> +config IMAGE_FORMATS_SELFTESTS
> +	tristate "Test image format functions"
> +	depends on IMAGE_FORMATS
> diff --git a/lib/Makefile b/lib/Makefile
> index 6996d2b9f401..203336b91248 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -280,3 +280,6 @@ obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
>  obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
>  obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
>  obj-$(CONFIG_OBJAGG) += objagg.o
> +
> +obj-$(CONFIG_IMAGE_FORMATS) += image-formats.o
> +obj-$(CONFIG_IMAGE_FORMATS_SELFTESTS) += image-formats-selftests.o
> diff --git a/lib/image-formats-selftests.c b/lib/image-formats-selftests.c
> new file mode 100644
> index 000000000000..d0f0011b535e
> --- /dev/null
> +++ b/lib/image-formats-selftests.c
> @@ -0,0 +1,325 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Test cases for the image_format functions
> + */
> +
> +#define pr_fmt(fmt) "image_format: " fmt
> +
> +#include <linux/errno.h>
> +#include <linux/image-formats.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +#include <drm/drm_fourcc.h>
> +
> +#define FAIL(test, msg, ...) \
> +	do { \
> +		if (test) { \
> +			pr_err("%s/%u: " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
> +			return -EINVAL; \
> +		} \
> +	} while (0)
> +
> +#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n")
> +
> +static int test_image_format_info_block_width(void)
> +{
> +	const struct image_format_info *info = NULL;
> +
> +	/* Test invalid arguments */
> +	FAIL_ON(image_format_info_block_width(info, 0) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 0);
> +
> +	/* Test 1 plane format */
> +	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_width(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +
> +	/* Test 2 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_NV12);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_width(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 2) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +
> +	/* Test 3 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_width(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 2) != 1);
> +	FAIL_ON(image_format_info_block_width(info, 3) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +
> +	/* Test a tiled format */
> +	info = image_format_drm_lookup(DRM_FORMAT_X0L0);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_width(info, 0) != 2);
> +	FAIL_ON(image_format_info_block_width(info, 1) != 0);
> +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> +
> +	return 0;
> +}
> +
> +static int test_image_format_info_block_height(void)
> +{
> +	const struct image_format_info *info = NULL;
> +
> +	/* Test invalid arguments */
> +	FAIL_ON(image_format_info_block_height(info, 0) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 0);
> +
> +	/* Test 1 plane format */
> +	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_height(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +
> +	/* Test 2 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_NV12);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_height(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 2) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +
> +	/* Test 3 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_height(info, 0) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 2) != 1);
> +	FAIL_ON(image_format_info_block_height(info, 3) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +
> +	/* Test a tiled format */
> +	info = image_format_drm_lookup(DRM_FORMAT_X0L0);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_block_height(info, 0) != 2);
> +	FAIL_ON(image_format_info_block_height(info, 1) != 0);
> +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> +
> +	return 0;
> +}
> +
> +static int test_image_format_info_min_pitch(void)
> +{
> +	const struct image_format_info *info = NULL;
> +
> +	/* Test invalid arguments */
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	/* Test 1 plane 8 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_RGB332);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
> +			(uint64_t)(UINT_MAX - 1));
> +
> +	/* Test 1 plane 16 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 4);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1280);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 2048);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 3840);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 8192);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 1342);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX * 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
> +			(uint64_t)(UINT_MAX - 1) * 2);
> +
> +	/* Test 1 plane 24 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_RGB888);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 3);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 6);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 3072);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 5760);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 12288);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 2013);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX * 3);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
> +			(uint64_t)(UINT_MAX - 1) * 3);
> +
> +	/* Test 1 plane 32 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_ABGR8888);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 4);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 8);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 2560);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 7680);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 16384);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 2684);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX * 4);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
> +			(uint64_t)(UINT_MAX - 1) * 4);
> +
> +	/* Test 2 planes format */
> +	info = image_format_drm_lookup(DRM_FORMAT_NV12);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 320) != 640);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 512) != 1024);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 960) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 2048) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 336) != 672);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) !=
> +			(uint64_t)UINT_MAX + 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
> +			(uint64_t)(UINT_MAX - 1));
> +	FAIL_ON(image_format_info_min_pitch(info, 1, (UINT_MAX - 1) /  2) !=
> +			(uint64_t)(UINT_MAX - 1));
> +
> +	/* Test 3 planes 8 bits per pixel format */
> +	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 3, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 1) != 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 2) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 320) != 320);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 320) != 320);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 512) != 512);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 512) != 512);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 960) != 960);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 960) != 960);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 2048) != 2048);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 2048) != 2048);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 336) != 336);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, 336) != 336);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) !=
> +			(uint64_t)UINT_MAX / 2 + 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, UINT_MAX / 2 + 1) !=
> +			(uint64_t)UINT_MAX / 2 + 1);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1) / 2) !=
> +			(uint64_t)(UINT_MAX - 1) / 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2) !=
> +			(uint64_t)(UINT_MAX - 1) / 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 2, (UINT_MAX - 1) / 2) !=
> +			(uint64_t)(UINT_MAX - 1) / 2);
> +
> +	/* Test tiled format */
> +	info = image_format_drm_lookup(DRM_FORMAT_X0L2);
> +	FAIL_ON(!info);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> +
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 4);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1280);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 2048);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 3840);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 8192);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 1342);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> +			(uint64_t)UINT_MAX * 2);
> +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
> +			(uint64_t)(UINT_MAX - 1) * 2);
> +
> +	return 0;
> +}
> +
> +#define selftest(test)	{ .name = #test, .func = test, }
> +
> +static struct image_format_test {
> +	char	*name;
> +	int	(*func)(void);
> +} tests[] = {
> +	selftest(test_image_format_info_block_height),
> +	selftest(test_image_format_info_block_width),
> +	selftest(test_image_format_info_min_pitch),
> +};
> +
> +static int __init image_format_test_init(void)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(tests); i++) {
> +		struct image_format_test *test = &tests[i];
> +		int ret;
> +
> +		ret = test->func();
> +		if (ret) {
> +			pr_err("Failed test %s\n", test->name);
> +			return ret;
> +		}
> +	}
> +
> +	pr_info("All tests executed properly.\n");
> +	return 0;
> +}
> +module_init(image_format_test_init);
> +
> +MODULE_AUTHOR("Intel Corporation");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/lib/image-formats.c b/lib/image-formats.c
> new file mode 100644
> index 000000000000..1e52a7410222
> --- /dev/null
> +++ b/lib/image-formats.c
> @@ -0,0 +1,655 @@
> +/*
> + * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> + * Copyright (c) 2019 Maxime Ripard <maxime.ripard@bootlin.com>
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission.  The copyright holders make no representations
> + * about the suitability of this software for any purpose.  It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> + * OF THIS SOFTWARE.
> + */
> +
> +#include <linux/bug.h>
> +#include <linux/image-formats.h>
> +#include <linux/kernel.h>
> +#include <linux/math64.h>
> +
> +#include <uapi/drm/drm_fourcc.h>
> +
> +static const struct image_format_info formats[] = {
> +	{
> +		.drm_fmt = DRM_FORMAT_C8,
> +		.depth = 8,
> +		.num_planes = 1,
> +		.cpp = { 1, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB332,
> +		.depth = 8,
> +		.num_planes = 1,
> +		.cpp = { 1, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR233,
> +		.depth = 8,
> +		.num_planes = 1,
> +		.cpp = { 1, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ARGB4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ABGR4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBA4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRA4444,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB1555,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR1555,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX5551,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX5551,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ARGB1555,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ABGR1555,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBA5551,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRA5551,
> +		.depth = 15,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB565,
> +		.depth = 16,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR565,
> +		.depth = 16,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 3, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 3, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB8888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR8888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX8888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX8888,
> +		.depth = 24,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB565_A8,
> +		.depth = 24,
> +		.num_planes = 2,
> +		.cpp = { 2, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR565_A8,
> +		.depth = 24,
> +		.num_planes = 2,
> +		.cpp = { 2, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB2101010,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR2101010,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX1010102,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX1010102,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ARGB2101010,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ABGR2101010,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBA1010102,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRA1010102,
> +		.depth = 30,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ARGB8888,
> +		.depth = 32,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_ABGR8888,
> +		.depth = 32,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBA8888,
> +		.depth = 32,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRA8888,
> +		.depth = 32,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGB888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 3, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGR888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 3, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XRGB8888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 4, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XBGR8888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 4, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_RGBX8888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 4, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_BGRX8888_A8,
> +		.depth = 32,
> +		.num_planes = 2,
> +		.cpp = { 4, 1, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV410,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 4,
> +		.vsub = 4,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU410,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 4,
> +		.vsub = 4,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV411,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 4,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU411,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 4,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV420,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU420,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV422,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU422,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUV444,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVU444,
> +		.depth = 0,
> +		.num_planes = 3,
> +		.cpp = { 1, 1, 1 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV12,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV21,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV16,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV61,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV24,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_NV42,
> +		.depth = 0,
> +		.num_planes = 2,
> +		.cpp = { 1, 2, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YUYV,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_YVYU,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_UYVY,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_VYUY,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_XYUV8888,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_AYUV,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.cpp = { 4, 0, 0 },
> +		.hsub = 1,
> +		.vsub = 1,
> +		.has_alpha = true,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_Y0L0,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.char_per_block = { 8, 0, 0 },
> +		.block_w = { 2, 0, 0 },
> +		.block_h = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.has_alpha = true,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_X0L0,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.char_per_block = { 8, 0, 0 },
> +		.block_w = { 2, 0, 0 },
> +		.block_h = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_Y0L2,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.char_per_block = { 8, 0, 0 },
> +		.block_w = { 2, 0, 0 },
> +		.block_h = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.has_alpha = true,
> +		.is_yuv = true,
> +	}, {
> +		.drm_fmt = DRM_FORMAT_X0L2,
> +		.depth = 0,
> +		.num_planes = 1,
> +		.char_per_block = { 8, 0, 0 },
> +		.block_w = { 2, 0, 0 },
> +		.block_h = { 2, 0, 0 },
> +		.hsub = 2,
> +		.vsub = 2,
> +		.is_yuv = true,
> +	},
> +};
> +
> +#define __image_format_lookup(_field, _fmt)			\
> +	({							\
> +		const struct image_format_info *format = NULL;	\
> +		unsigned i;					\
> +								\
> +		for (i = 0; i < ARRAY_SIZE(formats); i++)	\
> +			if (formats[i]._field == _fmt)		\
> +				format = &formats[i];		\
> +								\
> +		format;						\
> +	})
> +
> +/**
> + * __image_format_drm_lookup - query information for a given format
> + * @drm: DRM fourcc pixel format (DRM_FORMAT_*)
> + *
> + * The caller should only pass a supported pixel format to this function.
> + *
> + * Returns:
> + * The instance of struct image_format_info that describes the pixel format, or
> + * NULL if the format is unsupported.
> + */
> +const struct image_format_info *__image_format_drm_lookup(u32 drm)
> +{
> +	return __image_format_lookup(drm_fmt, drm);
> +}
> +EXPORT_SYMBOL(__image_format_drm_lookup);
> +
> +/**
> + * image_format_drm_lookup - query information for a given format
> + * @drm: DRM fourcc pixel format (DRM_FORMAT_*)
> + *
> + * The caller should only pass a supported pixel format to this function.
> + * Unsupported pixel formats will generate a warning in the kernel log.
> + *
> + * Returns:
> + * The instance of struct image_format_info that describes the pixel format, or
> + * NULL if the format is unsupported.
> + */
> +const struct image_format_info *image_format_drm_lookup(u32 drm)
> +{
> +	const struct image_format_info *format;
> +
> +	format = __image_format_drm_lookup(drm);
> +
> +	WARN_ON(!format);
> +	return format;
> +}
> +EXPORT_SYMBOL(image_format_drm_lookup);
>
Paul Kocialkowski April 23, 2019, 4:56 p.m. UTC | #5
Hi,

Le mardi 23 avril 2019 à 13:22 +0200, Thomas Zimmermann a écrit :
> Hi
> 
> Am 17.04.19 um 09:54 schrieb Maxime Ripard:
> > Move the DRM formats API to turn this into a more generic image formats API
> > to be able to leverage it into some other places of the kernel, such as
> > v4l2 drivers.
> 
> Here's some nitpicking: 'image_format_*' appears slightly vague; it
> could refer to a disk or ROM image as well. Something like
> 'pixel_format_*', 'pixbuf_format_*', etc. would clearly indicate graphics.

Yeah I'm with you on this one. It's bike-shedding, but I think it's
fine as long as more fundamental topics are moving as well.

Cheers,

Paul

> Best regards
> Thomas
> 
> > Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > ---
> >  include/linux/image-formats.h | 387 +++++++++++++++++++++-
> >  lib/Kconfig                   |   7 +-
> >  lib/Makefile                  |   3 +-
> >  lib/image-formats-selftests.c | 325 +++++++++++++++++-
> >  lib/image-formats.c           | 655 +++++++++++++++++++++++++++++++++++-
> >  5 files changed, 1377 insertions(+)
> >  create mode 100644 include/linux/image-formats.h
> >  create mode 100644 lib/image-formats-selftests.c
> >  create mode 100644 lib/image-formats.c
> > 
> > diff --git a/include/linux/image-formats.h b/include/linux/image-formats.h
> > new file mode 100644
> > index 000000000000..ec43d9f9a527
> > --- /dev/null
> > +++ b/include/linux/image-formats.h
> > @@ -0,0 +1,387 @@
> > +/*
> > + * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > + * Copyright (c) 2019 Maxime Ripard <maxime.ripard@bootlin.com>
> > + *
> > + * Permission to use, copy, modify, distribute, and sell this software and its
> > + * documentation for any purpose is hereby granted without fee, provided that
> > + * the above copyright notice appear in all copies and that both that copyright
> > + * notice and this permission notice appear in supporting documentation, and
> > + * that the name of the copyright holders not be used in advertising or
> > + * publicity pertaining to distribution of the software without specific,
> > + * written prior permission.  The copyright holders make no representations
> > + * about the suitability of this software for any purpose.  It is provided "as
> > + * is" without express or implied warranty.
> > + *
> > + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> > + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> > + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> > + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> > + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> > + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> > + * OF THIS SOFTWARE.
> > + */
> > +
> > +#ifndef _IMAGE_FORMATS_H_
> > +#define _IMAGE_FORMATS_H_
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/types.h>
> > +
> > +/**
> > + * struct image_format_info - information about a image format
> > + */
> > +struct image_format_info {
> > +	union {
> > +		/**
> > +		 * @drm_fmt:
> > +		 *
> > +		 * DRM 4CC format identifier (DRM_FORMAT_*)
> > +		 */
> > +		u32 drm_fmt;
> > +
> > +		/**
> > +		 * @format:
> > +		 *
> > +		 * DRM 4CC format identifier (DRM_FORMAT_*). Kept
> > +		 * around for compatibility reasons with the current
> > +		 * DRM drivers.
> > +		 */
> > +		u32 format;
> > +	};
> > +
> > +	/**
> > +	 * @depth:
> > +	 *
> > +	 * Color depth (number of bits per pixel excluding padding bits),
> > +	 * valid for a subset of RGB formats only. This is a legacy field, do
> > +	 * not use in new code and set to 0 for new formats.
> > +	 */
> > +	u8 depth;
> > +
> > +	/** @num_planes: Number of color planes (1 to 3) */
> > +	u8 num_planes;
> > +
> > +	union {
> > +		/**
> > +		 * @cpp:
> > +		 *
> > +		 * Number of bytes per pixel (per plane), this is aliased with
> > +		 * @char_per_block. It is deprecated in favour of using the
> > +		 * triplet @char_per_block, @block_w, @block_h for better
> > +		 * describing the pixel format.
> > +		 */
> > +		u8 cpp[3];
> > +
> > +		/**
> > +		 * @char_per_block:
> > +		 *
> > +		 * Number of bytes per block (per plane), where blocks are
> > +		 * defined as a rectangle of pixels which are stored next to
> > +		 * each other in a byte aligned memory region. Together with
> > +		 * @block_w and @block_h this is used to properly describe tiles
> > +		 * in tiled formats or to describe groups of pixels in packed
> > +		 * formats for which the memory needed for a single pixel is not
> > +		 * byte aligned.
> > +		 *
> > +		 * @cpp has been kept for historical reasons because there are
> > +		 * a lot of places in drivers where it's used. In drm core for
> > +		 * generic code paths the preferred way is to use
> > +		 * @char_per_block, image_format_info_block_width() and
> > +		 * image_format_info_block_height() which allows handling both
> > +		 * block and non-block formats in the same way.
> > +		 *
> > +		 * For formats that are intended to be used only with non-linear
> > +		 * modifiers both @cpp and @char_per_block must be 0 in the
> > +		 * generic format table. Drivers could supply accurate
> > +		 * information from their drm_mode_config.get_format_info hook
> > +		 * if they want the core to be validating the pitch.
> > +		 */
> > +		u8 char_per_block[3];
> > +	};
> > +
> > +	/**
> > +	 * @block_w:
> > +	 *
> > +	 * Block width in pixels, this is intended to be accessed through
> > +	 * image_format_info_block_width()
> > +	 */
> > +	u8 block_w[3];
> > +
> > +	/**
> > +	 * @block_h:
> > +	 *
> > +	 * Block height in pixels, this is intended to be accessed through
> > +	 * image_format_info_block_height()
> > +	 */
> > +	u8 block_h[3];
> > +
> > +	/** @hsub: Horizontal chroma subsampling factor */
> > +	u8 hsub;
> > +	/** @vsub: Vertical chroma subsampling factor */
> > +	u8 vsub;
> > +
> > +	/** @has_alpha: Does the format embeds an alpha component? */
> > +	bool has_alpha;
> > +
> > +	/** @is_yuv: Is it a YUV format? */
> > +	bool is_yuv;
> > +};
> > +
> > +/**
> > + * image_format_info_is_yuv_packed - check that the format info matches a YUV
> > + * format with data laid in a single plane
> > + * @info: format info
> > + *
> > + * Returns:
> > + * A boolean indicating whether the format info matches a packed YUV format.
> > + */
> > +static inline bool
> > +image_format_info_is_yuv_packed(const struct image_format_info *info)
> > +{
> > +	return info->is_yuv && info->num_planes == 1;
> > +}
> > +
> > +/**
> > + * image_format_info_is_yuv_semiplanar - check that the format info matches a YUV
> > + * format with data laid in two planes (luminance and chrominance)
> > + * @info: format info
> > + *
> > + * Returns:
> > + * A boolean indicating whether the format info matches a semiplanar YUV format.
> > + */
> > +static inline bool
> > +image_format_info_is_yuv_semiplanar(const struct image_format_info *info)
> > +{
> > +	return info->is_yuv && info->num_planes == 2;
> > +}
> > +
> > +/**
> > + * image_format_info_is_yuv_planar - check that the format info matches a YUV
> > + * format with data laid in three planes (one for each YUV component)
> > + * @info: format info
> > + *
> > + * Returns:
> > + * A boolean indicating whether the format info matches a planar YUV format.
> > + */
> > +static inline bool
> > +image_format_info_is_yuv_planar(const struct image_format_info *info)
> > +{
> > +	return info->is_yuv && info->num_planes == 3;
> > +}
> > +
> > +/**
> > + * image_format_info_is_yuv_sampling_410 - check that the format info matches a
> > + * YUV format with 4:1:0 sub-sampling
> > + * @info: format info
> > + *
> > + * Returns:
> > + * A boolean indicating whether the format info matches a YUV format with 4:1:0
> > + * sub-sampling.
> > + */
> > +static inline bool
> > +image_format_info_is_yuv_sampling_410(const struct image_format_info *info)
> > +{
> > +	return info->is_yuv && info->hsub == 4 && info->vsub == 4;
> > +}
> > +
> > +/**
> > + * image_format_info_is_yuv_sampling_411 - check that the format info matches a
> > + * YUV format with 4:1:1 sub-sampling
> > + * @info: format info
> > + *
> > + * Returns:
> > + * A boolean indicating whether the format info matches a YUV format with 4:1:1
> > + * sub-sampling.
> > + */
> > +static inline bool
> > +image_format_info_is_yuv_sampling_411(const struct image_format_info *info)
> > +{
> > +	return info->is_yuv && info->hsub == 4 && info->vsub == 1;
> > +}
> > +
> > +/**
> > + * image_format_info_is_yuv_sampling_420 - check that the format info matches a
> > + * YUV format with 4:2:0 sub-sampling
> > + * @info: format info
> > + *
> > + * Returns:
> > + * A boolean indicating whether the format info matches a YUV format with 4:2:0
> > + * sub-sampling.
> > + */
> > +static inline bool
> > +image_format_info_is_yuv_sampling_420(const struct image_format_info *info)
> > +{
> > +	return info->is_yuv && info->hsub == 2 && info->vsub == 2;
> > +}
> > +
> > +/**
> > + * image_format_info_is_yuv_sampling_422 - check that the format info matches a
> > + * YUV format with 4:2:2 sub-sampling
> > + * @info: format info
> > + *
> > + * Returns:
> > + * A boolean indicating whether the format info matches a YUV format with 4:2:2
> > + * sub-sampling.
> > + */
> > +static inline bool
> > +image_format_info_is_yuv_sampling_422(const struct image_format_info *info)
> > +{
> > +	return info->is_yuv && info->hsub == 2 && info->vsub == 1;
> > +}
> > +
> > +/**
> > + * image_format_info_is_yuv_sampling_444 - check that the format info matches a
> > + * YUV format with 4:4:4 sub-sampling
> > + * @info: format info
> > + *
> > + * Returns:
> > + * A boolean indicating whether the format info matches a YUV format with 4:4:4
> > + * sub-sampling.
> > + */
> > +static inline bool
> > +image_format_info_is_yuv_sampling_444(const struct image_format_info *info)
> > +{
> > +	return info->is_yuv && info->hsub == 1 && info->vsub == 1;
> > +}
> > +
> > +/**
> > + * image_format_info_plane_cpp - determine the bytes per pixel value
> > + * @format: pixel format info
> > + * @plane: plane index
> > + *
> > + * Returns:
> > + * The bytes per pixel value for the specified plane.
> > + */
> > +static inline
> > +int image_format_info_plane_cpp(const struct image_format_info *info, int plane)
> > +{
> > +	if (!info || plane >= info->num_planes)
> > +		return 0;
> > +
> > +	return info->cpp[plane];
> > +}
> > +
> > +/**
> > + * image_format_info_plane_width - width of the plane given the first plane
> > + * @format: pixel format info
> > + * @width: width of the first plane
> > + * @plane: plane index
> > + *
> > + * Returns:
> > + * The width of @plane, given that the width of the first plane is @width.
> > + */
> > +static inline
> > +int image_format_info_plane_width(const struct image_format_info *info, int width,
> > +				  int plane)
> > +{
> > +	if (!info || plane >= info->num_planes)
> > +		return 0;
> > +
> > +	if (plane == 0)
> > +		return width;
> > +
> > +	return width / info->hsub;
> > +}
> > +
> > +/**
> > + * image_format_info_plane_height - height of the plane given the first plane
> > + * @format: pixel format info
> > + * @height: height of the first plane
> > + * @plane: plane index
> > + *
> > + * Returns:
> > + * The height of @plane, given that the height of the first plane is @height.
> > + */
> > +static inline
> > +int image_format_info_plane_height(const struct image_format_info *info, int height,
> > +				   int plane)
> > +{
> > +	if (!info || plane >= info->num_planes)
> > +		return 0;
> > +
> > +	if (plane == 0)
> > +		return height;
> > +
> > +	return height / info->vsub;
> > +}
> > +
> > +/**
> > + * image_format_info_block_width - width in pixels of block.
> > + * @format: pointer to the image_format_info
> > + * @plane: plane index
> > + *
> > + * Returns:
> > + * The width in pixels of a block, depending on the plane index.
> > + */
> > +static inline
> > +unsigned int image_format_info_block_width(const struct image_format_info *format,
> > +					   int plane)
> > +{
> > +	if (!format)
> > +		return 0;
> > +
> > +	if (plane < 0 || plane >= ARRAY_SIZE(format->block_w))
> > +		return 0;
> > +
> > +	if (plane >= format->num_planes)
> > +		return 0;
> > +
> > +	if (!format->block_w[plane])
> > +		return 1;
> > +
> > +	return format->block_w[plane];
> > +}
> > +
> > +/**
> > + * image_format_info_block_height - height in pixels of a block
> > + * @info: pointer to the image_format_info
> > + * @plane: plane index
> > + *
> > + * Returns:
> > + * The height in pixels of a block, depending on the plane index.
> > + */
> > +static inline
> > +unsigned int image_format_info_block_height(const struct image_format_info *format,
> > +					    int plane)
> > +{
> > +	if (!format)
> > +		return 0;
> > +
> > +	if (plane < 0 || plane >= ARRAY_SIZE(format->block_w))
> > +		return 0;
> > +
> > +	if (plane >= format->num_planes)
> > +		return 0;
> > +
> > +	if (!format->block_h[plane])
> > +		return 1;
> > +
> > +	return format->block_h[plane];
> > +}
> > +
> > +/**
> > + * image_format_info_min_pitch - computes the minimum required pitch in bytes
> > + * @info: pixel format info
> > + * @plane: plane index
> > + * @buffer_width: buffer width in pixels
> > + *
> > + * Returns:
> > + * The minimum required pitch in bytes for a buffer by taking into consideration
> > + * the pixel format information and the buffer width.
> > + */
> > +static inline
> > +uint64_t image_format_info_min_pitch(const struct image_format_info *info,
> > +				     int plane, unsigned int buffer_width)
> > +{
> > +	if (!info || plane < 0 || plane >= info->num_planes)
> > +		return 0;
> > +
> > +	return DIV_ROUND_UP_ULL((u64)buffer_width * info->char_per_block[plane],
> > +			    image_format_info_block_width(info, plane) *
> > +			    image_format_info_block_height(info, plane));
> > +}
> > +
> > +const struct image_format_info *__image_format_drm_lookup(u32 drm);
> > +const struct image_format_info *image_format_drm_lookup(u32 drm);
> > +
> > +#endif /* _IMAGE_FORMATS_H_ */
> > diff --git a/lib/Kconfig b/lib/Kconfig
> > index fb453afff32e..9a0160d3123b 100644
> > --- a/lib/Kconfig
> > +++ b/lib/Kconfig
> > @@ -625,3 +625,10 @@ config GENERIC_LIB_UCMPDI2
> >  
> >  config OBJAGG
> >  	tristate "objagg" if COMPILE_TEST
> > +
> > +config IMAGE_FORMATS
> > +	bool
> > +
> > +config IMAGE_FORMATS_SELFTESTS
> > +	tristate "Test image format functions"
> > +	depends on IMAGE_FORMATS
> > diff --git a/lib/Makefile b/lib/Makefile
> > index 6996d2b9f401..203336b91248 100644
> > --- a/lib/Makefile
> > +++ b/lib/Makefile
> > @@ -280,3 +280,6 @@ obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
> >  obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
> >  obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
> >  obj-$(CONFIG_OBJAGG) += objagg.o
> > +
> > +obj-$(CONFIG_IMAGE_FORMATS) += image-formats.o
> > +obj-$(CONFIG_IMAGE_FORMATS_SELFTESTS) += image-formats-selftests.o
> > diff --git a/lib/image-formats-selftests.c b/lib/image-formats-selftests.c
> > new file mode 100644
> > index 000000000000..d0f0011b535e
> > --- /dev/null
> > +++ b/lib/image-formats-selftests.c
> > @@ -0,0 +1,325 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Test cases for the image_format functions
> > + */
> > +
> > +#define pr_fmt(fmt) "image_format: " fmt
> > +
> > +#include <linux/errno.h>
> > +#include <linux/image-formats.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +
> > +#include <drm/drm_fourcc.h>
> > +
> > +#define FAIL(test, msg, ...) \
> > +	do { \
> > +		if (test) { \
> > +			pr_err("%s/%u: " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
> > +			return -EINVAL; \
> > +		} \
> > +	} while (0)
> > +
> > +#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n")
> > +
> > +static int test_image_format_info_block_width(void)
> > +{
> > +	const struct image_format_info *info = NULL;
> > +
> > +	/* Test invalid arguments */
> > +	FAIL_ON(image_format_info_block_width(info, 0) != 0);
> > +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> > +	FAIL_ON(image_format_info_block_width(info, 1) != 0);
> > +
> > +	/* Test 1 plane format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_block_width(info, 0) != 1);
> > +	FAIL_ON(image_format_info_block_width(info, 1) != 0);
> > +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> > +
> > +	/* Test 2 planes format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_NV12);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_block_width(info, 0) != 1);
> > +	FAIL_ON(image_format_info_block_width(info, 1) != 1);
> > +	FAIL_ON(image_format_info_block_width(info, 2) != 0);
> > +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> > +
> > +	/* Test 3 planes format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_block_width(info, 0) != 1);
> > +	FAIL_ON(image_format_info_block_width(info, 1) != 1);
> > +	FAIL_ON(image_format_info_block_width(info, 2) != 1);
> > +	FAIL_ON(image_format_info_block_width(info, 3) != 0);
> > +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> > +
> > +	/* Test a tiled format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_X0L0);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_block_width(info, 0) != 2);
> > +	FAIL_ON(image_format_info_block_width(info, 1) != 0);
> > +	FAIL_ON(image_format_info_block_width(info, -1) != 0);
> > +
> > +	return 0;
> > +}
> > +
> > +static int test_image_format_info_block_height(void)
> > +{
> > +	const struct image_format_info *info = NULL;
> > +
> > +	/* Test invalid arguments */
> > +	FAIL_ON(image_format_info_block_height(info, 0) != 0);
> > +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> > +	FAIL_ON(image_format_info_block_height(info, 1) != 0);
> > +
> > +	/* Test 1 plane format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_block_height(info, 0) != 1);
> > +	FAIL_ON(image_format_info_block_height(info, 1) != 0);
> > +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> > +
> > +	/* Test 2 planes format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_NV12);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_block_height(info, 0) != 1);
> > +	FAIL_ON(image_format_info_block_height(info, 1) != 1);
> > +	FAIL_ON(image_format_info_block_height(info, 2) != 0);
> > +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> > +
> > +	/* Test 3 planes format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_block_height(info, 0) != 1);
> > +	FAIL_ON(image_format_info_block_height(info, 1) != 1);
> > +	FAIL_ON(image_format_info_block_height(info, 2) != 1);
> > +	FAIL_ON(image_format_info_block_height(info, 3) != 0);
> > +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> > +
> > +	/* Test a tiled format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_X0L0);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_block_height(info, 0) != 2);
> > +	FAIL_ON(image_format_info_block_height(info, 1) != 0);
> > +	FAIL_ON(image_format_info_block_height(info, -1) != 0);
> > +
> > +	return 0;
> > +}
> > +
> > +static int test_image_format_info_min_pitch(void)
> > +{
> > +	const struct image_format_info *info = NULL;
> > +
> > +	/* Test invalid arguments */
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> > +
> > +	/* Test 1 plane 8 bits per pixel format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_RGB332);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> > +
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> > +			(uint64_t)UINT_MAX);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
> > +			(uint64_t)(UINT_MAX - 1));
> > +
> > +	/* Test 1 plane 16 bits per pixel format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> > +
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 4);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1280);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 2048);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 3840);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 8192);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 1342);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> > +			(uint64_t)UINT_MAX * 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
> > +			(uint64_t)(UINT_MAX - 1) * 2);
> > +
> > +	/* Test 1 plane 24 bits per pixel format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_RGB888);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> > +
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 3);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 6);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1920);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 3072);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 5760);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 12288);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 2013);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> > +			(uint64_t)UINT_MAX * 3);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
> > +			(uint64_t)(UINT_MAX - 1) * 3);
> > +
> > +	/* Test 1 plane 32 bits per pixel format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_ABGR8888);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> > +
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 4);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 8);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 2560);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 4096);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 7680);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 16384);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 2684);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> > +			(uint64_t)UINT_MAX * 4);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
> > +			(uint64_t)(UINT_MAX - 1) * 4);
> > +
> > +	/* Test 2 planes format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_NV12);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, 0) != 0);
> > +
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 320) != 640);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 512) != 1024);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 960) != 1920);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 2048) != 4096);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 336) != 672);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> > +			(uint64_t)UINT_MAX);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) !=
> > +			(uint64_t)UINT_MAX + 1);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
> > +			(uint64_t)(UINT_MAX - 1));
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, (UINT_MAX - 1) /  2) !=
> > +			(uint64_t)(UINT_MAX - 1));
> > +
> > +	/* Test 3 planes 8 bits per pixel format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 3, 0) != 0);
> > +
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 1);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, 1) != 1);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 2) != 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, 2) != 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 320) != 320);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, 320) != 320);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 512) != 512);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, 512) != 512);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 960) != 960);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, 960) != 960);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 2048) != 2048);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, 2048) != 2048);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 336) != 336);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, 336) != 336);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> > +			(uint64_t)UINT_MAX);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) !=
> > +			(uint64_t)UINT_MAX / 2 + 1);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, UINT_MAX / 2 + 1) !=
> > +			(uint64_t)UINT_MAX / 2 + 1);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1) / 2) !=
> > +			(uint64_t)(UINT_MAX - 1) / 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2) !=
> > +			(uint64_t)(UINT_MAX - 1) / 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 2, (UINT_MAX - 1) / 2) !=
> > +			(uint64_t)(UINT_MAX - 1) / 2);
> > +
> > +	/* Test tiled format */
> > +	info = image_format_drm_lookup(DRM_FORMAT_X0L2);
> > +	FAIL_ON(!info);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
> > +	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
> > +
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 4);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1280);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 2048);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 3840);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 8192);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 1342);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
> > +			(uint64_t)UINT_MAX * 2);
> > +	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
> > +			(uint64_t)(UINT_MAX - 1) * 2);
> > +
> > +	return 0;
> > +}
> > +
> > +#define selftest(test)	{ .name = #test, .func = test, }
> > +
> > +static struct image_format_test {
> > +	char	*name;
> > +	int	(*func)(void);
> > +} tests[] = {
> > +	selftest(test_image_format_info_block_height),
> > +	selftest(test_image_format_info_block_width),
> > +	selftest(test_image_format_info_min_pitch),
> > +};
> > +
> > +static int __init image_format_test_init(void)
> > +{
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(tests); i++) {
> > +		struct image_format_test *test = &tests[i];
> > +		int ret;
> > +
> > +		ret = test->func();
> > +		if (ret) {
> > +			pr_err("Failed test %s\n", test->name);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	pr_info("All tests executed properly.\n");
> > +	return 0;
> > +}
> > +module_init(image_format_test_init);
> > +
> > +MODULE_AUTHOR("Intel Corporation");
> > +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
> > +MODULE_LICENSE("GPL");
> > diff --git a/lib/image-formats.c b/lib/image-formats.c
> > new file mode 100644
> > index 000000000000..1e52a7410222
> > --- /dev/null
> > +++ b/lib/image-formats.c
> > @@ -0,0 +1,655 @@
> > +/*
> > + * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > + * Copyright (c) 2019 Maxime Ripard <maxime.ripard@bootlin.com>
> > + *
> > + * Permission to use, copy, modify, distribute, and sell this software and its
> > + * documentation for any purpose is hereby granted without fee, provided that
> > + * the above copyright notice appear in all copies and that both that copyright
> > + * notice and this permission notice appear in supporting documentation, and
> > + * that the name of the copyright holders not be used in advertising or
> > + * publicity pertaining to distribution of the software without specific,
> > + * written prior permission.  The copyright holders make no representations
> > + * about the suitability of this software for any purpose.  It is provided "as
> > + * is" without express or implied warranty.
> > + *
> > + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> > + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> > + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> > + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> > + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> > + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> > + * OF THIS SOFTWARE.
> > + */
> > +
> > +#include <linux/bug.h>
> > +#include <linux/image-formats.h>
> > +#include <linux/kernel.h>
> > +#include <linux/math64.h>
> > +
> > +#include <uapi/drm/drm_fourcc.h>
> > +
> > +static const struct image_format_info formats[] = {
> > +	{
> > +		.drm_fmt = DRM_FORMAT_C8,
> > +		.depth = 8,
> > +		.num_planes = 1,
> > +		.cpp = { 1, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGB332,
> > +		.depth = 8,
> > +		.num_planes = 1,
> > +		.cpp = { 1, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGR233,
> > +		.depth = 8,
> > +		.num_planes = 1,
> > +		.cpp = { 1, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XRGB4444,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XBGR4444,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGBX4444,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGRX4444,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_ARGB4444,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_ABGR4444,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGBA4444,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGRA4444,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XRGB1555,
> > +		.depth = 15,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XBGR1555,
> > +		.depth = 15,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGBX5551,
> > +		.depth = 15,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGRX5551,
> > +		.depth = 15,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_ARGB1555,
> > +		.depth = 15,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_ABGR1555,
> > +		.depth = 15,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGBA5551,
> > +		.depth = 15,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGRA5551,
> > +		.depth = 15,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGB565,
> > +		.depth = 16,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGR565,
> > +		.depth = 16,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGB888,
> > +		.depth = 24,
> > +		.num_planes = 1,
> > +		.cpp = { 3, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGR888,
> > +		.depth = 24,
> > +		.num_planes = 1,
> > +		.cpp = { 3, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XRGB8888,
> > +		.depth = 24,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XBGR8888,
> > +		.depth = 24,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGBX8888,
> > +		.depth = 24,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGRX8888,
> > +		.depth = 24,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGB565_A8,
> > +		.depth = 24,
> > +		.num_planes = 2,
> > +		.cpp = { 2, 1, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGR565_A8,
> > +		.depth = 24,
> > +		.num_planes = 2,
> > +		.cpp = { 2, 1, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XRGB2101010,
> > +		.depth = 30,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XBGR2101010,
> > +		.depth = 30,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGBX1010102,
> > +		.depth = 30,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGRX1010102,
> > +		.depth = 30,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_ARGB2101010,
> > +		.depth = 30,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_ABGR2101010,
> > +		.depth = 30,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGBA1010102,
> > +		.depth = 30,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGRA1010102,
> > +		.depth = 30,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_ARGB8888,
> > +		.depth = 32,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_ABGR8888,
> > +		.depth = 32,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGBA8888,
> > +		.depth = 32,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGRA8888,
> > +		.depth = 32,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGB888_A8,
> > +		.depth = 32,
> > +		.num_planes = 2,
> > +		.cpp = { 3, 1, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGR888_A8,
> > +		.depth = 32,
> > +		.num_planes = 2,
> > +		.cpp = { 3, 1, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XRGB8888_A8,
> > +		.depth = 32,
> > +		.num_planes = 2,
> > +		.cpp = { 4, 1, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XBGR8888_A8,
> > +		.depth = 32,
> > +		.num_planes = 2,
> > +		.cpp = { 4, 1, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_RGBX8888_A8,
> > +		.depth = 32,
> > +		.num_planes = 2,
> > +		.cpp = { 4, 1, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_BGRX8888_A8,
> > +		.depth = 32,
> > +		.num_planes = 2,
> > +		.cpp = { 4, 1, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YUV410,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 4,
> > +		.vsub = 4,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YVU410,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 4,
> > +		.vsub = 4,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YUV411,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 4,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YVU411,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 4,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YUV420,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 2,
> > +		.vsub = 2,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YVU420,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 2,
> > +		.vsub = 2,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YUV422,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 2,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YVU422,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 2,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YUV444,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YVU444,
> > +		.depth = 0,
> > +		.num_planes = 3,
> > +		.cpp = { 1, 1, 1 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_NV12,
> > +		.depth = 0,
> > +		.num_planes = 2,
> > +		.cpp = { 1, 2, 0 },
> > +		.hsub = 2,
> > +		.vsub = 2,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_NV21,
> > +		.depth = 0,
> > +		.num_planes = 2,
> > +		.cpp = { 1, 2, 0 },
> > +		.hsub = 2,
> > +		.vsub = 2,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_NV16,
> > +		.depth = 0,
> > +		.num_planes = 2,
> > +		.cpp = { 1, 2, 0 },
> > +		.hsub = 2,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_NV61,
> > +		.depth = 0,
> > +		.num_planes = 2,
> > +		.cpp = { 1, 2, 0 },
> > +		.hsub = 2,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_NV24,
> > +		.depth = 0,
> > +		.num_planes = 2,
> > +		.cpp = { 1, 2, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_NV42,
> > +		.depth = 0,
> > +		.num_planes = 2,
> > +		.cpp = { 1, 2, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YUYV,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 2,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_YVYU,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 2,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_UYVY,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 2,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_VYUY,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 2, 0, 0 },
> > +		.hsub = 2,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_XYUV8888,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_AYUV,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.cpp = { 4, 0, 0 },
> > +		.hsub = 1,
> > +		.vsub = 1,
> > +		.has_alpha = true,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_Y0L0,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.char_per_block = { 8, 0, 0 },
> > +		.block_w = { 2, 0, 0 },
> > +		.block_h = { 2, 0, 0 },
> > +		.hsub = 2,
> > +		.vsub = 2,
> > +		.has_alpha = true,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_X0L0,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.char_per_block = { 8, 0, 0 },
> > +		.block_w = { 2, 0, 0 },
> > +		.block_h = { 2, 0, 0 },
> > +		.hsub = 2,
> > +		.vsub = 2,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_Y0L2,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.char_per_block = { 8, 0, 0 },
> > +		.block_w = { 2, 0, 0 },
> > +		.block_h = { 2, 0, 0 },
> > +		.hsub = 2,
> > +		.vsub = 2,
> > +		.has_alpha = true,
> > +		.is_yuv = true,
> > +	}, {
> > +		.drm_fmt = DRM_FORMAT_X0L2,
> > +		.depth = 0,
> > +		.num_planes = 1,
> > +		.char_per_block = { 8, 0, 0 },
> > +		.block_w = { 2, 0, 0 },
> > +		.block_h = { 2, 0, 0 },
> > +		.hsub = 2,
> > +		.vsub = 2,
> > +		.is_yuv = true,
> > +	},
> > +};
> > +
> > +#define __image_format_lookup(_field, _fmt)			\
> > +	({							\
> > +		const struct image_format_info *format = NULL;	\
> > +		unsigned i;					\
> > +								\
> > +		for (i = 0; i < ARRAY_SIZE(formats); i++)	\
> > +			if (formats[i]._field == _fmt)		\
> > +				format = &formats[i];		\
> > +								\
> > +		format;						\
> > +	})
> > +
> > +/**
> > + * __image_format_drm_lookup - query information for a given format
> > + * @drm: DRM fourcc pixel format (DRM_FORMAT_*)
> > + *
> > + * The caller should only pass a supported pixel format to this function.
> > + *
> > + * Returns:
> > + * The instance of struct image_format_info that describes the pixel format, or
> > + * NULL if the format is unsupported.
> > + */
> > +const struct image_format_info *__image_format_drm_lookup(u32 drm)
> > +{
> > +	return __image_format_lookup(drm_fmt, drm);
> > +}
> > +EXPORT_SYMBOL(__image_format_drm_lookup);
> > +
> > +/**
> > + * image_format_drm_lookup - query information for a given format
> > + * @drm: DRM fourcc pixel format (DRM_FORMAT_*)
> > + *
> > + * The caller should only pass a supported pixel format to this function.
> > + * Unsupported pixel formats will generate a warning in the kernel log.
> > + *
> > + * Returns:
> > + * The instance of struct image_format_info that describes the pixel format, or
> > + * NULL if the format is unsupported.
> > + */
> > +const struct image_format_info *image_format_drm_lookup(u32 drm)
> > +{
> > +	const struct image_format_info *format;
> > +
> > +	format = __image_format_drm_lookup(drm);
> > +
> > +	WARN_ON(!format);
> > +	return format;
> > +}
> > +EXPORT_SYMBOL(image_format_drm_lookup);
> >
diff mbox series

Patch

diff --git a/include/linux/image-formats.h b/include/linux/image-formats.h
new file mode 100644
index 000000000000..ec43d9f9a527
--- /dev/null
+++ b/include/linux/image-formats.h
@@ -0,0 +1,387 @@ 
+/*
+ * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Copyright (c) 2019 Maxime Ripard <maxime.ripard@bootlin.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _IMAGE_FORMATS_H_
+#define _IMAGE_FORMATS_H_
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+/**
+ * struct image_format_info - information about a image format
+ */
+struct image_format_info {
+	union {
+		/**
+		 * @drm_fmt:
+		 *
+		 * DRM 4CC format identifier (DRM_FORMAT_*)
+		 */
+		u32 drm_fmt;
+
+		/**
+		 * @format:
+		 *
+		 * DRM 4CC format identifier (DRM_FORMAT_*). Kept
+		 * around for compatibility reasons with the current
+		 * DRM drivers.
+		 */
+		u32 format;
+	};
+
+	/**
+	 * @depth:
+	 *
+	 * Color depth (number of bits per pixel excluding padding bits),
+	 * valid for a subset of RGB formats only. This is a legacy field, do
+	 * not use in new code and set to 0 for new formats.
+	 */
+	u8 depth;
+
+	/** @num_planes: Number of color planes (1 to 3) */
+	u8 num_planes;
+
+	union {
+		/**
+		 * @cpp:
+		 *
+		 * Number of bytes per pixel (per plane), this is aliased with
+		 * @char_per_block. It is deprecated in favour of using the
+		 * triplet @char_per_block, @block_w, @block_h for better
+		 * describing the pixel format.
+		 */
+		u8 cpp[3];
+
+		/**
+		 * @char_per_block:
+		 *
+		 * Number of bytes per block (per plane), where blocks are
+		 * defined as a rectangle of pixels which are stored next to
+		 * each other in a byte aligned memory region. Together with
+		 * @block_w and @block_h this is used to properly describe tiles
+		 * in tiled formats or to describe groups of pixels in packed
+		 * formats for which the memory needed for a single pixel is not
+		 * byte aligned.
+		 *
+		 * @cpp has been kept for historical reasons because there are
+		 * a lot of places in drivers where it's used. In drm core for
+		 * generic code paths the preferred way is to use
+		 * @char_per_block, image_format_info_block_width() and
+		 * image_format_info_block_height() which allows handling both
+		 * block and non-block formats in the same way.
+		 *
+		 * For formats that are intended to be used only with non-linear
+		 * modifiers both @cpp and @char_per_block must be 0 in the
+		 * generic format table. Drivers could supply accurate
+		 * information from their drm_mode_config.get_format_info hook
+		 * if they want the core to be validating the pitch.
+		 */
+		u8 char_per_block[3];
+	};
+
+	/**
+	 * @block_w:
+	 *
+	 * Block width in pixels, this is intended to be accessed through
+	 * image_format_info_block_width()
+	 */
+	u8 block_w[3];
+
+	/**
+	 * @block_h:
+	 *
+	 * Block height in pixels, this is intended to be accessed through
+	 * image_format_info_block_height()
+	 */
+	u8 block_h[3];
+
+	/** @hsub: Horizontal chroma subsampling factor */
+	u8 hsub;
+	/** @vsub: Vertical chroma subsampling factor */
+	u8 vsub;
+
+	/** @has_alpha: Does the format embeds an alpha component? */
+	bool has_alpha;
+
+	/** @is_yuv: Is it a YUV format? */
+	bool is_yuv;
+};
+
+/**
+ * image_format_info_is_yuv_packed - check that the format info matches a YUV
+ * format with data laid in a single plane
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a packed YUV format.
+ */
+static inline bool
+image_format_info_is_yuv_packed(const struct image_format_info *info)
+{
+	return info->is_yuv && info->num_planes == 1;
+}
+
+/**
+ * image_format_info_is_yuv_semiplanar - check that the format info matches a YUV
+ * format with data laid in two planes (luminance and chrominance)
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a semiplanar YUV format.
+ */
+static inline bool
+image_format_info_is_yuv_semiplanar(const struct image_format_info *info)
+{
+	return info->is_yuv && info->num_planes == 2;
+}
+
+/**
+ * image_format_info_is_yuv_planar - check that the format info matches a YUV
+ * format with data laid in three planes (one for each YUV component)
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a planar YUV format.
+ */
+static inline bool
+image_format_info_is_yuv_planar(const struct image_format_info *info)
+{
+	return info->is_yuv && info->num_planes == 3;
+}
+
+/**
+ * image_format_info_is_yuv_sampling_410 - check that the format info matches a
+ * YUV format with 4:1:0 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:1:0
+ * sub-sampling.
+ */
+static inline bool
+image_format_info_is_yuv_sampling_410(const struct image_format_info *info)
+{
+	return info->is_yuv && info->hsub == 4 && info->vsub == 4;
+}
+
+/**
+ * image_format_info_is_yuv_sampling_411 - check that the format info matches a
+ * YUV format with 4:1:1 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:1:1
+ * sub-sampling.
+ */
+static inline bool
+image_format_info_is_yuv_sampling_411(const struct image_format_info *info)
+{
+	return info->is_yuv && info->hsub == 4 && info->vsub == 1;
+}
+
+/**
+ * image_format_info_is_yuv_sampling_420 - check that the format info matches a
+ * YUV format with 4:2:0 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:2:0
+ * sub-sampling.
+ */
+static inline bool
+image_format_info_is_yuv_sampling_420(const struct image_format_info *info)
+{
+	return info->is_yuv && info->hsub == 2 && info->vsub == 2;
+}
+
+/**
+ * image_format_info_is_yuv_sampling_422 - check that the format info matches a
+ * YUV format with 4:2:2 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:2:2
+ * sub-sampling.
+ */
+static inline bool
+image_format_info_is_yuv_sampling_422(const struct image_format_info *info)
+{
+	return info->is_yuv && info->hsub == 2 && info->vsub == 1;
+}
+
+/**
+ * image_format_info_is_yuv_sampling_444 - check that the format info matches a
+ * YUV format with 4:4:4 sub-sampling
+ * @info: format info
+ *
+ * Returns:
+ * A boolean indicating whether the format info matches a YUV format with 4:4:4
+ * sub-sampling.
+ */
+static inline bool
+image_format_info_is_yuv_sampling_444(const struct image_format_info *info)
+{
+	return info->is_yuv && info->hsub == 1 && info->vsub == 1;
+}
+
+/**
+ * image_format_info_plane_cpp - determine the bytes per pixel value
+ * @format: pixel format info
+ * @plane: plane index
+ *
+ * Returns:
+ * The bytes per pixel value for the specified plane.
+ */
+static inline
+int image_format_info_plane_cpp(const struct image_format_info *info, int plane)
+{
+	if (!info || plane >= info->num_planes)
+		return 0;
+
+	return info->cpp[plane];
+}
+
+/**
+ * image_format_info_plane_width - width of the plane given the first plane
+ * @format: pixel format info
+ * @width: width of the first plane
+ * @plane: plane index
+ *
+ * Returns:
+ * The width of @plane, given that the width of the first plane is @width.
+ */
+static inline
+int image_format_info_plane_width(const struct image_format_info *info, int width,
+				  int plane)
+{
+	if (!info || plane >= info->num_planes)
+		return 0;
+
+	if (plane == 0)
+		return width;
+
+	return width / info->hsub;
+}
+
+/**
+ * image_format_info_plane_height - height of the plane given the first plane
+ * @format: pixel format info
+ * @height: height of the first plane
+ * @plane: plane index
+ *
+ * Returns:
+ * The height of @plane, given that the height of the first plane is @height.
+ */
+static inline
+int image_format_info_plane_height(const struct image_format_info *info, int height,
+				   int plane)
+{
+	if (!info || plane >= info->num_planes)
+		return 0;
+
+	if (plane == 0)
+		return height;
+
+	return height / info->vsub;
+}
+
+/**
+ * image_format_info_block_width - width in pixels of block.
+ * @format: pointer to the image_format_info
+ * @plane: plane index
+ *
+ * Returns:
+ * The width in pixels of a block, depending on the plane index.
+ */
+static inline
+unsigned int image_format_info_block_width(const struct image_format_info *format,
+					   int plane)
+{
+	if (!format)
+		return 0;
+
+	if (plane < 0 || plane >= ARRAY_SIZE(format->block_w))
+		return 0;
+
+	if (plane >= format->num_planes)
+		return 0;
+
+	if (!format->block_w[plane])
+		return 1;
+
+	return format->block_w[plane];
+}
+
+/**
+ * image_format_info_block_height - height in pixels of a block
+ * @info: pointer to the image_format_info
+ * @plane: plane index
+ *
+ * Returns:
+ * The height in pixels of a block, depending on the plane index.
+ */
+static inline
+unsigned int image_format_info_block_height(const struct image_format_info *format,
+					    int plane)
+{
+	if (!format)
+		return 0;
+
+	if (plane < 0 || plane >= ARRAY_SIZE(format->block_w))
+		return 0;
+
+	if (plane >= format->num_planes)
+		return 0;
+
+	if (!format->block_h[plane])
+		return 1;
+
+	return format->block_h[plane];
+}
+
+/**
+ * image_format_info_min_pitch - computes the minimum required pitch in bytes
+ * @info: pixel format info
+ * @plane: plane index
+ * @buffer_width: buffer width in pixels
+ *
+ * Returns:
+ * The minimum required pitch in bytes for a buffer by taking into consideration
+ * the pixel format information and the buffer width.
+ */
+static inline
+uint64_t image_format_info_min_pitch(const struct image_format_info *info,
+				     int plane, unsigned int buffer_width)
+{
+	if (!info || plane < 0 || plane >= info->num_planes)
+		return 0;
+
+	return DIV_ROUND_UP_ULL((u64)buffer_width * info->char_per_block[plane],
+			    image_format_info_block_width(info, plane) *
+			    image_format_info_block_height(info, plane));
+}
+
+const struct image_format_info *__image_format_drm_lookup(u32 drm);
+const struct image_format_info *image_format_drm_lookup(u32 drm);
+
+#endif /* _IMAGE_FORMATS_H_ */
diff --git a/lib/Kconfig b/lib/Kconfig
index fb453afff32e..9a0160d3123b 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -625,3 +625,10 @@  config GENERIC_LIB_UCMPDI2
 
 config OBJAGG
 	tristate "objagg" if COMPILE_TEST
+
+config IMAGE_FORMATS
+	bool
+
+config IMAGE_FORMATS_SELFTESTS
+	tristate "Test image format functions"
+	depends on IMAGE_FORMATS
diff --git a/lib/Makefile b/lib/Makefile
index 6996d2b9f401..203336b91248 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -280,3 +280,6 @@  obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
 obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
 obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
 obj-$(CONFIG_OBJAGG) += objagg.o
+
+obj-$(CONFIG_IMAGE_FORMATS) += image-formats.o
+obj-$(CONFIG_IMAGE_FORMATS_SELFTESTS) += image-formats-selftests.o
diff --git a/lib/image-formats-selftests.c b/lib/image-formats-selftests.c
new file mode 100644
index 000000000000..d0f0011b535e
--- /dev/null
+++ b/lib/image-formats-selftests.c
@@ -0,0 +1,325 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test cases for the image_format functions
+ */
+
+#define pr_fmt(fmt) "image_format: " fmt
+
+#include <linux/errno.h>
+#include <linux/image-formats.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <drm/drm_fourcc.h>
+
+#define FAIL(test, msg, ...) \
+	do { \
+		if (test) { \
+			pr_err("%s/%u: " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
+			return -EINVAL; \
+		} \
+	} while (0)
+
+#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n")
+
+static int test_image_format_info_block_width(void)
+{
+	const struct image_format_info *info = NULL;
+
+	/* Test invalid arguments */
+	FAIL_ON(image_format_info_block_width(info, 0) != 0);
+	FAIL_ON(image_format_info_block_width(info, -1) != 0);
+	FAIL_ON(image_format_info_block_width(info, 1) != 0);
+
+	/* Test 1 plane format */
+	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_block_width(info, 0) != 1);
+	FAIL_ON(image_format_info_block_width(info, 1) != 0);
+	FAIL_ON(image_format_info_block_width(info, -1) != 0);
+
+	/* Test 2 planes format */
+	info = image_format_drm_lookup(DRM_FORMAT_NV12);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_block_width(info, 0) != 1);
+	FAIL_ON(image_format_info_block_width(info, 1) != 1);
+	FAIL_ON(image_format_info_block_width(info, 2) != 0);
+	FAIL_ON(image_format_info_block_width(info, -1) != 0);
+
+	/* Test 3 planes format */
+	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_block_width(info, 0) != 1);
+	FAIL_ON(image_format_info_block_width(info, 1) != 1);
+	FAIL_ON(image_format_info_block_width(info, 2) != 1);
+	FAIL_ON(image_format_info_block_width(info, 3) != 0);
+	FAIL_ON(image_format_info_block_width(info, -1) != 0);
+
+	/* Test a tiled format */
+	info = image_format_drm_lookup(DRM_FORMAT_X0L0);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_block_width(info, 0) != 2);
+	FAIL_ON(image_format_info_block_width(info, 1) != 0);
+	FAIL_ON(image_format_info_block_width(info, -1) != 0);
+
+	return 0;
+}
+
+static int test_image_format_info_block_height(void)
+{
+	const struct image_format_info *info = NULL;
+
+	/* Test invalid arguments */
+	FAIL_ON(image_format_info_block_height(info, 0) != 0);
+	FAIL_ON(image_format_info_block_height(info, -1) != 0);
+	FAIL_ON(image_format_info_block_height(info, 1) != 0);
+
+	/* Test 1 plane format */
+	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_block_height(info, 0) != 1);
+	FAIL_ON(image_format_info_block_height(info, 1) != 0);
+	FAIL_ON(image_format_info_block_height(info, -1) != 0);
+
+	/* Test 2 planes format */
+	info = image_format_drm_lookup(DRM_FORMAT_NV12);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_block_height(info, 0) != 1);
+	FAIL_ON(image_format_info_block_height(info, 1) != 1);
+	FAIL_ON(image_format_info_block_height(info, 2) != 0);
+	FAIL_ON(image_format_info_block_height(info, -1) != 0);
+
+	/* Test 3 planes format */
+	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_block_height(info, 0) != 1);
+	FAIL_ON(image_format_info_block_height(info, 1) != 1);
+	FAIL_ON(image_format_info_block_height(info, 2) != 1);
+	FAIL_ON(image_format_info_block_height(info, 3) != 0);
+	FAIL_ON(image_format_info_block_height(info, -1) != 0);
+
+	/* Test a tiled format */
+	info = image_format_drm_lookup(DRM_FORMAT_X0L0);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_block_height(info, 0) != 2);
+	FAIL_ON(image_format_info_block_height(info, 1) != 0);
+	FAIL_ON(image_format_info_block_height(info, -1) != 0);
+
+	return 0;
+}
+
+static int test_image_format_info_min_pitch(void)
+{
+	const struct image_format_info *info = NULL;
+
+	/* Test invalid arguments */
+	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
+
+	/* Test 1 plane 8 bits per pixel format */
+	info = image_format_drm_lookup(DRM_FORMAT_RGB332);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
+
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
+			(uint64_t)UINT_MAX);
+	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
+			(uint64_t)(UINT_MAX - 1));
+
+	/* Test 1 plane 16 bits per pixel format */
+	info = image_format_drm_lookup(DRM_FORMAT_XRGB4444);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
+
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 2);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 4);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1280);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 2048);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 3840);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 8192);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 1342);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
+			(uint64_t)UINT_MAX * 2);
+	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
+			(uint64_t)(UINT_MAX - 1) * 2);
+
+	/* Test 1 plane 24 bits per pixel format */
+	info = image_format_drm_lookup(DRM_FORMAT_RGB888);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
+
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 3);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 6);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1920);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 3072);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 5760);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 12288);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 2013);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
+			(uint64_t)UINT_MAX * 3);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
+			(uint64_t)(UINT_MAX - 1) * 3);
+
+	/* Test 1 plane 32 bits per pixel format */
+	info = image_format_drm_lookup(DRM_FORMAT_ABGR8888);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
+
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 4);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 8);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 2560);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 4096);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 7680);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 16384);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 2684);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
+			(uint64_t)UINT_MAX * 4);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
+			(uint64_t)(UINT_MAX - 1) * 4);
+
+	/* Test 2 planes format */
+	info = image_format_drm_lookup(DRM_FORMAT_NV12);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 2, 0) != 0);
+
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 2);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 2);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 320) != 640);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 512) != 1024);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 960) != 1920);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 2048) != 4096);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 336) != 672);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
+			(uint64_t)UINT_MAX);
+	FAIL_ON(image_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) !=
+			(uint64_t)UINT_MAX + 1);
+	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1)) !=
+			(uint64_t)(UINT_MAX - 1));
+	FAIL_ON(image_format_info_min_pitch(info, 1, (UINT_MAX - 1) /  2) !=
+			(uint64_t)(UINT_MAX - 1));
+
+	/* Test 3 planes 8 bits per pixel format */
+	info = image_format_drm_lookup(DRM_FORMAT_YUV422);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 2, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 3, 0) != 0);
+
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 1);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 1) != 1);
+	FAIL_ON(image_format_info_min_pitch(info, 2, 1) != 1);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 2);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 2) != 2);
+	FAIL_ON(image_format_info_min_pitch(info, 2, 2) != 2);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 640);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 320) != 320);
+	FAIL_ON(image_format_info_min_pitch(info, 2, 320) != 320);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 1024);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 512) != 512);
+	FAIL_ON(image_format_info_min_pitch(info, 2, 512) != 512);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 1920);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 960) != 960);
+	FAIL_ON(image_format_info_min_pitch(info, 2, 960) != 960);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 4096);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 2048) != 2048);
+	FAIL_ON(image_format_info_min_pitch(info, 2, 2048) != 2048);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 671);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 336) != 336);
+	FAIL_ON(image_format_info_min_pitch(info, 2, 336) != 336);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
+			(uint64_t)UINT_MAX);
+	FAIL_ON(image_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) !=
+			(uint64_t)UINT_MAX / 2 + 1);
+	FAIL_ON(image_format_info_min_pitch(info, 2, UINT_MAX / 2 + 1) !=
+			(uint64_t)UINT_MAX / 2 + 1);
+	FAIL_ON(image_format_info_min_pitch(info, 0, (UINT_MAX - 1) / 2) !=
+			(uint64_t)(UINT_MAX - 1) / 2);
+	FAIL_ON(image_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2) !=
+			(uint64_t)(UINT_MAX - 1) / 2);
+	FAIL_ON(image_format_info_min_pitch(info, 2, (UINT_MAX - 1) / 2) !=
+			(uint64_t)(UINT_MAX - 1) / 2);
+
+	/* Test tiled format */
+	info = image_format_drm_lookup(DRM_FORMAT_X0L2);
+	FAIL_ON(!info);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, -1, 0) != 0);
+	FAIL_ON(image_format_info_min_pitch(info, 1, 0) != 0);
+
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1) != 2);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 2) != 4);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 640) != 1280);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1024) != 2048);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 1920) != 3840);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 4096) != 8192);
+	FAIL_ON(image_format_info_min_pitch(info, 0, 671) != 1342);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX) !=
+			(uint64_t)UINT_MAX * 2);
+	FAIL_ON(image_format_info_min_pitch(info, 0, UINT_MAX - 1) !=
+			(uint64_t)(UINT_MAX - 1) * 2);
+
+	return 0;
+}
+
+#define selftest(test)	{ .name = #test, .func = test, }
+
+static struct image_format_test {
+	char	*name;
+	int	(*func)(void);
+} tests[] = {
+	selftest(test_image_format_info_block_height),
+	selftest(test_image_format_info_block_width),
+	selftest(test_image_format_info_min_pitch),
+};
+
+static int __init image_format_test_init(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		struct image_format_test *test = &tests[i];
+		int ret;
+
+		ret = test->func();
+		if (ret) {
+			pr_err("Failed test %s\n", test->name);
+			return ret;
+		}
+	}
+
+	pr_info("All tests executed properly.\n");
+	return 0;
+}
+module_init(image_format_test_init);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/lib/image-formats.c b/lib/image-formats.c
new file mode 100644
index 000000000000..1e52a7410222
--- /dev/null
+++ b/lib/image-formats.c
@@ -0,0 +1,655 @@ 
+/*
+ * Copyright (c) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Copyright (c) 2019 Maxime Ripard <maxime.ripard@bootlin.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/bug.h>
+#include <linux/image-formats.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+
+#include <uapi/drm/drm_fourcc.h>
+
+static const struct image_format_info formats[] = {
+	{
+		.drm_fmt = DRM_FORMAT_C8,
+		.depth = 8,
+		.num_planes = 1,
+		.cpp = { 1, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGB332,
+		.depth = 8,
+		.num_planes = 1,
+		.cpp = { 1, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGR233,
+		.depth = 8,
+		.num_planes = 1,
+		.cpp = { 1, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_XRGB4444,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_XBGR4444,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGBX4444,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGRX4444,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_ARGB4444,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_ABGR4444,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGBA4444,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGRA4444,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_XRGB1555,
+		.depth = 15,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_XBGR1555,
+		.depth = 15,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGBX5551,
+		.depth = 15,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGRX5551,
+		.depth = 15,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_ARGB1555,
+		.depth = 15,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_ABGR1555,
+		.depth = 15,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGBA5551,
+		.depth = 15,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGRA5551,
+		.depth = 15,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGB565,
+		.depth = 16,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGR565,
+		.depth = 16,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGB888,
+		.depth = 24,
+		.num_planes = 1,
+		.cpp = { 3, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGR888,
+		.depth = 24,
+		.num_planes = 1,
+		.cpp = { 3, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_XRGB8888,
+		.depth = 24,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_XBGR8888,
+		.depth = 24,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGBX8888,
+		.depth = 24,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGRX8888,
+		.depth = 24,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGB565_A8,
+		.depth = 24,
+		.num_planes = 2,
+		.cpp = { 2, 1, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGR565_A8,
+		.depth = 24,
+		.num_planes = 2,
+		.cpp = { 2, 1, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_XRGB2101010,
+		.depth = 30,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_XBGR2101010,
+		.depth = 30,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGBX1010102,
+		.depth = 30,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGRX1010102,
+		.depth = 30,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+	}, {
+		.drm_fmt = DRM_FORMAT_ARGB2101010,
+		.depth = 30,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_ABGR2101010,
+		.depth = 30,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGBA1010102,
+		.depth = 30,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGRA1010102,
+		.depth = 30,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_ARGB8888,
+		.depth = 32,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_ABGR8888,
+		.depth = 32,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGBA8888,
+		.depth = 32,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGRA8888,
+		.depth = 32,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGB888_A8,
+		.depth = 32,
+		.num_planes = 2,
+		.cpp = { 3, 1, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGR888_A8,
+		.depth = 32,
+		.num_planes = 2,
+		.cpp = { 3, 1, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_XRGB8888_A8,
+		.depth = 32,
+		.num_planes = 2,
+		.cpp = { 4, 1, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_XBGR8888_A8,
+		.depth = 32,
+		.num_planes = 2,
+		.cpp = { 4, 1, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_RGBX8888_A8,
+		.depth = 32,
+		.num_planes = 2,
+		.cpp = { 4, 1, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_BGRX8888_A8,
+		.depth = 32,
+		.num_planes = 2,
+		.cpp = { 4, 1, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YUV410,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 4,
+		.vsub = 4,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YVU410,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 4,
+		.vsub = 4,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YUV411,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 4,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YVU411,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 4,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YUV420,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 2,
+		.vsub = 2,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YVU420,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 2,
+		.vsub = 2,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YUV422,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 2,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YVU422,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 2,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YUV444,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 1,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YVU444,
+		.depth = 0,
+		.num_planes = 3,
+		.cpp = { 1, 1, 1 },
+		.hsub = 1,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_NV12,
+		.depth = 0,
+		.num_planes = 2,
+		.cpp = { 1, 2, 0 },
+		.hsub = 2,
+		.vsub = 2,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_NV21,
+		.depth = 0,
+		.num_planes = 2,
+		.cpp = { 1, 2, 0 },
+		.hsub = 2,
+		.vsub = 2,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_NV16,
+		.depth = 0,
+		.num_planes = 2,
+		.cpp = { 1, 2, 0 },
+		.hsub = 2,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_NV61,
+		.depth = 0,
+		.num_planes = 2,
+		.cpp = { 1, 2, 0 },
+		.hsub = 2,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_NV24,
+		.depth = 0,
+		.num_planes = 2,
+		.cpp = { 1, 2, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_NV42,
+		.depth = 0,
+		.num_planes = 2,
+		.cpp = { 1, 2, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YUYV,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 2,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_YVYU,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 2,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_UYVY,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 2,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_VYUY,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 2, 0, 0 },
+		.hsub = 2,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_XYUV8888,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_AYUV,
+		.depth = 0,
+		.num_planes = 1,
+		.cpp = { 4, 0, 0 },
+		.hsub = 1,
+		.vsub = 1,
+		.has_alpha = true,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_Y0L0,
+		.depth = 0,
+		.num_planes = 1,
+		.char_per_block = { 8, 0, 0 },
+		.block_w = { 2, 0, 0 },
+		.block_h = { 2, 0, 0 },
+		.hsub = 2,
+		.vsub = 2,
+		.has_alpha = true,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_X0L0,
+		.depth = 0,
+		.num_planes = 1,
+		.char_per_block = { 8, 0, 0 },
+		.block_w = { 2, 0, 0 },
+		.block_h = { 2, 0, 0 },
+		.hsub = 2,
+		.vsub = 2,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_Y0L2,
+		.depth = 0,
+		.num_planes = 1,
+		.char_per_block = { 8, 0, 0 },
+		.block_w = { 2, 0, 0 },
+		.block_h = { 2, 0, 0 },
+		.hsub = 2,
+		.vsub = 2,
+		.has_alpha = true,
+		.is_yuv = true,
+	}, {
+		.drm_fmt = DRM_FORMAT_X0L2,
+		.depth = 0,
+		.num_planes = 1,
+		.char_per_block = { 8, 0, 0 },
+		.block_w = { 2, 0, 0 },
+		.block_h = { 2, 0, 0 },
+		.hsub = 2,
+		.vsub = 2,
+		.is_yuv = true,
+	},
+};
+
+#define __image_format_lookup(_field, _fmt)			\
+	({							\
+		const struct image_format_info *format = NULL;	\
+		unsigned i;					\
+								\
+		for (i = 0; i < ARRAY_SIZE(formats); i++)	\
+			if (formats[i]._field == _fmt)		\
+				format = &formats[i];		\
+								\
+		format;						\
+	})
+
+/**
+ * __image_format_drm_lookup - query information for a given format
+ * @drm: DRM fourcc pixel format (DRM_FORMAT_*)
+ *
+ * The caller should only pass a supported pixel format to this function.
+ *
+ * Returns:
+ * The instance of struct image_format_info that describes the pixel format, or
+ * NULL if the format is unsupported.
+ */
+const struct image_format_info *__image_format_drm_lookup(u32 drm)
+{
+	return __image_format_lookup(drm_fmt, drm);
+}
+EXPORT_SYMBOL(__image_format_drm_lookup);
+
+/**
+ * image_format_drm_lookup - query information for a given format
+ * @drm: DRM fourcc pixel format (DRM_FORMAT_*)
+ *
+ * The caller should only pass a supported pixel format to this function.
+ * Unsupported pixel formats will generate a warning in the kernel log.
+ *
+ * Returns:
+ * The instance of struct image_format_info that describes the pixel format, or
+ * NULL if the format is unsupported.
+ */
+const struct image_format_info *image_format_drm_lookup(u32 drm)
+{
+	const struct image_format_info *format;
+
+	format = __image_format_drm_lookup(drm);
+
+	WARN_ON(!format);
+	return format;
+}
+EXPORT_SYMBOL(image_format_drm_lookup);