summaryrefslogtreecommitdiff
path: root/src/ipa/rpi/vc4
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipa/rpi/vc4')
-rw-r--r--src/ipa/rpi/vc4/meson.build3
-rw-r--r--src/ipa/rpi/vc4/raspberrypi.cpp1839
-rw-r--r--src/ipa/rpi/vc4/vc4.cpp540
3 files changed, 542 insertions, 1840 deletions
diff --git a/src/ipa/rpi/vc4/meson.build b/src/ipa/rpi/vc4/meson.build
index df01c150..590e9197 100644
--- a/src/ipa/rpi/vc4/meson.build
+++ b/src/ipa/rpi/vc4/meson.build
@@ -9,6 +9,7 @@ vc4_ipa_deps = [
vc4_ipa_libs = [
rpi_ipa_cam_helper_lib,
+ rpi_ipa_common_lib,
rpi_ipa_controller_lib
]
@@ -18,7 +19,7 @@ vc4_ipa_includes = [
]
vc4_ipa_sources = files([
- 'raspberrypi.cpp',
+ 'vc4.cpp',
])
vc4_ipa_includes += include_directories('..')
diff --git a/src/ipa/rpi/vc4/raspberrypi.cpp b/src/ipa/rpi/vc4/raspberrypi.cpp
deleted file mode 100644
index 17ea5c04..00000000
--- a/src/ipa/rpi/vc4/raspberrypi.cpp
+++ /dev/null
@@ -1,1839 +0,0 @@
-/* SPDX-License-Identifier: BSD-2-Clause */
-/*
- * Copyright (C) 2019-2021, Raspberry Pi Ltd
- *
- * rpi.cpp - Raspberry Pi Image Processing Algorithms
- */
-
-#include <algorithm>
-#include <array>
-#include <cstring>
-#include <deque>
-#include <fcntl.h>
-#include <math.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <vector>
-
-#include <linux/bcm2835-isp.h>
-
-#include <libcamera/base/log.h>
-#include <libcamera/base/shared_fd.h>
-#include <libcamera/base/span.h>
-
-#include <libcamera/control_ids.h>
-#include <libcamera/controls.h>
-#include <libcamera/framebuffer.h>
-#include <libcamera/request.h>
-
-#include <libcamera/ipa/ipa_interface.h>
-#include <libcamera/ipa/ipa_module_info.h>
-#include <libcamera/ipa/raspberrypi_ipa_interface.h>
-
-#include "libcamera/internal/mapped_framebuffer.h"
-
-#include "cam_helper/cam_helper.h"
-#include "controller/af_algorithm.h"
-#include "controller/af_status.h"
-#include "controller/agc_algorithm.h"
-#include "controller/agc_status.h"
-#include "controller/alsc_status.h"
-#include "controller/awb_algorithm.h"
-#include "controller/awb_status.h"
-#include "controller/black_level_status.h"
-#include "controller/ccm_algorithm.h"
-#include "controller/ccm_status.h"
-#include "controller/contrast_algorithm.h"
-#include "controller/contrast_status.h"
-#include "controller/controller.h"
-#include "controller/denoise_algorithm.h"
-#include "controller/denoise_status.h"
-#include "controller/dpc_status.h"
-#include "controller/geq_status.h"
-#include "controller/lux_status.h"
-#include "controller/metadata.h"
-#include "controller/sharpen_algorithm.h"
-#include "controller/sharpen_status.h"
-#include "controller/statistics.h"
-
-namespace libcamera {
-
-using namespace std::literals::chrono_literals;
-using utils::Duration;
-
-/* Number of metadata objects available in the context list. */
-constexpr unsigned int numMetadataContexts = 16;
-
-/* Number of frame length times to hold in the queue. */
-constexpr unsigned int FrameLengthsQueueSize = 10;
-
-/* Configure the sensor with these values initially. */
-constexpr double defaultAnalogueGain = 1.0;
-constexpr Duration defaultExposureTime = 20.0ms;
-constexpr Duration defaultMinFrameDuration = 1.0s / 30.0;
-constexpr Duration defaultMaxFrameDuration = 250.0s;
-
-/*
- * Determine the minimum allowable inter-frame duration to run the controller
- * algorithms. If the pipeline handler provider frames at a rate higher than this,
- * we rate-limit the controller Prepare() and Process() calls to lower than or
- * equal to this rate.
- */
-constexpr Duration controllerMinFrameDuration = 1.0s / 30.0;
-
-/* List of controls handled by the Raspberry Pi IPA */
-static const ControlInfoMap::Map ipaControls{
- { &controls::AeEnable, ControlInfo(false, true) },
- { &controls::ExposureTime, ControlInfo(0, 66666) },
- { &controls::AnalogueGain, ControlInfo(1.0f, 16.0f) },
- { &controls::AeMeteringMode, ControlInfo(controls::AeMeteringModeValues) },
- { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
- { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
- { &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
- { &controls::AwbEnable, ControlInfo(false, true) },
- { &controls::ColourGains, ControlInfo(0.0f, 32.0f) },
- { &controls::AwbMode, ControlInfo(controls::AwbModeValues) },
- { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
- { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
- { &controls::Saturation, ControlInfo(0.0f, 32.0f, 1.0f) },
- { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
- { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) },
- { &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) },
- { &controls::FrameDurationLimits, ControlInfo(INT64_C(33333), INT64_C(120000)) },
- { &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) }
-};
-
-/* IPA controls handled conditionally, if the lens has a focus control */
-static const ControlInfoMap::Map ipaAfControls{
- { &controls::AfMode, ControlInfo(controls::AfModeValues) },
- { &controls::AfRange, ControlInfo(controls::AfRangeValues) },
- { &controls::AfSpeed, ControlInfo(controls::AfSpeedValues) },
- { &controls::AfMetering, ControlInfo(controls::AfMeteringValues) },
- { &controls::AfWindows, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) },
- { &controls::AfTrigger, ControlInfo(controls::AfTriggerValues) },
- { &controls::AfPause, ControlInfo(controls::AfPauseValues) },
- { &controls::LensPosition, ControlInfo(0.0f, 32.0f, 1.0f) }
-};
-
-LOG_DEFINE_CATEGORY(IPARPI)
-
-namespace ipa::RPi {
-
-class IPARPi : public IPARPiInterface
-{
-public:
- IPARPi()
- : controller_(), frameCount_(0), checkCount_(0), mistrustCount_(0),
- lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true),
- lastTimeout_(0s)
- {
- }
-
- ~IPARPi()
- {
- if (lsTable_)
- munmap(lsTable_, MaxLsGridSize);
- }
-
- int init(const IPASettings &settings, const InitParams &params, InitResult *result) override;
- void start(const ControlList &controls, StartResult *result) override;
- void stop() override {}
-
- int configure(const IPACameraSensorInfo &sensorInfo, const ConfigParams &params,
- ConfigResult *result) override;
- void mapBuffers(const std::vector<IPABuffer> &buffers) override;
- void unmapBuffers(const std::vector<unsigned int> &ids) override;
- void prepareIsp(const PrepareParams &params) override;
- void processStats(const ProcessParams &params) override;
-
-private:
- void setMode(const IPACameraSensorInfo &sensorInfo);
- bool validateSensorControls();
- bool validateIspControls();
- bool validateLensControls();
- void applyControls(const ControlList &controls);
- void prepare(const PrepareParams &params);
- void reportMetadata(unsigned int ipaContext);
- void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
- RPiController::StatisticsPtr fillStatistics(bcm2835_isp_stats *stats) const;
- void process(unsigned int bufferId, unsigned int ipaContext);
- void setCameraTimeoutValue();
- void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration);
- void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
- void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
- void applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls);
- void applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls);
- void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls);
- void applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls);
- void applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls);
- void applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls);
- void applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls);
- void applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls);
- void applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls);
- void applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls);
- void resampleTable(uint16_t dest[], const std::vector<double> &src, int destW, int destH);
-
- std::map<unsigned int, MappedFrameBuffer> buffers_;
-
- ControlInfoMap sensorCtrls_;
- ControlInfoMap ispCtrls_;
- ControlInfoMap lensCtrls_;
- bool lensPresent_;
- ControlList libcameraMetadata_;
-
- /* Camera sensor params. */
- CameraMode mode_;
-
- /* Raspberry Pi controller specific defines. */
- std::unique_ptr<RPiController::CamHelper> helper_;
- RPiController::Controller controller_;
- std::array<RPiController::Metadata, numMetadataContexts> rpiMetadata_;
-
- /*
- * We count frames to decide if the frame must be hidden (e.g. from
- * display) or mistrusted (i.e. not given to the control algos).
- */
- uint64_t frameCount_;
-
- /* For checking the sequencing of Prepare/Process calls. */
- uint64_t checkCount_;
-
- /* How many frames we should avoid running control algos on. */
- unsigned int mistrustCount_;
-
- /* Number of frames that need to be dropped on startup. */
- unsigned int dropFrameCount_;
-
- /* Frame timestamp for the last run of the controller. */
- uint64_t lastRunTimestamp_;
-
- /* Do we run a Controller::process() for this frame? */
- bool processPending_;
-
- /* LS table allocation passed in from the pipeline handler. */
- SharedFD lsTableHandle_;
- void *lsTable_;
-
- /* Distinguish the first camera start from others. */
- bool firstStart_;
-
- /* Frame duration (1/fps) limits. */
- Duration minFrameDuration_;
- Duration maxFrameDuration_;
-
- /* Track the frame length times over FrameLengthsQueueSize frames. */
- std::deque<Duration> frameLengths_;
- Duration lastTimeout_;
-};
-
-int IPARPi::init(const IPASettings &settings, const InitParams &params, InitResult *result)
-{
- /*
- * Load the "helper" for this sensor. This tells us all the device specific stuff
- * that the kernel driver doesn't. We only do this the first time; we don't need
- * to re-parse the metadata after a simple mode-switch for no reason.
- */
- helper_ = std::unique_ptr<RPiController::CamHelper>(RPiController::CamHelper::create(settings.sensorModel));
- if (!helper_) {
- LOG(IPARPI, Error) << "Could not create camera helper for "
- << settings.sensorModel;
- return -EINVAL;
- }
-
- /*
- * Pass out the sensor config to the pipeline handler in order
- * to setup the staggered writer class.
- */
- int gainDelay, exposureDelay, vblankDelay, hblankDelay, sensorMetadata;
- helper_->getDelays(exposureDelay, gainDelay, vblankDelay, hblankDelay);
- sensorMetadata = helper_->sensorEmbeddedDataPresent();
-
- result->sensorConfig.gainDelay = gainDelay;
- result->sensorConfig.exposureDelay = exposureDelay;
- result->sensorConfig.vblankDelay = vblankDelay;
- result->sensorConfig.hblankDelay = hblankDelay;
- result->sensorConfig.sensorMetadata = sensorMetadata;
-
- /* Load the tuning file for this sensor. */
- int ret = controller_.read(settings.configurationFile.c_str());
- if (ret) {
- LOG(IPARPI, Error)
- << "Failed to load tuning data file "
- << settings.configurationFile;
- return ret;
- }
-
- const std::string &target = controller_.getTarget();
- if (target != "bcm2835") {
- LOG(IPARPI, Error)
- << "Tuning data file target returned \"" << target << "\""
- << ", expected \"bcm2835\"";
- return -EINVAL;
- }
-
- lensPresent_ = params.lensPresent;
-
- controller_.initialise();
-
- /* Return the controls handled by the IPA */
- ControlInfoMap::Map ctrlMap = ipaControls;
- if (lensPresent_)
- ctrlMap.merge(ControlInfoMap::Map(ipaAfControls));
- result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);
-
- return 0;
-}
-
-void IPARPi::start(const ControlList &controls, StartResult *result)
-{
- RPiController::Metadata metadata;
-
- if (!controls.empty()) {
- /* We have been given some controls to action before start. */
- applyControls(controls);
- }
-
- controller_.switchMode(mode_, &metadata);
-
- /* Reset the frame lengths queue state. */
- lastTimeout_ = 0s;
- frameLengths_.clear();
- frameLengths_.resize(FrameLengthsQueueSize, 0s);
-
- /* SwitchMode may supply updated exposure/gain values to use. */
- AgcStatus agcStatus;
- agcStatus.shutterTime = 0.0s;
- agcStatus.analogueGain = 0.0;
-
- metadata.get("agc.status", agcStatus);
- if (agcStatus.shutterTime && agcStatus.analogueGain) {
- ControlList ctrls(sensorCtrls_);
- applyAGC(&agcStatus, ctrls);
- result->controls = std::move(ctrls);
- setCameraTimeoutValue();
- }
-
- /*
- * Initialise frame counts, and decide how many frames must be hidden or
- * "mistrusted", which depends on whether this is a startup from cold,
- * or merely a mode switch in a running system.
- */
- frameCount_ = 0;
- checkCount_ = 0;
- if (firstStart_) {
- dropFrameCount_ = helper_->hideFramesStartup();
- mistrustCount_ = helper_->mistrustFramesStartup();
-
- /*
- * Query the AGC/AWB for how many frames they may take to
- * converge sufficiently. Where these numbers are non-zero
- * we must allow for the frames with bad statistics
- * (mistrustCount_) that they won't see. But if zero (i.e.
- * no convergence necessary), no frames need to be dropped.
- */
- unsigned int agcConvergenceFrames = 0;
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- if (agc) {
- agcConvergenceFrames = agc->getConvergenceFrames();
- if (agcConvergenceFrames)
- agcConvergenceFrames += mistrustCount_;
- }
-
- unsigned int awbConvergenceFrames = 0;
- RPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(
- controller_.getAlgorithm("awb"));
- if (awb) {
- awbConvergenceFrames = awb->getConvergenceFrames();
- if (awbConvergenceFrames)
- awbConvergenceFrames += mistrustCount_;
- }
-
- dropFrameCount_ = std::max({ dropFrameCount_, agcConvergenceFrames, awbConvergenceFrames });
- LOG(IPARPI, Debug) << "Drop " << dropFrameCount_ << " frames on startup";
- } else {
- dropFrameCount_ = helper_->hideFramesModeSwitch();
- mistrustCount_ = helper_->mistrustFramesModeSwitch();
- }
-
- result->dropFrameCount = dropFrameCount_;
-
- firstStart_ = false;
- lastRunTimestamp_ = 0;
-}
-
-void IPARPi::setMode(const IPACameraSensorInfo &sensorInfo)
-{
- mode_.bitdepth = sensorInfo.bitsPerPixel;
- mode_.width = sensorInfo.outputSize.width;
- mode_.height = sensorInfo.outputSize.height;
- mode_.sensorWidth = sensorInfo.activeAreaSize.width;
- mode_.sensorHeight = sensorInfo.activeAreaSize.height;
- mode_.cropX = sensorInfo.analogCrop.x;
- mode_.cropY = sensorInfo.analogCrop.y;
- mode_.pixelRate = sensorInfo.pixelRate;
-
- /*
- * Calculate scaling parameters. The scale_[xy] factors are determined
- * by the ratio between the crop rectangle size and the output size.
- */
- mode_.scaleX = sensorInfo.analogCrop.width / sensorInfo.outputSize.width;
- mode_.scaleY = sensorInfo.analogCrop.height / sensorInfo.outputSize.height;
-
- /*
- * We're not told by the pipeline handler how scaling is split between
- * binning and digital scaling. For now, as a heuristic, assume that
- * downscaling up to 2 is achieved through binning, and that any
- * additional scaling is achieved through digital scaling.
- *
- * \todo Get the pipeline handle to provide the full data
- */
- mode_.binX = std::min(2, static_cast<int>(mode_.scaleX));
- mode_.binY = std::min(2, static_cast<int>(mode_.scaleY));
-
- /* The noise factor is the square root of the total binning factor. */
- mode_.noiseFactor = sqrt(mode_.binX * mode_.binY);
-
- /*
- * Calculate the line length as the ratio between the line length in
- * pixels and the pixel rate.
- */
- mode_.minLineLength = sensorInfo.minLineLength * (1.0s / sensorInfo.pixelRate);
- mode_.maxLineLength = sensorInfo.maxLineLength * (1.0s / sensorInfo.pixelRate);
-
- /*
- * Set the frame length limits for the mode to ensure exposure and
- * framerate calculations are clipped appropriately.
- */
- mode_.minFrameLength = sensorInfo.minFrameLength;
- mode_.maxFrameLength = sensorInfo.maxFrameLength;
-
- /* Store these for convenience. */
- mode_.minFrameDuration = mode_.minFrameLength * mode_.minLineLength;
- mode_.maxFrameDuration = mode_.maxFrameLength * mode_.maxLineLength;
-
- /*
- * Some sensors may have different sensitivities in different modes;
- * the CamHelper will know the correct value.
- */
- mode_.sensitivity = helper_->getModeSensitivity(mode_);
-
- const ControlInfo &gainCtrl = sensorCtrls_.at(V4L2_CID_ANALOGUE_GAIN);
- const ControlInfo &shutterCtrl = sensorCtrls_.at(V4L2_CID_EXPOSURE);
-
- mode_.minAnalogueGain = helper_->gain(gainCtrl.min().get<int32_t>());
- mode_.maxAnalogueGain = helper_->gain(gainCtrl.max().get<int32_t>());
-
- /* Shutter speed is calculated based on the limits of the frame durations. */
- mode_.minShutter = helper_->exposure(shutterCtrl.min().get<int32_t>(), mode_.minLineLength);
- mode_.maxShutter = Duration::max();
- helper_->getBlanking(mode_.maxShutter,
- mode_.minFrameDuration, mode_.maxFrameDuration);
-}
-
-int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, const ConfigParams &params,
- ConfigResult *result)
-{
- sensorCtrls_ = params.sensorControls;
- ispCtrls_ = params.ispControls;
-
- if (!validateSensorControls()) {
- LOG(IPARPI, Error) << "Sensor control validation failed.";
- return -1;
- }
-
- if (!validateIspControls()) {
- LOG(IPARPI, Error) << "ISP control validation failed.";
- return -1;
- }
-
- if (lensPresent_) {
- lensCtrls_ = params.lensControls;
- if (!validateLensControls()) {
- LOG(IPARPI, Warning) << "Lens validation failed, "
- << "no lens control will be available.";
- lensPresent_ = false;
- }
- }
-
- /* Setup a metadata ControlList to output metadata. */
- libcameraMetadata_ = ControlList(controls::controls);
-
- /* Re-assemble camera mode using the sensor info. */
- setMode(sensorInfo);
-
- mode_.transform = static_cast<libcamera::Transform>(params.transform);
-
- /* Store the lens shading table pointer and handle if available. */
- if (params.lsTableHandle.isValid()) {
- /* Remove any previous table, if there was one. */
- if (lsTable_) {
- munmap(lsTable_, MaxLsGridSize);
- lsTable_ = nullptr;
- }
-
- /* Map the LS table buffer into user space. */
- lsTableHandle_ = std::move(params.lsTableHandle);
- if (lsTableHandle_.isValid()) {
- lsTable_ = mmap(nullptr, MaxLsGridSize, PROT_READ | PROT_WRITE,
- MAP_SHARED, lsTableHandle_.get(), 0);
-
- if (lsTable_ == MAP_FAILED) {
- LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table.";
- lsTable_ = nullptr;
- }
- }
- }
-
- /* Pass the camera mode to the CamHelper to setup algorithms. */
- helper_->setCameraMode(mode_);
-
- /*
- * Initialise this ControlList correctly, even if empty, in case the IPA is
- * running is isolation mode (passing the ControlList through the IPC layer).
- */
- ControlList ctrls(sensorCtrls_);
-
- /* The pipeline handler passes out the mode's sensitivity. */
- result->modeSensitivity = mode_.sensitivity;
-
- if (firstStart_) {
- /* Supply initial values for frame durations. */
- applyFrameDurations(defaultMinFrameDuration, defaultMaxFrameDuration);
-
- /* Supply initial values for gain and exposure. */
- AgcStatus agcStatus;
- agcStatus.shutterTime = defaultExposureTime;
- agcStatus.analogueGain = defaultAnalogueGain;
- applyAGC(&agcStatus, ctrls);
- }
-
- result->controls = std::move(ctrls);
-
- /*
- * Apply the correct limits to the exposure, gain and frame duration controls
- * based on the current sensor mode.
- */
- ControlInfoMap::Map ctrlMap = ipaControls;
- ctrlMap[&controls::FrameDurationLimits] =
- ControlInfo(static_cast<int64_t>(mode_.minFrameDuration.get<std::micro>()),
- static_cast<int64_t>(mode_.maxFrameDuration.get<std::micro>()));
-
- ctrlMap[&controls::AnalogueGain] =
- ControlInfo(static_cast<float>(mode_.minAnalogueGain),
- static_cast<float>(mode_.maxAnalogueGain));
-
- ctrlMap[&controls::ExposureTime] =
- ControlInfo(static_cast<int32_t>(mode_.minShutter.get<std::micro>()),
- static_cast<int32_t>(mode_.maxShutter.get<std::micro>()));
-
- /* Declare Autofocus controls, only if we have a controllable lens */
- if (lensPresent_)
- ctrlMap.merge(ControlInfoMap::Map(ipaAfControls));
-
- result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);
- return 0;
-}
-
-void IPARPi::mapBuffers(const std::vector<IPABuffer> &buffers)
-{
- for (const IPABuffer &buffer : buffers) {
- const FrameBuffer fb(buffer.planes);
- buffers_.emplace(buffer.id,
- MappedFrameBuffer(&fb, MappedFrameBuffer::MapFlag::ReadWrite));
- }
-}
-
-void IPARPi::unmapBuffers(const std::vector<unsigned int> &ids)
-{
- for (unsigned int id : ids) {
- auto it = buffers_.find(id);
- if (it == buffers_.end())
- continue;
-
- buffers_.erase(id);
- }
-}
-
-void IPARPi::processStats(const ProcessParams &params)
-{
- unsigned int context = params.ipaContext % rpiMetadata_.size();
-
- if (++checkCount_ != frameCount_) /* assert here? */
- LOG(IPARPI, Error) << "WARNING: Prepare/Process mismatch!!!";
- if (processPending_ && frameCount_ > mistrustCount_)
- process(params.buffers.stats, context);
-
- reportMetadata(context);
- processStatsComplete.emit(params.buffers);
-}
-
-
-void IPARPi::prepareIsp(const PrepareParams &params)
-{
- applyControls(params.requestControls);
-
- /*
- * At start-up, or after a mode-switch, we may want to
- * avoid running the control algos for a few frames in case
- * they are "unreliable".
- */
- prepare(params);
- frameCount_++;
-
- /* Ready to push the input buffer into the ISP. */
- prepareIspComplete.emit(params.buffers);
-}
-
-void IPARPi::reportMetadata(unsigned int ipaContext)
-{
- RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
- std::unique_lock<RPiController::Metadata> lock(rpiMetadata);
-
- /*
- * Certain information about the current frame and how it will be
- * processed can be extracted and placed into the libcamera metadata
- * buffer, where an application could query it.
- */
- DeviceStatus *deviceStatus = rpiMetadata.getLocked<DeviceStatus>("device.status");
- if (deviceStatus) {
- libcameraMetadata_.set(controls::ExposureTime,
- deviceStatus->shutterSpeed.get<std::micro>());
- libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogueGain);
- libcameraMetadata_.set(controls::FrameDuration,
- helper_->exposure(deviceStatus->frameLength, deviceStatus->lineLength).get<std::micro>());
- if (deviceStatus->sensorTemperature)
- libcameraMetadata_.set(controls::SensorTemperature, *deviceStatus->sensorTemperature);
- if (deviceStatus->lensPosition)
- libcameraMetadata_.set(controls::LensPosition, *deviceStatus->lensPosition);
- }
-
- AgcStatus *agcStatus = rpiMetadata.getLocked<AgcStatus>("agc.status");
- if (agcStatus) {
- libcameraMetadata_.set(controls::AeLocked, agcStatus->locked);
- libcameraMetadata_.set(controls::DigitalGain, agcStatus->digitalGain);
- }
-
- LuxStatus *luxStatus = rpiMetadata.getLocked<LuxStatus>("lux.status");
- if (luxStatus)
- libcameraMetadata_.set(controls::Lux, luxStatus->lux);
-
- AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
- if (awbStatus) {
- libcameraMetadata_.set(controls::ColourGains, { static_cast<float>(awbStatus->gainR),
- static_cast<float>(awbStatus->gainB) });
- libcameraMetadata_.set(controls::ColourTemperature, awbStatus->temperatureK);
- }
-
- BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked<BlackLevelStatus>("black_level.status");
- if (blackLevelStatus)
- libcameraMetadata_.set(controls::SensorBlackLevels,
- { static_cast<int32_t>(blackLevelStatus->blackLevelR),
- static_cast<int32_t>(blackLevelStatus->blackLevelG),
- static_cast<int32_t>(blackLevelStatus->blackLevelG),
- static_cast<int32_t>(blackLevelStatus->blackLevelB) });
-
- RPiController::FocusRegions *focusStatus =
- rpiMetadata.getLocked<RPiController::FocusRegions>("focus.status");
- if (focusStatus) {
- /*
- * Calculate the average FoM over the central (symmetric) positions
- * to give an overall scene FoM. This can change later if it is
- * not deemed suitable.
- */
- libcamera::Size size = focusStatus->size();
- unsigned rows = size.height;
- unsigned cols = size.width;
-
- uint64_t sum = 0;
- unsigned int numRegions = 0;
- for (unsigned r = rows / 3; r < rows - rows / 3; ++r) {
- for (unsigned c = cols / 4; c < cols - cols / 4; ++c) {
- sum += focusStatus->get({ (int)c, (int)r }).val;
- numRegions++;
- }
- }
-
- uint32_t focusFoM = (sum / numRegions) >> 16;
- libcameraMetadata_.set(controls::FocusFoM, focusFoM);
- }
-
- CcmStatus *ccmStatus = rpiMetadata.getLocked<CcmStatus>("ccm.status");
- if (ccmStatus) {
- float m[9];
- for (unsigned int i = 0; i < 9; i++)
- m[i] = ccmStatus->matrix[i];
- libcameraMetadata_.set(controls::ColourCorrectionMatrix, m);
- }
-
- const AfStatus *afStatus = rpiMetadata.getLocked<AfStatus>("af.status");
- if (afStatus) {
- int32_t s, p;
- switch (afStatus->state) {
- case AfState::Scanning:
- s = controls::AfStateScanning;
- break;
- case AfState::Focused:
- s = controls::AfStateFocused;
- break;
- case AfState::Failed:
- s = controls::AfStateFailed;
- break;
- default:
- s = controls::AfStateIdle;
- }
- switch (afStatus->pauseState) {
- case AfPauseState::Pausing:
- p = controls::AfPauseStatePausing;
- break;
- case AfPauseState::Paused:
- p = controls::AfPauseStatePaused;
- break;
- default:
- p = controls::AfPauseStateRunning;
- }
- libcameraMetadata_.set(controls::AfState, s);
- libcameraMetadata_.set(controls::AfPauseState, p);
- }
-
- metadataReady.emit(libcameraMetadata_);
-}
-
-bool IPARPi::validateSensorControls()
-{
- static const uint32_t ctrls[] = {
- V4L2_CID_ANALOGUE_GAIN,
- V4L2_CID_EXPOSURE,
- V4L2_CID_VBLANK,
- V4L2_CID_HBLANK,
- };
-
- for (auto c : ctrls) {
- if (sensorCtrls_.find(c) == sensorCtrls_.end()) {
- LOG(IPARPI, Error) << "Unable to find sensor control "
- << utils::hex(c);
- return false;
- }
- }
-
- return true;
-}
-
-bool IPARPi::validateIspControls()
-{
- static const uint32_t ctrls[] = {
- V4L2_CID_RED_BALANCE,
- V4L2_CID_BLUE_BALANCE,
- V4L2_CID_DIGITAL_GAIN,
- V4L2_CID_USER_BCM2835_ISP_CC_MATRIX,
- V4L2_CID_USER_BCM2835_ISP_GAMMA,
- V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL,
- V4L2_CID_USER_BCM2835_ISP_GEQ,
- V4L2_CID_USER_BCM2835_ISP_DENOISE,
- V4L2_CID_USER_BCM2835_ISP_SHARPEN,
- V4L2_CID_USER_BCM2835_ISP_DPC,
- V4L2_CID_USER_BCM2835_ISP_LENS_SHADING,
- V4L2_CID_USER_BCM2835_ISP_CDN,
- };
-
- for (auto c : ctrls) {
- if (ispCtrls_.find(c) == ispCtrls_.end()) {
- LOG(IPARPI, Error) << "Unable to find ISP control "
- << utils::hex(c);
- return false;
- }
- }
-
- return true;
-}
-
-bool IPARPi::validateLensControls()
-{
- if (lensCtrls_.find(V4L2_CID_FOCUS_ABSOLUTE) == lensCtrls_.end()) {
- LOG(IPARPI, Error) << "Unable to find Lens control V4L2_CID_FOCUS_ABSOLUTE";
- return false;
- }
-
- return true;
-}
-
-/*
- * Converting between enums (used in the libcamera API) and the names that
- * we use to identify different modes. Unfortunately, the conversion tables
- * must be kept up-to-date by hand.
- */
-static const std::map<int32_t, std::string> MeteringModeTable = {
- { controls::MeteringCentreWeighted, "centre-weighted" },
- { controls::MeteringSpot, "spot" },
- { controls::MeteringMatrix, "matrix" },
- { controls::MeteringCustom, "custom" },
-};
-
-static const std::map<int32_t, std::string> ConstraintModeTable = {
- { controls::ConstraintNormal, "normal" },
- { controls::ConstraintHighlight, "highlight" },
- { controls::ConstraintShadows, "shadows" },
- { controls::ConstraintCustom, "custom" },
-};
-
-static const std::map<int32_t, std::string> ExposureModeTable = {
- { controls::ExposureNormal, "normal" },
- { controls::ExposureShort, "short" },
- { controls::ExposureLong, "long" },
- { controls::ExposureCustom, "custom" },
-};
-
-static const std::map<int32_t, std::string> AwbModeTable = {
- { controls::AwbAuto, "auto" },
- { controls::AwbIncandescent, "incandescent" },
- { controls::AwbTungsten, "tungsten" },
- { controls::AwbFluorescent, "fluorescent" },
- { controls::AwbIndoor, "indoor" },
- { controls::AwbDaylight, "daylight" },
- { controls::AwbCloudy, "cloudy" },
- { controls::AwbCustom, "custom" },
-};
-
-static const std::map<int32_t, RPiController::DenoiseMode> DenoiseModeTable = {
- { controls::draft::NoiseReductionModeOff, RPiController::DenoiseMode::Off },
- { controls::draft::NoiseReductionModeFast, RPiController::DenoiseMode::ColourFast },
- { controls::draft::NoiseReductionModeHighQuality, RPiController::DenoiseMode::ColourHighQuality },
- { controls::draft::NoiseReductionModeMinimal, RPiController::DenoiseMode::ColourOff },
- { controls::draft::NoiseReductionModeZSL, RPiController::DenoiseMode::ColourHighQuality },
-};
-
-static const std::map<int32_t, RPiController::AfAlgorithm::AfMode> AfModeTable = {
- { controls::AfModeManual, RPiController::AfAlgorithm::AfModeManual },
- { controls::AfModeAuto, RPiController::AfAlgorithm::AfModeAuto },
- { controls::AfModeContinuous, RPiController::AfAlgorithm::AfModeContinuous },
-};
-
-static const std::map<int32_t, RPiController::AfAlgorithm::AfRange> AfRangeTable = {
- { controls::AfRangeNormal, RPiController::AfAlgorithm::AfRangeNormal },
- { controls::AfRangeMacro, RPiController::AfAlgorithm::AfRangeMacro },
- { controls::AfRangeFull, RPiController::AfAlgorithm::AfRangeFull },
-};
-
-static const std::map<int32_t, RPiController::AfAlgorithm::AfPause> AfPauseTable = {
- { controls::AfPauseImmediate, RPiController::AfAlgorithm::AfPauseImmediate },
- { controls::AfPauseDeferred, RPiController::AfAlgorithm::AfPauseDeferred },
- { controls::AfPauseResume, RPiController::AfAlgorithm::AfPauseResume },
-};
-
-void IPARPi::applyControls(const ControlList &controls)
-{
- using RPiController::AfAlgorithm;
-
- /* Clear the return metadata buffer. */
- libcameraMetadata_.clear();
-
- /* Because some AF controls are mode-specific, handle AF mode change first. */
- if (controls.contains(controls::AF_MODE)) {
- AfAlgorithm *af = dynamic_cast<AfAlgorithm *>(controller_.getAlgorithm("af"));
- if (!af) {
- LOG(IPARPI, Warning)
- << "Could not set AF_MODE - no AF algorithm";
- }
-
- int32_t idx = controls.get(controls::AF_MODE).get<int32_t>();
- auto mode = AfModeTable.find(idx);
- if (mode == AfModeTable.end()) {
- LOG(IPARPI, Error) << "AF mode " << idx
- << " not recognised";
- } else
- af->setMode(mode->second);
- }
-
- /* Iterate over controls */
- for (auto const &ctrl : controls) {
- LOG(IPARPI, Debug) << "Request ctrl: "
- << controls::controls.at(ctrl.first)->name()
- << " = " << ctrl.second.toString();
-
- switch (ctrl.first) {
- case controls::AE_ENABLE: {
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- if (!agc) {
- LOG(IPARPI, Warning)
- << "Could not set AE_ENABLE - no AGC algorithm";
- break;
- }
-
- if (ctrl.second.get<bool>() == false)
- agc->disableAuto();
- else
- agc->enableAuto();
-
- libcameraMetadata_.set(controls::AeEnable, ctrl.second.get<bool>());
- break;
- }
-
- case controls::EXPOSURE_TIME: {
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- if (!agc) {
- LOG(IPARPI, Warning)
- << "Could not set EXPOSURE_TIME - no AGC algorithm";
- break;
- }
-
- /* The control provides units of microseconds. */
- agc->setFixedShutter(ctrl.second.get<int32_t>() * 1.0us);
-
- libcameraMetadata_.set(controls::ExposureTime, ctrl.second.get<int32_t>());
- break;
- }
-
- case controls::ANALOGUE_GAIN: {
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- if (!agc) {
- LOG(IPARPI, Warning)
- << "Could not set ANALOGUE_GAIN - no AGC algorithm";
- break;
- }
-
- agc->setFixedAnalogueGain(ctrl.second.get<float>());
-
- libcameraMetadata_.set(controls::AnalogueGain,
- ctrl.second.get<float>());
- break;
- }
-
- case controls::AE_METERING_MODE: {
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- if (!agc) {
- LOG(IPARPI, Warning)
- << "Could not set AE_METERING_MODE - no AGC algorithm";
- break;
- }
-
- int32_t idx = ctrl.second.get<int32_t>();
- if (MeteringModeTable.count(idx)) {
- agc->setMeteringMode(MeteringModeTable.at(idx));
- libcameraMetadata_.set(controls::AeMeteringMode, idx);
- } else {
- LOG(IPARPI, Error) << "Metering mode " << idx
- << " not recognised";
- }
- break;
- }
-
- case controls::AE_CONSTRAINT_MODE: {
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- if (!agc) {
- LOG(IPARPI, Warning)
- << "Could not set AE_CONSTRAINT_MODE - no AGC algorithm";
- break;
- }
-
- int32_t idx = ctrl.second.get<int32_t>();
- if (ConstraintModeTable.count(idx)) {
- agc->setConstraintMode(ConstraintModeTable.at(idx));
- libcameraMetadata_.set(controls::AeConstraintMode, idx);
- } else {
- LOG(IPARPI, Error) << "Constraint mode " << idx
- << " not recognised";
- }
- break;
- }
-
- case controls::AE_EXPOSURE_MODE: {
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- if (!agc) {
- LOG(IPARPI, Warning)
- << "Could not set AE_EXPOSURE_MODE - no AGC algorithm";
- break;
- }
-
- int32_t idx = ctrl.second.get<int32_t>();
- if (ExposureModeTable.count(idx)) {
- agc->setExposureMode(ExposureModeTable.at(idx));
- libcameraMetadata_.set(controls::AeExposureMode, idx);
- } else {
- LOG(IPARPI, Error) << "Exposure mode " << idx
- << " not recognised";
- }
- break;
- }
-
- case controls::EXPOSURE_VALUE: {
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- if (!agc) {
- LOG(IPARPI, Warning)
- << "Could not set EXPOSURE_VALUE - no AGC algorithm";
- break;
- }
-
- /*
- * The SetEv() function takes in a direct exposure multiplier.
- * So convert to 2^EV
- */
- double ev = pow(2.0, ctrl.second.get<float>());
- agc->setEv(ev);
- libcameraMetadata_.set(controls::ExposureValue,
- ctrl.second.get<float>());
- break;
- }
-
- case controls::AWB_ENABLE: {
- RPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(
- controller_.getAlgorithm("awb"));
- if (!awb) {
- LOG(IPARPI, Warning)
- << "Could not set AWB_ENABLE - no AWB algorithm";
- break;
- }
-
- if (ctrl.second.get<bool>() == false)
- awb->disableAuto();
- else
- awb->enableAuto();
-
- libcameraMetadata_.set(controls::AwbEnable,
- ctrl.second.get<bool>());
- break;
- }
-
- case controls::AWB_MODE: {
- RPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(
- controller_.getAlgorithm("awb"));
- if (!awb) {
- LOG(IPARPI, Warning)
- << "Could not set AWB_MODE - no AWB algorithm";
- break;
- }
-
- int32_t idx = ctrl.second.get<int32_t>();
- if (AwbModeTable.count(idx)) {
- awb->setMode(AwbModeTable.at(idx));
- libcameraMetadata_.set(controls::AwbMode, idx);
- } else {
- LOG(IPARPI, Error) << "AWB mode " << idx
- << " not recognised";
- }
- break;
- }
-
- case controls::COLOUR_GAINS: {
- auto gains = ctrl.second.get<Span<const float>>();
- RPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>(
- controller_.getAlgorithm("awb"));
- if (!awb) {
- LOG(IPARPI, Warning)
- << "Could not set COLOUR_GAINS - no AWB algorithm";
- break;
- }
-
- awb->setManualGains(gains[0], gains[1]);
- if (gains[0] != 0.0f && gains[1] != 0.0f)
- /* A gain of 0.0f will switch back to auto mode. */
- libcameraMetadata_.set(controls::ColourGains,
- { gains[0], gains[1] });
- break;
- }
-
- case controls::BRIGHTNESS: {
- RPiController::ContrastAlgorithm *contrast = dynamic_cast<RPiController::ContrastAlgorithm *>(
- controller_.getAlgorithm("contrast"));
- if (!contrast) {
- LOG(IPARPI, Warning)
- << "Could not set BRIGHTNESS - no contrast algorithm";
- break;
- }
-
- contrast->setBrightness(ctrl.second.get<float>() * 65536);
- libcameraMetadata_.set(controls::Brightness,
- ctrl.second.get<float>());
- break;
- }
-
- case controls::CONTRAST: {
- RPiController::ContrastAlgorithm *contrast = dynamic_cast<RPiController::ContrastAlgorithm *>(
- controller_.getAlgorithm("contrast"));
- if (!contrast) {
- LOG(IPARPI, Warning)
- << "Could not set CONTRAST - no contrast algorithm";
- break;
- }
-
- contrast->setContrast(ctrl.second.get<float>());
- libcameraMetadata_.set(controls::Contrast,
- ctrl.second.get<float>());
- break;
- }
-
- case controls::SATURATION: {
- RPiController::CcmAlgorithm *ccm = dynamic_cast<RPiController::CcmAlgorithm *>(
- controller_.getAlgorithm("ccm"));
- if (!ccm) {
- LOG(IPARPI, Warning)
- << "Could not set SATURATION - no ccm algorithm";
- break;
- }
-
- ccm->setSaturation(ctrl.second.get<float>());
- libcameraMetadata_.set(controls::Saturation,
- ctrl.second.get<float>());
- break;
- }
-
- case controls::SHARPNESS: {
- RPiController::SharpenAlgorithm *sharpen = dynamic_cast<RPiController::SharpenAlgorithm *>(
- controller_.getAlgorithm("sharpen"));
- if (!sharpen) {
- LOG(IPARPI, Warning)
- << "Could not set SHARPNESS - no sharpen algorithm";
- break;
- }
-
- sharpen->setStrength(ctrl.second.get<float>());
- libcameraMetadata_.set(controls::Sharpness,
- ctrl.second.get<float>());
- break;
- }
-
- case controls::SCALER_CROP: {
- /* We do nothing with this, but should avoid the warning below. */
- break;
- }
-
- case controls::FRAME_DURATION_LIMITS: {
- auto frameDurations = ctrl.second.get<Span<const int64_t>>();
- applyFrameDurations(frameDurations[0] * 1.0us, frameDurations[1] * 1.0us);
- break;
- }
-
- case controls::NOISE_REDUCTION_MODE: {
- RPiController::DenoiseAlgorithm *sdn = dynamic_cast<RPiController::DenoiseAlgorithm *>(
- controller_.getAlgorithm("SDN"));
- if (!sdn) {
- LOG(IPARPI, Warning)
- << "Could not set NOISE_REDUCTION_MODE - no SDN algorithm";
- break;
- }
-
- int32_t idx = ctrl.second.get<int32_t>();
- auto mode = DenoiseModeTable.find(idx);
- if (mode != DenoiseModeTable.end()) {
- sdn->setMode(mode->second);
-
- /*
- * \todo If the colour denoise is not going to run due to an
- * analysis image resolution or format mismatch, we should
- * report the status correctly in the metadata.
- */
- libcameraMetadata_.set(controls::draft::NoiseReductionMode, idx);
- } else {
- LOG(IPARPI, Error) << "Noise reduction mode " << idx
- << " not recognised";
- }
- break;
- }
-
- case controls::AF_MODE:
- break; /* We already handled this one above */
-
- case controls::AF_RANGE: {
- AfAlgorithm *af = dynamic_cast<AfAlgorithm *>(controller_.getAlgorithm("af"));
- if (!af) {
- LOG(IPARPI, Warning)
- << "Could not set AF_RANGE - no focus algorithm";
- break;
- }
-
- auto range = AfRangeTable.find(ctrl.second.get<int32_t>());
- if (range == AfRangeTable.end()) {
- LOG(IPARPI, Error) << "AF range " << ctrl.second.get<int32_t>()
- << " not recognised";
- break;
- }
- af->setRange(range->second);
- break;
- }
-
- case controls::AF_SPEED: {
- AfAlgorithm *af = dynamic_cast<AfAlgorithm *>(controller_.getAlgorithm("af"));
- if (!af) {
- LOG(IPARPI, Warning)
- << "Could not set AF_SPEED - no focus algorithm";
- break;
- }
-
- AfAlgorithm::AfSpeed speed = ctrl.second.get<int32_t>() == controls::AfSpeedFast ?
- AfAlgorithm::AfSpeedFast : AfAlgorithm::AfSpeedNormal;
- af->setSpeed(speed);
- break;
- }
-
- case controls::AF_METERING: {
- AfAlgorithm *af = dynamic_cast<AfAlgorithm *>(controller_.getAlgorithm("af"));
- if (!af) {
- LOG(IPARPI, Warning)
- << "Could not set AF_METERING - no AF algorithm";
- break;
- }
- af->setMetering(ctrl.second.get<int32_t>() == controls::AfMeteringWindows);
- break;
- }
-
- case controls::AF_WINDOWS: {
- AfAlgorithm *af = dynamic_cast<AfAlgorithm *>(controller_.getAlgorithm("af"));
- if (!af) {
- LOG(IPARPI, Warning)
- << "Could not set AF_WINDOWS - no AF algorithm";
- break;
- }
- af->setWindows(ctrl.second.get<Span<const Rectangle>>());
- break;
- }
-
- case controls::AF_PAUSE: {
- AfAlgorithm *af = dynamic_cast<AfAlgorithm *>(controller_.getAlgorithm("af"));
- if (!af || af->getMode() != AfAlgorithm::AfModeContinuous) {
- LOG(IPARPI, Warning)
- << "Could not set AF_PAUSE - no AF algorithm or not Continuous";
- break;
- }
- auto pause = AfPauseTable.find(ctrl.second.get<int32_t>());
- if (pause == AfPauseTable.end()) {
- LOG(IPARPI, Error) << "AF pause " << ctrl.second.get<int32_t>()
- << " not recognised";
- break;
- }
- af->pause(pause->second);
- break;
- }
-
- case controls::AF_TRIGGER: {
- AfAlgorithm *af = dynamic_cast<AfAlgorithm *>(controller_.getAlgorithm("af"));
- if (!af || af->getMode() != AfAlgorithm::AfModeAuto) {
- LOG(IPARPI, Warning)
- << "Could not set AF_TRIGGER - no AF algorithm or not Auto";
- break;
- } else {
- if (ctrl.second.get<int32_t>() == controls::AfTriggerStart)
- af->triggerScan();
- else
- af->cancelScan();
- }
- break;
- }
-
- case controls::LENS_POSITION: {
- AfAlgorithm *af = dynamic_cast<AfAlgorithm *>(controller_.getAlgorithm("af"));
- if (af) {
- int32_t hwpos;
- if (af->setLensPosition(ctrl.second.get<float>(), &hwpos)) {
- ControlList lensCtrls(lensCtrls_);
- lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, hwpos);
- setLensControls.emit(lensCtrls);
- }
- } else {
- LOG(IPARPI, Warning)
- << "Could not set LENS_POSITION - no AF algorithm";
- }
- break;
- }
-
- default:
- LOG(IPARPI, Warning)
- << "Ctrl " << controls::controls.at(ctrl.first)->name()
- << " is not handled.";
- break;
- }
- }
-}
-
-void IPARPi::prepare(const PrepareParams &params)
-{
- int64_t frameTimestamp = params.sensorControls.get(controls::SensorTimestamp).value_or(0);
- unsigned int ipaContext = params.ipaContext % rpiMetadata_.size();
- RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
- Span<uint8_t> embeddedBuffer;
-
- rpiMetadata.clear();
- fillDeviceStatus(params.sensorControls, ipaContext);
-
- if (params.buffers.embedded) {
- /*
- * Pipeline handler has supplied us with an embedded data buffer,
- * we must pass it to the CamHelper for parsing.
- */
- auto it = buffers_.find(params.buffers.embedded);
- ASSERT(it != buffers_.end());
- embeddedBuffer = it->second.planes()[0];
- }
-
- /*
- * AGC wants to know the algorithm status from the time it actioned the
- * sensor exposure/gain changes. So fetch it from the metadata list
- * indexed by the IPA cookie returned, and put it in the current frame
- * metadata.
- */
- AgcStatus agcStatus;
- RPiController::Metadata &delayedMetadata = rpiMetadata_[params.delayContext];
- if (!delayedMetadata.get<AgcStatus>("agc.status", agcStatus))
- rpiMetadata.set("agc.delayed_status", agcStatus);
-
- /*
- * This may overwrite the DeviceStatus using values from the sensor
- * metadata, and may also do additional custom processing.
- */
- helper_->prepare(embeddedBuffer, rpiMetadata);
-
- /* Allow a 10% margin on the comparison below. */
- Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns;
- if (lastRunTimestamp_ && frameCount_ > dropFrameCount_ &&
- delta < controllerMinFrameDuration * 0.9) {
- /*
- * Ensure we merge the previous frame's metadata with the current
- * frame. This will not overwrite exposure/gain values for the
- * current frame, or any other bits of metadata that were added
- * in helper_->Prepare().
- */
- RPiController::Metadata &lastMetadata =
- rpiMetadata_[(ipaContext ? ipaContext : rpiMetadata_.size()) - 1];
- rpiMetadata.mergeCopy(lastMetadata);
- processPending_ = false;
- return;
- }
-
- lastRunTimestamp_ = frameTimestamp;
- processPending_ = true;
-
- ControlList ctrls(ispCtrls_);
-
- controller_.prepare(&rpiMetadata);
-
- /* Lock the metadata buffer to avoid constant locks/unlocks. */
- std::unique_lock<RPiController::Metadata> lock(rpiMetadata);
-
- AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
- if (awbStatus)
- applyAWB(awbStatus, ctrls);
-
- CcmStatus *ccmStatus = rpiMetadata.getLocked<CcmStatus>("ccm.status");
- if (ccmStatus)
- applyCCM(ccmStatus, ctrls);
-
- AgcStatus *dgStatus = rpiMetadata.getLocked<AgcStatus>("agc.status");
- if (dgStatus)
- applyDG(dgStatus, ctrls);
-
- AlscStatus *lsStatus = rpiMetadata.getLocked<AlscStatus>("alsc.status");
- if (lsStatus)
- applyLS(lsStatus, ctrls);
-
- ContrastStatus *contrastStatus = rpiMetadata.getLocked<ContrastStatus>("contrast.status");
- if (contrastStatus)
- applyGamma(contrastStatus, ctrls);
-
- BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked<BlackLevelStatus>("black_level.status");
- if (blackLevelStatus)
- applyBlackLevel(blackLevelStatus, ctrls);
-
- GeqStatus *geqStatus = rpiMetadata.getLocked<GeqStatus>("geq.status");
- if (geqStatus)
- applyGEQ(geqStatus, ctrls);
-
- DenoiseStatus *denoiseStatus = rpiMetadata.getLocked<DenoiseStatus>("denoise.status");
- if (denoiseStatus)
- applyDenoise(denoiseStatus, ctrls);
-
- SharpenStatus *sharpenStatus = rpiMetadata.getLocked<SharpenStatus>("sharpen.status");
- if (sharpenStatus)
- applySharpen(sharpenStatus, ctrls);
-
- DpcStatus *dpcStatus = rpiMetadata.getLocked<DpcStatus>("dpc.status");
- if (dpcStatus)
- applyDPC(dpcStatus, ctrls);
-
- const AfStatus *afStatus = rpiMetadata.getLocked<AfStatus>("af.status");
- if (afStatus) {
- ControlList lensctrls(lensCtrls_);
- applyAF(afStatus, lensctrls);
- if (!lensctrls.empty())
- setLensControls.emit(lensctrls);
- }
-
- if (!ctrls.empty())
- setIspControls.emit(ctrls);
-}
-
-void IPARPi::fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext)
-{
- DeviceStatus deviceStatus = {};
-
- int32_t exposureLines = sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();
- int32_t gainCode = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
- int32_t vblank = sensorControls.get(V4L2_CID_VBLANK).get<int32_t>();
- int32_t hblank = sensorControls.get(V4L2_CID_HBLANK).get<int32_t>();
-
- deviceStatus.lineLength = helper_->hblankToLineLength(hblank);
- deviceStatus.shutterSpeed = helper_->exposure(exposureLines, deviceStatus.lineLength);
- deviceStatus.analogueGain = helper_->gain(gainCode);
- deviceStatus.frameLength = mode_.height + vblank;
-
- RPiController::AfAlgorithm *af = dynamic_cast<RPiController::AfAlgorithm *>(
- controller_.getAlgorithm("af"));
- if (af)
- deviceStatus.lensPosition = af->getLensPosition();
-
- LOG(IPARPI, Debug) << "Metadata - " << deviceStatus;
-
- rpiMetadata_[ipaContext].set("device.status", deviceStatus);
-}
-
-RPiController::StatisticsPtr IPARPi::fillStatistics(bcm2835_isp_stats *stats) const
-{
- using namespace RPiController;
-
- const Controller::HardwareConfig &hw = controller_.getHardwareConfig();
- unsigned int i;
- StatisticsPtr statistics =
- std::make_unique<Statistics>(Statistics::AgcStatsPos::PreWb, Statistics::ColourStatsPos::PostLsc);
-
- /* RGB histograms are not used, so do not populate them. */
- statistics->yHist = RPiController::Histogram(stats->hist[0].g_hist,
- hw.numHistogramBins);
-
- /* All region sums are based on a 16-bit normalised pipeline bit-depth. */
- unsigned int scale = Statistics::NormalisationFactorPow2 - hw.pipelineWidth;
-
- statistics->awbRegions.init(hw.awbRegions);
- for (i = 0; i < statistics->awbRegions.numRegions(); i++)
- statistics->awbRegions.set(i, { { stats->awb_stats[i].r_sum << scale,
- stats->awb_stats[i].g_sum << scale,
- stats->awb_stats[i].b_sum << scale },
- stats->awb_stats[i].counted,
- stats->awb_stats[i].notcounted });
-
- statistics->agcRegions.init(hw.agcRegions);
- for (i = 0; i < statistics->agcRegions.numRegions(); i++)
- statistics->agcRegions.set(i, { { stats->agc_stats[i].r_sum << scale,
- stats->agc_stats[i].g_sum << scale,
- stats->agc_stats[i].b_sum << scale },
- stats->agc_stats[i].counted,
- stats->awb_stats[i].notcounted });
-
- statistics->focusRegions.init(hw.focusRegions);
- for (i = 0; i < statistics->focusRegions.numRegions(); i++)
- statistics->focusRegions.set(i, { stats->focus_stats[i].contrast_val[1][1] / 1000,
- stats->focus_stats[i].contrast_val_num[1][1],
- stats->focus_stats[i].contrast_val_num[1][0] });
- return statistics;
-}
-
-void IPARPi::process(unsigned int bufferId, unsigned int ipaContext)
-{
- RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
-
- auto it = buffers_.find(bufferId);
- if (it == buffers_.end()) {
- LOG(IPARPI, Error) << "Could not find stats buffer!";
- return;
- }
-
- Span<uint8_t> mem = it->second.planes()[0];
- bcm2835_isp_stats *stats = reinterpret_cast<bcm2835_isp_stats *>(mem.data());
- RPiController::StatisticsPtr statistics = fillStatistics(stats);
-
- /* Save the focus stats in the metadata structure to report out later. */
- rpiMetadata_[ipaContext].set("focus.status", statistics->focusRegions);
-
- helper_->process(statistics, rpiMetadata);
- controller_.process(statistics, &rpiMetadata);
-
- struct AgcStatus agcStatus;
- if (rpiMetadata.get("agc.status", agcStatus) == 0) {
- ControlList ctrls(sensorCtrls_);
- applyAGC(&agcStatus, ctrls);
-
- setDelayedControls.emit(ctrls, ipaContext);
- setCameraTimeoutValue();
- }
-}
-
-void IPARPi::setCameraTimeoutValue()
-{
- /*
- * Take the maximum value of the exposure queue as the camera timeout
- * value to pass back to the pipeline handler. Only signal if it has changed
- * from the last set value.
- */
- auto max = std::max_element(frameLengths_.begin(), frameLengths_.end());
-
- if (*max != lastTimeout_) {
- setCameraTimeout.emit(max->get<std::milli>());
- lastTimeout_ = *max;
- }
-}
-
-void IPARPi::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls)
-{
- LOG(IPARPI, Debug) << "Applying WB R: " << awbStatus->gainR << " B: "
- << awbStatus->gainB;
-
- ctrls.set(V4L2_CID_RED_BALANCE,
- static_cast<int32_t>(awbStatus->gainR * 1000));
- ctrls.set(V4L2_CID_BLUE_BALANCE,
- static_cast<int32_t>(awbStatus->gainB * 1000));
-}
-
-void IPARPi::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration)
-{
- /*
- * This will only be applied once AGC recalculations occur.
- * The values may be clamped based on the sensor mode capabilities as well.
- */
- minFrameDuration_ = minFrameDuration ? minFrameDuration : defaultMinFrameDuration;
- maxFrameDuration_ = maxFrameDuration ? maxFrameDuration : defaultMaxFrameDuration;
- minFrameDuration_ = std::clamp(minFrameDuration_,
- mode_.minFrameDuration, mode_.maxFrameDuration);
- maxFrameDuration_ = std::clamp(maxFrameDuration_,
- mode_.minFrameDuration, mode_.maxFrameDuration);
- maxFrameDuration_ = std::max(maxFrameDuration_, minFrameDuration_);
-
- /* Return the validated limits via metadata. */
- libcameraMetadata_.set(controls::FrameDurationLimits,
- { static_cast<int64_t>(minFrameDuration_.get<std::micro>()),
- static_cast<int64_t>(maxFrameDuration_.get<std::micro>()) });
-
- /*
- * Calculate the maximum exposure time possible for the AGC to use.
- * getBlanking() will update maxShutter with the largest exposure
- * value possible.
- */
- Duration maxShutter = Duration::max();
- helper_->getBlanking(maxShutter, minFrameDuration_, maxFrameDuration_);
-
- RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
- controller_.getAlgorithm("agc"));
- agc->setMaxShutter(maxShutter);
-}
-
-void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
-{
- const int32_t minGainCode = helper_->gainCode(mode_.minAnalogueGain);
- const int32_t maxGainCode = helper_->gainCode(mode_.maxAnalogueGain);
- int32_t gainCode = helper_->gainCode(agcStatus->analogueGain);
-
- /*
- * Ensure anything larger than the max gain code will not be passed to
- * DelayedControls. The AGC will correctly handle a lower gain returned
- * by the sensor, provided it knows the actual gain used.
- */
- gainCode = std::clamp<int32_t>(gainCode, minGainCode, maxGainCode);
-
- /* getBlanking might clip exposure time to the fps limits. */
- Duration exposure = agcStatus->shutterTime;
- auto [vblank, hblank] = helper_->getBlanking(exposure, minFrameDuration_, maxFrameDuration_);
- int32_t exposureLines = helper_->exposureLines(exposure,
- helper_->hblankToLineLength(hblank));
-
- LOG(IPARPI, Debug) << "Applying AGC Exposure: " << exposure
- << " (Shutter lines: " << exposureLines << ", AGC requested "
- << agcStatus->shutterTime << ") Gain: "
- << agcStatus->analogueGain << " (Gain Code: "
- << gainCode << ")";
-
- ctrls.set(V4L2_CID_VBLANK, static_cast<int32_t>(vblank));
- ctrls.set(V4L2_CID_EXPOSURE, exposureLines);
- ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode);
-
- /*
- * 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. This seems to be the case for all the cameras our IPA
- * works with.
- *
- * \todo The control API ought to have a flag to specify if a control
- * is read-only which could be used below.
- */
- if (mode_.minLineLength != mode_.maxLineLength)
- ctrls.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblank));
-
- /*
- * Store the frame length times in a circular queue, up-to FrameLengthsQueueSize
- * elements. This will be used to advertise a camera timeout value to the
- * pipeline handler.
- */
- frameLengths_.pop_front();
- frameLengths_.push_back(helper_->exposure(vblank + mode_.height,
- helper_->hblankToLineLength(hblank)));
-}
-
-void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)
-{
- ctrls.set(V4L2_CID_DIGITAL_GAIN,
- static_cast<int32_t>(dgStatus->digitalGain * 1000));
-}
-
-void IPARPi::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls)
-{
- bcm2835_isp_custom_ccm ccm;
-
- for (int i = 0; i < 9; i++) {
- ccm.ccm.ccm[i / 3][i % 3].den = 1000;
- ccm.ccm.ccm[i / 3][i % 3].num = 1000 * ccmStatus->matrix[i];
- }
-
- ccm.enabled = 1;
- ccm.ccm.offsets[0] = ccm.ccm.offsets[1] = ccm.ccm.offsets[2] = 0;
-
- ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ccm),
- sizeof(ccm) });
- ctrls.set(V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, c);
-}
-
-void IPARPi::applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls)
-{
- const unsigned int numGammaPoints = controller_.getHardwareConfig().numGammaPoints;
- struct bcm2835_isp_gamma gamma;
-
- for (unsigned int i = 0; i < numGammaPoints - 1; i++) {
- int x = i < 16 ? i * 1024
- : (i < 24 ? (i - 16) * 2048 + 16384
- : (i - 24) * 4096 + 32768);
- gamma.x[i] = x;
- gamma.y[i] = std::min<uint16_t>(65535, contrastStatus->gammaCurve.eval(x));
- }
-
- gamma.x[numGammaPoints - 1] = 65535;
- gamma.y[numGammaPoints - 1] = 65535;
- gamma.enabled = 1;
-
- ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&gamma),
- sizeof(gamma) });
- ctrls.set(V4L2_CID_USER_BCM2835_ISP_GAMMA, c);
-}
-
-void IPARPi::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls)
-{
- bcm2835_isp_black_level blackLevel;
-
- blackLevel.enabled = 1;
- blackLevel.black_level_r = blackLevelStatus->blackLevelR;
- blackLevel.black_level_g = blackLevelStatus->blackLevelG;
- blackLevel.black_level_b = blackLevelStatus->blackLevelB;
-
- ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&blackLevel),
- sizeof(blackLevel) });
- ctrls.set(V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, c);
-}
-
-void IPARPi::applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls)
-{
- bcm2835_isp_geq geq;
-
- geq.enabled = 1;
- geq.offset = geqStatus->offset;
- geq.slope.den = 1000;
- geq.slope.num = 1000 * geqStatus->slope;
-
- ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&geq),
- sizeof(geq) });
- ctrls.set(V4L2_CID_USER_BCM2835_ISP_GEQ, c);
-}
-
-void IPARPi::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls)
-{
- using RPiController::DenoiseMode;
-
- bcm2835_isp_denoise denoise;
- DenoiseMode mode = static_cast<DenoiseMode>(denoiseStatus->mode);
-
- denoise.enabled = mode != DenoiseMode::Off;
- denoise.constant = denoiseStatus->noiseConstant;
- denoise.slope.num = 1000 * denoiseStatus->noiseSlope;
- denoise.slope.den = 1000;
- denoise.strength.num = 1000 * denoiseStatus->strength;
- denoise.strength.den = 1000;
-
- /* Set the CDN mode to match the SDN operating mode. */
- bcm2835_isp_cdn cdn;
- switch (mode) {
- case DenoiseMode::ColourFast:
- cdn.enabled = 1;
- cdn.mode = CDN_MODE_FAST;
- break;
- case DenoiseMode::ColourHighQuality:
- cdn.enabled = 1;
- cdn.mode = CDN_MODE_HIGH_QUALITY;
- break;
- default:
- cdn.enabled = 0;
- }
-
- ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&denoise),
- sizeof(denoise) });
- ctrls.set(V4L2_CID_USER_BCM2835_ISP_DENOISE, c);
-
- c = ControlValue(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&cdn),
- sizeof(cdn) });
- ctrls.set(V4L2_CID_USER_BCM2835_ISP_CDN, c);
-}
-
-void IPARPi::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls)
-{
- bcm2835_isp_sharpen sharpen;
-
- sharpen.enabled = 1;
- sharpen.threshold.num = 1000 * sharpenStatus->threshold;
- sharpen.threshold.den = 1000;
- sharpen.strength.num = 1000 * sharpenStatus->strength;
- sharpen.strength.den = 1000;
- sharpen.limit.num = 1000 * sharpenStatus->limit;
- sharpen.limit.den = 1000;
-
- ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&sharpen),
- sizeof(sharpen) });
- ctrls.set(V4L2_CID_USER_BCM2835_ISP_SHARPEN, c);
-}
-
-void IPARPi::applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls)
-{
- bcm2835_isp_dpc dpc;
-
- dpc.enabled = 1;
- dpc.strength = dpcStatus->strength;
-
- ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&dpc),
- sizeof(dpc) });
- ctrls.set(V4L2_CID_USER_BCM2835_ISP_DPC, c);
-}
-
-void IPARPi::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)
-{
- /*
- * Program lens shading tables into pipeline.
- * Choose smallest cell size that won't exceed 63x48 cells.
- */
- const int cellSizes[] = { 16, 32, 64, 128, 256 };
- unsigned int numCells = std::size(cellSizes);
- unsigned int i, w, h, cellSize;
- for (i = 0; i < numCells; i++) {
- cellSize = cellSizes[i];
- w = (mode_.width + cellSize - 1) / cellSize;
- h = (mode_.height + cellSize - 1) / cellSize;
- if (w < 64 && h <= 48)
- break;
- }
-
- if (i == numCells) {
- LOG(IPARPI, Error) << "Cannot find cell size";
- return;
- }
-
- /* We're going to supply corner sampled tables, 16 bit samples. */
- w++, h++;
- bcm2835_isp_lens_shading ls = {
- .enabled = 1,
- .grid_cell_size = cellSize,
- .grid_width = w,
- .grid_stride = w,
- .grid_height = h,
- /* .dmabuf will be filled in by pipeline handler. */
- .dmabuf = 0,
- .ref_transform = 0,
- .corner_sampled = 1,
- .gain_format = GAIN_FORMAT_U4P10
- };
-
- if (!lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) {
- LOG(IPARPI, Error) << "Do not have a correctly allocate lens shading table!";
- return;
- }
-
- if (lsStatus) {
- /* Format will be u4.10 */
- uint16_t *grid = static_cast<uint16_t *>(lsTable_);
-
- resampleTable(grid, lsStatus->r, w, h);
- resampleTable(grid + w * h, lsStatus->g, w, h);
- std::memcpy(grid + 2 * w * h, grid + w * h, w * h * sizeof(uint16_t));
- resampleTable(grid + 3 * w * h, lsStatus->b, w, h);
- }
-
- ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ls),
- sizeof(ls) });
- ctrls.set(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, c);
-}
-
-void IPARPi::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls)
-{
- if (afStatus->lensSetting) {
- ControlValue v(afStatus->lensSetting.value());
- lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, v);
- }
-}
-
-/*
- * Resamples a 16x12 table with central sampling to destW x destH with corner
- * sampling.
- */
-void IPARPi::resampleTable(uint16_t dest[], const std::vector<double> &src,
- int destW, int destH)
-{
- /*
- * Precalculate and cache the x sampling locations and phases to
- * save recomputing them on every row.
- */
- assert(destW > 1 && destH > 1 && destW <= 64);
- int xLo[64], xHi[64];
- double xf[64];
- double x = -0.5, xInc = 16.0 / (destW - 1);
- for (int i = 0; i < destW; i++, x += xInc) {
- xLo[i] = floor(x);
- xf[i] = x - xLo[i];
- xHi[i] = xLo[i] < 15 ? xLo[i] + 1 : 15;
- xLo[i] = xLo[i] > 0 ? xLo[i] : 0;
- }
-
- /* Now march over the output table generating the new values. */
- double y = -0.5, yInc = 12.0 / (destH - 1);
- for (int j = 0; j < destH; j++, y += yInc) {
- int yLo = floor(y);
- double yf = y - yLo;
- int yHi = yLo < 11 ? yLo + 1 : 11;
- yLo = yLo > 0 ? yLo : 0;
- double const *rowAbove = src.data() + yLo * 16;
- double const *rowBelow = src.data() + yHi * 16;
- for (int i = 0; i < destW; i++) {
- double above = rowAbove[xLo[i]] * (1 - xf[i]) + rowAbove[xHi[i]] * xf[i];
- double below = rowBelow[xLo[i]] * (1 - xf[i]) + rowBelow[xHi[i]] * xf[i];
- int result = floor(1024 * (above * (1 - yf) + below * yf) + .5);
- *(dest++) = result > 16383 ? 16383 : result; /* want u4.10 */
- }
- }
-}
-
-} /* namespace ipa::RPi */
-
-/*
- * External IPA module interface
- */
-extern "C" {
-const struct IPAModuleInfo ipaModuleInfo = {
- IPA_MODULE_API_VERSION,
- 1,
- "PipelineHandlerRPi",
- "rpi/vc4",
-};
-
-IPAInterface *ipaCreate()
-{
- return new ipa::RPi::IPARPi();
-}
-
-} /* extern "C" */
-
-} /* namespace libcamera */
diff --git a/src/ipa/rpi/vc4/vc4.cpp b/src/ipa/rpi/vc4/vc4.cpp
new file mode 100644
index 00000000..a32db9bc
--- /dev/null
+++ b/src/ipa/rpi/vc4/vc4.cpp
@@ -0,0 +1,540 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2019-2021, Raspberry Pi Ltd
+ *
+ * rpi.cpp - Raspberry Pi VC4/BCM2835 ISP IPA.
+ */
+
+#include <string.h>
+#include <sys/mman.h>
+
+#include <linux/bcm2835-isp.h>
+
+#include <libcamera/base/log.h>
+#include <libcamera/ipa/ipa_module_info.h>
+
+#include "common/ipa_base.h"
+#include "controller/af_status.h"
+#include "controller/agc_algorithm.h"
+#include "controller/alsc_status.h"
+#include "controller/awb_status.h"
+#include "controller/black_level_status.h"
+#include "controller/ccm_status.h"
+#include "controller/contrast_status.h"
+#include "controller/denoise_algorithm.h"
+#include "controller/denoise_status.h"
+#include "controller/dpc_status.h"
+#include "controller/geq_status.h"
+#include "controller/lux_status.h"
+#include "controller/noise_status.h"
+#include "controller/sharpen_status.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPARPI)
+
+namespace ipa::RPi {
+
+class IpaVc4 final : public IpaBase
+{
+public:
+ IpaVc4()
+ : IpaBase(), lsTable_(nullptr)
+ {
+ }
+
+ ~IpaVc4()
+ {
+ if (lsTable_)
+ munmap(lsTable_, MaxLsGridSize);
+ }
+
+private:
+ int32_t platformInit(const InitParams &params, InitResult *result) override;
+ int32_t platformConfigure(const ConfigParams &params, ConfigResult *result) override;
+
+ void platformPrepareIsp(const PrepareParams &params, RPiController::Metadata &rpiMetadata) override;
+ RPiController::StatisticsPtr platformProcessStats(Span<uint8_t> mem) override;
+
+ void handleControls(const ControlList &controls) override;
+ bool validateIspControls();
+
+ void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
+ void applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls);
+ void applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls);
+ void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls);
+ void applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls);
+ void applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls);
+ void applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls);
+ void applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls);
+ void applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls);
+ void applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls);
+ void applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls);
+ void resampleTable(uint16_t dest[], const std::vector<double> &src, int destW, int destH);
+
+ /* VC4 ISP controls. */
+ ControlInfoMap ispCtrls_;
+
+ /* LS table allocation passed in from the pipeline handler. */
+ SharedFD lsTableHandle_;
+ void *lsTable_;
+};
+
+int32_t IpaVc4::platformInit([[maybe_unused]] const InitParams &params, [[maybe_unused]] InitResult *result)
+{
+ const std::string &target = controller_.getTarget();
+
+ if (target != "bcm2835") {
+ LOG(IPARPI, Error)
+ << "Tuning data file target returned \"" << target << "\""
+ << ", expected \"bcm2835\"";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int32_t IpaVc4::platformConfigure(const ConfigParams &params, [[maybe_unused]] ConfigResult *result)
+{
+ ispCtrls_ = params.ispControls;
+ if (!validateIspControls()) {
+ LOG(IPARPI, Error) << "ISP control validation failed.";
+ return -1;
+ }
+
+ /* Store the lens shading table pointer and handle if available. */
+ if (params.lsTableHandle.isValid()) {
+ /* Remove any previous table, if there was one. */
+ if (lsTable_) {
+ munmap(lsTable_, MaxLsGridSize);
+ lsTable_ = nullptr;
+ }
+
+ /* Map the LS table buffer into user space. */
+ lsTableHandle_ = std::move(params.lsTableHandle);
+ if (lsTableHandle_.isValid()) {
+ lsTable_ = mmap(nullptr, MaxLsGridSize, PROT_READ | PROT_WRITE,
+ MAP_SHARED, lsTableHandle_.get(), 0);
+
+ if (lsTable_ == MAP_FAILED) {
+ LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table.";
+ lsTable_ = nullptr;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void IpaVc4::platformPrepareIsp([[maybe_unused]] const PrepareParams &params,
+ RPiController::Metadata &rpiMetadata)
+{
+ ControlList ctrls(ispCtrls_);
+
+ /* Lock the metadata buffer to avoid constant locks/unlocks. */
+ std::unique_lock<RPiController::Metadata> lock(rpiMetadata);
+
+ AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
+ if (awbStatus)
+ applyAWB(awbStatus, ctrls);
+
+ CcmStatus *ccmStatus = rpiMetadata.getLocked<CcmStatus>("ccm.status");
+ if (ccmStatus)
+ applyCCM(ccmStatus, ctrls);
+
+ AgcStatus *dgStatus = rpiMetadata.getLocked<AgcStatus>("agc.status");
+ if (dgStatus)
+ applyDG(dgStatus, ctrls);
+
+ AlscStatus *lsStatus = rpiMetadata.getLocked<AlscStatus>("alsc.status");
+ if (lsStatus)
+ applyLS(lsStatus, ctrls);
+
+ ContrastStatus *contrastStatus = rpiMetadata.getLocked<ContrastStatus>("contrast.status");
+ if (contrastStatus)
+ applyGamma(contrastStatus, ctrls);
+
+ BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked<BlackLevelStatus>("black_level.status");
+ if (blackLevelStatus)
+ applyBlackLevel(blackLevelStatus, ctrls);
+
+ GeqStatus *geqStatus = rpiMetadata.getLocked<GeqStatus>("geq.status");
+ if (geqStatus)
+ applyGEQ(geqStatus, ctrls);
+
+ DenoiseStatus *denoiseStatus = rpiMetadata.getLocked<DenoiseStatus>("denoise.status");
+ if (denoiseStatus)
+ applyDenoise(denoiseStatus, ctrls);
+
+ SharpenStatus *sharpenStatus = rpiMetadata.getLocked<SharpenStatus>("sharpen.status");
+ if (sharpenStatus)
+ applySharpen(sharpenStatus, ctrls);
+
+ DpcStatus *dpcStatus = rpiMetadata.getLocked<DpcStatus>("dpc.status");
+ if (dpcStatus)
+ applyDPC(dpcStatus, ctrls);
+
+ const AfStatus *afStatus = rpiMetadata.getLocked<AfStatus>("af.status");
+ if (afStatus) {
+ ControlList lensctrls(lensCtrls_);
+ applyAF(afStatus, lensctrls);
+ if (!lensctrls.empty())
+ setLensControls.emit(lensctrls);
+ }
+
+ if (!ctrls.empty())
+ setIspControls.emit(ctrls);
+}
+
+RPiController::StatisticsPtr IpaVc4::platformProcessStats(Span<uint8_t> mem)
+{
+ using namespace RPiController;
+
+ const bcm2835_isp_stats *stats = reinterpret_cast<bcm2835_isp_stats *>(mem.data());
+ StatisticsPtr statistics = std::make_unique<Statistics>(Statistics::AgcStatsPos::PreWb,
+ Statistics::ColourStatsPos::PostLsc);
+ const Controller::HardwareConfig &hw = controller_.getHardwareConfig();
+ unsigned int i;
+
+ /* RGB histograms are not used, so do not populate them. */
+ statistics->yHist = RPiController::Histogram(stats->hist[0].g_hist,
+ hw.numHistogramBins);
+
+ /* All region sums are based on a 16-bit normalised pipeline bit-depth. */
+ unsigned int scale = Statistics::NormalisationFactorPow2 - hw.pipelineWidth;
+
+ statistics->awbRegions.init(hw.awbRegions);
+ for (i = 0; i < statistics->awbRegions.numRegions(); i++)
+ statistics->awbRegions.set(i, { { stats->awb_stats[i].r_sum << scale,
+ stats->awb_stats[i].g_sum << scale,
+ stats->awb_stats[i].b_sum << scale },
+ stats->awb_stats[i].counted,
+ stats->awb_stats[i].notcounted });
+
+ statistics->agcRegions.init(hw.agcRegions);
+ for (i = 0; i < statistics->agcRegions.numRegions(); i++)
+ statistics->agcRegions.set(i, { { stats->agc_stats[i].r_sum << scale,
+ stats->agc_stats[i].g_sum << scale,
+ stats->agc_stats[i].b_sum << scale },
+ stats->agc_stats[i].counted,
+ stats->awb_stats[i].notcounted });
+
+ statistics->focusRegions.init(hw.focusRegions);
+ for (i = 0; i < statistics->focusRegions.numRegions(); i++)
+ statistics->focusRegions.set(i, { stats->focus_stats[i].contrast_val[1][1] / 1000,
+ stats->focus_stats[i].contrast_val_num[1][1],
+ stats->focus_stats[i].contrast_val_num[1][0] });
+
+ return statistics;
+}
+
+void IpaVc4::handleControls([[maybe_unused]] const ControlList &controls)
+{
+ /* No controls require any special updates to the hardware configuration. */
+}
+
+bool IpaVc4::validateIspControls()
+{
+ static const uint32_t ctrls[] = {
+ V4L2_CID_RED_BALANCE,
+ V4L2_CID_BLUE_BALANCE,
+ V4L2_CID_DIGITAL_GAIN,
+ V4L2_CID_USER_BCM2835_ISP_CC_MATRIX,
+ V4L2_CID_USER_BCM2835_ISP_GAMMA,
+ V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL,
+ V4L2_CID_USER_BCM2835_ISP_GEQ,
+ V4L2_CID_USER_BCM2835_ISP_DENOISE,
+ V4L2_CID_USER_BCM2835_ISP_SHARPEN,
+ V4L2_CID_USER_BCM2835_ISP_DPC,
+ V4L2_CID_USER_BCM2835_ISP_LENS_SHADING,
+ V4L2_CID_USER_BCM2835_ISP_CDN,
+ };
+
+ for (auto c : ctrls) {
+ if (ispCtrls_.find(c) == ispCtrls_.end()) {
+ LOG(IPARPI, Error) << "Unable to find ISP control "
+ << utils::hex(c);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void IpaVc4::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls)
+{
+ LOG(IPARPI, Debug) << "Applying WB R: " << awbStatus->gainR << " B: "
+ << awbStatus->gainB;
+
+ ctrls.set(V4L2_CID_RED_BALANCE,
+ static_cast<int32_t>(awbStatus->gainR * 1000));
+ ctrls.set(V4L2_CID_BLUE_BALANCE,
+ static_cast<int32_t>(awbStatus->gainB * 1000));
+}
+
+void IpaVc4::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)
+{
+ ctrls.set(V4L2_CID_DIGITAL_GAIN,
+ static_cast<int32_t>(dgStatus->digitalGain * 1000));
+}
+
+void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls)
+{
+ bcm2835_isp_custom_ccm ccm;
+
+ for (int i = 0; i < 9; i++) {
+ ccm.ccm.ccm[i / 3][i % 3].den = 1000;
+ ccm.ccm.ccm[i / 3][i % 3].num = 1000 * ccmStatus->matrix[i];
+ }
+
+ ccm.enabled = 1;
+ ccm.ccm.offsets[0] = ccm.ccm.offsets[1] = ccm.ccm.offsets[2] = 0;
+
+ ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ccm),
+ sizeof(ccm) });
+ ctrls.set(V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, c);
+}
+
+void IpaVc4::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls)
+{
+ bcm2835_isp_black_level blackLevel;
+
+ blackLevel.enabled = 1;
+ blackLevel.black_level_r = blackLevelStatus->blackLevelR;
+ blackLevel.black_level_g = blackLevelStatus->blackLevelG;
+ blackLevel.black_level_b = blackLevelStatus->blackLevelB;
+
+ ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&blackLevel),
+ sizeof(blackLevel) });
+ ctrls.set(V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, c);
+}
+
+void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls)
+{
+ const unsigned int numGammaPoints = controller_.getHardwareConfig().numGammaPoints;
+ struct bcm2835_isp_gamma gamma;
+
+ for (unsigned int i = 0; i < numGammaPoints - 1; i++) {
+ int x = i < 16 ? i * 1024
+ : (i < 24 ? (i - 16) * 2048 + 16384
+ : (i - 24) * 4096 + 32768);
+ gamma.x[i] = x;
+ gamma.y[i] = std::min<uint16_t>(65535, contrastStatus->gammaCurve.eval(x));
+ }
+
+ gamma.x[numGammaPoints - 1] = 65535;
+ gamma.y[numGammaPoints - 1] = 65535;
+ gamma.enabled = 1;
+
+ ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&gamma),
+ sizeof(gamma) });
+ ctrls.set(V4L2_CID_USER_BCM2835_ISP_GAMMA, c);
+}
+
+void IpaVc4::applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls)
+{
+ bcm2835_isp_geq geq;
+
+ geq.enabled = 1;
+ geq.offset = geqStatus->offset;
+ geq.slope.den = 1000;
+ geq.slope.num = 1000 * geqStatus->slope;
+
+ ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&geq),
+ sizeof(geq) });
+ ctrls.set(V4L2_CID_USER_BCM2835_ISP_GEQ, c);
+}
+
+void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls)
+{
+ using RPiController::DenoiseMode;
+
+ bcm2835_isp_denoise denoise;
+ DenoiseMode mode = static_cast<DenoiseMode>(denoiseStatus->mode);
+
+ denoise.enabled = mode != DenoiseMode::Off;
+ denoise.constant = denoiseStatus->noiseConstant;
+ denoise.slope.num = 1000 * denoiseStatus->noiseSlope;
+ denoise.slope.den = 1000;
+ denoise.strength.num = 1000 * denoiseStatus->strength;
+ denoise.strength.den = 1000;
+
+ /* Set the CDN mode to match the SDN operating mode. */
+ bcm2835_isp_cdn cdn;
+ switch (mode) {
+ case DenoiseMode::ColourFast:
+ cdn.enabled = 1;
+ cdn.mode = CDN_MODE_FAST;
+ break;
+ case DenoiseMode::ColourHighQuality:
+ cdn.enabled = 1;
+ cdn.mode = CDN_MODE_HIGH_QUALITY;
+ break;
+ default:
+ cdn.enabled = 0;
+ }
+
+ ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&denoise),
+ sizeof(denoise) });
+ ctrls.set(V4L2_CID_USER_BCM2835_ISP_DENOISE, c);
+
+ c = ControlValue(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&cdn),
+ sizeof(cdn) });
+ ctrls.set(V4L2_CID_USER_BCM2835_ISP_CDN, c);
+}
+
+void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls)
+{
+ bcm2835_isp_sharpen sharpen;
+
+ sharpen.enabled = 1;
+ sharpen.threshold.num = 1000 * sharpenStatus->threshold;
+ sharpen.threshold.den = 1000;
+ sharpen.strength.num = 1000 * sharpenStatus->strength;
+ sharpen.strength.den = 1000;
+ sharpen.limit.num = 1000 * sharpenStatus->limit;
+ sharpen.limit.den = 1000;
+
+ ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&sharpen),
+ sizeof(sharpen) });
+ ctrls.set(V4L2_CID_USER_BCM2835_ISP_SHARPEN, c);
+}
+
+void IpaVc4::applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls)
+{
+ bcm2835_isp_dpc dpc;
+
+ dpc.enabled = 1;
+ dpc.strength = dpcStatus->strength;
+
+ ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&dpc),
+ sizeof(dpc) });
+ ctrls.set(V4L2_CID_USER_BCM2835_ISP_DPC, c);
+}
+
+void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)
+{
+ /*
+ * Program lens shading tables into pipeline.
+ * Choose smallest cell size that won't exceed 63x48 cells.
+ */
+ const int cellSizes[] = { 16, 32, 64, 128, 256 };
+ unsigned int numCells = std::size(cellSizes);
+ unsigned int i, w, h, cellSize;
+ for (i = 0; i < numCells; i++) {
+ cellSize = cellSizes[i];
+ w = (mode_.width + cellSize - 1) / cellSize;
+ h = (mode_.height + cellSize - 1) / cellSize;
+ if (w < 64 && h <= 48)
+ break;
+ }
+
+ if (i == numCells) {
+ LOG(IPARPI, Error) << "Cannot find cell size";
+ return;
+ }
+
+ /* We're going to supply corner sampled tables, 16 bit samples. */
+ w++, h++;
+ bcm2835_isp_lens_shading ls = {
+ .enabled = 1,
+ .grid_cell_size = cellSize,
+ .grid_width = w,
+ .grid_stride = w,
+ .grid_height = h,
+ /* .dmabuf will be filled in by pipeline handler. */
+ .dmabuf = 0,
+ .ref_transform = 0,
+ .corner_sampled = 1,
+ .gain_format = GAIN_FORMAT_U4P10
+ };
+
+ if (!lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) {
+ LOG(IPARPI, Error) << "Do not have a correctly allocate lens shading table!";
+ return;
+ }
+
+ if (lsStatus) {
+ /* Format will be u4.10 */
+ uint16_t *grid = static_cast<uint16_t *>(lsTable_);
+
+ resampleTable(grid, lsStatus->r, w, h);
+ resampleTable(grid + w * h, lsStatus->g, w, h);
+ memcpy(grid + 2 * w * h, grid + w * h, w * h * sizeof(uint16_t));
+ resampleTable(grid + 3 * w * h, lsStatus->b, w, h);
+ }
+
+ ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ls),
+ sizeof(ls) });
+ ctrls.set(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, c);
+}
+
+void IpaVc4::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls)
+{
+ if (afStatus->lensSetting) {
+ ControlValue v(afStatus->lensSetting.value());
+ lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, v);
+ }
+}
+
+/*
+ * Resamples a 16x12 table with central sampling to destW x destH with corner
+ * sampling.
+ */
+void IpaVc4::resampleTable(uint16_t dest[], const std::vector<double> &src,
+ int destW, int destH)
+{
+ /*
+ * Precalculate and cache the x sampling locations and phases to
+ * save recomputing them on every row.
+ */
+ assert(destW > 1 && destH > 1 && destW <= 64);
+ int xLo[64], xHi[64];
+ double xf[64];
+ double x = -0.5, xInc = 16.0 / (destW - 1);
+ for (int i = 0; i < destW; i++, x += xInc) {
+ xLo[i] = floor(x);
+ xf[i] = x - xLo[i];
+ xHi[i] = xLo[i] < 15 ? xLo[i] + 1 : 15;
+ xLo[i] = xLo[i] > 0 ? xLo[i] : 0;
+ }
+
+ /* Now march over the output table generating the new values. */
+ double y = -0.5, yInc = 12.0 / (destH - 1);
+ for (int j = 0; j < destH; j++, y += yInc) {
+ int yLo = floor(y);
+ double yf = y - yLo;
+ int yHi = yLo < 11 ? yLo + 1 : 11;
+ yLo = yLo > 0 ? yLo : 0;
+ double const *rowAbove = src.data() + yLo * 16;
+ double const *rowBelow = src.data() + yHi * 16;
+ for (int i = 0; i < destW; i++) {
+ double above = rowAbove[xLo[i]] * (1 - xf[i]) + rowAbove[xHi[i]] * xf[i];
+ double below = rowBelow[xLo[i]] * (1 - xf[i]) + rowBelow[xHi[i]] * xf[i];
+ int result = floor(1024 * (above * (1 - yf) + below * yf) + .5);
+ *(dest++) = result > 16383 ? 16383 : result; /* want u4.10 */
+ }
+ }
+}
+
+} /* namespace ipa::RPi */
+
+/*
+ * External IPA module interface
+ */
+extern "C" {
+const struct IPAModuleInfo ipaModuleInfo = {
+ IPA_MODULE_API_VERSION,
+ 1,
+ "PipelineHandlerRPi",
+ "rpi/vc4",
+};
+
+IPAInterface *ipaCreate()
+{
+ return new ipa::RPi::IpaVc4();
+}
+
+} /* extern "C" */
+
+} /* namespace libcamera */