diff --git a/include/libcamera/pixelformats.h b/include/libcamera/pixelformats.h
index 6e25b8d8b76e5961..f0951e983192d5e8 100644
--- a/include/libcamera/pixelformats.h
+++ b/include/libcamera/pixelformats.h
@@ -7,11 +7,49 @@
 #ifndef __LIBCAMERA_PIXEL_FORMATS_H__
 #define __LIBCAMERA_PIXEL_FORMATS_H__
 
+#include <set>
 #include <stdint.h>
+#include <string>
+
+/* Needs to be forward declared in the global namespace. */
+class V4L2CameraProxy;
 
 namespace libcamera {
 
-using PixelFormat = uint32_t;
+struct PixelFormatEntry;
+
+class PixelFormat
+{
+public:
+	PixelFormat();
+	PixelFormat(const PixelFormat &other);
+	explicit PixelFormat(uint32_t drm_fourcc, const std::set<uint32_t> &drm_modifiers);
+
+	PixelFormat &operator=(const PixelFormat &other);
+
+	bool operator==(const PixelFormat &other) const;
+	bool operator!=(const PixelFormat &other) const;
+	bool operator<(const PixelFormat &other) const;
+
+	uint32_t v4l2() const;
+	uint32_t fourcc() const;
+	const std::set<uint32_t> &modifiers() const;
+
+protected:
+	/* Needed so V4L2VideoDevice can create PixelFormat from V4L2 fourcc. */
+	friend class V4L2VideoDevice;
+
+	/* Needed so V4L2CameraProxy can create PixelFormat from V4L2 fourcc. */
+	friend V4L2CameraProxy;
+
+	explicit PixelFormat(uint32_t v4l2_fourcc);
+
+private:
+	const PixelFormatEntry *fromDRM(uint32_t drm_fourcc, const std::set<uint32_t> &drm_modifiers) const;
+	const PixelFormatEntry *fromV4L2(uint32_t v4l2_fourcc) const;
+
+	const PixelFormatEntry *format_;
+};
 
 } /* namespace libcamera */
 
diff --git a/src/cam/main.cpp b/src/cam/main.cpp
index ad4be55fc114fe22..c8ef79daea37d8b6 100644
--- a/src/cam/main.cpp
+++ b/src/cam/main.cpp
@@ -249,7 +249,7 @@ int CamApp::prepareConfig()
 
 			/* TODO: Translate 4CC string to ID. */
 			if (opt.isSet("pixelformat"))
-				cfg.pixelFormat = opt["pixelformat"];
+				cfg.pixelFormat = PixelFormat(opt["pixelformat"], {});
 		}
 	}
 
@@ -283,7 +283,7 @@ int CamApp::infoConfiguration()
 		const StreamFormats &formats = cfg.formats();
 		for (PixelFormat pixelformat : formats.pixelformats()) {
 			std::cout << " * Pixelformat: 0x" << std::hex
-				  << std::setw(8) << pixelformat << " "
+				  << std::setw(8) << pixelformat.fourcc() << " "
 				  << formats.range(pixelformat).toString()
 				  << std::endl;
 
diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp
index 98817deee2b54c84..e3a89121e3c60151 100644
--- a/src/libcamera/formats.cpp
+++ b/src/libcamera/formats.cpp
@@ -9,6 +9,8 @@
 
 #include <errno.h>
 
+#include <libcamera/pixelformats.h>
+
 /**
  * \file formats.h
  * \brief Types and helper methods to handle libcamera image formats
@@ -110,5 +112,6 @@ const std::map<T, std::vector<SizeRange>> &ImageFormats<T>::data() const
 }
 
 template class ImageFormats<unsigned int>;
+template class ImageFormats<PixelFormat>;
 
 } /* namespace libcamera */
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 33f97b340716abd0..71ac0ae33921f548 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -247,7 +247,7 @@ IPU3CameraConfiguration::IPU3CameraConfiguration(Camera *camera,
 void IPU3CameraConfiguration::adjustStream(StreamConfiguration &cfg, bool scale)
 {
 	/* The only pixel format the driver supports is NV12. */
-	cfg.pixelFormat = DRM_FORMAT_NV12;
+	cfg.pixelFormat = PixelFormat(DRM_FORMAT_NV12, {});
 
 	if (scale) {
 		/*
@@ -402,7 +402,7 @@ CameraConfiguration *PipelineHandlerIPU3::generateConfiguration(Camera *camera,
 		StreamConfiguration cfg = {};
 		IPU3Stream *stream = nullptr;
 
-		cfg.pixelFormat = DRM_FORMAT_NV12;
+		cfg.pixelFormat = PixelFormat(DRM_FORMAT_NV12, {});
 
 		switch (role) {
 		case StreamRole::StillCapture:
@@ -1142,7 +1142,7 @@ int ImgUDevice::configureOutput(ImgUOutput *output,
 		return 0;
 
 	V4L2DeviceFormat outputFormat = {};
-	outputFormat.fourcc = dev->toV4L2Fourcc(DRM_FORMAT_NV12);
+	outputFormat.fourcc = PixelFormat(DRM_FORMAT_NV12, {}).v4l2();
 	outputFormat.size = cfg.size;
 	outputFormat.planesCount = 2;
 
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 13433b216747cb8b..323fa3596c6ee242 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -433,14 +433,14 @@ RkISP1CameraConfiguration::RkISP1CameraConfiguration(Camera *camera,
 
 CameraConfiguration::Status RkISP1CameraConfiguration::validate()
 {
-	static const std::array<unsigned int, 8> formats{
-		DRM_FORMAT_YUYV,
-		DRM_FORMAT_YVYU,
-		DRM_FORMAT_VYUY,
-		DRM_FORMAT_NV16,
-		DRM_FORMAT_NV61,
-		DRM_FORMAT_NV21,
-		DRM_FORMAT_NV12,
+	static const std::array<PixelFormat, 8> formats{
+		PixelFormat(DRM_FORMAT_YUYV, {}),
+		PixelFormat(DRM_FORMAT_YVYU, {}),
+		PixelFormat(DRM_FORMAT_VYUY, {}),
+		PixelFormat(DRM_FORMAT_NV16, {}),
+		PixelFormat(DRM_FORMAT_NV61, {}),
+		PixelFormat(DRM_FORMAT_NV21, {}),
+		PixelFormat(DRM_FORMAT_NV12, {}),
 		/* \todo Add support for 8-bit greyscale to DRM formats */
 	};
 
@@ -462,7 +462,7 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()
 	if (std::find(formats.begin(), formats.end(), cfg.pixelFormat) ==
 	    formats.end()) {
 		LOG(RkISP1, Debug) << "Adjusting format to NV12";
-		cfg.pixelFormat = DRM_FORMAT_NV12,
+		cfg.pixelFormat = PixelFormat(DRM_FORMAT_NV12, {});
 		status = Adjusted;
 	}
 
@@ -541,7 +541,7 @@ CameraConfiguration *PipelineHandlerRkISP1::generateConfiguration(Camera *camera
 		return config;
 
 	StreamConfiguration cfg{};
-	cfg.pixelFormat = DRM_FORMAT_NV12;
+	cfg.pixelFormat = PixelFormat(DRM_FORMAT_NV12, {});
 	cfg.size = data->sensor_->resolution();
 
 	config->addConfiguration(cfg);
@@ -796,7 +796,7 @@ int PipelineHandlerRkISP1::start(Camera *camera)
 	/* Inform IPA of stream configuration and sensor controls. */
 	std::map<unsigned int, IPAStream> streamConfig;
 	streamConfig[0] = {
-		.pixelFormat = data->stream_.configuration().pixelFormat,
+		.pixelFormat = data->stream_.configuration().pixelFormat.fourcc(),
 		.size = data->stream_.configuration().size,
 	};
 
diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp
index 8efd188e75a3d135..5f3e52f691aaeae4 100644
--- a/src/libcamera/pipeline/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo.cpp
@@ -115,8 +115,8 @@ CameraConfiguration::Status UVCCameraConfiguration::validate()
 	if (iter == pixelFormats.end()) {
 		cfg.pixelFormat = pixelFormats.front();
 		LOG(UVC, Debug)
-			<< "Adjusting pixel format from " << pixelFormat
-			<< " to " << cfg.pixelFormat;
+			<< "Adjusting pixel format from " << pixelFormat.fourcc()
+			<< " to " << cfg.pixelFormat.fourcc();
 		status = Adjusted;
 	}
 
diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
index 9fbe33c626e327d4..a591c424919b0783 100644
--- a/src/libcamera/pipeline/vimc.cpp
+++ b/src/libcamera/pipeline/vimc.cpp
@@ -106,10 +106,10 @@ private:
 
 namespace {
 
-constexpr std::array<unsigned int, 3> pixelformats{
-	DRM_FORMAT_RGB888,
-	DRM_FORMAT_BGR888,
-	DRM_FORMAT_BGRA8888,
+static const std::array<PixelFormat, 3> pixelformats{
+	PixelFormat(DRM_FORMAT_RGB888, {}),
+	PixelFormat(DRM_FORMAT_BGR888, {}),
+	PixelFormat(DRM_FORMAT_BGRA8888, {}),
 };
 
 } /* namespace */
@@ -138,7 +138,7 @@ CameraConfiguration::Status VimcCameraConfiguration::validate()
 	if (std::find(pixelformats.begin(), pixelformats.end(), cfg.pixelFormat) ==
 	    pixelformats.end()) {
 		LOG(VIMC, Debug) << "Adjusting format to RGB24";
-		cfg.pixelFormat = DRM_FORMAT_BGR888;
+		cfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888, {});
 		status = Adjusted;
 	}
 
@@ -187,7 +187,7 @@ CameraConfiguration *PipelineHandlerVimc::generateConfiguration(Camera *camera,
 
 	StreamConfiguration cfg(formats.data());
 
-	cfg.pixelFormat = DRM_FORMAT_BGR888;
+	cfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888, {});
 	cfg.size = { 1920, 1080 };
 	cfg.bufferCount = 4;
 
diff --git a/src/libcamera/pixelformats.cpp b/src/libcamera/pixelformats.cpp
index c03335400b709d9b..b2aacbc39b9ca16a 100644
--- a/src/libcamera/pixelformats.cpp
+++ b/src/libcamera/pixelformats.cpp
@@ -7,6 +7,13 @@
 
 #include <libcamera/pixelformats.h>
 
+#include <vector>
+
+#include <linux/drm_fourcc.h>
+#include <linux/videodev2.h>
+
+#include "log.h"
+
 /**
  * \file pixelformats.h
  * \brief libcamera pixel formats
@@ -14,6 +21,8 @@
 
 namespace libcamera {
 
+LOG_DEFINE_CATEGORY(PixelFormats)
+
 /**
  * \typedef PixelFormat
  * \brief libcamera image pixel format
@@ -25,4 +34,119 @@ namespace libcamera {
  * \todo Add support for format modifiers
  */
 
+struct PixelFormatEntry {
+	uint32_t v4l2;
+	uint32_t drm;
+	std::set<uint32_t> modifiers;
+};
+
+static const std::vector<PixelFormatEntry> pixelFormats = {
+	/* Invalid format, important to be first in list. */
+	{ .v4l2 = 0,			.drm = DRM_FORMAT_INVALID,	.modifiers = {} },
+	/* RGB formats. */
+	{ .v4l2 = V4L2_PIX_FMT_RGB24,	.drm = DRM_FORMAT_BGR888,	.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_BGR24,	.drm = DRM_FORMAT_RGB888,	.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_ARGB32,	.drm = DRM_FORMAT_BGRA8888,	.modifiers = {} },
+	/* YUV packed formats. */
+	{ .v4l2 = V4L2_PIX_FMT_YUYV,	.drm = DRM_FORMAT_YUYV,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_YVYU,	.drm = DRM_FORMAT_YVYU,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_UYVY,	.drm = DRM_FORMAT_UYVY,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_VYUY,	.drm = DRM_FORMAT_VYUY,		.modifiers = {} },
+	/* YUY planar formats. */
+	{ .v4l2 = V4L2_PIX_FMT_NV16,	.drm = DRM_FORMAT_NV16,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_NV16M,	.drm = DRM_FORMAT_NV16,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_NV61,	.drm = DRM_FORMAT_NV61,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_NV61M,	.drm = DRM_FORMAT_NV61,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_NV12,	.drm = DRM_FORMAT_NV12,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_NV12M,	.drm = DRM_FORMAT_NV12,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_NV21,	.drm = DRM_FORMAT_NV21,		.modifiers = {} },
+	{ .v4l2 = V4L2_PIX_FMT_NV21M,	.drm = DRM_FORMAT_NV21,		.modifiers = {} },
+	/* Compressed formats. */
+	{ .v4l2 = V4L2_PIX_FMT_MJPEG,	.drm = DRM_FORMAT_MJPEG,	.modifiers = {} },
+};
+
+PixelFormat::PixelFormat()
+	: format_(&pixelFormats[0])
+{
+}
+
+PixelFormat::PixelFormat(const PixelFormat &other)
+	: format_(other.format_)
+{
+}
+
+PixelFormat::PixelFormat(uint32_t drm_fourcc, const std::set<uint32_t> &drm_modifiers)
+	: format_(fromDRM(drm_fourcc, drm_modifiers))
+{
+}
+
+PixelFormat::PixelFormat(uint32_t v4l2_fourcc)
+	: format_(fromV4L2(v4l2_fourcc))
+{
+}
+
+PixelFormat &PixelFormat::operator=(const PixelFormat &other)
+{
+	format_ = other.format_;
+
+	return *this;
+}
+
+bool PixelFormat::operator==(const PixelFormat &other) const
+{
+	return format_ == other.format_;
+}
+
+bool PixelFormat::operator!=(const PixelFormat &other) const
+{
+	return format_ != other.format_;
+}
+
+bool PixelFormat::operator<(const PixelFormat &other) const
+{
+	return format_ > other.format_;
+}
+
+uint32_t PixelFormat::v4l2() const
+{
+	return format_->v4l2;
+}
+
+uint32_t PixelFormat::fourcc() const
+{
+	return format_->drm;
+}
+
+const std::set<uint32_t> &PixelFormat::modifiers() const
+{
+	return format_->modifiers;
+}
+
+const PixelFormatEntry *PixelFormat::fromDRM(uint32_t drm_fourcc, const std::set<uint32_t> &drm_modifiers) const
+{
+	for (const PixelFormatEntry &entry : pixelFormats)
+		if (entry.drm == drm_fourcc &&
+		    entry.modifiers == drm_modifiers)
+			return &entry;
+
+	LOG(PixelFormats, Error)
+		<< "Unsupported DRM pixel format "
+		<< utils::hex(drm_fourcc);
+
+	return &pixelFormats[0];
+}
+
+const PixelFormatEntry *PixelFormat::fromV4L2(uint32_t v4l2_fourcc) const
+{
+	for (const PixelFormatEntry &entry : pixelFormats)
+		if (entry.v4l2 == v4l2_fourcc)
+			return &entry;
+
+	LOG(PixelFormats, Error)
+		<< "Unsupported V4L2 pixel format "
+		<< utils::hex(v4l2_fourcc);
+
+	return &pixelFormats[0];
+}
+
 } /* namespace libcamera */
diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
index 13789e9eb344f95c..dbce550ca8d0b7b1 100644
--- a/src/libcamera/stream.cpp
+++ b/src/libcamera/stream.cpp
@@ -279,7 +279,7 @@ SizeRange StreamFormats::range(PixelFormat pixelformat) const
  * handlers provied StreamFormats.
  */
 StreamConfiguration::StreamConfiguration()
-	: pixelFormat(0), stream_(nullptr)
+	: pixelFormat(), stream_(nullptr)
 {
 }
 
@@ -287,7 +287,7 @@ StreamConfiguration::StreamConfiguration()
  * \brief Construct a configuration with stream formats
  */
 StreamConfiguration::StreamConfiguration(const StreamFormats &formats)
-	: pixelFormat(0), stream_(nullptr), formats_(formats)
+	: pixelFormat(), stream_(nullptr), formats_(formats)
 {
 }
 
@@ -348,7 +348,7 @@ StreamConfiguration::StreamConfiguration(const StreamFormats &formats)
 std::string StreamConfiguration::toString() const
 {
 	std::stringstream ss;
-	ss << size.toString() << "-" << utils::hex(pixelFormat);
+	ss << size.toString() << "-" << utils::hex(pixelFormat.fourcc());
 	return ss.str();
 }
 
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index f84bd00570afa38c..e9d3e60198e140a0 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -846,7 +846,7 @@ ImageFormats<PixelFormat> V4L2VideoDevice::formats()
 		if (sizes.empty())
 			return {};
 
-		if (formats.addFormat(pixelformat, sizes)) {
+		if (formats.addFormat(PixelFormat(pixelformat), sizes)) {
 			LOG(V4L2, Error)
 				<< "Could not add sizes for pixel format "
 				<< pixelformat;
@@ -1417,56 +1417,7 @@ V4L2VideoDevice *V4L2VideoDevice::fromEntityName(const MediaDevice *media,
  */
 PixelFormat V4L2VideoDevice::toPixelFormat(uint32_t v4l2Fourcc)
 {
-	switch (v4l2Fourcc) {
-	/* RGB formats. */
-	case V4L2_PIX_FMT_RGB24:
-		return DRM_FORMAT_BGR888;
-	case V4L2_PIX_FMT_BGR24:
-		return DRM_FORMAT_RGB888;
-	case V4L2_PIX_FMT_ARGB32:
-		return DRM_FORMAT_BGRA8888;
-
-	/* YUV packed formats. */
-	case V4L2_PIX_FMT_YUYV:
-		return DRM_FORMAT_YUYV;
-	case V4L2_PIX_FMT_YVYU:
-		return DRM_FORMAT_YVYU;
-	case V4L2_PIX_FMT_UYVY:
-		return DRM_FORMAT_UYVY;
-	case V4L2_PIX_FMT_VYUY:
-		return DRM_FORMAT_VYUY;
-
-	/* YUY planar formats. */
-	case V4L2_PIX_FMT_NV16:
-	case V4L2_PIX_FMT_NV16M:
-		return DRM_FORMAT_NV16;
-	case V4L2_PIX_FMT_NV61:
-	case V4L2_PIX_FMT_NV61M:
-		return DRM_FORMAT_NV61;
-	case V4L2_PIX_FMT_NV12:
-	case V4L2_PIX_FMT_NV12M:
-		return DRM_FORMAT_NV12;
-	case V4L2_PIX_FMT_NV21:
-	case V4L2_PIX_FMT_NV21M:
-		return DRM_FORMAT_NV21;
-
-	/* Compressed formats. */
-	case V4L2_PIX_FMT_MJPEG:
-		return DRM_FORMAT_MJPEG;
-
-	/* V4L2 formats not yet supported by DRM. */
-	case V4L2_PIX_FMT_GREY:
-	default:
-		/*
-		 * \todo We can't use LOG() in a static method of a Loggable
-		 * class. Until we fix the logger, work around it.
-		 */
-		libcamera::_log(__FILE__, __LINE__, _LOG_CATEGORY(V4L2)(),
-				LogError).stream()
-			<< "Unsupported V4L2 pixel format "
-			<< utils::hex(v4l2Fourcc);
-		return 0;
-	}
+	return PixelFormat(v4l2Fourcc);
 }
 
 /**
@@ -1500,53 +1451,7 @@ uint32_t V4L2VideoDevice::toV4L2Fourcc(PixelFormat pixelFormat)
  */
 uint32_t V4L2VideoDevice::toV4L2Fourcc(PixelFormat pixelFormat, bool multiplanar)
 {
-	switch (pixelFormat) {
-	/* RGB formats. */
-	case DRM_FORMAT_BGR888:
-		return V4L2_PIX_FMT_RGB24;
-	case DRM_FORMAT_RGB888:
-		return V4L2_PIX_FMT_BGR24;
-	case DRM_FORMAT_BGRA8888:
-		return V4L2_PIX_FMT_ARGB32;
-
-	/* YUV packed formats. */
-	case DRM_FORMAT_YUYV:
-		return V4L2_PIX_FMT_YUYV;
-	case DRM_FORMAT_YVYU:
-		return V4L2_PIX_FMT_YVYU;
-	case DRM_FORMAT_UYVY:
-		return V4L2_PIX_FMT_UYVY;
-	case DRM_FORMAT_VYUY:
-		return V4L2_PIX_FMT_VYUY;
-
-	/*
-	 * YUY planar formats.
-	 * \todo Add support for non-contiguous memory planes
-	 * \todo Select the format variant not only based on \a multiplanar but
-	 * also take into account the formats supported by the device.
-	 */
-	case DRM_FORMAT_NV16:
-		return V4L2_PIX_FMT_NV16;
-	case DRM_FORMAT_NV61:
-		return V4L2_PIX_FMT_NV61;
-	case DRM_FORMAT_NV12:
-		return V4L2_PIX_FMT_NV12;
-	case DRM_FORMAT_NV21:
-		return V4L2_PIX_FMT_NV21;
-
-	/* Compressed formats. */
-	case DRM_FORMAT_MJPEG:
-		return V4L2_PIX_FMT_MJPEG;
-	}
-
-	/*
-	 * \todo We can't use LOG() in a static method of a Loggable
-	 * class. Until we fix the logger, work around it.
-	 */
-	libcamera::_log(__FILE__, __LINE__, _LOG_CATEGORY(V4L2)(), LogError).stream()
-		<< "Unsupported V4L2 pixel format "
-		<< utils::hex(pixelFormat);
-	return 0;
+	return pixelFormat.v4l2();
 }
 
 /**
diff --git a/src/qcam/format_converter.cpp b/src/qcam/format_converter.cpp
index 368cb43fbf17ae4d..c071ea9b4022750c 100644
--- a/src/qcam/format_converter.cpp
+++ b/src/qcam/format_converter.cpp
@@ -30,7 +30,7 @@
 int FormatConverter::configure(libcamera::PixelFormat format, unsigned int width,
 			       unsigned int height)
 {
-	switch (format) {
+	switch (format.fourcc()) {
 	case DRM_FORMAT_NV12:
 		formatFamily_ = NV;
 		horzSubSample_ = 2;
diff --git a/src/qcam/viewfinder.cpp b/src/qcam/viewfinder.cpp
index 0ebb8edd49efd1b1..d86c97dab8374d5e 100644
--- a/src/qcam/viewfinder.cpp
+++ b/src/qcam/viewfinder.cpp
@@ -14,7 +14,7 @@
 #include "viewfinder.h"
 
 ViewFinder::ViewFinder(QWidget *parent)
-	: QWidget(parent), format_(0), width_(0), height_(0), image_(nullptr)
+	: QWidget(parent), format_(), width_(0), height_(0), image_(nullptr)
 {
 }
 
diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
index 622520479be01f58..e9d7bfd8ee243b78 100644
--- a/src/v4l2/v4l2_camera_proxy.cpp
+++ b/src/v4l2/v4l2_camera_proxy.cpp
@@ -525,7 +525,6 @@ struct PixelFormatPlaneInfo {
 };
 
 struct PixelFormatInfo {
-	PixelFormat format;
 	uint32_t v4l2Format;
 	unsigned int numPlanes;
 	std::array<PixelFormatPlaneInfo, 3> planes;
@@ -533,24 +532,24 @@ struct PixelFormatInfo {
 
 namespace {
 
-constexpr std::array<PixelFormatInfo, 13> pixelFormatInfo = {{
+static const std::array<PixelFormatInfo, 13> pixelFormatInfo = { {
 	/* RGB formats. */
-	{ DRM_FORMAT_RGB888,	V4L2_PIX_FMT_BGR24,	1, {{ { 24, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_BGR888,	V4L2_PIX_FMT_RGB24,	1, {{ { 24, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_BGRA8888,	V4L2_PIX_FMT_ARGB32,	1, {{ { 32, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_BGR24,	1, {{ { 24, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_RGB24,	1, {{ { 24, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_ARGB32,	1, {{ { 32, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
 	/* YUV packed formats. */
-	{ DRM_FORMAT_UYVY,	V4L2_PIX_FMT_UYVY,	1, {{ { 16, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_VYUY,	V4L2_PIX_FMT_VYUY,	1, {{ { 16, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_YUYV,	V4L2_PIX_FMT_YUYV,	1, {{ { 16, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_YVYU,	V4L2_PIX_FMT_YVYU,	1, {{ { 16, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_UYVY,	1, {{ { 16, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_VYUY,	1, {{ { 16, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_YUYV,	1, {{ { 16, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_YVYU,	1, {{ { 16, 1, 1 }, {  0, 0, 0 }, {  0, 0, 0 } }} },
 	/* YUY planar formats. */
-	{ DRM_FORMAT_NV12,	V4L2_PIX_FMT_NV12,	2, {{ {  8, 1, 1 }, { 16, 2, 2 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_NV21,	V4L2_PIX_FMT_NV21,	2, {{ {  8, 1, 1 }, { 16, 2, 2 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_NV16,	V4L2_PIX_FMT_NV16,	2, {{ {  8, 1, 1 }, { 16, 2, 1 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_NV61,	V4L2_PIX_FMT_NV61,	2, {{ {  8, 1, 1 }, { 16, 2, 1 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_NV24,	V4L2_PIX_FMT_NV24,	2, {{ {  8, 1, 1 }, { 16, 2, 1 }, {  0, 0, 0 } }} },
-	{ DRM_FORMAT_NV42,	V4L2_PIX_FMT_NV42,	2, {{ {  8, 1, 1 }, { 16, 1, 1 }, {  0, 0, 0 } }} },
-}};
+	{ V4L2_PIX_FMT_NV12,	2, {{ {  8, 1, 1 }, { 16, 2, 2 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_NV21,	2, {{ {  8, 1, 1 }, { 16, 2, 2 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_NV16,	2, {{ {  8, 1, 1 }, { 16, 2, 1 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_NV61,	2, {{ {  8, 1, 1 }, { 16, 2, 1 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_NV24,	2, {{ {  8, 1, 1 }, { 16, 2, 1 }, {  0, 0, 0 } }} },
+	{ V4L2_PIX_FMT_NV42,	2, {{ {  8, 1, 1 }, { 16, 1, 1 }, {  0, 0, 0 } }} },
+} };
 
 } /* namespace */
 
@@ -588,24 +587,10 @@ unsigned int V4L2CameraProxy::imageSize(uint32_t format, unsigned int width,
 
 PixelFormat V4L2CameraProxy::v4l2ToDrm(uint32_t format)
 {
-	auto info = std::find_if(pixelFormatInfo.begin(), pixelFormatInfo.end(),
-				 [format](const PixelFormatInfo &info) {
-					 return info.v4l2Format == format;
-				 });
-	if (info == pixelFormatInfo.end())
-		return format;
-
-	return info->format;
+	return PixelFormat(format);
 }
 
 uint32_t V4L2CameraProxy::drmToV4L2(PixelFormat format)
 {
-	auto info = std::find_if(pixelFormatInfo.begin(), pixelFormatInfo.end(),
-				 [format](const PixelFormatInfo &info) {
-					 return info.format == format;
-				 });
-	if (info == pixelFormatInfo.end())
-		return format;
-
-	return info->v4l2Format;
+	return format.v4l2();
 }
diff --git a/test/stream/stream_formats.cpp b/test/stream/stream_formats.cpp
index a391f5cd087d3872..a904d681c1691889 100644
--- a/test/stream/stream_formats.cpp
+++ b/test/stream/stream_formats.cpp
@@ -7,6 +7,8 @@
 
 #include <iostream>
 
+#include <linux/drm_fourcc.h>
+
 #include <libcamera/geometry.h>
 #include <libcamera/stream.h>
 
@@ -53,42 +55,49 @@ protected:
 
 	int run()
 	{
+		std::vector<PixelFormat> pixelFormats = {
+			PixelFormat(DRM_FORMAT_YUYV, {}),
+			PixelFormat(DRM_FORMAT_YVYU, {}),
+			PixelFormat(DRM_FORMAT_UYVY, {}),
+			PixelFormat(DRM_FORMAT_VYUY, {}),
+		};
+
 		/* Test discrete sizes */
 		StreamFormats discrete({
-			{ 1, { SizeRange(100, 100), SizeRange(200, 200) } },
-			{ 2, { SizeRange(300, 300), SizeRange(400, 400) } },
+			{ pixelFormats[0], { SizeRange(100, 100), SizeRange(200, 200) } },
+			{ pixelFormats[1], { SizeRange(300, 300), SizeRange(400, 400) } },
 		});
 
-		if (testSizes("discrete 1", discrete.sizes(1),
+		if (testSizes("discrete 1", discrete.sizes(pixelFormats[0]),
 			      { Size(100, 100), Size(200, 200) }))
 			return TestFail;
-		if (testSizes("discrete 2", discrete.sizes(2),
+		if (testSizes("discrete 2", discrete.sizes(pixelFormats[1]),
 			      { Size(300, 300), Size(400, 400) }))
 			return TestFail;
 
 		/* Test range sizes */
 		StreamFormats range({
-			{ 1, { SizeRange(640, 480, 640, 480) } },
-			{ 2, { SizeRange(640, 480, 800, 600, 8, 8) } },
-			{ 3, { SizeRange(640, 480, 800, 600, 16, 16) } },
-			{ 4, { SizeRange(128, 128, 4096, 4096, 128, 128) } },
+			{ pixelFormats[0], { SizeRange(640, 480, 640, 480) } },
+			{ pixelFormats[1], { SizeRange(640, 480, 800, 600, 8, 8) } },
+			{ pixelFormats[2], { SizeRange(640, 480, 800, 600, 16, 16) } },
+			{ pixelFormats[3], { SizeRange(128, 128, 4096, 4096, 128, 128) } },
 		});
 
-		if (testSizes("range 1", range.sizes(1), { Size(640, 480) }))
+		if (testSizes("range 1", range.sizes(pixelFormats[0]), { Size(640, 480) }))
 			return TestFail;
 
-		if (testSizes("range 2", range.sizes(2), {
+		if (testSizes("range 2", range.sizes(pixelFormats[1]), {
 			      Size(640, 480), Size(720, 480),
 			      Size(720, 576), Size(768, 480),
 			      Size(800, 600) }))
 			return TestFail;
 
-		if (testSizes("range 3", range.sizes(3), {
+		if (testSizes("range 3", range.sizes(pixelFormats[2]), {
 			      Size(640, 480), Size(720, 480),
 			      Size(720, 576), Size(768, 480) }))
 			return TestFail;
 
-		if (testSizes("range 4", range.sizes(4), {
+		if (testSizes("range 4", range.sizes(pixelFormats[3]), {
 			      Size(1024, 768), Size(1280, 1024),
 			      Size(2048, 1152), Size(2048, 1536),
 			      Size(2560, 2048), Size(3200, 2048), }))
