@@ -32,12 +32,12 @@ struct BufferIds {
uint32 bayer;
uint32 embedded;
uint32 stats;
+ uint32 params;
};
struct ConfigParams {
uint32 transform;
libcamera.ControlInfoMap sensorControls;
- libcamera.ControlInfoMap ispControls;
libcamera.ControlInfoMap lensControls;
/* VC4 specific */
libcamera.SharedFD lsTableHandle;
@@ -226,7 +226,7 @@ interface IPARPiEventInterface {
* processing the frame. The embedded data buffer may be recycled after
* this event.
*/
- prepareIspComplete(BufferIds buffers, bool stitchSwapBuffers);
+ prepareIspComplete(BufferIds buffers, bool stitchSwapBuffers, uint32 paramsBytesUsed);
/**
* \fn processStatsComplete()
@@ -251,17 +251,6 @@ interface IPARPiEventInterface {
*/
metadataReady(libcamera.ControlList metadata);
- /**
- * \fn setIspControls()
- * \brief Signal ISP controls to be applied.
- * \param[in] controls List of controls to be applied.
- *
- * This asynchronous event is signalled to the pipeline handler during
- * the \a prepareISP signal after all algorithms have been run and the
- * IPA requires ISP controls to be applied for the frame.
- */
- setIspControls(libcamera.ControlList controls);
-
/**
* \fn setDelayedControls()
* \brief Signal Sensor controls to be applied.
@@ -427,6 +427,7 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms)
unsigned int ipaContext = params.ipaContext % rpiMetadata_.size();
RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
Span<uint8_t> embeddedBuffer;
+ Span<uint8_t> paramsBuffer;
rpiMetadata.clear();
fillDeviceStatus(params.sensorControls, ipaContext);
@@ -441,6 +442,13 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms)
embeddedBuffer = it->second.planes()[0];
}
+ if (params.buffers.params) {
+ auto it = buffers_.find(params.buffers.params);
+ ASSERT(it != buffers_.end());
+ paramsBuffer = it->second.planes()[0];
+ platformParamsBufferInit(paramsBuffer);
+ }
+
/*
* AGC wants to know the algorithm status from the time it actioned the
* sensor exposure/gain changes. So fetch it from the metadata list
@@ -506,7 +514,8 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms)
reportMetadata(ipaContext);
/* Ready to push the input buffer into the ISP. */
- prepareIspComplete.emit(params.buffers, stitchSwapBuffers_);
+ prepareIspComplete.emit(params.buffers, stitchSwapBuffers_,
+ platformParamsBytesUsed());
}
void IpaBase::processStats(const ProcessParams ¶ms)
@@ -79,6 +79,8 @@ protected:
/* Whether the stitch block (if available) needs to swap buffers. */
bool stitchSwapBuffers_;
+ virtual size_t platformParamsBytesUsed() const { return 0; }
+
private:
/* Number of metadata objects available in the context list. */
static constexpr unsigned int numMetadataContexts = 16;
@@ -87,6 +89,7 @@ private:
virtual int32_t platformStart(const ControlList &controls, StartResult *result) = 0;
virtual int32_t platformConfigure(const ConfigParams ¶ms, ConfigResult *result) = 0;
+ virtual void platformParamsBufferInit([[maybe_unused]] Span<uint8_t> paramsBuffer) {}
virtual void platformPrepareIsp(const PrepareParams ¶ms,
RPiController::Metadata &rpiMetadata) = 0;
virtual void platformPrepareAgc(RPiController::Metadata &rpiMetadata) = 0;
new file mode 100644
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026, Ideas On Board
+ *
+ * Raspberry Pi VC4/BCM2835 ISP Parameters
+ */
+
+#pragma once
+
+#include <linux/bcm2835-isp.h>
+
+#include <libipa/v4l2_params.h>
+
+namespace libcamera {
+
+namespace ipa::RPi {
+
+enum class BlockType {
+ BlackLevel,
+ Geq,
+ Gamma,
+ Denoise,
+ Sharpen,
+ Dpc,
+ Cdn,
+ CcMatrix,
+ LensShading,
+ AwbGains,
+ DGain,
+};
+
+namespace details {
+
+template<BlockType B>
+struct block_type {
+};
+
+#define BCM2835_DEFINE_BLOCK_TYPE(id, structName, blockId) \
+ template<> \
+ struct block_type<BlockType::id> { \
+ using type = struct bcm2835_isp_params_##structName; \
+ static constexpr bcm2835_isp_param_block_type blockType = \
+ BCM2835_ISP_PARAM_BLOCK_##blockId; \
+ };
+
+BCM2835_DEFINE_BLOCK_TYPE(BlackLevel, black_level, BLACK_LEVEL)
+BCM2835_DEFINE_BLOCK_TYPE(Geq, geq, GEQ)
+BCM2835_DEFINE_BLOCK_TYPE(Gamma, gamma, GAMMA)
+BCM2835_DEFINE_BLOCK_TYPE(Denoise, denoise, DENOISE)
+BCM2835_DEFINE_BLOCK_TYPE(Sharpen, sharpen, SHARPEN)
+BCM2835_DEFINE_BLOCK_TYPE(Dpc, dpc, DPC)
+BCM2835_DEFINE_BLOCK_TYPE(Cdn, cdn, CDN)
+BCM2835_DEFINE_BLOCK_TYPE(CcMatrix, cc_matrix, CC_MATRIX)
+BCM2835_DEFINE_BLOCK_TYPE(LensShading, lens_shading, LENS_SHADING)
+BCM2835_DEFINE_BLOCK_TYPE(AwbGains, awb_gains, AWB_GAINS)
+BCM2835_DEFINE_BLOCK_TYPE(DGain, digital_gain, DIGITAL_GAIN)
+
+struct params_traits {
+ using id_type = BlockType;
+ template<id_type Id>
+ using id_to_details = block_type<Id>;
+};
+
+} /* namespace details */
+
+class Bcm2835Params : public V4L2Params<details::params_traits>
+{
+public:
+ Bcm2835Params(Span<uint8_t> data) : V4L2Params(data,
+ BCM2835_ISP_PARAM_BUFFER_V1)
+ {
+ }
+};
+
+} /* namespace ipa::RPi */
+
+} /* namespace libcamera */
@@ -30,6 +30,7 @@
#include "controller/lux_status.h"
#include "controller/noise_status.h"
#include "controller/sharpen_status.h"
+#include "params.h"
namespace libcamera {
@@ -59,34 +60,39 @@ private:
int32_t platformStart(const ControlList &controls, StartResult *result) override;
int32_t platformConfigure(const ConfigParams ¶ms, ConfigResult *result) override;
- void platformPrepareIsp(const PrepareParams ¶ms, RPiController::Metadata &rpiMetadata) override;
- void platformPrepareAgc([[maybe_unused]] RPiController::Metadata &rpiMetadata) override;
+ void platformParamsBufferInit(Span<uint8_t> paramsBuffer) override;
+ void platformPrepareIsp([[maybe_unused]] const PrepareParams ¶ms,
+ RPiController::Metadata &rpiMetadata) override;
+ void platformPrepareAgc(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(double digitalGain, const struct AwbStatus *awbStatus, 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);
+
+ size_t platformParamsBytesUsed() const override
+ {
+ return (ispParams_.has_value()) ? ispParams_->bytesused() : 0;
+ }
+
+ void applyAWB(const struct AwbStatus *awbStatus, Bcm2835Params ¶ms);
+ void applyDG(double digitalGain, const struct AwbStatus *awbStatus, Bcm2835Params ¶ms);
+ void applyCCM(const struct CcmStatus *ccmStatus, Bcm2835Params ¶ms);
+ void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, Bcm2835Params ¶ms);
+ void applyGamma(const struct ContrastStatus *contrastStatus, Bcm2835Params ¶ms);
+ void applyGEQ(const struct GeqStatus *geqStatus, Bcm2835Params ¶ms);
+ void applyDenoise(const struct DenoiseStatus *denoiseStatus, Bcm2835Params ¶ms);
+ void applySharpen(const struct SharpenStatus *sharpenStatus, Bcm2835Params ¶ms);
+ void applyDPC(const struct DpcStatus *dpcStatus, Bcm2835Params ¶ms);
+ void applyLS(const struct AlscStatus *lsStatus, Bcm2835Params ¶ms);
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_;
- ControlList ctrls_;
-
/* LS table allocation passed in from the pipeline handler. */
SharedFD lsTableHandle_;
void *lsTable_;
+ /* Params buffer for the current frame. */
+ std::optional<Bcm2835Params> ispParams_;
+
/* Remember the most recent AWB values. */
AwbStatus lastAwbStatus_;
};
@@ -113,13 +119,6 @@ int32_t IpaVc4::platformStart([[maybe_unused]] const ControlList &controls,
int32_t IpaVc4::platformConfigure(const ConfigParams ¶ms, [[maybe_unused]] ConfigResult *result)
{
- ispCtrls_ = params.ispControls;
- ctrls_ = ControlList(ispCtrls_);
- 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. */
@@ -144,51 +143,55 @@ int32_t IpaVc4::platformConfigure(const ConfigParams ¶ms, [[maybe_unused]] C
return 0;
}
+void IpaVc4::platformParamsBufferInit(Span<uint8_t> paramsBuffer)
+{
+ /* Initialize the extensible parameter buffer */
+ ispParams_.emplace(paramsBuffer);
+}
+
void IpaVc4::platformPrepareIsp([[maybe_unused]] const PrepareParams ¶ms,
RPiController::Metadata &rpiMetadata)
{
- ControlList &ctrls = ctrls_;
-
/* 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);
+ applyAWB(awbStatus, *ispParams_);
lastAwbStatus_ = *awbStatus;
}
CcmStatus *ccmStatus = rpiMetadata.getLocked<CcmStatus>("ccm.status");
if (ccmStatus)
- applyCCM(ccmStatus, ctrls);
+ applyCCM(ccmStatus, *ispParams_);
AlscStatus *lsStatus = rpiMetadata.getLocked<AlscStatus>("alsc.status");
if (lsStatus)
- applyLS(lsStatus, ctrls);
+ applyLS(lsStatus, *ispParams_);
ContrastStatus *contrastStatus = rpiMetadata.getLocked<ContrastStatus>("contrast.status");
if (contrastStatus)
- applyGamma(contrastStatus, ctrls);
+ applyGamma(contrastStatus, *ispParams_);
BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked<BlackLevelStatus>("black_level.status");
if (blackLevelStatus)
- applyBlackLevel(blackLevelStatus, ctrls);
+ applyBlackLevel(blackLevelStatus, *ispParams_);
GeqStatus *geqStatus = rpiMetadata.getLocked<GeqStatus>("geq.status");
if (geqStatus)
- applyGEQ(geqStatus, ctrls);
+ applyGEQ(geqStatus, *ispParams_);
DenoiseStatus *denoiseStatus = rpiMetadata.getLocked<DenoiseStatus>("denoise.status");
if (denoiseStatus)
- applyDenoise(denoiseStatus, ctrls);
+ applyDenoise(denoiseStatus, *ispParams_);
SharpenStatus *sharpenStatus = rpiMetadata.getLocked<SharpenStatus>("sharpen.status");
if (sharpenStatus)
- applySharpen(sharpenStatus, ctrls);
+ applySharpen(sharpenStatus, *ispParams_);
DpcStatus *dpcStatus = rpiMetadata.getLocked<DpcStatus>("dpc.status");
if (dpcStatus)
- applyDPC(dpcStatus, ctrls);
+ applyDPC(dpcStatus, *ispParams_);
const AfStatus *afStatus = rpiMetadata.getLocked<AfStatus>("af.status");
if (afStatus) {
@@ -205,10 +208,7 @@ void IpaVc4::platformPrepareAgc(RPiController::Metadata &rpiMetadata)
double digitalGain = delayedAgcStatus ? delayedAgcStatus->digitalGain : agcStatus_.digitalGain;
AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
- applyDG(digitalGain, awbStatus, ctrls_);
-
- setIspControls.emit(ctrls_);
- ctrls_ = ControlList(ispCtrls_);
+ applyDG(digitalGain, awbStatus, *ispParams_);
}
RPiController::StatisticsPtr IpaVc4::platformProcessStats(Span<uint8_t> mem)
@@ -325,48 +325,26 @@ void IpaVc4::handleControls(const ControlList &controls)
}
}
-bool IpaVc4::validateIspControls()
+void IpaVc4::applyAWB(const struct AwbStatus *awbStatus, Bcm2835Params ¶ms)
{
- 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;
-}
+ auto block = params.block<BlockType::AwbGains>();
-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));
+ block->awb_gains.r_gain.num = static_cast<int32_t>(awbStatus->gainR * 1000);
+ block->awb_gains.r_gain.den = 1000;
+ block->awb_gains.b_gain.num = static_cast<int32_t>(awbStatus->gainB * 1000);
+ block->awb_gains.b_gain.den = 1000;
+
+ block.setEnabled(true);
}
void IpaVc4::applyDG(double digitalGain,
- const struct AwbStatus *awbStatus, ControlList &ctrls)
+ const struct AwbStatus *awbStatus, Bcm2835Params ¶ms)
{
+ auto block = params.block<BlockType::DGain>();
+
if (awbStatus) {
/*
* We must apply sufficient extra digital gain to stop any of the channel gains being
@@ -380,13 +358,16 @@ void IpaVc4::applyDG(double digitalGain,
digitalGain *= extraGain;
}
- ctrls.set(V4L2_CID_DIGITAL_GAIN,
- static_cast<int32_t>(digitalGain * 1000));
+ block->digital_gain.gain.num = static_cast<int32_t>(digitalGain * 1000);
+ block->digital_gain.gain.den = 1000;
+
+ block.setEnabled(true);
}
-void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls)
+void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, Bcm2835Params ¶ms)
{
- bcm2835_isp_custom_ccm ccm;
+ auto block = params.block<BlockType::CcMatrix>();
+ bcm2835_isp_custom_ccm &ccm = block->ccm;
for (int i = 0; i < 9; i++) {
ccm.ccm.ccm[i / 3][i % 3].den = 1000;
@@ -395,30 +376,28 @@ void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls)
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);
+ block.setEnabled(true);
}
-void IpaVc4::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls)
+void IpaVc4::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus,
+ Bcm2835Params ¶ms)
{
- bcm2835_isp_black_level blackLevel;
+ auto block = params.block<BlockType::BlackLevel>();
+ bcm2835_isp_black_level &blackLevel = block->black_level;
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);
+ block.setEnabled(true);
}
-void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls)
+void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus,
+ Bcm2835Params ¶ms)
{
const unsigned int numGammaPoints = controller_.getHardwareConfig().numGammaPoints;
- struct bcm2835_isp_gamma gamma;
+ auto block = params.block<BlockType::Gamma>();
+ struct bcm2835_isp_gamma &gamma = block->gamma;
for (unsigned int i = 0; i < numGammaPoints - 1; i++) {
int x = i < 16 ? i * 1024
@@ -431,31 +410,27 @@ void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, ControlList
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);
+ block.setEnabled(true);
}
-void IpaVc4::applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls)
+void IpaVc4::applyGEQ(const struct GeqStatus *geqStatus, Bcm2835Params ¶ms)
{
- bcm2835_isp_geq geq;
+ auto block = params.block<BlockType::Geq>();
+ bcm2835_isp_geq &geq = block->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);
+ block.setEnabled(true);
}
-void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls)
+void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, Bcm2835Params ¶ms)
{
+ auto blockDenoise = params.block<BlockType::Denoise>();
using RPiController::DenoiseMode;
- bcm2835_isp_denoise denoise;
+ bcm2835_isp_denoise &denoise = blockDenoise->denoise;
DenoiseMode mode = static_cast<DenoiseMode>(denoiseStatus->mode);
denoise.enabled = mode != DenoiseMode::Off;
@@ -464,9 +439,11 @@ void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList
denoise.slope.den = 1000;
denoise.strength.num = 1000 * denoiseStatus->strength;
denoise.strength.den = 1000;
+ blockDenoise.setEnabled(denoise.enabled);
/* Set the CDN mode to match the SDN operating mode. */
- bcm2835_isp_cdn cdn;
+ auto blockCdn = params.block<BlockType::Cdn>();
+ bcm2835_isp_cdn &cdn = blockCdn->cdn;
switch (mode) {
case DenoiseMode::ColourFast:
cdn.enabled = 1;
@@ -479,19 +456,13 @@ void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList
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);
+ blockCdn.setEnabled(cdn.enabled);
}
-void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls)
+void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, Bcm2835Params ¶ms)
{
- bcm2835_isp_sharpen sharpen;
+ auto block = params.block<BlockType::Sharpen>();
+ bcm2835_isp_sharpen &sharpen = block->sharpen;
sharpen.enabled = 1;
sharpen.threshold.num = 1000 * sharpenStatus->threshold;
@@ -500,25 +471,20 @@ void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList
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);
+ block.setEnabled(true);
}
-void IpaVc4::applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls)
+void IpaVc4::applyDPC(const struct DpcStatus *dpcStatus, Bcm2835Params ¶ms)
{
- bcm2835_isp_dpc dpc;
+ auto block = params.block<BlockType::Dpc>();
+ bcm2835_isp_dpc &dpc = block->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);
+ block.setEnabled(true);
}
-void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)
+void IpaVc4::applyLS(const struct AlscStatus *lsStatus, Bcm2835Params ¶ms)
{
/*
* Program lens shading tables into pipeline.
@@ -542,24 +508,24 @@ void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)
/* We're going to supply corner sampled tables, 16 bit samples. */
w++, h++;
- bcm2835_isp_lens_shading ls = {
+ if (!lsTableHandle_.isValid() || !lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) {
+ LOG(IPARPI, Error) << "Do not have a correctly allocated lens shading table!";
+ return;
+ }
+
+ auto block = params.block<BlockType::LensShading>();
+ block->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,
+ .dmabuf = lsTableHandle_.get(),
.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_);
@@ -570,9 +536,7 @@ void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)
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);
+ block.setEnabled(true);
}
void IpaVc4::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls)
@@ -30,6 +30,7 @@ enum BufferMask {
MaskStats = 0x010000,
MaskEmbeddedData = 0x020000,
MaskBayerData = 0x040000,
+ MaskParams = 0x080000,
};
struct BufferObject {
@@ -754,7 +754,8 @@ public:
void beOutputDequeue(FrameBuffer *buffer);
void processStatsComplete(const ipa::RPi::BufferIds &buffers);
- void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers);
+ void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers,
+ unsigned int paramsBytesUsed);
void setCameraTimeout(uint32_t maxFrameLengthMs);
/* Array of CFE and ISP device streams and associated buffers/streams. */
@@ -1868,7 +1869,8 @@ void PiSPCameraData::setCameraTimeout(uint32_t maxFrameLengthMs)
cfe_[Cfe::Output0].dev()->setDequeueTimeout(timeout);
}
-void PiSPCameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers)
+void PiSPCameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers,
+ [[maybe_unused]] unsigned int paramsBytesUsed)
{
unsigned int embeddedId = buffers.embedded & RPi::MaskID;
unsigned int bayerId = buffers.bayer & RPi::MaskID;
@@ -32,7 +32,7 @@ using StreamParams = RPi::RPiCameraConfiguration::StreamParams;
namespace {
enum class Unicam : unsigned int { Image, Embedded };
-enum class Isp : unsigned int { Input, Output0, Output1, Stats };
+enum class Isp : unsigned int { Input, Output0, Output1, Stats, Params };
static constexpr unsigned int kUnicamSinkPad = 0;
static constexpr unsigned int kUnicamSourceImagePad = 1;
@@ -84,15 +84,15 @@ public:
void ispOutputDequeue(FrameBuffer *buffer);
void processStatsComplete(const ipa::RPi::BufferIds &buffers);
- void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers);
- void setIspControls(const ControlList &controls);
+ void prepareIspComplete(const ipa::RPi::BufferIds &buffers, bool stitchSwapBuffers,
+ unsigned int paramsBytesUsed);
void setCameraTimeout(uint32_t maxFrameLengthMs);
std::unique_ptr<V4L2Subdevice> unicamSubdev_;
/* Array of Unicam and ISP device streams and associated buffers/streams. */
RPi::Device<Unicam, 2> unicam_;
- RPi::Device<Isp, 4> isp_;
+ RPi::Device<Isp, 5> isp_;
/* DMAHEAP allocation helper. */
DmaBufAllocator dmaHeap_;
@@ -266,6 +266,13 @@ int PipelineHandlerVc4::allocateBuffers(Camera *camera)
std::max<int>(data->config_.minUnicamBuffers,
minBuffers - numRawBuffers);
+ } else if (stream == &data->isp_[Isp::Params]) {
+ /*
+ * Parameter buffers are dequeued asyncrhonously by the driver
+ * as soon as it sends each parameter to VC4. Ideally, 1 buffer
+ * would be sufficient, but we alot 2 to be safe.
+ */
+ numBuffers = 2;
} else if (stream == &data->unicam_[Unicam::Embedded]) {
/*
* Embedded data buffers are (currently) for internal use, and
@@ -302,10 +309,11 @@ int PipelineHandlerVc4::allocateBuffers(Camera *camera)
}
/*
- * Pass the stats and embedded data buffers to the IPA. No other
+ * Pass the stats, embedded data and params buffers to the IPA. No other
* buffers need to be passed.
*/
mapBuffers(camera, data->isp_[Isp::Stats].getBuffers(), RPi::MaskStats);
+ mapBuffers(camera, data->isp_[Isp::Params].getBuffers(), RPi::MaskParams);
if (data->sensorMetadata_)
mapBuffers(camera, data->unicam_[Unicam::Embedded].getBuffers(),
RPi::MaskEmbeddedData);
@@ -324,13 +332,14 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
MediaEntity *unicamSubdev = unicam->getEntityByName("unicam");
MediaEntity *unicamImage = unicam->getEntityByName("unicam-image");
- MediaEntity *ispOutput0 = isp->getEntityByName("bcm2835-isp0-output0");
- MediaEntity *ispCapture1 = isp->getEntityByName("bcm2835-isp0-capture1");
- MediaEntity *ispCapture2 = isp->getEntityByName("bcm2835-isp0-capture2");
- MediaEntity *ispCapture3 = isp->getEntityByName("bcm2835-isp0-capture3");
-
- if (!unicamSubdev || !unicamImage || !ispOutput0 || !ispCapture1 ||
- !ispCapture2 || !ispCapture3)
+ MediaEntity *ispOutput0 = isp->getEntityByName("bcm2835-isp-output0");
+ MediaEntity *ispCapture0 = isp->getEntityByName("bcm2835-isp-capture0");
+ MediaEntity *ispCapture1 = isp->getEntityByName("bcm2835-isp-capture1");
+ MediaEntity *ispCapture2 = isp->getEntityByName("bcm2835-isp-stats2");
+ MediaEntity *ispParams = isp->getEntityByName("bcm2835-isp-params");
+
+ if (!unicamSubdev || !unicamImage || !ispOutput0 || !ispCapture0 ||
+ !ispCapture1 || !ispCapture2 || !ispParams)
return -ENOENT;
/* Create the unicam subdev and video streams. */
@@ -347,9 +356,12 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
/* Tag the ISP input stream as an import stream. */
data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, StreamFlag::ImportOnly);
- data->isp_[Isp::Output0] = RPi::Stream("ISP Output0", ispCapture1);
- data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2);
- data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3);
+ data->isp_[Isp::Output0] = RPi::Stream("ISP Output0", ispCapture0);
+ data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture1);
+ data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture2);
+ /* Tag the ISP params stream as MMAP (for writing into it in the IPA) and recurrent. */
+ data->isp_[Isp::Params] = RPi::Stream("ISP Params", ispParams,
+ StreamFlag::RequiresMmap | StreamFlag::Recurrent);
/* Wire up all the buffer connections. */
data->unicam_[Unicam::Image].dev()->bufferReady.connect(data, &Vc4CameraData::unicamBufferDequeue);
@@ -357,6 +369,7 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
data->isp_[Isp::Output0].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
data->isp_[Isp::Output1].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
data->isp_[Isp::Stats].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
+ data->isp_[Isp::Params].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
if (data->sensorMetadata_ ^ !!data->unicam_[Unicam::Embedded].dev()) {
LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!";
@@ -398,7 +411,6 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
/* Write up all the IPA connections. */
data->ipa_->processStatsComplete.connect(data, &Vc4CameraData::processStatsComplete);
data->ipa_->prepareIspComplete.connect(data, &Vc4CameraData::prepareIspComplete);
- data->ipa_->setIspControls.connect(data, &Vc4CameraData::setIspControls);
data->ipa_->setCameraTimeout.connect(data, &Vc4CameraData::setCameraTimeout);
/*
@@ -757,6 +769,16 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi
return ret;
}
+ /* ISP parameters input format. */
+ format = {};
+ format.fourcc = V4L2PixelFormat(V4L2_META_FMT_BCM2835_ISP_PARAMS);
+ ret = isp_[Isp::Params].dev()->setFormat(&format);
+ if (ret) {
+ LOG(RPI, Error) << "Failed to set format on ISP params stream: "
+ << format;
+ return ret;
+ }
+
/*
* Configure the Unicam embedded data output format only if the sensor
* supports it.
@@ -800,8 +822,6 @@ int Vc4CameraData::platformConfigure(const RPi::RPiCameraConfiguration *rpiConfi
int Vc4CameraData::platformConfigureIpa(ipa::RPi::ConfigParams ¶ms)
{
- params.ispControls = isp_[Isp::Input].dev()->controls();
-
/* Allocate the lens shading table via dmaHeap and pass to the IPA. */
if (!lsTable_.isValid()) {
lsTable_ = SharedFD(dmaHeap_.alloc("ls_grid", ipa::RPi::MaxLsGridSize));
@@ -945,46 +965,41 @@ void Vc4CameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers)
}
void Vc4CameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers,
- [[maybe_unused]] bool stitchSwapBuffers)
+ [[maybe_unused]] bool stitchSwapBuffers,
+ unsigned int paramsBytesUsed)
{
unsigned int embeddedId = buffers.embedded & RPi::MaskID;
- unsigned int bayer = buffers.bayer & RPi::MaskID;
+ unsigned int bayerId = buffers.bayer & RPi::MaskID;
+ unsigned int paramsId = buffers.params & RPi::MaskID;
FrameBuffer *buffer;
if (!isRunning())
return;
- buffer = unicam_[Unicam::Image].getBuffers().at(bayer & RPi::MaskID).buffer;
- LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << (bayer & RPi::MaskID)
+ /* Queue params buffer */
+ buffer = isp_[Isp::Params].getBuffers().at(paramsId).buffer;
+ buffer->_d()->metadata().planes()[0].bytesused = paramsBytesUsed;
+ LOG(RPI, Debug) << "Params re-queue to ISP, buffer id " << paramsId
+ << ", timestamp: " << buffer->metadata().timestamp
+ << ", bytes used: " << buffer->_d()->metadata().planes()[0].bytesused;
+
+ isp_[Isp::Params].queueBuffer(buffer);
+
+ /* Queue input buffer */
+ buffer = unicam_[Unicam::Image].getBuffers().at(bayerId).buffer;
+ LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << bayerId
<< ", timestamp: " << buffer->metadata().timestamp;
isp_[Isp::Input].queueBuffer(buffer);
if (sensorMetadata_ && embeddedId) {
- buffer = unicam_[Unicam::Embedded].getBuffers().at(embeddedId & RPi::MaskID).buffer;
+ buffer = unicam_[Unicam::Embedded].getBuffers().at(embeddedId).buffer;
handleStreamBuffer(buffer, &unicam_[Unicam::Embedded]);
}
handleState();
}
-void Vc4CameraData::setIspControls(const ControlList &controls)
-{
- ControlList ctrls = controls;
-
- if (ctrls.contains(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING)) {
- ControlValue &value =
- const_cast<ControlValue &>(ctrls.get(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING));
- Span<uint8_t> s = value.data();
- bcm2835_isp_lens_shading *ls =
- reinterpret_cast<bcm2835_isp_lens_shading *>(s.data());
- ls->dmabuf = lsTable_.get();
- }
-
- isp_[Isp::Input].dev()->setControls(&ctrls);
- handleState();
-}
-
void Vc4CameraData::setCameraTimeout(uint32_t maxFrameLengthMs)
{
/*
@@ -1026,9 +1041,6 @@ void Vc4CameraData::tryRunPipeline()
unsigned int bayer = unicam_[Unicam::Image].getBufferId(bayerFrame.buffer);
- LOG(RPI, Debug) << "Signalling prepareIsp:"
- << " Bayer buffer id: " << bayer;
-
ipa::RPi::PrepareParams params;
params.buffers.bayer = RPi::MaskBayerData | bayer;
params.sensorControls = std::move(bayerFrame.controls);
@@ -1037,6 +1049,16 @@ void Vc4CameraData::tryRunPipeline()
params.delayContext = bayerFrame.delayContext;
params.buffers.embedded = 0;
+ const RPi::BufferObject ¶mBufObj = isp_[Isp::Params].acquireBuffer();
+ ASSERT(paramBufObj.mapped);
+
+ unsigned int param = isp_[Isp::Params].getBufferId(paramBufObj.buffer);
+ params.buffers.params = RPi::MaskParams | param;
+
+ LOG(RPI, Debug) << "Signalling prepareIsp:"
+ << " Bayer buffer id: " << bayer
+ << " Param buffer id: " << param;
+
if (embeddedBuffer) {
unsigned int embeddedId = unicam_[Unicam::Embedded].getBufferId(embeddedBuffer);
The downstream VC4 (BCM2835) ISP driver used custom V4L2 controls to pass per-frame ISP configuration from userspace, which diverged from upstream ISP drivers (rkisp1, mali-c55, PiSP) which all use parameter buffers. As the driver is being upstreamed now, switch the VC4 IPA and pipeline handler to use the extensible parameter buffer interface instead. Introduce a Bcm2835Params helper class (params.h) that wraps V4L2Params. Each algorithm's applyXYZ function now writes directly into typed parameter blocks of the params buffer using this class. On the pipeline handler side, update the ISP media entity names to match the upstream driver. Register the new bcm2835-isp-params metadata output node as an additional ISP stream. Parameter buffer handling is a bit more complicated compared to PiSP, as we use extensible buffers. So, the buffer is acquired per frame in tryRunPipeline(), and is mapped and passed to the IPA for populating. The IPA signals the completion along with the actual number of bytes used in prepareIspComplete(). Also clean up the now unused ISP control related code, and populate the lens shading DMABUF in the IPA itself instead of patching it in the pipeline handler. Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com> --- include/libcamera/ipa/raspberrypi.mojom | 15 +- src/ipa/rpi/common/ipa_base.cpp | 11 +- src/ipa/rpi/common/ipa_base.h | 3 + src/ipa/rpi/vc4/params.h | 77 ++++++++ src/ipa/rpi/vc4/vc4.cpp | 232 +++++++++++-------------- src/libcamera/pipeline/rpi/common/rpi_stream.h | 1 + src/libcamera/pipeline/rpi/pisp/pisp.cpp | 6 +- src/libcamera/pipeline/rpi/vc4/vc4.cpp | 108 +++++++----- 8 files changed, 260 insertions(+), 193 deletions(-)