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/sensor/camera_sensor.cpp | 1203 ++++++++++++++++++++++++++++++++ 1 file changed, 1203 insertions(+) create mode 100644 src/libcamera/sensor/camera_sensor.cpp (limited to 'src/libcamera/sensor/camera_sensor.cpp') 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 */ -- cgit v1.2.1