[PATCH/RFC 20/32] libcamera: camera_sensor: Create abstract base class

Stefan Klug stefan.klug at ideasonboard.com
Wed Mar 6 17:55:07 CET 2024


Hi Laurent,

On Fri, Mar 01, 2024 at 11:21:09PM +0200, Laurent Pinchart wrote:
> With a camera sensor factory in place, the next step is to create an
> abstract base class that all camera sensors implement, providing a
> uniform API to pipeline handler. Turn all public functions of the
> CameraSensor class into pure virtual functions, and move the
> implementation to the CameraSensorLegacy class.
> 
> Part of the code is likely worth keeping as common helpers in a base
> class. However, to follow the principle of not designing helpers with a
> single user, this commit moves the whole implementation. Common helpers
> will be introduced later, along with other CameraSensor subclasses.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

uff, that's a big patch (not content wise). 
We should better get that in sooner than later.

Reviewed-by: Stefan Klug <stefan.klug at ideasonboard.com>

Cheers,
Stefan

> ---
>  Documentation/Doxyfile.in                     |    1 +
>  include/libcamera/internal/camera_sensor.h    |  126 +-
>  src/libcamera/sensor/camera_sensor.cpp        |  981 +---------------
>  src/libcamera/sensor/camera_sensor_legacy.cpp | 1015 +++++++++++++++++
>  src/libcamera/sensor/meson.build              |    1 +
>  5 files changed, 1090 insertions(+), 1034 deletions(-)
>  create mode 100644 src/libcamera/sensor/camera_sensor_legacy.cpp
> 
> diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
> index a86ea6c1ec95..75326c1964e9 100644
> --- a/Documentation/Doxyfile.in
> +++ b/Documentation/Doxyfile.in
> @@ -42,6 +42,7 @@ EXCLUDE                = @TOP_SRCDIR@/include/libcamera/base/span.h \
>                           @TOP_SRCDIR@/src/libcamera/device_enumerator_udev.cpp \
>                           @TOP_SRCDIR@/src/libcamera/ipc_pipe_unixsocket.cpp \
>                           @TOP_SRCDIR@/src/libcamera/pipeline/ \
> +                         @TOP_SRCDIR@/src/libcamera/sensor/camera_sensor_legacy.cpp \
>                           @TOP_SRCDIR@/src/libcamera/tracepoints.cpp \
>                           @TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \
>                           @TOP_BUILDDIR@/src/libcamera/proxy/
> diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
> index 577af779cd6e..d37e66773195 100644
> --- a/include/libcamera/internal/camera_sensor.h
> +++ b/include/libcamera/internal/camera_sensor.h
> @@ -9,10 +9,10 @@
>  
>  #include <memory>
>  #include <string>
> +#include <variant>
>  #include <vector>
>  
>  #include <libcamera/base/class.h>
> -#include <libcamera/base/log.h>
>  
>  #include <libcamera/control_ids.h>
>  #include <libcamera/controls.h>
> @@ -20,8 +20,6 @@
>  #include <libcamera/orientation.h>
>  #include <libcamera/transform.h>
>  
> -#include <libcamera/ipa/core_ipa_interface.h>
> -
>  #include "libcamera/internal/bayer_format.h"
>  #include "libcamera/internal/formats.h"
>  #include "libcamera/internal/v4l2_subdevice.h"
> @@ -32,95 +30,50 @@ class CameraLens;
>  class MediaEntity;
>  class SensorConfiguration;
>  
> -struct CameraSensorProperties;
> -
>  enum class Orientation;
>  
> -class CameraSensor : protected Loggable
> +struct IPACameraSensorInfo;
> +
> +class CameraSensor
>  {
>  public:
> -	~CameraSensor();
> +	virtual ~CameraSensor();
>  
> -	int init();
> +	virtual const std::string &model() const = 0;
> +	virtual const std::string &id() const = 0;
>  
> -	const std::string &model() const { return model_; }
> -	const std::string &id() const { return id_; }
> +	virtual const MediaEntity *entity() const = 0;
> +	virtual V4L2Subdevice *device() = 0;
>  
> -	const MediaEntity *entity() const { return entity_; }
> -	V4L2Subdevice *device() { return subdev_.get(); }
> +	virtual CameraLens *focusLens() = 0;
>  
> -	CameraLens *focusLens() { return focusLens_.get(); }
> +	virtual const std::vector<unsigned int> &mbusCodes() const = 0;
> +	virtual std::vector<Size> sizes(unsigned int mbusCode) const = 0;
> +	virtual Size resolution() const = 0;
>  
> -	const std::vector<unsigned int> &mbusCodes() const { return mbusCodes_; }
> -	std::vector<Size> sizes(unsigned int mbusCode) const;
> -	Size resolution() const;
> +	virtual V4L2SubdeviceFormat
> +	getFormat(const std::vector<unsigned int> &mbusCodes,
> +		  const Size &size) const = 0;
> +	virtual int setFormat(V4L2SubdeviceFormat *format,
> +			      Transform transform = Transform::Identity) = 0;
> +	virtual int tryFormat(V4L2SubdeviceFormat *format) const = 0;
>  
> -	V4L2SubdeviceFormat getFormat(const std::vector<unsigned int> &mbusCodes,
> -				      const Size &size) const;
> -	int setFormat(V4L2SubdeviceFormat *format,
> -		      Transform transform = Transform::Identity);
> -	int tryFormat(V4L2SubdeviceFormat *format) const;
> +	virtual int applyConfiguration(const SensorConfiguration &config,
> +				       Transform transform = Transform::Identity,
> +				       V4L2SubdeviceFormat *sensorFormat = nullptr) = 0;
>  
> -	int applyConfiguration(const SensorConfiguration &config,
> -			       Transform transform = Transform::Identity,
> -			       V4L2SubdeviceFormat *sensorFormat = nullptr);
> +	virtual const ControlList &properties() const = 0;
> +	virtual int sensorInfo(IPACameraSensorInfo *info) const = 0;
> +	virtual Transform computeTransform(Orientation *orientation) const = 0;
> +	virtual BayerFormat::Order bayerOrder(Transform t) const = 0;
>  
> -	const ControlList &properties() const { return properties_; }
> -	int sensorInfo(IPACameraSensorInfo *info) const;
> -	Transform computeTransform(Orientation *orientation) const;
> -	BayerFormat::Order bayerOrder(Transform t) const;
> +	virtual const ControlInfoMap &controls() const = 0;
> +	virtual ControlList getControls(const std::vector<uint32_t> &ids) = 0;
> +	virtual int setControls(ControlList *ctrls) = 0;
>  
> -	const ControlInfoMap &controls() const;
> -	ControlList getControls(const std::vector<uint32_t> &ids);
> -	int setControls(ControlList *ctrls);
> -
> -	const std::vector<controls::draft::TestPatternModeEnum> &testPatternModes() const
> -	{
> -		return testPatternModes_;
> -	}
> -	int setTestPatternMode(controls::draft::TestPatternModeEnum mode);
> -
> -protected:
> -	explicit CameraSensor(const MediaEntity *entity);
> -	std::string logPrefix() const override;
> -
> -private:
> -	LIBCAMERA_DISABLE_COPY(CameraSensor)
> -
> -	int generateId();
> -	int validateSensorDriver();
> -	void initVimcDefaultProperties();
> -	void initStaticProperties();
> -	void initTestPatternModes();
> -	int initProperties();
> -	int discoverAncillaryDevices();
> -	int applyTestPatternMode(controls::draft::TestPatternModeEnum mode);
> -
> -	const MediaEntity *entity_;
> -	std::unique_ptr<V4L2Subdevice> subdev_;
> -	unsigned int pad_;
> -
> -	const CameraSensorProperties *staticProps_;
> -
> -	std::string model_;
> -	std::string id_;
> -
> -	V4L2Subdevice::Formats formats_;
> -	std::vector<unsigned int> mbusCodes_;
> -	std::vector<Size> sizes_;
> -	std::vector<controls::draft::TestPatternModeEnum> testPatternModes_;
> -	controls::draft::TestPatternModeEnum testPatternMode_;
> -
> -	Size pixelArraySize_;
> -	Rectangle activeArea_;
> -	const BayerFormat *bayerFormat_;
> -	bool supportFlips_;
> -	bool flipsAlterBayerOrder_;
> -	Orientation mountingOrientation_;
> -
> -	ControlList properties_;
> -
> -	std::unique_ptr<CameraLens> focusLens_;
> +	virtual const std::vector<controls::draft::TestPatternModeEnum> &
> +	testPatternModes() const = 0;
> +	virtual int setTestPatternMode(controls::draft::TestPatternModeEnum mode) = 0;
>  };
>  
>  class CameraSensorFactoryBase
> @@ -138,10 +91,8 @@ private:
>  
>  	static void registerFactory(CameraSensorFactoryBase *factory);
>  
> -	virtual bool match(const MediaEntity *entity) const = 0;
> -
> -	virtual std::unique_ptr<CameraSensor>
> -	createInstance(MediaEntity *entity) const = 0;
> +	virtual std::variant<std::unique_ptr<CameraSensor>, int>
> +	match(MediaEntity *entity) const = 0;
>  };
>  
>  template<typename _CameraSensor>
> @@ -154,16 +105,11 @@ public:
>  	}
>  
>  private:
> -	bool match(const MediaEntity *entity) const override
> +	std::variant<std::unique_ptr<CameraSensor>, int>
> +	match(MediaEntity *entity) const override
>  	{
>  		return _CameraSensor::match(entity);
>  	}
> -
> -	std::unique_ptr<CameraSensor>
> -	createInstance(MediaEntity *entity) const override
> -	{
> -		return _CameraSensor::create(entity);
> -	}
>  };
>  
>  #define REGISTER_CAMERA_SENSOR(sensor) \
> diff --git a/src/libcamera/sensor/camera_sensor.cpp b/src/libcamera/sensor/camera_sensor.cpp
> index a35683eb4b58..7abbe2c76596 100644
> --- a/src/libcamera/sensor/camera_sensor.cpp
> +++ b/src/libcamera/sensor/camera_sensor.cpp
> @@ -6,27 +6,13 @@
>   */
>  
>  #include "libcamera/internal/camera_sensor.h"
> -#include "libcamera/internal/media_device.h"
>  
> -#include <algorithm>
> -#include <float.h>
> -#include <iomanip>
> -#include <limits.h>
> -#include <map>
> -#include <math.h>
> -#include <string.h>
> +#include <memory>
> +#include <vector>
>  
> -#include <libcamera/camera.h>
> -#include <libcamera/orientation.h>
> -#include <libcamera/property_ids.h>
> +#include <libcamera/base/log.h>
>  
> -#include <libcamera/base/utils.h>
> -
> -#include "libcamera/internal/bayer_format.h"
> -#include "libcamera/internal/camera_lens.h"
> -#include "libcamera/internal/camera_sensor_properties.h"
> -#include "libcamera/internal/formats.h"
> -#include "libcamera/internal/sysfs.h"
> +#include "libcamera/internal/media_object.h"
>  
>  /**
>   * \file camera_sensor.h
> @@ -39,537 +25,16 @@ LOG_DEFINE_CATEGORY(CameraSensor)
>  
>  /**
>   * \class CameraSensor
> - * \brief A camera sensor based on V4L2 subdevices
> + * \brief A abstract camera sensor
>   *
>   * The CameraSensor class eases handling of sensors for pipeline handlers by
> - * hiding the details of the V4L2 subdevice kernel API and caching sensor
> - * information.
> - *
> - * The implementation is currently limited to sensors that expose a single V4L2
> - * subdevice with a single pad. It will be extended to support more complex
> - * devices as the needs arise.
> + * hiding the details of the kernel API and caching sensor information.
>   */
>  
> -/**
> - * \brief Construct a CameraSensor
> - * \param[in] entity The media entity backing the camera sensor
> - *
> - * Once constructed the instance must be initialized with init().
> - */
> -CameraSensor::CameraSensor(const MediaEntity *entity)
> -	: entity_(entity), pad_(UINT_MAX), staticProps_(nullptr),
> -	  bayerFormat_(nullptr), supportFlips_(false),
> -	  flipsAlterBayerOrder_(false), properties_(properties::properties)
> -{
> -}
> -
>  /**
>   * \brief Destroy a CameraSensor
>   */
> -CameraSensor::~CameraSensor()
> -{
> -}
> -
> -/**
> - * \brief Initialize the camera sensor instance
> - *
> - * This function performs the initialisation steps of the CameraSensor that may
> - * fail. It shall be called once and only once after constructing the instance.
> - *
> - * \return 0 on success or a negative error code otherwise
> - */
> -int CameraSensor::init()
> -{
> -	for (const MediaPad *pad : entity_->pads()) {
> -		if (pad->flags() & MEDIA_PAD_FL_SOURCE) {
> -			pad_ = pad->index();
> -			break;
> -		}
> -	}
> -
> -	if (pad_ == UINT_MAX) {
> -		LOG(CameraSensor, Error)
> -			<< "Sensors with more than one pad are not supported";
> -		return -EINVAL;
> -	}
> -
> -	switch (entity_->function()) {
> -	case MEDIA_ENT_F_CAM_SENSOR:
> -	case MEDIA_ENT_F_PROC_VIDEO_ISP:
> -		break;
> -
> -	default:
> -		LOG(CameraSensor, Error)
> -			<< "Invalid sensor function "
> -			<< utils::hex(entity_->function());
> -		return -EINVAL;
> -	}
> -
> -	/* Create and open the subdev. */
> -	subdev_ = std::make_unique<V4L2Subdevice>(entity_);
> -	int ret = subdev_->open();
> -	if (ret < 0)
> -		return ret;
> -
> -	/*
> -	 * Clear any flips to be sure we get the "native" Bayer order. This is
> -	 * harmless for sensors where the flips don't affect the Bayer order.
> -	 */
> -	ControlList ctrls(subdev_->controls());
> -	if (subdev_->controls().find(V4L2_CID_HFLIP) != subdev_->controls().end())
> -		ctrls.set(V4L2_CID_HFLIP, 0);
> -	if (subdev_->controls().find(V4L2_CID_VFLIP) != subdev_->controls().end())
> -		ctrls.set(V4L2_CID_VFLIP, 0);
> -	subdev_->setControls(&ctrls);
> -
> -	/* Enumerate, sort and cache media bus codes and sizes. */
> -	formats_ = subdev_->formats(pad_);
> -	if (formats_.empty()) {
> -		LOG(CameraSensor, Error) << "No image format found";
> -		return -EINVAL;
> -	}
> -
> -	mbusCodes_ = utils::map_keys(formats_);
> -	std::sort(mbusCodes_.begin(), mbusCodes_.end());
> -
> -	for (const auto &format : formats_) {
> -		const std::vector<SizeRange> &ranges = format.second;
> -		std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes_),
> -			       [](const SizeRange &range) { return range.max; });
> -	}
> -
> -	std::sort(sizes_.begin(), sizes_.end());
> -
> -	/* Remove duplicates. */
> -	auto last = std::unique(sizes_.begin(), sizes_.end());
> -	sizes_.erase(last, sizes_.end());
> -
> -	/*
> -	 * VIMC is a bit special, as it does not yet support all the mandatory
> -	 * requirements regular sensors have to respect.
> -	 *
> -	 * Do not validate the driver if it's VIMC and initialize the sensor
> -	 * properties with static information.
> -	 *
> -	 * \todo Remove the special case once the VIMC driver has been
> -	 * updated in all test platforms.
> -	 */
> -	if (entity_->device()->driver() == "vimc") {
> -		initVimcDefaultProperties();
> -
> -		ret = initProperties();
> -		if (ret)
> -			return ret;
> -
> -		return discoverAncillaryDevices();
> -	}
> -
> -	/* Get the color filter array pattern (only for RAW sensors). */
> -	for (unsigned int mbusCode : mbusCodes_) {
> -		const BayerFormat &bayerFormat = BayerFormat::fromMbusCode(mbusCode);
> -		if (bayerFormat.isValid()) {
> -			bayerFormat_ = &bayerFormat;
> -			break;
> -		}
> -	}
> -
> -	ret = validateSensorDriver();
> -	if (ret)
> -		return ret;
> -
> -	ret = initProperties();
> -	if (ret)
> -		return ret;
> -
> -	ret = discoverAncillaryDevices();
> -	if (ret)
> -		return ret;
> -
> -	/*
> -	 * Set HBLANK to the minimum to start with a well-defined line length,
> -	 * allowing IPA modules that do not modify HBLANK to use the sensor
> -	 * minimum line length in their calculations.
> -	 */
> -	const struct v4l2_query_ext_ctrl *hblankInfo = subdev_->controlInfo(V4L2_CID_HBLANK);
> -	if (hblankInfo && !(hblankInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {
> -		ControlList ctrl(subdev_->controls());
> -
> -		ctrl.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblankInfo->minimum));
> -		ret = subdev_->setControls(&ctrl);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	return applyTestPatternMode(controls::draft::TestPatternModeEnum::TestPatternModeOff);
> -}
> -
> -int CameraSensor::generateId()
> -{
> -	const std::string devPath = subdev_->devicePath();
> -
> -	/* Try to get ID from firmware description. */
> -	id_ = sysfs::firmwareNodePath(devPath);
> -	if (!id_.empty())
> -		return 0;
> -
> -	/*
> -	 * Virtual sensors not described in firmware
> -	 *
> -	 * Verify it's a platform device and construct ID from the device path
> -	 * and model of sensor.
> -	 */
> -	if (devPath.find("/sys/devices/platform/", 0) == 0) {
> -		id_ = devPath.substr(strlen("/sys/devices/")) + " " + model();
> -		return 0;
> -	}
> -
> -	LOG(CameraSensor, Error) << "Can't generate sensor ID";
> -	return -EINVAL;
> -}
> -
> -int CameraSensor::validateSensorDriver()
> -{
> -	int err = 0;
> -
> -	/*
> -	 * Optional controls are used to register optional sensor properties. If
> -	 * not present, some values will be defaulted.
> -	 */
> -	static constexpr uint32_t optionalControls[] = {
> -		V4L2_CID_CAMERA_SENSOR_ROTATION,
> -	};
> -
> -	const ControlIdMap &controls = subdev_->controls().idmap();
> -	for (uint32_t ctrl : optionalControls) {
> -		if (!controls.count(ctrl))
> -			LOG(CameraSensor, Debug)
> -				<< "Optional V4L2 control " << utils::hex(ctrl)
> -				<< " not supported";
> -	}
> -
> -	/*
> -	 * Recommended controls are similar to optional controls, but will
> -	 * become mandatory in the near future. Be loud if they're missing.
> -	 */
> -	static constexpr uint32_t recommendedControls[] = {
> -		V4L2_CID_CAMERA_ORIENTATION,
> -	};
> -
> -	for (uint32_t ctrl : recommendedControls) {
> -		if (!controls.count(ctrl)) {
> -			LOG(CameraSensor, Warning)
> -				<< "Recommended V4L2 control " << utils::hex(ctrl)
> -				<< " not supported";
> -			err = -EINVAL;
> -		}
> -	}
> -
> -	/*
> -	 * Verify if sensor supports horizontal/vertical flips
> -	 *
> -	 * \todo Handle horizontal and vertical flips independently.
> -	 */
> -	const struct v4l2_query_ext_ctrl *hflipInfo = subdev_->controlInfo(V4L2_CID_HFLIP);
> -	const struct v4l2_query_ext_ctrl *vflipInfo = subdev_->controlInfo(V4L2_CID_VFLIP);
> -	if (hflipInfo && !(hflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY) &&
> -	    vflipInfo && !(vflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {
> -		supportFlips_ = true;
> -
> -		if (hflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT ||
> -		    vflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT)
> -			flipsAlterBayerOrder_ = true;
> -	}
> -
> -	if (!supportFlips_)
> -		LOG(CameraSensor, Debug)
> -			<< "Camera sensor does not support horizontal/vertical flip";
> -
> -	/*
> -	 * Make sure the required selection targets are supported.
> -	 *
> -	 * Failures in reading any of the targets are not deemed to be fatal,
> -	 * but some properties and features, like constructing a
> -	 * IPACameraSensorInfo for the IPA module, won't be supported.
> -	 *
> -	 * \todo Make support for selection targets mandatory as soon as all
> -	 * test platforms have been updated.
> -	 */
> -	Rectangle rect;
> -	int ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP_BOUNDS, &rect);
> -	if (ret) {
> -		/*
> -		 * Default the pixel array size to the largest size supported
> -		 * by the sensor. The sizes_ vector is sorted in ascending
> -		 * order, the largest size is thus the last element.
> -		 */
> -		pixelArraySize_ = sizes_.back();
> -
> -		LOG(CameraSensor, Warning)
> -			<< "The PixelArraySize property has been defaulted to "
> -			<< pixelArraySize_;
> -		err = -EINVAL;
> -	} else {
> -		pixelArraySize_ = rect.size();
> -	}
> -
> -	ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP_DEFAULT, &activeArea_);
> -	if (ret) {
> -		activeArea_ = Rectangle(pixelArraySize_);
> -		LOG(CameraSensor, Warning)
> -			<< "The PixelArrayActiveAreas property has been defaulted to "
> -			<< activeArea_;
> -		err = -EINVAL;
> -	}
> -
> -	ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP, &rect);
> -	if (ret) {
> -		LOG(CameraSensor, Warning)
> -			<< "Failed to retrieve the sensor crop rectangle";
> -		err = -EINVAL;
> -	}
> -
> -	if (err) {
> -		LOG(CameraSensor, Warning)
> -			<< "The sensor kernel driver needs to be fixed";
> -		LOG(CameraSensor, Warning)
> -			<< "See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information";
> -	}
> -
> -	if (!bayerFormat_)
> -		return 0;
> -
> -	/*
> -	 * For raw sensors, make sure the sensor driver supports the controls
> -	 * required by the CameraSensor class.
> -	 */
> -	static constexpr uint32_t mandatoryControls[] = {
> -		V4L2_CID_ANALOGUE_GAIN,
> -		V4L2_CID_EXPOSURE,
> -		V4L2_CID_HBLANK,
> -		V4L2_CID_PIXEL_RATE,
> -		V4L2_CID_VBLANK,
> -	};
> -
> -	err = 0;
> -	for (uint32_t ctrl : mandatoryControls) {
> -		if (!controls.count(ctrl)) {
> -			LOG(CameraSensor, Error)
> -				<< "Mandatory V4L2 control " << utils::hex(ctrl)
> -				<< " not available";
> -			err = -EINVAL;
> -		}
> -	}
> -
> -	if (err) {
> -		LOG(CameraSensor, Error)
> -			<< "The sensor kernel driver needs to be fixed";
> -		LOG(CameraSensor, Error)
> -			<< "See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information";
> -		return err;
> -	}
> -
> -	return 0;
> -}
> -
> -/*
> - * \brief Initialize properties that cannot be intialized by the
> - * regular initProperties() function for VIMC
> - */
> -void CameraSensor::initVimcDefaultProperties()
> -{
> -	/* Use the largest supported size. */
> -	pixelArraySize_ = sizes_.back();
> -	activeArea_ = Rectangle(pixelArraySize_);
> -}
> -
> -void CameraSensor::initStaticProperties()
> -{
> -	staticProps_ = CameraSensorProperties::get(model_);
> -	if (!staticProps_)
> -		return;
> -
> -	/* Register the properties retrieved from the sensor database. */
> -	properties_.set(properties::UnitCellSize, staticProps_->unitCellSize);
> -
> -	initTestPatternModes();
> -}
> -
> -void CameraSensor::initTestPatternModes()
> -{
> -	const auto &v4l2TestPattern = controls().find(V4L2_CID_TEST_PATTERN);
> -	if (v4l2TestPattern == controls().end()) {
> -		LOG(CameraSensor, Debug) << "V4L2_CID_TEST_PATTERN is not supported";
> -		return;
> -	}
> -
> -	const auto &testPatternModes = staticProps_->testPatternModes;
> -	if (testPatternModes.empty()) {
> -		/*
> -		 * The camera sensor supports test patterns but we don't know
> -		 * how to map them so this should be fixed.
> -		 */
> -		LOG(CameraSensor, Debug) << "No static test pattern map for \'"
> -					 << model() << "\'";
> -		return;
> -	}
> -
> -	/*
> -	 * Create a map that associates the V4L2 control index to the test
> -	 * pattern mode by reversing the testPatternModes map provided by the
> -	 * camera sensor properties. This makes it easier to verify if the
> -	 * control index is supported in the below for loop that creates the
> -	 * list of supported test patterns.
> -	 */
> -	std::map<int32_t, controls::draft::TestPatternModeEnum> indexToTestPatternMode;
> -	for (const auto &it : testPatternModes)
> -		indexToTestPatternMode[it.second] = it.first;
> -
> -	for (const ControlValue &value : v4l2TestPattern->second.values()) {
> -		const int32_t index = value.get<int32_t>();
> -
> -		const auto it = indexToTestPatternMode.find(index);
> -		if (it == indexToTestPatternMode.end()) {
> -			LOG(CameraSensor, Debug)
> -				<< "Test pattern mode " << index << " ignored";
> -			continue;
> -		}
> -
> -		testPatternModes_.push_back(it->second);
> -	}
> -}
> -
> -int CameraSensor::initProperties()
> -{
> -	model_ = subdev_->model();
> -	properties_.set(properties::Model, utils::toAscii(model_));
> -
> -	/* Generate a unique ID for the sensor. */
> -	int ret = generateId();
> -	if (ret)
> -		return ret;
> -
> -	/* Initialize the static properties from the sensor database. */
> -	initStaticProperties();
> -
> -	/* Retrieve and register properties from the kernel interface. */
> -	const ControlInfoMap &controls = subdev_->controls();
> -
> -	const auto &orientation = controls.find(V4L2_CID_CAMERA_ORIENTATION);
> -	if (orientation != controls.end()) {
> -		int32_t v4l2Orientation = orientation->second.def().get<int32_t>();
> -		int32_t propertyValue;
> -
> -		switch (v4l2Orientation) {
> -		default:
> -			LOG(CameraSensor, Warning)
> -				<< "Unsupported camera location "
> -				<< v4l2Orientation << ", setting to External";
> -			[[fallthrough]];
> -		case V4L2_CAMERA_ORIENTATION_EXTERNAL:
> -			propertyValue = properties::CameraLocationExternal;
> -			break;
> -		case V4L2_CAMERA_ORIENTATION_FRONT:
> -			propertyValue = properties::CameraLocationFront;
> -			break;
> -		case V4L2_CAMERA_ORIENTATION_BACK:
> -			propertyValue = properties::CameraLocationBack;
> -			break;
> -		}
> -		properties_.set(properties::Location, propertyValue);
> -	} else {
> -		LOG(CameraSensor, Warning) << "Failed to retrieve the camera location";
> -	}
> -
> -	const auto &rotationControl = controls.find(V4L2_CID_CAMERA_SENSOR_ROTATION);
> -	if (rotationControl != controls.end()) {
> -		int32_t propertyValue = rotationControl->second.def().get<int32_t>();
> -
> -		/*
> -		 * Cache the Transform associated with the camera mounting
> -		 * rotation for later use in computeTransform().
> -		 */
> -		bool success;
> -		mountingOrientation_ = orientationFromRotation(propertyValue, &success);
> -		if (!success) {
> -			LOG(CameraSensor, Warning)
> -				<< "Invalid rotation of " << propertyValue
> -				<< " degrees - ignoring";
> -			mountingOrientation_ = Orientation::Rotate0;
> -		}
> -
> -		properties_.set(properties::Rotation, propertyValue);
> -	} else {
> -		LOG(CameraSensor, Warning)
> -			<< "Rotation control not available, default to 0 degrees";
> -		properties_.set(properties::Rotation, 0);
> -		mountingOrientation_ = Orientation::Rotate0;
> -	}
> -
> -	properties_.set(properties::PixelArraySize, pixelArraySize_);
> -	properties_.set(properties::PixelArrayActiveAreas, { activeArea_ });
> -
> -	/* Color filter array pattern, register only for RAW sensors. */
> -	if (bayerFormat_) {
> -		int32_t cfa;
> -		switch (bayerFormat_->order) {
> -		case BayerFormat::BGGR:
> -			cfa = properties::draft::BGGR;
> -			break;
> -		case BayerFormat::GBRG:
> -			cfa = properties::draft::GBRG;
> -			break;
> -		case BayerFormat::GRBG:
> -			cfa = properties::draft::GRBG;
> -			break;
> -		case BayerFormat::RGGB:
> -			cfa = properties::draft::RGGB;
> -			break;
> -		case BayerFormat::MONO:
> -			cfa = properties::draft::MONO;
> -			break;
> -		}
> -
> -		properties_.set(properties::draft::ColorFilterArrangement, cfa);
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * \brief Check for and initialise any ancillary devices
> - *
> - * Sensors sometimes have ancillary devices such as a Lens or Flash that could
> - * be linked to their MediaEntity by the kernel. Search for and handle any
> - * such device.
> - *
> - * \todo Handle MEDIA_ENT_F_FLASH too.
> - */
> -int CameraSensor::discoverAncillaryDevices()
> -{
> -	int ret;
> -
> -	for (MediaEntity *ancillary : entity_->ancillaryEntities()) {
> -		switch (ancillary->function()) {
> -		case MEDIA_ENT_F_LENS:
> -			focusLens_ = std::make_unique<CameraLens>(ancillary);
> -			ret = focusLens_->init();
> -			if (ret) {
> -				LOG(CameraSensor, Error)
> -					<< "Lens initialisation failed, lens disabled";
> -				focusLens_.reset();
> -			}
> -			break;
> -
> -		default:
> -			LOG(CameraSensor, Warning)
> -				<< "Unsupported ancillary entity function "
> -				<< ancillary->function();
> -			break;
> -		}
> -	}
> -
> -	return 0;
> -}
> +CameraSensor::~CameraSensor() = default;
>  
>  /**
>   * \fn CameraSensor::model()
> @@ -624,29 +89,15 @@ int CameraSensor::discoverAncillaryDevices()
>   */
>  
>  /**
> + * \fn CameraSensor::sizes()
>   * \brief Retrieve the supported frame sizes for a media bus code
>   * \param[in] mbusCode The media bus code for which sizes are requested
>   *
>   * \return The supported frame sizes for \a mbusCode sorted in increasing order
>   */
> -std::vector<Size> CameraSensor::sizes(unsigned int mbusCode) const
> -{
> -	std::vector<Size> sizes;
> -
> -	const auto &format = formats_.find(mbusCode);
> -	if (format == formats_.end())
> -		return sizes;
> -
> -	const std::vector<SizeRange> &ranges = format->second;
> -	std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes),
> -		       [](const SizeRange &range) { return range.max; });
> -
> -	std::sort(sizes.begin(), sizes.end());
> -
> -	return sizes;
> -}
>  
>  /**
> + * \fn CameraSensor::resolution()
>   * \brief Retrieve the camera sensor resolution
>   *
>   * The camera sensor resolution is the active pixel area size, clamped to the
> @@ -659,12 +110,9 @@ std::vector<Size> CameraSensor::sizes(unsigned int mbusCode) const
>   *
>   * \return The camera sensor resolution in pixels
>   */
> -Size CameraSensor::resolution() const
> -{
> -	return std::min(sizes_.back(), activeArea_.size());
> -}
>  
>  /**
> + * \fn CameraSensor::getFormat()
>   * \brief Retrieve the best sensor format for a desired output
>   * \param[in] mbusCodes The list of acceptable media bus codes
>   * \param[in] size The desired size
> @@ -700,59 +148,9 @@ Size CameraSensor::resolution() const
>   * \return The best sensor output format matching the desired media bus codes
>   * and size on success, or an empty format otherwise.
>   */
> -V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbusCodes,
> -					    const Size &size) const
> -{
> -	unsigned int desiredArea = size.width * size.height;
> -	unsigned int bestArea = UINT_MAX;
> -	float desiredRatio = static_cast<float>(size.width) / size.height;
> -	float bestRatio = FLT_MAX;
> -	const Size *bestSize = nullptr;
> -	uint32_t bestCode = 0;
> -
> -	for (unsigned int code : mbusCodes) {
> -		const auto formats = formats_.find(code);
> -		if (formats == formats_.end())
> -			continue;
> -
> -		for (const SizeRange &range : formats->second) {
> -			const Size &sz = range.max;
> -
> -			if (sz.width < size.width || sz.height < size.height)
> -				continue;
> -
> -			float ratio = static_cast<float>(sz.width) / sz.height;
> -			float ratioDiff = fabsf(ratio - desiredRatio);
> -			unsigned int area = sz.width * sz.height;
> -			unsigned int areaDiff = area - desiredArea;
> -
> -			if (ratioDiff > bestRatio)
> -				continue;
> -
> -			if (ratioDiff < bestRatio || areaDiff < bestArea) {
> -				bestRatio = ratioDiff;
> -				bestArea = areaDiff;
> -				bestSize = &sz;
> -				bestCode = code;
> -			}
> -		}
> -	}
> -
> -	if (!bestSize) {
> -		LOG(CameraSensor, Debug) << "No supported format or size found";
> -		return {};
> -	}
> -
> -	V4L2SubdeviceFormat format{
> -		.code = bestCode,
> -		.size = *bestSize,
> -		.colorSpace = ColorSpace::Raw,
> -	};
> -
> -	return format;
> -}
>  
>  /**
> + * \fn CameraSensor::setFormat()
>   * \brief Set the sensor output format
>   * \param[in] format The desired sensor output format
>   * \param[in] transform The transform to be applied on the sensor.
> @@ -767,32 +165,9 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
>   *
>   * \return 0 on success or a negative error code otherwise
>   */
> -int CameraSensor::setFormat(V4L2SubdeviceFormat *format, Transform transform)
> -{
> -	/* Configure flips if the sensor supports that. */
> -	if (supportFlips_) {
> -		ControlList flipCtrls(subdev_->controls());
> -
> -		flipCtrls.set(V4L2_CID_HFLIP,
> -			      static_cast<int32_t>(!!(transform & Transform::HFlip)));
> -		flipCtrls.set(V4L2_CID_VFLIP,
> -			      static_cast<int32_t>(!!(transform & Transform::VFlip)));
> -
> -		int ret = subdev_->setControls(&flipCtrls);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	/* Apply format on the subdev. */
> -	int ret = subdev_->setFormat(pad_, format);
> -	if (ret)
> -		return ret;
> -
> -	subdev_->updateControlInfo();
> -	return 0;
> -}
>  
>  /**
> + * \fn CameraSensor::tryFormat()
>   * \brief Try the sensor output format
>   * \param[in] format The desired sensor output format
>   *
> @@ -803,13 +178,9 @@ int CameraSensor::setFormat(V4L2SubdeviceFormat *format, Transform transform)
>   *
>   * \return 0 on success or a negative error code otherwise
>   */
> -int CameraSensor::tryFormat(V4L2SubdeviceFormat *format) const
> -{
> -	return subdev_->setFormat(pad_, format,
> -				  V4L2Subdevice::Whence::TryFormat);
> -}
>  
>  /**
> + * \fn CameraSensor::applyConfiguration()
>   * \brief Apply a sensor configuration to the camera sensor
>   * \param[in] config The sensor configuration
>   * \param[in] transform The transform to be applied on the sensor.
> @@ -824,74 +195,6 @@ int CameraSensor::tryFormat(V4L2SubdeviceFormat *format) const
>   * \return 0 if \a config is applied correctly to the camera sensor, a negative
>   * error code otherwise
>   */
> -int CameraSensor::applyConfiguration(const SensorConfiguration &config,
> -				     Transform transform,
> -				     V4L2SubdeviceFormat *sensorFormat)
> -{
> -	if (!config.isValid()) {
> -		LOG(CameraSensor, Error) << "Invalid sensor configuration";
> -		return -EINVAL;
> -	}
> -
> -	std::vector<unsigned int> filteredCodes;
> -	std::copy_if(mbusCodes_.begin(), mbusCodes_.end(),
> -		     std::back_inserter(filteredCodes),
> -		     [&config](unsigned int mbusCode) {
> -			     BayerFormat bayer = BayerFormat::fromMbusCode(mbusCode);
> -			     if (bayer.bitDepth == config.bitDepth)
> -				     return true;
> -			     return false;
> -		     });
> -	if (filteredCodes.empty()) {
> -		LOG(CameraSensor, Error)
> -			<< "Cannot find any format with bit depth "
> -			<< config.bitDepth;
> -		return -EINVAL;
> -	}
> -
> -	/*
> -	 * Compute the sensor's data frame size by applying the cropping
> -	 * rectangle, subsampling and output crop to the sensor's pixel array
> -	 * size.
> -	 *
> -	 * \todo The actual size computation is for now ignored and only the
> -	 * output size is considered. This implies that resolutions obtained
> -	 * with two different cropping/subsampling will look identical and
> -	 * only the first found one will be considered.
> -	 */
> -	V4L2SubdeviceFormat subdevFormat = {};
> -	for (unsigned int code : filteredCodes) {
> -		for (const Size &size : sizes(code)) {
> -			if (size.width != config.outputSize.width ||
> -			    size.height != config.outputSize.height)
> -				continue;
> -
> -			subdevFormat.code = code;
> -			subdevFormat.size = size;
> -			break;
> -		}
> -	}
> -	if (!subdevFormat.code) {
> -		LOG(CameraSensor, Error) << "Invalid output size in sensor configuration";
> -		return -EINVAL;
> -	}
> -
> -	int ret = setFormat(&subdevFormat, transform);
> -	if (ret)
> -		return ret;
> -
> -	/*
> -	 * Return to the caller the format actually applied to the sensor.
> -	 * This is relevant if transform has changed the bayer pattern order.
> -	 */
> -	if (sensorFormat)
> -		*sensorFormat = subdevFormat;
> -
> -	/* \todo Handle AnalogCrop. Most sensors do not support set_selection */
> -	/* \todo Handle scaling in the digital domain. */
> -
> -	return 0;
> -}
>  
>  /**
>   * \fn CameraSensor::properties()
> @@ -900,6 +203,7 @@ int CameraSensor::applyConfiguration(const SensorConfiguration &config,
>   */
>  
>  /**
> + * \fn CameraSensor::sensorInfo()
>   * \brief Assemble and return the camera sensor info
>   * \param[out] info The camera sensor info
>   *
> @@ -913,82 +217,9 @@ int CameraSensor::applyConfiguration(const SensorConfiguration &config,
>   *
>   * \return 0 on success, a negative error code otherwise
>   */
> -int CameraSensor::sensorInfo(IPACameraSensorInfo *info) const
> -{
> -	if (!bayerFormat_)
> -		return -EINVAL;
> -
> -	info->model = model();
> -
> -	/*
> -	 * The active area size is a static property, while the crop
> -	 * rectangle needs to be re-read as it depends on the sensor
> -	 * configuration.
> -	 */
> -	info->activeAreaSize = { activeArea_.width, activeArea_.height };
> -
> -	/*
> -	 * \todo Support for retreiving the crop rectangle is scheduled to
> -	 * become mandatory. For the time being use the default value if it has
> -	 * been initialized at sensor driver validation time.
> -	 */
> -	int ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP, &info->analogCrop);
> -	if (ret) {
> -		info->analogCrop = activeArea_;
> -		LOG(CameraSensor, Warning)
> -			<< "The analogue crop rectangle has been defaulted to the active area size";
> -	}
> -
> -	/*
> -	 * IPACameraSensorInfo::analogCrop::x and IPACameraSensorInfo::analogCrop::y
> -	 * are defined relatively to the active pixel area, while V4L2's
> -	 * TGT_CROP target is defined in respect to the full pixel array.
> -	 *
> -	 * Compensate it by subtracting the active area offset.
> -	 */
> -	info->analogCrop.x -= activeArea_.x;
> -	info->analogCrop.y -= activeArea_.y;
> -
> -	/* The bit depth and image size depend on the currently applied format. */
> -	V4L2SubdeviceFormat format{};
> -	ret = subdev_->getFormat(pad_, &format);
> -	if (ret)
> -		return ret;
> -
> -	info->bitsPerPixel = MediaBusFormatInfo::info(format.code).bitsPerPixel;
> -	info->outputSize = format.size;
> -
> -	std::optional<int32_t> cfa = properties_.get(properties::draft::ColorFilterArrangement);
> -	info->cfaPattern = cfa ? *cfa : properties::draft::RGB;
> -
> -	/*
> -	 * Retrieve the pixel rate, line length and minimum/maximum frame
> -	 * duration through V4L2 controls. Support for the V4L2_CID_PIXEL_RATE,
> -	 * V4L2_CID_HBLANK and V4L2_CID_VBLANK controls is mandatory.
> -	 */
> -	ControlList ctrls = subdev_->getControls({ V4L2_CID_PIXEL_RATE,
> -						   V4L2_CID_HBLANK,
> -						   V4L2_CID_VBLANK });
> -	if (ctrls.empty()) {
> -		LOG(CameraSensor, Error)
> -			<< "Failed to retrieve camera info controls";
> -		return -EINVAL;
> -	}
> -
> -	info->pixelRate = ctrls.get(V4L2_CID_PIXEL_RATE).get<int64_t>();
> -
> -	const ControlInfo hblank = ctrls.infoMap()->at(V4L2_CID_HBLANK);
> -	info->minLineLength = info->outputSize.width + hblank.min().get<int32_t>();
> -	info->maxLineLength = info->outputSize.width + hblank.max().get<int32_t>();
> -
> -	const ControlInfo vblank = ctrls.infoMap()->at(V4L2_CID_VBLANK);
> -	info->minFrameLength = info->outputSize.height + vblank.min().get<int32_t>();
> -	info->maxFrameLength = info->outputSize.height + vblank.max().get<int32_t>();
> -
> -	return 0;
> -}
>  
>  /**
> + * \fn CameraSensor::computeTransform()
>   * \brief Compute the Transform that gives the requested \a orientation
>   * \param[inout] orientation The desired image orientation
>   *
> @@ -1014,40 +245,9 @@ int CameraSensor::sensorInfo(IPACameraSensorInfo *info) const
>   * \return A Transform instance that applied to the CameraSensor produces images
>   * with \a orientation
>   */
> -Transform CameraSensor::computeTransform(Orientation *orientation) const
> -{
> -	/*
> -	 * If we cannot do any flips we cannot change the native camera mounting
> -	 * orientation.
> -	 */
> -	if (!supportFlips_) {
> -		*orientation = mountingOrientation_;
> -		return Transform::Identity;
> -	}
> -
> -	/*
> -	 * Now compute the required transform to obtain 'orientation' starting
> -	 * from the mounting rotation.
> -	 *
> -	 * As a note:
> -	 * 	orientation / mountingOrientation_ = transform
> -	 * 	mountingOrientation_ * transform = orientation
> -	 */
> -	Transform transform = *orientation / mountingOrientation_;
> -
> -	/*
> -	 * If transform contains any Transpose we cannot do it, so adjust
> -	 * 'orientation' to report the image native orientation and return Identity.
> -	 */
> -	if (!!(transform & Transform::Transpose)) {
> -		*orientation = mountingOrientation_;
> -		return Transform::Identity;
> -	}
> -
> -	return transform;
> -}
>  
>  /**
> + * \fn CameraSensor::bayerOrder()
>   * \brief Compute the Bayer order that results from the given Transform
>   * \param[in] t The Transform to apply to the sensor
>   *
> @@ -1059,23 +259,9 @@ Transform CameraSensor::computeTransform(Orientation *orientation) const
>   *
>   * \return The Bayer order produced by the sensor when the Transform is applied
>   */
> -BayerFormat::Order CameraSensor::bayerOrder(Transform t) const
> -{
> -	/* Return a defined by meaningless value for non-Bayer sensors. */
> -	if (!bayerFormat_)
> -		return BayerFormat::Order::BGGR;
> -
> -	if (!flipsAlterBayerOrder_)
> -		return bayerFormat_->order;
> -
> -	/*
> -	 * Apply the transform to the native (i.e. untransformed) Bayer order,
> -	 * using the rest of the Bayer format supplied by the caller.
> -	 */
> -	return bayerFormat_->transform(t).order;
> -}
>  
>  /**
> + * \fn CameraSensor::controls()
>   * \brief Retrieve the supported V4L2 controls and their information
>   *
>   * Control information is updated automatically to reflect the current sensor
> @@ -1084,12 +270,9 @@ BayerFormat::Order CameraSensor::bayerOrder(Transform t) const
>   *
>   * \return A map of the V4L2 controls supported by the sensor
>   */
> -const ControlInfoMap &CameraSensor::controls() const
> -{
> -	return subdev_->controls();
> -}
>  
>  /**
> + * \fn CameraSensor::getControls()
>   * \brief Read V4L2 controls from the sensor
>   * \param[in] ids The list of controls to read, specified by their ID
>   *
> @@ -1107,12 +290,9 @@ const ControlInfoMap &CameraSensor::controls() const
>   * \return The control values in a ControlList on success, or an empty list on
>   * error
>   */
> -ControlList CameraSensor::getControls(const std::vector<uint32_t> &ids)
> -{
> -	return subdev_->getControls(ids);
> -}
>  
>  /**
> + * \fn CameraSensor::setControls()
>   * \brief Write V4L2 controls to the sensor
>   * \param[in] ctrls The list of controls to write
>   *
> @@ -1137,10 +317,6 @@ ControlList CameraSensor::getControls(const std::vector<uint32_t> &ids)
>   * \retval -EINVAL One of the control is not supported or not accessible
>   * \retval i The index of the control that failed
>   */
> -int CameraSensor::setControls(ControlList *ctrls)
> -{
> -	return subdev_->setControls(ctrls);
> -}
>  
>  /**
>   * \fn CameraSensor::testPatternModes()
> @@ -1151,6 +327,7 @@ int CameraSensor::setControls(ControlList *ctrls)
>   */
>  
>  /**
> + * \fn CameraSensor::setTestPatternMode()
>   * \brief Set the test pattern mode for the camera sensor
>   * \param[in] mode The test pattern mode
>   *
> @@ -1158,84 +335,6 @@ int CameraSensor::setControls(ControlList *ctrls)
>   * pattern mode. Otherwise, this function is a no-op. Setting the same test
>   * pattern mode for every frame thus incurs no performance penalty.
>   */
> -int CameraSensor::setTestPatternMode(controls::draft::TestPatternModeEnum mode)
> -{
> -	if (testPatternMode_ == mode)
> -		return 0;
> -
> -	if (testPatternModes_.empty()) {
> -		LOG(CameraSensor, Error)
> -			<< "Camera sensor does not support test pattern modes.";
> -		return -EINVAL;
> -	}
> -
> -	return applyTestPatternMode(mode);
> -}
> -
> -int CameraSensor::applyTestPatternMode(controls::draft::TestPatternModeEnum mode)
> -{
> -	if (testPatternModes_.empty())
> -		return 0;
> -
> -	auto it = std::find(testPatternModes_.begin(), testPatternModes_.end(),
> -			    mode);
> -	if (it == testPatternModes_.end()) {
> -		LOG(CameraSensor, Error) << "Unsupported test pattern mode "
> -					 << mode;
> -		return -EINVAL;
> -	}
> -
> -	LOG(CameraSensor, Debug) << "Apply test pattern mode " << mode;
> -
> -	int32_t index = staticProps_->testPatternModes.at(mode);
> -	ControlList ctrls{ controls() };
> -	ctrls.set(V4L2_CID_TEST_PATTERN, index);
> -
> -	int ret = setControls(&ctrls);
> -	if (ret)
> -		return ret;
> -
> -	testPatternMode_ = mode;
> -
> -	return 0;
> -}
> -
> -std::string CameraSensor::logPrefix() const
> -{
> -	return "'" + entity_->name() + "'";
> -}
> -
> -namespace {
> -
> -/* Transitory default camera sensor implementation */
> -class CameraSensorDefault : public CameraSensor
> -{
> -public:
> -	CameraSensorDefault(MediaEntity *entity)
> -		: CameraSensor(entity)
> -	{
> -	}
> -
> -	static bool match([[maybe_unused]] const MediaEntity *entity)
> -	{
> -		return true;
> -	}
> -
> -	static std::unique_ptr<CameraSensorDefault> create(MediaEntity *entity)
> -	{
> -		std::unique_ptr<CameraSensorDefault> sensor =
> -			std::make_unique<CameraSensorDefault>(entity);
> -
> -		if (sensor->init())
> -			return nullptr;
> -
> -		return sensor;
> -	}
> -};
> -
> -REGISTER_CAMERA_SENSOR(CameraSensorDefault)
> -
> -}; /* namespace */
>  
>  /**
>   * \class CameraSensorFactoryBase
> @@ -1271,18 +370,18 @@ std::unique_ptr<CameraSensor> CameraSensorFactoryBase::create(MediaEntity *entit
>  		CameraSensorFactoryBase::factories();
>  
>  	for (const CameraSensorFactoryBase *factory : factories) {
> -		if (!factory->match(entity))
> -			continue;
> +		std::variant<std::unique_ptr<CameraSensor>, int> result =
> +			factory->match(entity);
>  
> -		std::unique_ptr<CameraSensor> sensor = factory->createInstance(entity);
> -		if (!sensor) {
> +		if (std::holds_alternative<std::unique_ptr<CameraSensor>>(result))
> +			return std::get<std::unique_ptr<CameraSensor>>(std::move(result));
> +
> +		if (std::get<int>(result)) {
>  			LOG(CameraSensor, Error)
>  				<< "Failed to create sensor for '"
> -				<< entity->name();
> +				<< entity->name() << ": " << std::get<int>(result);
>  			return nullptr;
>  		}
> -
> -		return sensor;
>  	}
>  
>  	return nullptr;
> @@ -1337,33 +436,27 @@ void CameraSensorFactoryBase::registerFactory(CameraSensorFactoryBase *factory)
>   * function.
>   */
>  
> -/**
> - * \fn CameraSensorFactory::createInstance() const
> - * \brief Create an instance of the CameraSensor corresponding to the factory
> - *
> - * \return A unique pointer to a newly constructed instance of the CameraSensor
> - * subclass corresponding to the factory
> - */
> -
>  /**
>   * \def REGISTER_CAMERA_SENSOR(sensor)
>   * \brief Register a camera sensor type to the sensor factory
>   * \param[in] sensor Class name of the CameraSensor derived class to register
>   *
>   * Register a CameraSensor subclass with the factory and make it available to
> - * try and match sensors. The subclass needs to implement two static functions:
> + * try and match sensors. The subclass needs to implement a static match
> + * function:
>   *
>   * \code{.cpp}
> - * static bool match(const MediaEntity *entity);
> - * static std::unique_ptr<sensor> create(MediaEntity *entity);
> + * static std::variant<std::unique_ptr<CameraSensor>, int> match(MediaEntity *entity);
>   * \endcode
>   *
> - * The match() function tests if the sensor class supports the camera sensor
> - * identified by a MediaEntity.
> + * The function tests if the sensor class supports the camera sensor identified
> + * by a MediaEntity. If so, it creates a new instance of the sensor class. The
> + * return value is a variant that contains
>   *
> - * The create() function creates a new instance of the sensor class. It may
> - * return a null pointer if initialization of the instance fails. It will only
> - * be called if the match() function has returned true for the given entity.
> + * - A new instance of the camera sensor class if the entity matched and
> + *   creation succeeded ;
> + * - A non-zero error code if the entity matched and the creation failed ; or
> + * - A zero error code if the entity didn't match.
>   */
>  
>  } /* namespace libcamera */
> diff --git a/src/libcamera/sensor/camera_sensor_legacy.cpp b/src/libcamera/sensor/camera_sensor_legacy.cpp
> new file mode 100644
> index 000000000000..34677339241c
> --- /dev/null
> +++ b/src/libcamera/sensor/camera_sensor_legacy.cpp
> @@ -0,0 +1,1015 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * camera_sensor_legacy.cpp - A V4L2-backed camera sensor
> + */
> +
> +#include <algorithm>
> +#include <float.h>
> +#include <iomanip>
> +#include <limits.h>
> +#include <map>
> +#include <math.h>
> +#include <memory>
> +#include <string.h>
> +#include <string>
> +#include <vector>
> +
> +#include <libcamera/base/class.h>
> +#include <libcamera/base/log.h>
> +#include <libcamera/base/utils.h>
> +
> +#include <libcamera/camera.h>
> +#include <libcamera/control_ids.h>
> +#include <libcamera/controls.h>
> +#include <libcamera/geometry.h>
> +#include <libcamera/orientation.h>
> +#include <libcamera/property_ids.h>
> +#include <libcamera/transform.h>
> +
> +#include <libcamera/ipa/core_ipa_interface.h>
> +
> +#include "libcamera/internal/bayer_format.h"
> +#include "libcamera/internal/camera_lens.h"
> +#include "libcamera/internal/camera_sensor.h"
> +#include "libcamera/internal/camera_sensor_properties.h"
> +#include "libcamera/internal/formats.h"
> +#include "libcamera/internal/media_device.h"
> +#include "libcamera/internal/sysfs.h"
> +#include "libcamera/internal/v4l2_subdevice.h"
> +
> +namespace libcamera {
> +
> +class BayerFormat;
> +class CameraLens;
> +class MediaEntity;
> +class SensorConfiguration;
> +
> +struct CameraSensorProperties;
> +
> +enum class Orientation;
> +
> +LOG_DECLARE_CATEGORY(CameraSensor)
> +
> +class CameraSensorLegacy : public CameraSensor, protected Loggable
> +{
> +public:
> +	CameraSensorLegacy(const MediaEntity *entity);
> +	~CameraSensorLegacy();
> +
> +	static std::variant<std::unique_ptr<CameraSensor>, int>
> +	match(MediaEntity *entity);
> +
> +	const std::string &model() const override { return model_; }
> +	const std::string &id() const override { return id_; }
> +
> +	const MediaEntity *entity() const override { return entity_; }
> +	V4L2Subdevice *device() override { return subdev_.get(); }
> +
> +	CameraLens *focusLens() override { return focusLens_.get(); }
> +
> +	const std::vector<unsigned int> &mbusCodes() const override { return mbusCodes_; }
> +	std::vector<Size> sizes(unsigned int mbusCode) const override;
> +	Size resolution() const override;
> +
> +	V4L2SubdeviceFormat getFormat(const std::vector<unsigned int> &mbusCodes,
> +				      const Size &size) const override;
> +	int setFormat(V4L2SubdeviceFormat *format,
> +		      Transform transform = Transform::Identity) override;
> +	int tryFormat(V4L2SubdeviceFormat *format) const override;
> +
> +	int applyConfiguration(const SensorConfiguration &config,
> +			       Transform transform = Transform::Identity,
> +			       V4L2SubdeviceFormat *sensorFormat = nullptr) override;
> +
> +	const ControlList &properties() const override { return properties_; }
> +	int sensorInfo(IPACameraSensorInfo *info) const override;
> +	Transform computeTransform(Orientation *orientation) const override;
> +	BayerFormat::Order bayerOrder(Transform t) const override;
> +
> +	const ControlInfoMap &controls() const override;
> +	ControlList getControls(const std::vector<uint32_t> &ids) override;
> +	int setControls(ControlList *ctrls) override;
> +
> +	const std::vector<controls::draft::TestPatternModeEnum> &
> +	testPatternModes() const override { return testPatternModes_; }
> +	int setTestPatternMode(controls::draft::TestPatternModeEnum mode) override;
> +
> +protected:
> +	std::string logPrefix() const override;
> +
> +private:
> +	LIBCAMERA_DISABLE_COPY(CameraSensorLegacy)
> +
> +	int init();
> +	int generateId();
> +	int validateSensorDriver();
> +	void initVimcDefaultProperties();
> +	void initStaticProperties();
> +	void initTestPatternModes();
> +	int initProperties();
> +	int applyTestPatternMode(controls::draft::TestPatternModeEnum mode);
> +	int discoverAncillaryDevices();
> +
> +	const MediaEntity *entity_;
> +	std::unique_ptr<V4L2Subdevice> subdev_;
> +	unsigned int pad_;
> +
> +	const CameraSensorProperties *staticProps_;
> +
> +	std::string model_;
> +	std::string id_;
> +
> +	V4L2Subdevice::Formats formats_;
> +	std::vector<unsigned int> mbusCodes_;
> +	std::vector<Size> sizes_;
> +	std::vector<controls::draft::TestPatternModeEnum> testPatternModes_;
> +	controls::draft::TestPatternModeEnum testPatternMode_;
> +
> +	Size pixelArraySize_;
> +	Rectangle activeArea_;
> +	const BayerFormat *bayerFormat_;
> +	bool supportFlips_;
> +	bool flipsAlterBayerOrder_;
> +	Orientation mountingOrientation_;
> +
> +	ControlList properties_;
> +
> +	std::unique_ptr<CameraLens> focusLens_;
> +};
> +
> +/**
> + * \class CameraSensorLegacy
> + * \brief A camera sensor based on V4L2 subdevices
> + *
> + * The implementation is currently limited to sensors that expose a single V4L2
> + * subdevice with a single pad. It will be extended to support more complex
> + * devices as the needs arise.
> + */
> +
> +CameraSensorLegacy::CameraSensorLegacy(const MediaEntity *entity)
> +	: entity_(entity), pad_(UINT_MAX), staticProps_(nullptr),
> +	  bayerFormat_(nullptr), supportFlips_(false),
> +	  flipsAlterBayerOrder_(false), properties_(properties::properties)
> +{
> +}
> +
> +CameraSensorLegacy::~CameraSensorLegacy() = default;
> +
> +std::variant<std::unique_ptr<CameraSensor>, int>
> +CameraSensorLegacy::match(MediaEntity *entity)
> +{
> +	std::unique_ptr<CameraSensorLegacy> sensor =
> +		std::make_unique<CameraSensorLegacy>(entity);
> +
> +	int ret = sensor->init();
> +	if (ret)
> +		return { ret };
> +
> +	return { std::move(sensor) };
> +}
> +
> +int CameraSensorLegacy::init()
> +{
> +	for (const MediaPad *pad : entity_->pads()) {
> +		if (pad->flags() & MEDIA_PAD_FL_SOURCE) {
> +			pad_ = pad->index();
> +			break;
> +		}
> +	}
> +
> +	if (pad_ == UINT_MAX) {
> +		LOG(CameraSensor, Error)
> +			<< "Sensors with more than one pad are not supported";
> +		return -EINVAL;
> +	}
> +
> +	switch (entity_->function()) {
> +	case MEDIA_ENT_F_CAM_SENSOR:
> +	case MEDIA_ENT_F_PROC_VIDEO_ISP:
> +		break;
> +
> +	default:
> +		LOG(CameraSensor, Error)
> +			<< "Invalid sensor function "
> +			<< utils::hex(entity_->function());
> +		return -EINVAL;
> +	}
> +
> +	/* Create and open the subdev. */
> +	subdev_ = std::make_unique<V4L2Subdevice>(entity_);
> +	int ret = subdev_->open();
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * Clear any flips to be sure we get the "native" Bayer order. This is
> +	 * harmless for sensors where the flips don't affect the Bayer order.
> +	 */
> +	ControlList ctrls(subdev_->controls());
> +	if (subdev_->controls().find(V4L2_CID_HFLIP) != subdev_->controls().end())
> +		ctrls.set(V4L2_CID_HFLIP, 0);
> +	if (subdev_->controls().find(V4L2_CID_VFLIP) != subdev_->controls().end())
> +		ctrls.set(V4L2_CID_VFLIP, 0);
> +	subdev_->setControls(&ctrls);
> +
> +	/* Enumerate, sort and cache media bus codes and sizes. */
> +	formats_ = subdev_->formats(pad_);
> +	if (formats_.empty()) {
> +		LOG(CameraSensor, Error) << "No image format found";
> +		return -EINVAL;
> +	}
> +
> +	mbusCodes_ = utils::map_keys(formats_);
> +	std::sort(mbusCodes_.begin(), mbusCodes_.end());
> +
> +	for (const auto &format : formats_) {
> +		const std::vector<SizeRange> &ranges = format.second;
> +		std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes_),
> +			       [](const SizeRange &range) { return range.max; });
> +	}
> +
> +	std::sort(sizes_.begin(), sizes_.end());
> +
> +	/* Remove duplicates. */
> +	auto last = std::unique(sizes_.begin(), sizes_.end());
> +	sizes_.erase(last, sizes_.end());
> +
> +	/*
> +	 * VIMC is a bit special, as it does not yet support all the mandatory
> +	 * requirements regular sensors have to respect.
> +	 *
> +	 * Do not validate the driver if it's VIMC and initialize the sensor
> +	 * properties with static information.
> +	 *
> +	 * \todo Remove the special case once the VIMC driver has been
> +	 * updated in all test platforms.
> +	 */
> +	if (entity_->device()->driver() == "vimc") {
> +		initVimcDefaultProperties();
> +
> +		ret = initProperties();
> +		if (ret)
> +			return ret;
> +
> +		return discoverAncillaryDevices();
> +	}
> +
> +	/* Get the color filter array pattern (only for RAW sensors). */
> +	for (unsigned int mbusCode : mbusCodes_) {
> +		const BayerFormat &bayerFormat = BayerFormat::fromMbusCode(mbusCode);
> +		if (bayerFormat.isValid()) {
> +			bayerFormat_ = &bayerFormat;
> +			break;
> +		}
> +	}
> +
> +	ret = validateSensorDriver();
> +	if (ret)
> +		return ret;
> +
> +	ret = initProperties();
> +	if (ret)
> +		return ret;
> +
> +	ret = discoverAncillaryDevices();
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Set HBLANK to the minimum to start with a well-defined line length,
> +	 * allowing IPA modules that do not modify HBLANK to use the sensor
> +	 * minimum line length in their calculations.
> +	 */
> +	const struct v4l2_query_ext_ctrl *hblankInfo = subdev_->controlInfo(V4L2_CID_HBLANK);
> +	if (hblankInfo && !(hblankInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {
> +		ControlList ctrl(subdev_->controls());
> +
> +		ctrl.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblankInfo->minimum));
> +		ret = subdev_->setControls(&ctrl);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return applyTestPatternMode(controls::draft::TestPatternModeEnum::TestPatternModeOff);
> +}
> +
> +int CameraSensorLegacy::generateId()
> +{
> +	const std::string devPath = subdev_->devicePath();
> +
> +	/* Try to get ID from firmware description. */
> +	id_ = sysfs::firmwareNodePath(devPath);
> +	if (!id_.empty())
> +		return 0;
> +
> +	/*
> +	 * Virtual sensors not described in firmware
> +	 *
> +	 * Verify it's a platform device and construct ID from the device path
> +	 * and model of sensor.
> +	 */
> +	if (devPath.find("/sys/devices/platform/", 0) == 0) {
> +		id_ = devPath.substr(strlen("/sys/devices/")) + " " + model();
> +		return 0;
> +	}
> +
> +	LOG(CameraSensor, Error) << "Can't generate sensor ID";
> +	return -EINVAL;
> +}
> +
> +int CameraSensorLegacy::validateSensorDriver()
> +{
> +	int err = 0;
> +
> +	/*
> +	 * Optional controls are used to register optional sensor properties. If
> +	 * not present, some values will be defaulted.
> +	 */
> +	static constexpr uint32_t optionalControls[] = {
> +		V4L2_CID_CAMERA_SENSOR_ROTATION,
> +	};
> +
> +	const ControlIdMap &controls = subdev_->controls().idmap();
> +	for (uint32_t ctrl : optionalControls) {
> +		if (!controls.count(ctrl))
> +			LOG(CameraSensor, Debug)
> +				<< "Optional V4L2 control " << utils::hex(ctrl)
> +				<< " not supported";
> +	}
> +
> +	/*
> +	 * Recommended controls are similar to optional controls, but will
> +	 * become mandatory in the near future. Be loud if they're missing.
> +	 */
> +	static constexpr uint32_t recommendedControls[] = {
> +		V4L2_CID_CAMERA_ORIENTATION,
> +	};
> +
> +	for (uint32_t ctrl : recommendedControls) {
> +		if (!controls.count(ctrl)) {
> +			LOG(CameraSensor, Warning)
> +				<< "Recommended V4L2 control " << utils::hex(ctrl)
> +				<< " not supported";
> +			err = -EINVAL;
> +		}
> +	}
> +
> +	/*
> +	 * Verify if sensor supports horizontal/vertical flips
> +	 *
> +	 * \todo Handle horizontal and vertical flips independently.
> +	 */
> +	const struct v4l2_query_ext_ctrl *hflipInfo = subdev_->controlInfo(V4L2_CID_HFLIP);
> +	const struct v4l2_query_ext_ctrl *vflipInfo = subdev_->controlInfo(V4L2_CID_VFLIP);
> +	if (hflipInfo && !(hflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY) &&
> +	    vflipInfo && !(vflipInfo->flags & V4L2_CTRL_FLAG_READ_ONLY)) {
> +		supportFlips_ = true;
> +
> +		if (hflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT ||
> +		    vflipInfo->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT)
> +			flipsAlterBayerOrder_ = true;
> +	}
> +
> +	if (!supportFlips_)
> +		LOG(CameraSensor, Debug)
> +			<< "Camera sensor does not support horizontal/vertical flip";
> +
> +	/*
> +	 * Make sure the required selection targets are supported.
> +	 *
> +	 * Failures in reading any of the targets are not deemed to be fatal,
> +	 * but some properties and features, like constructing a
> +	 * IPACameraSensorInfo for the IPA module, won't be supported.
> +	 *
> +	 * \todo Make support for selection targets mandatory as soon as all
> +	 * test platforms have been updated.
> +	 */
> +	Rectangle rect;
> +	int ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP_BOUNDS, &rect);
> +	if (ret) {
> +		/*
> +		 * Default the pixel array size to the largest size supported
> +		 * by the sensor. The sizes_ vector is sorted in ascending
> +		 * order, the largest size is thus the last element.
> +		 */
> +		pixelArraySize_ = sizes_.back();
> +
> +		LOG(CameraSensor, Warning)
> +			<< "The PixelArraySize property has been defaulted to "
> +			<< pixelArraySize_;
> +		err = -EINVAL;
> +	} else {
> +		pixelArraySize_ = rect.size();
> +	}
> +
> +	ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP_DEFAULT, &activeArea_);
> +	if (ret) {
> +		activeArea_ = Rectangle(pixelArraySize_);
> +		LOG(CameraSensor, Warning)
> +			<< "The PixelArrayActiveAreas property has been defaulted to "
> +			<< activeArea_;
> +		err = -EINVAL;
> +	}
> +
> +	ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP, &rect);
> +	if (ret) {
> +		LOG(CameraSensor, Warning)
> +			<< "Failed to retrieve the sensor crop rectangle";
> +		err = -EINVAL;
> +	}
> +
> +	if (err) {
> +		LOG(CameraSensor, Warning)
> +			<< "The sensor kernel driver needs to be fixed";
> +		LOG(CameraSensor, Warning)
> +			<< "See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information";
> +	}
> +
> +	if (!bayerFormat_)
> +		return 0;
> +
> +	/*
> +	 * For raw sensors, make sure the sensor driver supports the controls
> +	 * required by the CameraSensor class.
> +	 */
> +	static constexpr uint32_t mandatoryControls[] = {
> +		V4L2_CID_ANALOGUE_GAIN,
> +		V4L2_CID_EXPOSURE,
> +		V4L2_CID_HBLANK,
> +		V4L2_CID_PIXEL_RATE,
> +		V4L2_CID_VBLANK,
> +	};
> +
> +	err = 0;
> +	for (uint32_t ctrl : mandatoryControls) {
> +		if (!controls.count(ctrl)) {
> +			LOG(CameraSensor, Error)
> +				<< "Mandatory V4L2 control " << utils::hex(ctrl)
> +				<< " not available";
> +			err = -EINVAL;
> +		}
> +	}
> +
> +	if (err) {
> +		LOG(CameraSensor, Error)
> +			<< "The sensor kernel driver needs to be fixed";
> +		LOG(CameraSensor, Error)
> +			<< "See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information";
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +void CameraSensorLegacy::initVimcDefaultProperties()
> +{
> +	/* Use the largest supported size. */
> +	pixelArraySize_ = sizes_.back();
> +	activeArea_ = Rectangle(pixelArraySize_);
> +}
> +
> +void CameraSensorLegacy::initStaticProperties()
> +{
> +	staticProps_ = CameraSensorProperties::get(model_);
> +	if (!staticProps_)
> +		return;
> +
> +	/* Register the properties retrieved from the sensor database. */
> +	properties_.set(properties::UnitCellSize, staticProps_->unitCellSize);
> +
> +	initTestPatternModes();
> +}
> +
> +void CameraSensorLegacy::initTestPatternModes()
> +{
> +	const auto &v4l2TestPattern = controls().find(V4L2_CID_TEST_PATTERN);
> +	if (v4l2TestPattern == controls().end()) {
> +		LOG(CameraSensor, Debug) << "V4L2_CID_TEST_PATTERN is not supported";
> +		return;
> +	}
> +
> +	const auto &testPatternModes = staticProps_->testPatternModes;
> +	if (testPatternModes.empty()) {
> +		/*
> +		 * The camera sensor supports test patterns but we don't know
> +		 * how to map them so this should be fixed.
> +		 */
> +		LOG(CameraSensor, Debug) << "No static test pattern map for \'"
> +					 << model() << "\'";
> +		return;
> +	}
> +
> +	/*
> +	 * Create a map that associates the V4L2 control index to the test
> +	 * pattern mode by reversing the testPatternModes map provided by the
> +	 * camera sensor properties. This makes it easier to verify if the
> +	 * control index is supported in the below for loop that creates the
> +	 * list of supported test patterns.
> +	 */
> +	std::map<int32_t, controls::draft::TestPatternModeEnum> indexToTestPatternMode;
> +	for (const auto &it : testPatternModes)
> +		indexToTestPatternMode[it.second] = it.first;
> +
> +	for (const ControlValue &value : v4l2TestPattern->second.values()) {
> +		const int32_t index = value.get<int32_t>();
> +
> +		const auto it = indexToTestPatternMode.find(index);
> +		if (it == indexToTestPatternMode.end()) {
> +			LOG(CameraSensor, Debug)
> +				<< "Test pattern mode " << index << " ignored";
> +			continue;
> +		}
> +
> +		testPatternModes_.push_back(it->second);
> +	}
> +}
> +
> +int CameraSensorLegacy::initProperties()
> +{
> +	model_ = subdev_->model();
> +	properties_.set(properties::Model, utils::toAscii(model_));
> +
> +	/* Generate a unique ID for the sensor. */
> +	int ret = generateId();
> +	if (ret)
> +		return ret;
> +
> +	/* Initialize the static properties from the sensor database. */
> +	initStaticProperties();
> +
> +	/* Retrieve and register properties from the kernel interface. */
> +	const ControlInfoMap &controls = subdev_->controls();
> +
> +	const auto &orientation = controls.find(V4L2_CID_CAMERA_ORIENTATION);
> +	if (orientation != controls.end()) {
> +		int32_t v4l2Orientation = orientation->second.def().get<int32_t>();
> +		int32_t propertyValue;
> +
> +		switch (v4l2Orientation) {
> +		default:
> +			LOG(CameraSensor, Warning)
> +				<< "Unsupported camera location "
> +				<< v4l2Orientation << ", setting to External";
> +			[[fallthrough]];
> +		case V4L2_CAMERA_ORIENTATION_EXTERNAL:
> +			propertyValue = properties::CameraLocationExternal;
> +			break;
> +		case V4L2_CAMERA_ORIENTATION_FRONT:
> +			propertyValue = properties::CameraLocationFront;
> +			break;
> +		case V4L2_CAMERA_ORIENTATION_BACK:
> +			propertyValue = properties::CameraLocationBack;
> +			break;
> +		}
> +		properties_.set(properties::Location, propertyValue);
> +	} else {
> +		LOG(CameraSensor, Warning) << "Failed to retrieve the camera location";
> +	}
> +
> +	const auto &rotationControl = controls.find(V4L2_CID_CAMERA_SENSOR_ROTATION);
> +	if (rotationControl != controls.end()) {
> +		int32_t propertyValue = rotationControl->second.def().get<int32_t>();
> +
> +		/*
> +		 * Cache the Transform associated with the camera mounting
> +		 * rotation for later use in computeTransform().
> +		 */
> +		bool success;
> +		mountingOrientation_ = orientationFromRotation(propertyValue, &success);
> +		if (!success) {
> +			LOG(CameraSensor, Warning)
> +				<< "Invalid rotation of " << propertyValue
> +				<< " degrees - ignoring";
> +			mountingOrientation_ = Orientation::Rotate0;
> +		}
> +
> +		properties_.set(properties::Rotation, propertyValue);
> +	} else {
> +		LOG(CameraSensor, Warning)
> +			<< "Rotation control not available, default to 0 degrees";
> +		properties_.set(properties::Rotation, 0);
> +		mountingOrientation_ = Orientation::Rotate0;
> +	}
> +
> +	properties_.set(properties::PixelArraySize, pixelArraySize_);
> +	properties_.set(properties::PixelArrayActiveAreas, { activeArea_ });
> +
> +	/* Color filter array pattern, register only for RAW sensors. */
> +	if (bayerFormat_) {
> +		int32_t cfa;
> +		switch (bayerFormat_->order) {
> +		case BayerFormat::BGGR:
> +			cfa = properties::draft::BGGR;
> +			break;
> +		case BayerFormat::GBRG:
> +			cfa = properties::draft::GBRG;
> +			break;
> +		case BayerFormat::GRBG:
> +			cfa = properties::draft::GRBG;
> +			break;
> +		case BayerFormat::RGGB:
> +			cfa = properties::draft::RGGB;
> +			break;
> +		case BayerFormat::MONO:
> +			cfa = properties::draft::MONO;
> +			break;
> +		}
> +
> +		properties_.set(properties::draft::ColorFilterArrangement, cfa);
> +	}
> +
> +	return 0;
> +}
> +
> +int CameraSensorLegacy::discoverAncillaryDevices()
> +{
> +	int ret;
> +
> +	for (MediaEntity *ancillary : entity_->ancillaryEntities()) {
> +		switch (ancillary->function()) {
> +		case MEDIA_ENT_F_LENS:
> +			focusLens_ = std::make_unique<CameraLens>(ancillary);
> +			ret = focusLens_->init();
> +			if (ret) {
> +				LOG(CameraSensor, Error)
> +					<< "Lens initialisation failed, lens disabled";
> +				focusLens_.reset();
> +			}
> +			break;
> +
> +		default:
> +			LOG(CameraSensor, Warning)
> +				<< "Unsupported ancillary entity function "
> +				<< ancillary->function();
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +std::vector<Size> CameraSensorLegacy::sizes(unsigned int mbusCode) const
> +{
> +	std::vector<Size> sizes;
> +
> +	const auto &format = formats_.find(mbusCode);
> +	if (format == formats_.end())
> +		return sizes;
> +
> +	const std::vector<SizeRange> &ranges = format->second;
> +	std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes),
> +		       [](const SizeRange &range) { return range.max; });
> +
> +	std::sort(sizes.begin(), sizes.end());
> +
> +	return sizes;
> +}
> +
> +Size CameraSensorLegacy::resolution() const
> +{
> +	return std::min(sizes_.back(), activeArea_.size());
> +}
> +
> +V4L2SubdeviceFormat
> +CameraSensorLegacy::getFormat(const std::vector<unsigned int> &mbusCodes,
> +			      const Size &size) const
> +{
> +	unsigned int desiredArea = size.width * size.height;
> +	unsigned int bestArea = UINT_MAX;
> +	float desiredRatio = static_cast<float>(size.width) / size.height;
> +	float bestRatio = FLT_MAX;
> +	const Size *bestSize = nullptr;
> +	uint32_t bestCode = 0;
> +
> +	for (unsigned int code : mbusCodes) {
> +		const auto formats = formats_.find(code);
> +		if (formats == formats_.end())
> +			continue;
> +
> +		for (const SizeRange &range : formats->second) {
> +			const Size &sz = range.max;
> +
> +			if (sz.width < size.width || sz.height < size.height)
> +				continue;
> +
> +			float ratio = static_cast<float>(sz.width) / sz.height;
> +			float ratioDiff = fabsf(ratio - desiredRatio);
> +			unsigned int area = sz.width * sz.height;
> +			unsigned int areaDiff = area - desiredArea;
> +
> +			if (ratioDiff > bestRatio)
> +				continue;
> +
> +			if (ratioDiff < bestRatio || areaDiff < bestArea) {
> +				bestRatio = ratioDiff;
> +				bestArea = areaDiff;
> +				bestSize = &sz;
> +				bestCode = code;
> +			}
> +		}
> +	}
> +
> +	if (!bestSize) {
> +		LOG(CameraSensor, Debug) << "No supported format or size found";
> +		return {};
> +	}
> +
> +	V4L2SubdeviceFormat format{
> +		.code = bestCode,
> +		.size = *bestSize,
> +		.colorSpace = ColorSpace::Raw,
> +	};
> +
> +	return format;
> +}
> +
> +int CameraSensorLegacy::setFormat(V4L2SubdeviceFormat *format, Transform transform)
> +{
> +	/* Configure flips if the sensor supports that. */
> +	if (supportFlips_) {
> +		ControlList flipCtrls(subdev_->controls());
> +
> +		flipCtrls.set(V4L2_CID_HFLIP,
> +			      static_cast<int32_t>(!!(transform & Transform::HFlip)));
> +		flipCtrls.set(V4L2_CID_VFLIP,
> +			      static_cast<int32_t>(!!(transform & Transform::VFlip)));
> +
> +		int ret = subdev_->setControls(&flipCtrls);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Apply format on the subdev. */
> +	int ret = subdev_->setFormat(pad_, format);
> +	if (ret)
> +		return ret;
> +
> +	subdev_->updateControlInfo();
> +	return 0;
> +}
> +
> +int CameraSensorLegacy::tryFormat(V4L2SubdeviceFormat *format) const
> +{
> +	return subdev_->setFormat(pad_, format,
> +				  V4L2Subdevice::Whence::TryFormat);
> +}
> +
> +int CameraSensorLegacy::applyConfiguration(const SensorConfiguration &config,
> +					   Transform transform,
> +					   V4L2SubdeviceFormat *sensorFormat)
> +{
> +	if (!config.isValid()) {
> +		LOG(CameraSensor, Error) << "Invalid sensor configuration";
> +		return -EINVAL;
> +	}
> +
> +	std::vector<unsigned int> filteredCodes;
> +	std::copy_if(mbusCodes_.begin(), mbusCodes_.end(),
> +		     std::back_inserter(filteredCodes),
> +		     [&config](unsigned int mbusCode) {
> +			     BayerFormat bayer = BayerFormat::fromMbusCode(mbusCode);
> +			     if (bayer.bitDepth == config.bitDepth)
> +				     return true;
> +			     return false;
> +		     });
> +	if (filteredCodes.empty()) {
> +		LOG(CameraSensor, Error)
> +			<< "Cannot find any format with bit depth "
> +			<< config.bitDepth;
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Compute the sensor's data frame size by applying the cropping
> +	 * rectangle, subsampling and output crop to the sensor's pixel array
> +	 * size.
> +	 *
> +	 * \todo The actual size computation is for now ignored and only the
> +	 * output size is considered. This implies that resolutions obtained
> +	 * with two different cropping/subsampling will look identical and
> +	 * only the first found one will be considered.
> +	 */
> +	V4L2SubdeviceFormat subdevFormat = {};
> +	for (unsigned int code : filteredCodes) {
> +		for (const Size &size : sizes(code)) {
> +			if (size.width != config.outputSize.width ||
> +			    size.height != config.outputSize.height)
> +				continue;
> +
> +			subdevFormat.code = code;
> +			subdevFormat.size = size;
> +			break;
> +		}
> +	}
> +	if (!subdevFormat.code) {
> +		LOG(CameraSensor, Error) << "Invalid output size in sensor configuration";
> +		return -EINVAL;
> +	}
> +
> +	int ret = setFormat(&subdevFormat, transform);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Return to the caller the format actually applied to the sensor.
> +	 * This is relevant if transform has changed the bayer pattern order.
> +	 */
> +	if (sensorFormat)
> +		*sensorFormat = subdevFormat;
> +
> +	/* \todo Handle AnalogCrop. Most sensors do not support set_selection */
> +	/* \todo Handle scaling in the digital domain. */
> +
> +	return 0;
> +}
> +
> +int CameraSensorLegacy::sensorInfo(IPACameraSensorInfo *info) const
> +{
> +	if (!bayerFormat_)
> +		return -EINVAL;
> +
> +	info->model = model();
> +
> +	/*
> +	 * The active area size is a static property, while the crop
> +	 * rectangle needs to be re-read as it depends on the sensor
> +	 * configuration.
> +	 */
> +	info->activeAreaSize = { activeArea_.width, activeArea_.height };
> +
> +	/*
> +	 * \todo Support for retreiving the crop rectangle is scheduled to
> +	 * become mandatory. For the time being use the default value if it has
> +	 * been initialized at sensor driver validation time.
> +	 */
> +	int ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP, &info->analogCrop);
> +	if (ret) {
> +		info->analogCrop = activeArea_;
> +		LOG(CameraSensor, Warning)
> +			<< "The analogue crop rectangle has been defaulted to the active area size";
> +	}
> +
> +	/*
> +	 * IPACameraSensorInfo::analogCrop::x and IPACameraSensorInfo::analogCrop::y
> +	 * are defined relatively to the active pixel area, while V4L2's
> +	 * TGT_CROP target is defined in respect to the full pixel array.
> +	 *
> +	 * Compensate it by subtracting the active area offset.
> +	 */
> +	info->analogCrop.x -= activeArea_.x;
> +	info->analogCrop.y -= activeArea_.y;
> +
> +	/* The bit depth and image size depend on the currently applied format. */
> +	V4L2SubdeviceFormat format{};
> +	ret = subdev_->getFormat(pad_, &format);
> +	if (ret)
> +		return ret;
> +	info->bitsPerPixel = MediaBusFormatInfo::info(format.code).bitsPerPixel;
> +	info->outputSize = format.size;
> +
> +	std::optional<int32_t> cfa = properties_.get(properties::draft::ColorFilterArrangement);
> +	info->cfaPattern = cfa ? *cfa : properties::draft::RGB;
> +
> +	/*
> +	 * Retrieve the pixel rate, line length and minimum/maximum frame
> +	 * duration through V4L2 controls. Support for the V4L2_CID_PIXEL_RATE,
> +	 * V4L2_CID_HBLANK and V4L2_CID_VBLANK controls is mandatory.
> +	 */
> +	ControlList ctrls = subdev_->getControls({ V4L2_CID_PIXEL_RATE,
> +						   V4L2_CID_HBLANK,
> +						   V4L2_CID_VBLANK });
> +	if (ctrls.empty()) {
> +		LOG(CameraSensor, Error)
> +			<< "Failed to retrieve camera info controls";
> +		return -EINVAL;
> +	}
> +
> +	info->pixelRate = ctrls.get(V4L2_CID_PIXEL_RATE).get<int64_t>();
> +
> +	const ControlInfo hblank = ctrls.infoMap()->at(V4L2_CID_HBLANK);
> +	info->minLineLength = info->outputSize.width + hblank.min().get<int32_t>();
> +	info->maxLineLength = info->outputSize.width + hblank.max().get<int32_t>();
> +
> +	const ControlInfo vblank = ctrls.infoMap()->at(V4L2_CID_VBLANK);
> +	info->minFrameLength = info->outputSize.height + vblank.min().get<int32_t>();
> +	info->maxFrameLength = info->outputSize.height + vblank.max().get<int32_t>();
> +
> +	return 0;
> +}
> +
> +Transform CameraSensorLegacy::computeTransform(Orientation *orientation) const
> +{
> +	/*
> +	 * If we cannot do any flips we cannot change the native camera mounting
> +	 * orientation.
> +	 */
> +	if (!supportFlips_) {
> +		*orientation = mountingOrientation_;
> +		return Transform::Identity;
> +	}
> +
> +	/*
> +	 * Now compute the required transform to obtain 'orientation' starting
> +	 * from the mounting rotation.
> +	 *
> +	 * As a note:
> +	 * 	orientation / mountingOrientation_ = transform
> +	 * 	mountingOrientation_ * transform = orientation
> +	 */
> +	Transform transform = *orientation / mountingOrientation_;
> +
> +	/*
> +	 * If transform contains any Transpose we cannot do it, so adjust
> +	 * 'orientation' to report the image native orientation and return Identity.
> +	 */
> +	if (!!(transform & Transform::Transpose)) {
> +		*orientation = mountingOrientation_;
> +		return Transform::Identity;
> +	}
> +
> +	return transform;
> +}
> +
> +BayerFormat::Order CameraSensorLegacy::bayerOrder(Transform t) const
> +{
> +	/* Return a defined by meaningless value for non-Bayer sensors. */
> +	if (!bayerFormat_)
> +		return BayerFormat::Order::BGGR;
> +
> +	if (!flipsAlterBayerOrder_)
> +		return bayerFormat_->order;
> +
> +	/*
> +	 * Apply the transform to the native (i.e. untransformed) Bayer order,
> +	 * using the rest of the Bayer format supplied by the caller.
> +	 */
> +	return bayerFormat_->transform(t).order;
> +}
> +
> +const ControlInfoMap &CameraSensorLegacy::controls() const
> +{
> +	return subdev_->controls();
> +}
> +
> +ControlList CameraSensorLegacy::getControls(const std::vector<uint32_t> &ids)
> +{
> +	return subdev_->getControls(ids);
> +}
> +
> +int CameraSensorLegacy::setControls(ControlList *ctrls)
> +{
> +	return subdev_->setControls(ctrls);
> +}
> +
> +int CameraSensorLegacy::setTestPatternMode(controls::draft::TestPatternModeEnum mode)
> +{
> +	if (testPatternMode_ == mode)
> +		return 0;
> +
> +	if (testPatternModes_.empty()) {
> +		LOG(CameraSensor, Error)
> +			<< "Camera sensor does not support test pattern modes.";
> +		return -EINVAL;
> +	}
> +
> +	return applyTestPatternMode(mode);
> +}
> +
> +int CameraSensorLegacy::applyTestPatternMode(controls::draft::TestPatternModeEnum mode)
> +{
> +	if (testPatternModes_.empty())
> +		return 0;
> +
> +	auto it = std::find(testPatternModes_.begin(), testPatternModes_.end(),
> +			    mode);
> +	if (it == testPatternModes_.end()) {
> +		LOG(CameraSensor, Error) << "Unsupported test pattern mode "
> +					 << mode;
> +		return -EINVAL;
> +	}
> +
> +	LOG(CameraSensor, Debug) << "Apply test pattern mode " << mode;
> +
> +	int32_t index = staticProps_->testPatternModes.at(mode);
> +	ControlList ctrls{ controls() };
> +	ctrls.set(V4L2_CID_TEST_PATTERN, index);
> +
> +	int ret = setControls(&ctrls);
> +	if (ret)
> +		return ret;
> +
> +	testPatternMode_ = mode;
> +
> +	return 0;
> +}
> +
> +std::string CameraSensorLegacy::logPrefix() const
> +{
> +	return "'" + entity_->name() + "'";
> +}
> +
> +REGISTER_CAMERA_SENSOR(CameraSensorLegacy)
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/sensor/meson.build b/src/libcamera/sensor/meson.build
> index bf4b131a94b1..e83020fc22c3 100644
> --- a/src/libcamera/sensor/meson.build
> +++ b/src/libcamera/sensor/meson.build
> @@ -2,5 +2,6 @@
>  
>  libcamera_sources += files([
>      'camera_sensor.cpp',
> +    'camera_sensor_legacy.cpp',
>      'camera_sensor_properties.cpp',
>  ])
> -- 
> Regards,
> 
> Laurent Pinchart
> 


More information about the libcamera-devel mailing list