@@ -11,12 +11,14 @@
#include <memory>
#include <set>
#include <string>
+#include <variant>
#include <linux/mali-c55-config.h>
#include <linux/media-bus-format.h>
#include <linux/media.h>
#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
#include <libcamera/camera.h>
#include <libcamera/formats.h>
@@ -92,17 +94,77 @@ struct MaliC55FrameInfo {
class MaliC55CameraData : public Camera::Private
{
public:
- MaliC55CameraData(PipelineHandler *pipe, MediaEntity *entity)
- : Camera::Private(pipe), entity_(entity)
+ struct Tpg {
+ std::vector<Size> sizes(unsigned int mbusCode) const;
+
+ Size resolution_;
+ std::unique_ptr<V4L2Subdevice> sd_;
+ };
+
+ struct Inline {
+ std::unique_ptr<V4L2Subdevice> csi2_;
+ std::unique_ptr<CameraSensor> sensor_;
+ };
+ using CameraType = std::variant<Tpg, Inline>;
+
+ MaliC55CameraData(PipelineHandler *pipe)
+ : Camera::Private(pipe)
{
}
- int init();
int loadIPA();
- /* Deflect these functionalities to either TPG or CameraSensor. */
- std::vector<Size> sizes(unsigned int mbusCode) const;
- Size resolution() const;
+ Tpg *initTpg(MediaEntity *entity);
+ Inline *initInline(MediaEntity *entity);
+
+ std::vector<Size> sizes(unsigned int mbusCode) const
+ {
+ return std::visit(utils::overloaded{
+ [&](const Tpg &tpg) -> std::vector<Size> {
+ return tpg.sizes(mbusCode);
+ },
+ [&](const Inline &in) -> std::vector<Size> {
+ return in.sensor_->sizes(mbusCode);
+ }
+ }, input_);
+ }
+
+ V4L2Subdevice *subdev() const
+ {
+ return std::visit(utils::overloaded{
+ [&](const Tpg &tpg) -> V4L2Subdevice * {
+ return tpg.sd_.get();
+ },
+ [&](const Inline &in) -> V4L2Subdevice * {
+ return in.sensor_->device();
+ },
+ }, input_);
+ }
+
+ CameraSensor *sensor() const
+ {
+ return std::visit(utils::overloaded{
+ [&](auto &) -> CameraSensor * {
+ ASSERT(false);
+ return nullptr;
+ },
+ [&](const Inline &in) -> CameraSensor * {
+ return in.sensor_.get();
+ },
+ }, input_);
+ }
+
+ Size resolution() const
+ {
+ return std::visit(utils::overloaded{
+ [&](const Tpg &tpg) -> Size {
+ return tpg.resolution_;
+ },
+ [&](const Inline &in) -> Size {
+ return in.sensor_->resolution();
+ },
+ }, input_);
+ }
int pixfmtToMbusCode(const PixelFormat &pixFmt) const;
const PixelFormat &bestRawFormat() const;
@@ -112,11 +174,6 @@ public:
PixelFormat adjustRawFormat(const PixelFormat &pixFmt) const;
Size adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const;
- std::unique_ptr<CameraSensor> sensor_;
-
- MediaEntity *entity_;
- std::unique_ptr<V4L2Subdevice> csi_;
- std::unique_ptr<V4L2Subdevice> sd_;
Stream frStream_;
Stream dsStream_;
@@ -126,58 +183,28 @@ public:
std::unique_ptr<DelayedControls> delayedCtrls_;
+ CameraType input_;
+
private:
- void initTPGData();
void setSensorControls(const ControlList &sensorControls);
-
std::string id_;
- Size tpgResolution_;
};
-int MaliC55CameraData::init()
+MaliC55CameraData::Tpg *MaliC55CameraData::initTpg(MediaEntity *entity)
{
- int ret;
+ Tpg tpg;
- sd_ = std::make_unique<V4L2Subdevice>(entity_);
- ret = sd_->open();
+ tpg.sd_ = std::make_unique<V4L2Subdevice>(entity);
+ int ret = tpg.sd_->open();
if (ret) {
- LOG(MaliC55, Error) << "Failed to open sensor subdevice";
- return ret;
- }
-
- /* If this camera is created from TPG, we return here. */
- if (entity_->name() == "mali-c55 tpg") {
- initTPGData();
- return 0;
- }
-
- /*
- * Register a CameraSensor if we connect to a sensor and create
- * an entity for the connected CSI-2 receiver.
- */
- sensor_ = CameraSensorFactoryBase::create(entity_);
- if (!sensor_)
- return -ENODEV;
-
- const MediaPad *sourcePad = entity_->getPadByIndex(0);
- MediaEntity *csiEntity = sourcePad->links()[0]->sink()->entity();
-
- csi_ = std::make_unique<V4L2Subdevice>(csiEntity);
- ret = csi_->open();
- if (ret) {
- LOG(MaliC55, Error) << "Failed to open CSI-2 subdevice";
- return ret;
+ LOG(MaliC55, Error) << "Failed to open TPG subdevice";
+ return nullptr;
}
- return 0;
-}
-
-void MaliC55CameraData::initTPGData()
-{
/* Replicate the CameraSensor implementation for TPG. */
- V4L2Subdevice::Formats formats = sd_->formats(0);
+ V4L2Subdevice::Formats formats = tpg.sd_->formats(0);
if (formats.empty())
- return;
+ return nullptr;
std::vector<Size> tpgSizes;
@@ -187,19 +214,35 @@ void MaliC55CameraData::initTPGData()
[](const SizeRange &range) { return range.max; });
}
- tpgResolution_ = tpgSizes.back();
+ tpg.resolution_ = tpgSizes.back();
+
+ return &input_.emplace<Tpg>(std::move(tpg));
}
-void MaliC55CameraData::setSensorControls(const ControlList &sensorControls)
+MaliC55CameraData::Inline *MaliC55CameraData::initInline(MediaEntity *sensor)
{
- delayedCtrls_->push(sensorControls);
+ Inline in;
+
+ /* Register a CameraSensor and create an entity for the CSI-2 receiver. */
+ in.sensor_ = CameraSensorFactoryBase::create(sensor);
+ if (!in.sensor_)
+ return nullptr;
+
+ const MediaPad *sourcePad = sensor->getPadByIndex(0);
+ MediaEntity *csiEntity = sourcePad->links()[0]->sink()->entity();
+
+ in.csi2_ = std::make_unique<V4L2Subdevice>(csiEntity);
+ int ret = in.csi2_->open();
+ if (ret) {
+ LOG(MaliC55, Error) << "Failed to open CSI-2 subdevice";
+ return nullptr;
+ }
+
+ return &input_.emplace<Inline>(std::move(in));
}
-std::vector<Size> MaliC55CameraData::sizes(unsigned int mbusCode) const
+std::vector<Size> MaliC55CameraData::Tpg::sizes(unsigned int mbusCode) const
{
- if (sensor_)
- return sensor_->sizes(mbusCode);
-
V4L2Subdevice::Formats formats = sd_->formats(0);
if (formats.empty())
return {};
@@ -218,12 +261,9 @@ std::vector<Size> MaliC55CameraData::sizes(unsigned int mbusCode) const
return sizes;
}
-Size MaliC55CameraData::resolution() const
+void MaliC55CameraData::setSensorControls(const ControlList &sensorControls)
{
- if (sensor_)
- return sensor_->resolution();
-
- return tpgResolution_;
+ delayedCtrls_->push(sensorControls);
}
/*
@@ -242,7 +282,7 @@ int MaliC55CameraData::pixfmtToMbusCode(const PixelFormat &pixFmt) const
if (!bayerFormat.isValid())
return -EINVAL;
- V4L2Subdevice::Formats formats = sd_->formats(0);
+ V4L2Subdevice::Formats formats = subdev()->formats(0);
unsigned int sensorMbusCode = 0;
unsigned int bitDepth = 0;
@@ -280,7 +320,7 @@ const PixelFormat &MaliC55CameraData::bestRawFormat() const
{
static const PixelFormat invalidPixFmt = {};
- for (const auto &fmt : sd_->formats(0)) {
+ for (const auto &fmt : subdev()->formats(0)) {
BayerFormat sensorBayer = BayerFormat::fromMbusCode(fmt.first);
if (!sensorBayer.isValid())
@@ -302,11 +342,11 @@ const PixelFormat &MaliC55CameraData::bestRawFormat() const
void MaliC55CameraData::updateControls(const ControlInfoMap &ipaControls)
{
- if (!sensor_)
+ if (std::holds_alternative<Tpg>(input_))
return;
IPACameraSensorInfo sensorInfo;
- int ret = sensor_->sensorInfo(&sensorInfo);
+ int ret = sensor()->sensorInfo(&sensorInfo);
if (ret) {
LOG(MaliC55, Error) << "Failed to retrieve sensor info";
return;
@@ -379,7 +419,7 @@ int MaliC55CameraData::loadIPA()
int ret;
/* Do not initialize IPA for TPG. */
- if (!sensor_)
+ if (std::holds_alternative<Tpg>(input_))
return 0;
ipa_ = IPAManager::createIPA<ipa::mali_c55::IPAProxyMaliC55>(pipe(), 1, 1);
@@ -388,20 +428,20 @@ int MaliC55CameraData::loadIPA()
ipa_->setSensorControls.connect(this, &MaliC55CameraData::setSensorControls);
- std::string ipaTuningFile = ipa_->configurationFile(sensor_->model() + ".yaml",
+ std::string ipaTuningFile = ipa_->configurationFile(sensor()->model() + ".yaml",
"uncalibrated.yaml");
/* We need to inform the IPA of the sensor configuration */
ipa::mali_c55::IPAConfigInfo ipaConfig{};
- ret = sensor_->sensorInfo(&ipaConfig.sensorInfo);
+ ret = sensor()->sensorInfo(&ipaConfig.sensorInfo);
if (ret)
return ret;
- ipaConfig.sensorControls = sensor_->controls();
+ ipaConfig.sensorControls = sensor()->controls();
ControlInfoMap ipaControls;
- ret = ipa_->init({ ipaTuningFile, sensor_->model() }, ipaConfig,
+ ret = ipa_->init({ ipaTuningFile, sensor()->model() }, ipaConfig,
&ipaControls);
if (ret) {
LOG(MaliC55, Error) << "Failed to initialise the Mali-C55 IPA";
@@ -444,13 +484,13 @@ CameraConfiguration::Status MaliC55CameraConfiguration::validate()
* The TPG doesn't support flips, so we only need to calculate a
* transform if we have a sensor.
*/
- if (data_->sensor_) {
+ if (std::holds_alternative<MaliC55CameraData::Tpg>(data_->input_)) {
+ combinedTransform_ = Transform::Rot0;
+ } else {
Orientation requestedOrientation = orientation;
- combinedTransform_ = data_->sensor_->computeTransform(&orientation);
+ combinedTransform_ = data_->sensor()->computeTransform(&orientation);
if (orientation != requestedOrientation)
status = Adjusted;
- } else {
- combinedTransform_ = Transform::Rot0;
}
/* Only 2 streams available. */
@@ -927,39 +967,44 @@ int PipelineHandlerMaliC55::configure(Camera *camera,
/* Link the graph depending if we are operating the TPG or a sensor. */
MaliC55CameraData *data = cameraData(camera);
- if (data->csi_) {
- const MediaEntity *csiEntity = data->csi_->entity();
- ret = csiEntity->getPadByIndex(1)->links()[0]->setEnabled(true);
- } else {
- ret = data->entity_->getPadByIndex(0)->links()[0]->setEnabled(true);
- }
+ ret = std::visit(utils::overloaded{
+ [](MaliC55CameraData::Tpg &tpg) {
+ const MediaEntity *tpgEntity = tpg.sd_->entity();
+ return tpgEntity->getPadByIndex(0)->links()[0]->setEnabled(true);
+ },
+ [](MaliC55CameraData::Inline &in) {
+ const MediaEntity *csi2Entity = in.csi2_->entity();
+ return csi2Entity->getPadByIndex(1)->links()[0]->setEnabled(true);
+ },
+ }, data->input_);
if (ret)
return ret;
MaliC55CameraConfiguration *maliConfig =
static_cast<MaliC55CameraConfiguration *>(config);
V4L2SubdeviceFormat subdevFormat = maliConfig->sensorFormat_;
- ret = data->sd_->getFormat(0, &subdevFormat);
+
+ /* Apply format to the origin of the pipeline and propagate it. */
+ ret = std::visit(utils::overloaded{
+ [&](MaliC55CameraData::Tpg &tpg) {
+ return tpg.sd_->setFormat(0, &subdevFormat);
+ },
+ [&](MaliC55CameraData::Inline &in) {
+ int r = in.sensor_->setFormat(&subdevFormat,
+ maliConfig->combinedTransform());
+ if (r)
+ return r;
+
+ r = in.csi2_->setFormat(0, &subdevFormat);
+ if (r)
+ return r;
+
+ return in.csi2_->getFormat(1, &subdevFormat);
+ },
+ }, data->input_);
if (ret)
return ret;
- if (data->sensor_) {
- ret = data->sensor_->setFormat(&subdevFormat,
- maliConfig->combinedTransform());
- if (ret)
- return ret;
- }
-
- if (data->csi_) {
- ret = data->csi_->setFormat(0, &subdevFormat);
- if (ret)
- return ret;
-
- ret = data->csi_->getFormat(1, &subdevFormat);
- if (ret)
- return ret;
- }
-
V4L2DeviceFormat statsFormat;
ret = stats_->getFormat(&statsFormat);
if (ret)
@@ -973,8 +1018,6 @@ int PipelineHandlerMaliC55::configure(Camera *camera,
/*
* Propagate the format to the ISP sink pad and configure the input
* crop rectangle (no crop at the moment).
- *
- * \todo Configure the CSI-2 receiver.
*/
ret = isp_->setFormat(0, &subdevFormat);
if (ret)
@@ -1058,18 +1101,18 @@ int PipelineHandlerMaliC55::configure(Camera *camera,
/* We need to inform the IPA of the sensor configuration */
ipa::mali_c55::IPAConfigInfo ipaConfig{};
- ret = data->sensor_->sensorInfo(&ipaConfig.sensorInfo);
+ ret = data->sensor()->sensorInfo(&ipaConfig.sensorInfo);
if (ret)
return ret;
- ipaConfig.sensorControls = data->sensor_->controls();
+ ipaConfig.sensorControls = data->sensor()->controls();
/*
* And we also need to tell the IPA the bayerOrder of the data (as
* affected by any flips that we've configured)
*/
const Transform &combinedTransform = maliConfig->combinedTransform();
- BayerFormat::Order bayerOrder = data->sensor_->bayerOrder(combinedTransform);
+ BayerFormat::Order bayerOrder = data->sensor()->bayerOrder(combinedTransform);
ControlInfoMap ipaControls;
ret = data->ipa_->configure(ipaConfig, utils::to_underlying(bayerOrder),
@@ -1283,7 +1326,7 @@ void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera,
if (!scalerCrop)
return;
- if (!data->sensor_) {
+ if (std::holds_alternative<MaliC55CameraData::Tpg>(data->input_)) {
LOG(MaliC55, Error) << "ScalerCrop not supported for TPG";
return;
}
@@ -1291,7 +1334,7 @@ void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera,
Rectangle nativeCrop = *scalerCrop;
IPACameraSensorInfo sensorInfo;
- int ret = data->sensor_->sensorInfo(&sensorInfo);
+ int ret = data->sensor()->sensorInfo(&sensorInfo);
if (ret) {
LOG(MaliC55, Error) << "Failed to retrieve sensor info";
return;
@@ -1573,9 +1616,9 @@ bool PipelineHandlerMaliC55::registerTPGCamera(MediaLink *link)
}
std::unique_ptr<MaliC55CameraData> data =
- std::make_unique<MaliC55CameraData>(this, link->source()->entity());
+ std::make_unique<MaliC55CameraData>(this);
- if (data->init())
+ if (!data->initTpg(link->source()->entity()))
return false;
return registerMaliCamera(std::move(data), name);
@@ -1600,21 +1643,24 @@ bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink)
continue;
std::unique_ptr<MaliC55CameraData> data =
- std::make_unique<MaliC55CameraData>(this, sensor);
- if (data->init())
+ std::make_unique<MaliC55CameraData>(this);
+
+ auto *in = data->initInline(sensor);
+ if (!in)
return false;
- data->properties_ = data->sensor_->properties();
+ data->properties_ = in->sensor_->properties();
- const CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays();
+ const CameraSensorProperties::SensorDelays &delays =
+ in->sensor_->sensorDelays();
std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
{ V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
{ V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },
};
- data->delayedCtrls_ =
- std::make_unique<DelayedControls>(data->sensor_->device(),
- params);
+ V4L2Subdevice *sensorSubdev = in->sensor_->device();
+ data->delayedCtrls_ = std::make_unique<DelayedControls>(sensorSubdev,
+ params);
isp_->frameStart.connect(data->delayedCtrls_.get(),
&DelayedControls::applyControls);