From 2a0baf47eb8a336f8378044dbeda9e7873a57d25 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 29 Jan 2024 17:36:30 +0200 Subject: libcamera: camera_sensor: Move related classes to subdirectory In preparation for adding alternative implementations of the CameraSensor class, move the code to a subdirectory to avoid cluttering the main src/libcamera/ directory. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Reviewed-by: Stefan Klug --- src/libcamera/camera_sensor.cpp | 1203 --------------------- src/libcamera/camera_sensor_properties.cpp | 272 ----- src/libcamera/meson.build | 3 +- src/libcamera/sensor/camera_sensor.cpp | 1203 +++++++++++++++++++++ src/libcamera/sensor/camera_sensor_properties.cpp | 272 +++++ src/libcamera/sensor/meson.build | 6 + 6 files changed, 1482 insertions(+), 1477 deletions(-) delete mode 100644 src/libcamera/camera_sensor.cpp delete mode 100644 src/libcamera/camera_sensor_properties.cpp create mode 100644 src/libcamera/sensor/camera_sensor.cpp create mode 100644 src/libcamera/sensor/camera_sensor_properties.cpp create mode 100644 src/libcamera/sensor/meson.build diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp deleted file mode 100644 index af5d97f3..00000000 --- a/src/libcamera/camera_sensor.cpp +++ /dev/null @@ -1,1203 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * camera_sensor.cpp - A camera sensor - */ - -#include "libcamera/internal/camera_sensor.h" -#include "libcamera/internal/media_device.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#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" - -/** - * \file camera_sensor.h - * \brief A camera sensor - */ - -namespace libcamera { - -LOG_DEFINE_CATEGORY(CameraSensor) - -/** - * \class CameraSensor - * \brief A camera sensor based on V4L2 subdevices - * - * 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. - */ - -/** - * \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), - 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(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 &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. - * - * At present, there is no way of knowing if a control is read-only. - * As a workaround, assume that if the minimum and maximum values of - * the V4L2_CID_HBLANK control are the same, it implies the control - * is read-only. - * - * \todo The control API ought to have a flag to specify if a control - * is read-only which could be used below. - */ - if (ctrls.infoMap()->find(V4L2_CID_HBLANK) != ctrls.infoMap()->end()) { - const ControlInfo hblank = ctrls.infoMap()->at(V4L2_CID_HBLANK); - const int32_t hblankMin = hblank.min().get(); - const int32_t hblankMax = hblank.max().get(); - - if (hblankMin != hblankMax) { - ControlList ctrl(subdev_->controls()); - - ctrl.set(V4L2_CID_HBLANK, hblankMin); - ret = subdev_->setControls(&ctrl); - if (ret) - return ret; - } - } - - return applyTestPatternMode(controls::draft::TestPatternModeEnum::TestPatternModeOff); -} - -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 (!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 indexToTestPatternMode; - for (const auto &it : testPatternModes) - indexToTestPatternMode[it.second] = it.first; - - for (const ControlValue &value : v4l2TestPattern->second.values()) { - const int32_t index = value.get(); - - 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 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(); - - /* - * 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(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; -} - -/** - * \fn CameraSensor::model() - * \brief Retrieve the sensor model name - * - * The sensor model name is a free-formed string that uniquely identifies the - * sensor model. - * - * \return The sensor model name - */ - -/** - * \fn CameraSensor::id() - * \brief Retrieve the sensor ID - * - * The sensor ID is a free-form string that uniquely identifies the sensor in - * the system. The ID satisfies the requirements to be used as a camera ID. - * - * \return The sensor ID - */ - -/** - * \fn CameraSensor::entity() - * \brief Retrieve the sensor media entity - * \return The sensor media entity - */ - -/** - * \fn CameraSensor::mbusCodes() - * \brief Retrieve the media bus codes supported by the camera sensor - * - * Any Bayer formats are listed using the sensor's native Bayer order, - * that is, with the effect of V4L2_CID_HFLIP and V4L2_CID_VFLIP undone - * (where these controls exist). - * - * \return The supported media bus codes sorted in increasing order - */ - -/** - * \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 CameraSensor::sizes(unsigned int mbusCode) const -{ - std::vector sizes; - - const auto &format = formats_.find(mbusCode); - if (format == formats_.end()) - return sizes; - - const std::vector &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; -} - -/** - * \brief Retrieve the camera sensor resolution - * - * The camera sensor resolution is the active pixel area size, clamped to the - * maximum frame size the sensor can produce if it is smaller than the active - * pixel area. - * - * \todo Consider if it desirable to distinguish between the maximum resolution - * the sensor can produce (also including upscaled ones) and the actual pixel - * array size by splitting this function in two. - * - * \return The camera sensor resolution in pixels - */ -Size CameraSensor::resolution() const -{ - return std::min(sizes_.back(), activeArea_.size()); -} - -/** - * \fn CameraSensor::testPatternModes() - * \brief Retrieve all the supported test pattern modes of the camera sensor - * The test pattern mode values correspond to the controls::TestPattern control. - * - * \return The list of test pattern modes - */ - -/** - * \brief Set the test pattern mode for the camera sensor - * \param[in] mode The test pattern mode - * - * The new \a mode is applied to the sensor if it differs from the active test - * 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; -} - -/** - * \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 - * - * Media bus codes are selected from \a mbusCodes, which lists all acceptable - * codes in decreasing order of preference. Media bus codes supported by the - * sensor but not listed in \a mbusCodes are ignored. If none of the desired - * codes is supported, it returns an error. - * - * \a size indicates the desired size at the output of the sensor. This function - * selects the best media bus code and size supported by the sensor according - * to the following criteria. - * - * - The desired \a size shall fit in the sensor output size to avoid the need - * to up-scale. - * - The sensor output size shall match the desired aspect ratio to avoid the - * need to crop the field of view. - * - The sensor output size shall be as small as possible to lower the required - * bandwidth. - * - The desired \a size shall be supported by one of the media bus code listed - * in \a mbusCodes. - * - * When multiple media bus codes can produce the same size, the code at the - * lowest position in \a mbusCodes is selected. - * - * The use of this function is optional, as the above criteria may not match the - * needs of all pipeline handlers. Pipeline handlers may implement custom - * sensor format selection when needed. - * - * The returned sensor output format is guaranteed to be acceptable by the - * setFormat() function without any modification. - * - * \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 &mbusCodes, - const Size &size) const -{ - unsigned int desiredArea = size.width * size.height; - unsigned int bestArea = UINT_MAX; - float desiredRatio = static_cast(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(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; -} - -/** - * \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. - * Defaults to Identity. - * - * If flips are writable they are configured according to the desired Transform. - * Transform::Identity always corresponds to H/V flip being disabled if the - * controls are writable. Flips are set before the new format is applied as - * they can effectively change the Bayer pattern ordering. - * - * The ranges of any controls associated with the sensor are also updated. - * - * \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(!!(transform & Transform::HFlip))); - flipCtrls.set(V4L2_CID_VFLIP, - static_cast(!!(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; - - updateControlInfo(); - return 0; -} - -/** - * \brief Try the sensor output format - * \param[in] format The desired sensor output format - * - * The ranges of any controls associated with the sensor are not updated. - * - * \todo Add support for Transform by changing the format's Bayer ordering - * before calling subdev_->setFormat(). - * - * \return 0 on success or a negative error code otherwise - */ -int CameraSensor::tryFormat(V4L2SubdeviceFormat *format) const -{ - return subdev_->setFormat(pad_, format, - V4L2Subdevice::Whence::TryFormat); -} - -/** - * \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. - * Defaults to Identity - * \param[out] sensorFormat Format applied to the sensor (optional) - * - * Apply to the camera sensor the configuration \a config. - * - * \todo The configuration shall be fully populated and if any of the fields - * specified cannot be applied exactly, an error code is returned. - * - * \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 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; -} - -/** - * \brief Retrieve the supported V4L2 controls and their information - * - * Control information is updated automatically to reflect the current sensor - * configuration when the setFormat() function is called, without invalidating - * any iterator on the ControlInfoMap. A manual update can also be forced by - * calling the updateControlInfo() function for pipeline handlers that change - * the sensor configuration wihtout using setFormat(). - * - * \return A map of the V4L2 controls supported by the sensor - */ -const ControlInfoMap &CameraSensor::controls() const -{ - return subdev_->controls(); -} - -/** - * \brief Read V4L2 controls from the sensor - * \param[in] ids The list of controls to read, specified by their ID - * - * This function reads the value of all controls contained in \a ids, and - * returns their values as a ControlList. The control identifiers are defined by - * the V4L2 specification (V4L2_CID_*). - * - * If any control in \a ids is not supported by the device, is disabled (i.e. - * has the V4L2_CTRL_FLAG_DISABLED flag set), or if any other error occurs - * during validation of the requested controls, no control is read and this - * function returns an empty control list. - * - * \sa V4L2Device::getControls() - * - * \return The control values in a ControlList on success, or an empty list on - * error - */ -ControlList CameraSensor::getControls(const std::vector &ids) -{ - return subdev_->getControls(ids); -} - -/** - * \brief Write V4L2 controls to the sensor - * \param[in] ctrls The list of controls to write - * - * This function writes the value of all controls contained in \a ctrls, and - * stores the values actually applied to the device in the corresponding \a - * ctrls entry. The control identifiers are defined by the V4L2 specification - * (V4L2_CID_*). - * - * If any control in \a ctrls is not supported by the device, is disabled (i.e. - * has the V4L2_CTRL_FLAG_DISABLED flag set), is read-only, or if any other - * error occurs during validation of the requested controls, no control is - * written and this function returns -EINVAL. - * - * If an error occurs while writing the controls, the index of the first - * control that couldn't be written is returned. All controls below that index - * are written and their values are updated in \a ctrls, while all other - * controls are not written and their values are not changed. - * - * \sa V4L2Device::setControls() - * - * \return 0 on success or an error code otherwise - * \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::device() - * \brief Retrieve the camera sensor device - * \todo Remove this function by integrating DelayedControl with CameraSensor - * \return The camera sensor device - */ - -/** - * \fn CameraSensor::properties() - * \brief Retrieve the camera sensor properties - * \return The list of camera sensor properties - */ - -/** - * \brief Assemble and return the camera sensor info - * \param[out] info The camera sensor info - * - * This function fills \a info with information that describes the camera sensor - * and its current configuration. The information combines static data (such as - * the the sensor model or active pixel array size) and data specific to the - * current sensor configuration (such as the line length and pixel rate). - * - * Sensor information is only available for raw sensors. When called for a YUV - * sensor, this function returns -EINVAL. - * - * Pipeline handlers that do not change the sensor format using the setFormat() - * function may need to call updateControlInfo() beforehand, to ensure all the - * control ranges are up to date. - * - * \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 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(); - - const ControlInfo hblank = ctrls.infoMap()->at(V4L2_CID_HBLANK); - info->minLineLength = info->outputSize.width + hblank.min().get(); - info->maxLineLength = info->outputSize.width + hblank.max().get(); - - const ControlInfo vblank = ctrls.infoMap()->at(V4L2_CID_VBLANK); - info->minFrameLength = info->outputSize.height + vblank.min().get(); - info->maxFrameLength = info->outputSize.height + vblank.max().get(); - - return 0; -} - -/** - * \fn void CameraSensor::updateControlInfo() - * \brief Update the sensor's ControlInfoMap in case they have changed - * \sa V4L2Device::updateControlInfo() - */ -void CameraSensor::updateControlInfo() -{ - subdev_->updateControlInfo(); -} - -/** - * \fn CameraSensor::focusLens() - * \brief Retrieve the focus lens controller - * - * \return The focus lens controller. nullptr if no focus lens controller is - * connected to the sensor - */ - -/** - * \brief Compute the Transform that gives the requested \a orientation - * \param[inout] orientation The desired image orientation - * - * This function computes the Transform that the pipeline handler should apply - * to the CameraSensor to obtain the requested \a orientation. - * - * The intended caller of this function is the validate() implementation of - * pipeline handlers, that pass in the application requested - * CameraConfiguration::orientation and obtain a Transform to apply to the - * camera sensor, likely at configure() time. - * - * If the requested \a orientation cannot be obtained, the \a orientation - * parameter is adjusted to report the current image orientation and - * Transform::Identity is returned. - * - * If the requested \a orientation can be obtained, the function computes a - * Transform and does not adjust \a orientation. - * - * Pipeline handlers are expected to verify if \a orientation has been - * adjusted by this function and set the CameraConfiguration::status to - * Adjusted accordingly. - * - * \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; -} - -std::string CameraSensor::logPrefix() const -{ - return "'" + entity_->name() + "'"; -} - -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; -} - -} /* namespace libcamera */ diff --git a/src/libcamera/camera_sensor_properties.cpp b/src/libcamera/camera_sensor_properties.cpp deleted file mode 100644 index 6e28b09e..00000000 --- a/src/libcamera/camera_sensor_properties.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2021, Google Inc. - * - * camera_sensor_properties.cpp - Database of camera sensor properties - */ - -#include "libcamera/internal/camera_sensor_properties.h" - -#include - -#include - -#include - -/** - * \file camera_sensor_properties.h - * \brief Database of camera sensor properties - * - * The database of camera sensor properties collects static information about - * camera sensors that is not possible or desirable to retrieve from the device - * at run time. - * - * The database is indexed using the camera sensor model, as reported by the - * properties::Model property, and for each supported sensor it contains a - * list of properties. - */ - -namespace libcamera { - -LOG_DEFINE_CATEGORY(CameraSensorProperties) - -/** - * \struct CameraSensorProperties - * \brief Database of camera sensor properties - * - * \var CameraSensorProperties::unitCellSize - * \brief The physical size of a pixel, including pixel edges, in nanometers. - * - * \var CameraSensorProperties::testPatternModes - * \brief Map that associates the TestPattern control value with the indexes of - * the corresponding sensor test pattern modes as returned by - * V4L2_CID_TEST_PATTERN. - */ - -/** - * \brief Retrieve the properties associated with a sensor - * \param sensor The sensor model name as reported by properties::Model - * \return A pointer to the CameraSensorProperties instance associated with a sensor - * or nullptr if the sensor is not supported - */ -const CameraSensorProperties *CameraSensorProperties::get(const std::string &sensor) -{ - static const std::map sensorProps = { - { "ar0521", { - .unitCellSize = { 2200, 2200 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeSolidColor, 1 }, - { controls::draft::TestPatternModeColorBars, 2 }, - { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, - }, - } }, - { "hi846", { - .unitCellSize = { 1120, 1120 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeSolidColor, 1 }, - { controls::draft::TestPatternModeColorBars, 2 }, - { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, - { controls::draft::TestPatternModePn9, 4 }, - /* - * No corresponding test pattern mode for: - * 5: "Gradient Horizontal" - * 6: "Gradient Vertical" - * 7: "Check Board" - * 8: "Slant Pattern" - * 9: "Resolution Pattern" - */ - }, - } }, - { "imx219", { - .unitCellSize = { 1120, 1120 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1 }, - { controls::draft::TestPatternModeSolidColor, 2 }, - { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, - { controls::draft::TestPatternModePn9, 4 }, - }, - } }, - { "imx258", { - .unitCellSize = { 1120, 1120 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeSolidColor, 1 }, - { controls::draft::TestPatternModeColorBars, 2 }, - { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, - { controls::draft::TestPatternModePn9, 4 }, - }, - } }, - { "imx290", { - .unitCellSize = { 2900, 2900 }, - .testPatternModes = {}, - } }, - { "imx296", { - .unitCellSize = { 3450, 3450 }, - .testPatternModes = {}, - } }, - { "imx327", { - .unitCellSize = { 2900, 2900 }, - .testPatternModes = {}, - } }, - { "imx477", { - .unitCellSize = { 1550, 1550 }, - .testPatternModes = {}, - } }, - { "imx519", { - .unitCellSize = { 1220, 1220 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeSolidColor, 2 }, - { controls::draft::TestPatternModePn9, 4 }, - /* - * The driver reports ColorBars and ColorBarsFadeToGray as well but - * these two patterns do not comply with MIPI CCS v1.1 (Section 10.1). - */ - }, - } }, - { "imx708", { - .unitCellSize = { 1400, 1400 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1 }, - { controls::draft::TestPatternModeSolidColor, 2 }, - { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, - { controls::draft::TestPatternModePn9, 4 }, - }, - } }, - { "ov2685", { - .unitCellSize = { 1750, 1750 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1}, - { controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, - /* - * No corresponding test pattern mode for: - * 3: "Random Data" - * 4: "Black White Square" - * 5: "Color Square" - */ - }, - } }, - { "ov2740", { - .unitCellSize = { 1400, 1400 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1}, - }, - } }, - { "ov4689", { - .unitCellSize = { 2000, 2000 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1}, - { controls::draft::TestPatternModeColorBarsFadeToGray, 2}, - /* - * No corresponding test patterns in - * MIPI CCS specification for sensor's - * colorBarType2 and colorBarType3. - */ - }, - } }, - { "ov5640", { - .unitCellSize = { 1400, 1400 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1 }, - }, - } }, - { "ov5647", { - .unitCellSize = { 1400, 1400 }, - .testPatternModes = {}, - } }, - { "ov5670", { - .unitCellSize = { 1120, 1120 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1 }, - }, - } }, - { "ov5675", { - .unitCellSize = { 1120, 1120 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1 }, - }, - } }, - { "ov5693", { - .unitCellSize = { 1400, 1400 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 2 }, - /* - * No corresponding test pattern mode for - * 1: "Random data" and 3: "Colour Bars with - * Rolling Bar". - */ - }, - } }, - { "ov64a40", { - .unitCellSize = { 1008, 1008 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1 }, - { controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, - /* - * No corresponding test patter mode - * 3: "Vertical Color Bar Type 3", - * 4: "Vertical Color Bar Type 4" - */ - }, - } }, - { "ov8858", { - .unitCellSize = { 1120, 1120 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1 }, - { controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, - /* - * No corresponding test patter mode - * 3: "Vertical Color Bar Type 3", - * 4: "Vertical Color Bar Type 4" - */ - }, - } }, - { "ov8865", { - .unitCellSize = { 1400, 1400 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 2 }, - /* - * No corresponding test pattern mode for: - * 1: "Random data" - * 3: "Color bars with rolling bar" - * 4: "Color squares" - * 5: "Color squares with rolling bar" - */ - }, - } }, - { "ov13858", { - .unitCellSize = { 1120, 1120 }, - .testPatternModes = { - { controls::draft::TestPatternModeOff, 0 }, - { controls::draft::TestPatternModeColorBars, 1 }, - }, - } }, - }; - - const auto it = sensorProps.find(sensor); - if (it == sensorProps.end()) { - LOG(CameraSensorProperties, Warning) - << "No static properties available for '" << sensor << "'"; - LOG(CameraSensorProperties, Warning) - << "Please consider updating the camera sensor properties database"; - return nullptr; - } - - return &it->second; -} - -} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 45f63e93..2e7b0c77 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -7,8 +7,6 @@ libcamera_sources = files([ 'camera_controls.cpp', 'camera_lens.cpp', 'camera_manager.cpp', - 'camera_sensor.cpp', - 'camera_sensor_properties.cpp', 'color_space.cpp', 'controls.cpp', 'control_serializer.cpp', @@ -69,6 +67,7 @@ subdir('converter') subdir('ipa') subdir('pipeline') subdir('proxy') +subdir('sensor') null_dep = dependency('', required : false) diff --git a/src/libcamera/sensor/camera_sensor.cpp b/src/libcamera/sensor/camera_sensor.cpp new file mode 100644 index 00000000..af5d97f3 --- /dev/null +++ b/src/libcamera/sensor/camera_sensor.cpp @@ -0,0 +1,1203 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * camera_sensor.cpp - A camera sensor + */ + +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/media_device.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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" + +/** + * \file camera_sensor.h + * \brief A camera sensor + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(CameraSensor) + +/** + * \class CameraSensor + * \brief A camera sensor based on V4L2 subdevices + * + * 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. + */ + +/** + * \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), + 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(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 &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. + * + * At present, there is no way of knowing if a control is read-only. + * As a workaround, assume that if the minimum and maximum values of + * the V4L2_CID_HBLANK control are the same, it implies the control + * is read-only. + * + * \todo The control API ought to have a flag to specify if a control + * is read-only which could be used below. + */ + if (ctrls.infoMap()->find(V4L2_CID_HBLANK) != ctrls.infoMap()->end()) { + const ControlInfo hblank = ctrls.infoMap()->at(V4L2_CID_HBLANK); + const int32_t hblankMin = hblank.min().get(); + const int32_t hblankMax = hblank.max().get(); + + if (hblankMin != hblankMax) { + ControlList ctrl(subdev_->controls()); + + ctrl.set(V4L2_CID_HBLANK, hblankMin); + ret = subdev_->setControls(&ctrl); + if (ret) + return ret; + } + } + + return applyTestPatternMode(controls::draft::TestPatternModeEnum::TestPatternModeOff); +} + +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 (!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 indexToTestPatternMode; + for (const auto &it : testPatternModes) + indexToTestPatternMode[it.second] = it.first; + + for (const ControlValue &value : v4l2TestPattern->second.values()) { + const int32_t index = value.get(); + + 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 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(); + + /* + * 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(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; +} + +/** + * \fn CameraSensor::model() + * \brief Retrieve the sensor model name + * + * The sensor model name is a free-formed string that uniquely identifies the + * sensor model. + * + * \return The sensor model name + */ + +/** + * \fn CameraSensor::id() + * \brief Retrieve the sensor ID + * + * The sensor ID is a free-form string that uniquely identifies the sensor in + * the system. The ID satisfies the requirements to be used as a camera ID. + * + * \return The sensor ID + */ + +/** + * \fn CameraSensor::entity() + * \brief Retrieve the sensor media entity + * \return The sensor media entity + */ + +/** + * \fn CameraSensor::mbusCodes() + * \brief Retrieve the media bus codes supported by the camera sensor + * + * Any Bayer formats are listed using the sensor's native Bayer order, + * that is, with the effect of V4L2_CID_HFLIP and V4L2_CID_VFLIP undone + * (where these controls exist). + * + * \return The supported media bus codes sorted in increasing order + */ + +/** + * \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 CameraSensor::sizes(unsigned int mbusCode) const +{ + std::vector sizes; + + const auto &format = formats_.find(mbusCode); + if (format == formats_.end()) + return sizes; + + const std::vector &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; +} + +/** + * \brief Retrieve the camera sensor resolution + * + * The camera sensor resolution is the active pixel area size, clamped to the + * maximum frame size the sensor can produce if it is smaller than the active + * pixel area. + * + * \todo Consider if it desirable to distinguish between the maximum resolution + * the sensor can produce (also including upscaled ones) and the actual pixel + * array size by splitting this function in two. + * + * \return The camera sensor resolution in pixels + */ +Size CameraSensor::resolution() const +{ + return std::min(sizes_.back(), activeArea_.size()); +} + +/** + * \fn CameraSensor::testPatternModes() + * \brief Retrieve all the supported test pattern modes of the camera sensor + * The test pattern mode values correspond to the controls::TestPattern control. + * + * \return The list of test pattern modes + */ + +/** + * \brief Set the test pattern mode for the camera sensor + * \param[in] mode The test pattern mode + * + * The new \a mode is applied to the sensor if it differs from the active test + * 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; +} + +/** + * \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 + * + * Media bus codes are selected from \a mbusCodes, which lists all acceptable + * codes in decreasing order of preference. Media bus codes supported by the + * sensor but not listed in \a mbusCodes are ignored. If none of the desired + * codes is supported, it returns an error. + * + * \a size indicates the desired size at the output of the sensor. This function + * selects the best media bus code and size supported by the sensor according + * to the following criteria. + * + * - The desired \a size shall fit in the sensor output size to avoid the need + * to up-scale. + * - The sensor output size shall match the desired aspect ratio to avoid the + * need to crop the field of view. + * - The sensor output size shall be as small as possible to lower the required + * bandwidth. + * - The desired \a size shall be supported by one of the media bus code listed + * in \a mbusCodes. + * + * When multiple media bus codes can produce the same size, the code at the + * lowest position in \a mbusCodes is selected. + * + * The use of this function is optional, as the above criteria may not match the + * needs of all pipeline handlers. Pipeline handlers may implement custom + * sensor format selection when needed. + * + * The returned sensor output format is guaranteed to be acceptable by the + * setFormat() function without any modification. + * + * \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 &mbusCodes, + const Size &size) const +{ + unsigned int desiredArea = size.width * size.height; + unsigned int bestArea = UINT_MAX; + float desiredRatio = static_cast(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(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; +} + +/** + * \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. + * Defaults to Identity. + * + * If flips are writable they are configured according to the desired Transform. + * Transform::Identity always corresponds to H/V flip being disabled if the + * controls are writable. Flips are set before the new format is applied as + * they can effectively change the Bayer pattern ordering. + * + * The ranges of any controls associated with the sensor are also updated. + * + * \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(!!(transform & Transform::HFlip))); + flipCtrls.set(V4L2_CID_VFLIP, + static_cast(!!(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; + + updateControlInfo(); + return 0; +} + +/** + * \brief Try the sensor output format + * \param[in] format The desired sensor output format + * + * The ranges of any controls associated with the sensor are not updated. + * + * \todo Add support for Transform by changing the format's Bayer ordering + * before calling subdev_->setFormat(). + * + * \return 0 on success or a negative error code otherwise + */ +int CameraSensor::tryFormat(V4L2SubdeviceFormat *format) const +{ + return subdev_->setFormat(pad_, format, + V4L2Subdevice::Whence::TryFormat); +} + +/** + * \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. + * Defaults to Identity + * \param[out] sensorFormat Format applied to the sensor (optional) + * + * Apply to the camera sensor the configuration \a config. + * + * \todo The configuration shall be fully populated and if any of the fields + * specified cannot be applied exactly, an error code is returned. + * + * \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 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; +} + +/** + * \brief Retrieve the supported V4L2 controls and their information + * + * Control information is updated automatically to reflect the current sensor + * configuration when the setFormat() function is called, without invalidating + * any iterator on the ControlInfoMap. A manual update can also be forced by + * calling the updateControlInfo() function for pipeline handlers that change + * the sensor configuration wihtout using setFormat(). + * + * \return A map of the V4L2 controls supported by the sensor + */ +const ControlInfoMap &CameraSensor::controls() const +{ + return subdev_->controls(); +} + +/** + * \brief Read V4L2 controls from the sensor + * \param[in] ids The list of controls to read, specified by their ID + * + * This function reads the value of all controls contained in \a ids, and + * returns their values as a ControlList. The control identifiers are defined by + * the V4L2 specification (V4L2_CID_*). + * + * If any control in \a ids is not supported by the device, is disabled (i.e. + * has the V4L2_CTRL_FLAG_DISABLED flag set), or if any other error occurs + * during validation of the requested controls, no control is read and this + * function returns an empty control list. + * + * \sa V4L2Device::getControls() + * + * \return The control values in a ControlList on success, or an empty list on + * error + */ +ControlList CameraSensor::getControls(const std::vector &ids) +{ + return subdev_->getControls(ids); +} + +/** + * \brief Write V4L2 controls to the sensor + * \param[in] ctrls The list of controls to write + * + * This function writes the value of all controls contained in \a ctrls, and + * stores the values actually applied to the device in the corresponding \a + * ctrls entry. The control identifiers are defined by the V4L2 specification + * (V4L2_CID_*). + * + * If any control in \a ctrls is not supported by the device, is disabled (i.e. + * has the V4L2_CTRL_FLAG_DISABLED flag set), is read-only, or if any other + * error occurs during validation of the requested controls, no control is + * written and this function returns -EINVAL. + * + * If an error occurs while writing the controls, the index of the first + * control that couldn't be written is returned. All controls below that index + * are written and their values are updated in \a ctrls, while all other + * controls are not written and their values are not changed. + * + * \sa V4L2Device::setControls() + * + * \return 0 on success or an error code otherwise + * \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::device() + * \brief Retrieve the camera sensor device + * \todo Remove this function by integrating DelayedControl with CameraSensor + * \return The camera sensor device + */ + +/** + * \fn CameraSensor::properties() + * \brief Retrieve the camera sensor properties + * \return The list of camera sensor properties + */ + +/** + * \brief Assemble and return the camera sensor info + * \param[out] info The camera sensor info + * + * This function fills \a info with information that describes the camera sensor + * and its current configuration. The information combines static data (such as + * the the sensor model or active pixel array size) and data specific to the + * current sensor configuration (such as the line length and pixel rate). + * + * Sensor information is only available for raw sensors. When called for a YUV + * sensor, this function returns -EINVAL. + * + * Pipeline handlers that do not change the sensor format using the setFormat() + * function may need to call updateControlInfo() beforehand, to ensure all the + * control ranges are up to date. + * + * \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 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(); + + const ControlInfo hblank = ctrls.infoMap()->at(V4L2_CID_HBLANK); + info->minLineLength = info->outputSize.width + hblank.min().get(); + info->maxLineLength = info->outputSize.width + hblank.max().get(); + + const ControlInfo vblank = ctrls.infoMap()->at(V4L2_CID_VBLANK); + info->minFrameLength = info->outputSize.height + vblank.min().get(); + info->maxFrameLength = info->outputSize.height + vblank.max().get(); + + return 0; +} + +/** + * \fn void CameraSensor::updateControlInfo() + * \brief Update the sensor's ControlInfoMap in case they have changed + * \sa V4L2Device::updateControlInfo() + */ +void CameraSensor::updateControlInfo() +{ + subdev_->updateControlInfo(); +} + +/** + * \fn CameraSensor::focusLens() + * \brief Retrieve the focus lens controller + * + * \return The focus lens controller. nullptr if no focus lens controller is + * connected to the sensor + */ + +/** + * \brief Compute the Transform that gives the requested \a orientation + * \param[inout] orientation The desired image orientation + * + * This function computes the Transform that the pipeline handler should apply + * to the CameraSensor to obtain the requested \a orientation. + * + * The intended caller of this function is the validate() implementation of + * pipeline handlers, that pass in the application requested + * CameraConfiguration::orientation and obtain a Transform to apply to the + * camera sensor, likely at configure() time. + * + * If the requested \a orientation cannot be obtained, the \a orientation + * parameter is adjusted to report the current image orientation and + * Transform::Identity is returned. + * + * If the requested \a orientation can be obtained, the function computes a + * Transform and does not adjust \a orientation. + * + * Pipeline handlers are expected to verify if \a orientation has been + * adjusted by this function and set the CameraConfiguration::status to + * Adjusted accordingly. + * + * \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; +} + +std::string CameraSensor::logPrefix() const +{ + return "'" + entity_->name() + "'"; +} + +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; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/sensor/camera_sensor_properties.cpp b/src/libcamera/sensor/camera_sensor_properties.cpp new file mode 100644 index 00000000..6e28b09e --- /dev/null +++ b/src/libcamera/sensor/camera_sensor_properties.cpp @@ -0,0 +1,272 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * camera_sensor_properties.cpp - Database of camera sensor properties + */ + +#include "libcamera/internal/camera_sensor_properties.h" + +#include + +#include + +#include + +/** + * \file camera_sensor_properties.h + * \brief Database of camera sensor properties + * + * The database of camera sensor properties collects static information about + * camera sensors that is not possible or desirable to retrieve from the device + * at run time. + * + * The database is indexed using the camera sensor model, as reported by the + * properties::Model property, and for each supported sensor it contains a + * list of properties. + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(CameraSensorProperties) + +/** + * \struct CameraSensorProperties + * \brief Database of camera sensor properties + * + * \var CameraSensorProperties::unitCellSize + * \brief The physical size of a pixel, including pixel edges, in nanometers. + * + * \var CameraSensorProperties::testPatternModes + * \brief Map that associates the TestPattern control value with the indexes of + * the corresponding sensor test pattern modes as returned by + * V4L2_CID_TEST_PATTERN. + */ + +/** + * \brief Retrieve the properties associated with a sensor + * \param sensor The sensor model name as reported by properties::Model + * \return A pointer to the CameraSensorProperties instance associated with a sensor + * or nullptr if the sensor is not supported + */ +const CameraSensorProperties *CameraSensorProperties::get(const std::string &sensor) +{ + static const std::map sensorProps = { + { "ar0521", { + .unitCellSize = { 2200, 2200 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeSolidColor, 1 }, + { controls::draft::TestPatternModeColorBars, 2 }, + { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, + }, + } }, + { "hi846", { + .unitCellSize = { 1120, 1120 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeSolidColor, 1 }, + { controls::draft::TestPatternModeColorBars, 2 }, + { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, + { controls::draft::TestPatternModePn9, 4 }, + /* + * No corresponding test pattern mode for: + * 5: "Gradient Horizontal" + * 6: "Gradient Vertical" + * 7: "Check Board" + * 8: "Slant Pattern" + * 9: "Resolution Pattern" + */ + }, + } }, + { "imx219", { + .unitCellSize = { 1120, 1120 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1 }, + { controls::draft::TestPatternModeSolidColor, 2 }, + { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, + { controls::draft::TestPatternModePn9, 4 }, + }, + } }, + { "imx258", { + .unitCellSize = { 1120, 1120 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeSolidColor, 1 }, + { controls::draft::TestPatternModeColorBars, 2 }, + { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, + { controls::draft::TestPatternModePn9, 4 }, + }, + } }, + { "imx290", { + .unitCellSize = { 2900, 2900 }, + .testPatternModes = {}, + } }, + { "imx296", { + .unitCellSize = { 3450, 3450 }, + .testPatternModes = {}, + } }, + { "imx327", { + .unitCellSize = { 2900, 2900 }, + .testPatternModes = {}, + } }, + { "imx477", { + .unitCellSize = { 1550, 1550 }, + .testPatternModes = {}, + } }, + { "imx519", { + .unitCellSize = { 1220, 1220 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeSolidColor, 2 }, + { controls::draft::TestPatternModePn9, 4 }, + /* + * The driver reports ColorBars and ColorBarsFadeToGray as well but + * these two patterns do not comply with MIPI CCS v1.1 (Section 10.1). + */ + }, + } }, + { "imx708", { + .unitCellSize = { 1400, 1400 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1 }, + { controls::draft::TestPatternModeSolidColor, 2 }, + { controls::draft::TestPatternModeColorBarsFadeToGray, 3 }, + { controls::draft::TestPatternModePn9, 4 }, + }, + } }, + { "ov2685", { + .unitCellSize = { 1750, 1750 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1}, + { controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, + /* + * No corresponding test pattern mode for: + * 3: "Random Data" + * 4: "Black White Square" + * 5: "Color Square" + */ + }, + } }, + { "ov2740", { + .unitCellSize = { 1400, 1400 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1}, + }, + } }, + { "ov4689", { + .unitCellSize = { 2000, 2000 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1}, + { controls::draft::TestPatternModeColorBarsFadeToGray, 2}, + /* + * No corresponding test patterns in + * MIPI CCS specification for sensor's + * colorBarType2 and colorBarType3. + */ + }, + } }, + { "ov5640", { + .unitCellSize = { 1400, 1400 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1 }, + }, + } }, + { "ov5647", { + .unitCellSize = { 1400, 1400 }, + .testPatternModes = {}, + } }, + { "ov5670", { + .unitCellSize = { 1120, 1120 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1 }, + }, + } }, + { "ov5675", { + .unitCellSize = { 1120, 1120 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1 }, + }, + } }, + { "ov5693", { + .unitCellSize = { 1400, 1400 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 2 }, + /* + * No corresponding test pattern mode for + * 1: "Random data" and 3: "Colour Bars with + * Rolling Bar". + */ + }, + } }, + { "ov64a40", { + .unitCellSize = { 1008, 1008 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1 }, + { controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, + /* + * No corresponding test patter mode + * 3: "Vertical Color Bar Type 3", + * 4: "Vertical Color Bar Type 4" + */ + }, + } }, + { "ov8858", { + .unitCellSize = { 1120, 1120 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1 }, + { controls::draft::TestPatternModeColorBarsFadeToGray, 2 }, + /* + * No corresponding test patter mode + * 3: "Vertical Color Bar Type 3", + * 4: "Vertical Color Bar Type 4" + */ + }, + } }, + { "ov8865", { + .unitCellSize = { 1400, 1400 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 2 }, + /* + * No corresponding test pattern mode for: + * 1: "Random data" + * 3: "Color bars with rolling bar" + * 4: "Color squares" + * 5: "Color squares with rolling bar" + */ + }, + } }, + { "ov13858", { + .unitCellSize = { 1120, 1120 }, + .testPatternModes = { + { controls::draft::TestPatternModeOff, 0 }, + { controls::draft::TestPatternModeColorBars, 1 }, + }, + } }, + }; + + const auto it = sensorProps.find(sensor); + if (it == sensorProps.end()) { + LOG(CameraSensorProperties, Warning) + << "No static properties available for '" << sensor << "'"; + LOG(CameraSensorProperties, Warning) + << "Please consider updating the camera sensor properties database"; + return nullptr; + } + + return &it->second; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/sensor/meson.build b/src/libcamera/sensor/meson.build new file mode 100644 index 00000000..bf4b131a --- /dev/null +++ b/src/libcamera/sensor/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: CC0-1.0 + +libcamera_sources += files([ + 'camera_sensor.cpp', + 'camera_sensor_properties.cpp', +]) -- cgit v1.2.1