@@ -818,6 +818,7 @@ bool V4L2M2MConverter::supportsRequests()
static std::initializer_list<std::string> compatibles = {
"mtk-mdp",
"pxp",
+ "qcom-camss-ope",
};
REGISTER_CONVERTER("v4l2_m2m", V4L2M2MConverter, compatibles)
@@ -38,6 +38,7 @@
#include "camss_csi.h"
#include "camss_frames.h"
#include "camss_isp.h"
+#include "camss_isp_ope.h"
#include "camss_isp_soft.h"
namespace libcamera {
@@ -571,10 +572,13 @@ bool PipelineHandlerCamss::match(DeviceEnumerator *enumerator)
data->delayedCtrls_ =
std::make_unique<DelayedControls>(sensor->device(), params);
- data->isp_ = std::make_unique<CamssIspSoft>(this, sensor, &data->controlInfo_);
- if (!data->isp_->isValid()) {
- LOG(Camss, Error) << "Failed to create software ISP";
- continue;
+ data->isp_ = CamssIspOpe::match(this, enumerator, sensor, &data->controlInfo_);
+ if (data->isp_ == nullptr) {
+ data->isp_ = std::make_unique<CamssIspSoft>(this, sensor, &data->controlInfo_);
+ if (!data->isp_->isValid()) {
+ LOG(Camss, Error) << "Failed to create software ISP";
+ continue;
+ }
}
data->isp_->inputBufferReady.connect(data->csi_.get(),
new file mode 100644
@@ -0,0 +1,229 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Qualcomm CAMSS OPE ISP class
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include "camss_isp_ope.h"
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/controls.h>
+#include <libcamera/geometry.h>
+#include <libcamera/request.h>
+#include <libcamera/stream.h>
+
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/converter.h"
+#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/media_device.h"
+#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/v4l2_subdevice.h"
+#include "libcamera/internal/v4l2_videodevice.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(Camss)
+
+/**
+ * \class CamssIspOpe
+ * \brief CAMSS ISP class for the Offline Processing Engine (OPE) ISP
+ */
+
+/**
+ * \brief Constructs CamssIspOpe object
+ * \param[in] pipe The pipeline handler in use
+ * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline
+ * \param[out] ControlInfoMap to which to add ISP provided controls
+ */
+CamssIspOpe::CamssIspOpe([[maybe_unused]] PipelineHandler *pipe, const CameraSensor *sensor, [[maybe_unused]] ControlInfoMap *ispControls)
+ : sensor_(sensor)
+{
+#if 0
+ swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor, ispControls);
+
+ swIsp_->ispStatsReady.connect(this,
+ [&](uint32_t frame, uint32_t bufferId) {
+ statsReady.emit(frame, bufferId);
+ });
+ swIsp_->metadataReady.connect(this,
+ [&](uint32_t frame, const ControlList &metadata) {
+ metadataReady.emit(frame, metadata);
+ });
+ swIsp_->setSensorControls.connect(this,
+ [&](const ControlList &sensorControls) {
+ setSensorControls.emit(sensorControls);
+ });
+#endif
+}
+
+CamssIspOpe::~CamssIspOpe() = default;
+
+bool CamssIspOpe::isValid()
+{
+ return true;
+}
+
+StreamConfiguration CamssIspOpe::generateConfiguration(const StreamConfiguration &raw) const
+{
+ /* Converters support the same size-ranges for all output formats */
+ std::vector<PixelFormat> pixelFormats = converter_->formats(raw.pixelFormat);
+ SizeRange sizes = converter_->sizes(raw.size);
+
+ if (sizes.max.isNull() || pixelFormats.empty())
+ return {};
+
+ std::vector<SizeRange> sizesVector = { sizes };
+ std::map<PixelFormat, std::vector<SizeRange>> formats;
+
+ for (unsigned int i = 0; i < pixelFormats.size(); i++)
+ formats[pixelFormats[i]] = sizesVector;
+
+ StreamConfiguration cfg{ StreamFormats{ formats } };
+ cfg.size = sizes.max;
+ cfg.pixelFormat = pixelFormats[0];
+ cfg.bufferCount = kBufferCount;
+
+ return cfg;
+}
+
+namespace {
+
+/*
+ * \todo copy-pasted from src/libcamera/pipeline/simple/simple.cpp turn this
+ * into a member of SizeRange ?
+ */
+static Size adjustSize(const Size &requestedSize, const SizeRange &supportedSizes)
+{
+ ASSERT(supportedSizes.min <= supportedSizes.max);
+
+ if (supportedSizes.min == supportedSizes.max)
+ return supportedSizes.max;
+
+ unsigned int hStep = supportedSizes.hStep;
+ unsigned int vStep = supportedSizes.vStep;
+
+ if (hStep == 0)
+ hStep = supportedSizes.max.width - supportedSizes.min.width;
+ if (vStep == 0)
+ vStep = supportedSizes.max.height - supportedSizes.min.height;
+
+ Size adjusted = requestedSize.boundedTo(supportedSizes.max)
+ .expandedTo(supportedSizes.min);
+
+ return adjusted.shrunkBy(supportedSizes.min)
+ .alignedDownTo(hStep, vStep)
+ .grownBy(supportedSizes.min);
+}
+
+} /* namespace */
+
+StreamConfiguration CamssIspOpe::validate(const StreamConfiguration &raw, const StreamConfiguration &req) const
+{
+ StreamConfiguration cfg;
+
+ std::vector<PixelFormat> formats = converter_->formats(raw.pixelFormat);
+ SizeRange sizes = converter_->sizes(raw.size);
+
+ cfg.size = adjustSize(req.size, sizes);
+
+ if (cfg.size.isNull() || formats.empty())
+ return {};
+
+ for (unsigned int i = 0; i < formats.size(); i++) {
+ if (formats[i] == req.pixelFormat)
+ cfg.pixelFormat = req.pixelFormat;
+ }
+
+ if (!cfg.pixelFormat.isValid())
+ cfg.pixelFormat = formats[0];
+
+ std::tie(cfg.stride, cfg.frameSize) =
+ converter_->strideAndFrameSize(cfg.pixelFormat, cfg.size);
+
+ cfg.bufferCount = std::max(kBufferCount, req.bufferCount);
+ cfg.setStream(const_cast<Stream *>(&outStream_));
+
+ return cfg;
+}
+
+int CamssIspOpe::configure(const StreamConfiguration &inputCfg,
+ const StreamConfiguration &outputCfg)
+{
+ std::vector<std::reference_wrapper<const StreamConfiguration>> outputCfgs;
+ outputCfgs.push_back(outputCfg);
+
+ int ret = converter_->configure(inputCfg, outputCfgs);
+ if (ret)
+ return ret;
+
+ converter_->inputBufferReady.connect(this,
+ [&](FrameBuffer *f) { inputBufferReady.emit(f); });
+ converter_->outputBufferReady.connect(this,
+ [&](FrameBuffer *f) {
+ // HACK FIXME
+ metadataReady.emit(f->metadata().sequence, {});
+ outputBufferReady.emit(f);
+ });
+
+ return 0;
+}
+
+int CamssIspOpe::exportOutputBuffers(const Stream *stream, unsigned int count,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+ return converter_->exportBuffers(stream, count, buffers);
+}
+
+void CamssIspOpe::queueBuffers(Request *request, FrameBuffer *input)
+{
+ std::map<const Stream *, FrameBuffer *> outputs;
+ for (const auto &[stream, outbuffer] : request->buffers()) {
+ if (stream == &outStream_)
+ outputs[stream] = outbuffer;
+ }
+
+ // swIsp_->queueRequest(request->sequence(), request->controls());
+ converter_->queueBuffers(input, outputs);
+}
+
+void CamssIspOpe::processStats([[maybe_unused]] const uint32_t frame, [[maybe_unused]] const uint32_t bufferId,
+ [[maybe_unused]] const ControlList &sensorControls)
+{
+ // swIsp_->processStats(frame, bufferId, sensorControls);
+}
+
+int CamssIspOpe::start()
+{
+ return converter_->start();
+}
+
+void CamssIspOpe::stop()
+{
+ converter_->stop();
+}
+
+std::unique_ptr<CamssIspOpe> CamssIspOpe::match(PipelineHandler *pipe,
+ DeviceEnumerator *enumerator,
+ const CameraSensor *sensor,
+ ControlInfoMap *ispControls)
+{
+ std::unique_ptr<CamssIspOpe> ope = std::make_unique<CamssIspOpe>(pipe, sensor, ispControls);
+
+ DeviceMatch opeDm("qcom-camss-ope");
+
+ ope->opeMediaDev_ = pipe->acquireMediaDevice(enumerator, opeDm);
+ if (!ope->opeMediaDev_)
+ return nullptr;
+
+ ope->converter_ = ConverterFactoryBase::create(ope->opeMediaDev_);
+ if (!ope->converter_)
+ return nullptr;
+
+ LOG(Camss, Info) << "Using OPE for " << sensor->entity()->name();
+
+ return ope;
+}
+
+} /* namespace libcamera */
new file mode 100644
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Qualcomm CAMSS OPE ISP class
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "camss_isp.h"
+
+namespace libcamera {
+
+class CameraSensor;
+class ControlInfoMap;
+class Converter;
+class DeviceEnumerator;
+class MediaDevice;
+class PipelineHandler;
+class SoftwareIsp;
+
+class CamssIspOpe : public CamssIsp
+{
+public:
+ static std::unique_ptr<CamssIspOpe> match(PipelineHandler *pipe,
+ DeviceEnumerator *enumerator,
+ const CameraSensor *sensor,
+ ControlInfoMap *ispControls);
+
+ CamssIspOpe(PipelineHandler *pipe, const CameraSensor *sensor, ControlInfoMap *ispControls);
+ ~CamssIspOpe() override;
+
+ bool isValid() override;
+
+ StreamConfiguration generateConfiguration(const StreamConfiguration &raw) const override;
+ StreamConfiguration validate(const StreamConfiguration &raw, const StreamConfiguration &req) const override;
+ int configure(const StreamConfiguration &inputCfg,
+ const StreamConfiguration &outputCfg) override;
+
+ int exportOutputBuffers(const Stream *stream, unsigned int count,
+ std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
+ void queueBuffers(Request *request, FrameBuffer *input) override;
+
+ void processStats(const uint32_t frame, const uint32_t bufferId,
+ const ControlList &sensorControls) override;
+
+ int start() override;
+ void stop() override;
+
+private:
+ std::shared_ptr<MediaDevice> opeMediaDev_;
+ std::unique_ptr<Converter> converter_;
+ const CameraSensor *sensor_;
+};
+
+} /* namespace libcamera */
@@ -5,5 +5,6 @@ libcamera_internal_sources += files([
'camss_csi.cpp',
'camss_frames.cpp',
'camss_isp.cpp',
+ 'camss_isp_ope.cpp',
'camss_isp_soft.cpp',
])
This adds support for the OPE through the RFC OPE M2M kernel driver posted upstream recently. The M2M OPE kernel driver is mainly a RFC driver and this driver will need to be changed into a proper ISP driver with e.g. a second /dev/video# input node for passing parameter-buffers to the ISP. Likewise this libcamera OPE support should mostly be seen as a proof of concept of the CamssIsp virtual base class abstraction and this will need to change / evolve to also grow an IPA and parameter buffer support as the kernel driver evolves. Link: https://lore.kernel.org/linux-media/20260323125824.211615-1-loic.poulain@oss.qualcomm.com/ Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com> --- .../converter/converter_v4l2_m2m.cpp | 1 + src/libcamera/pipeline/camss/camss.cpp | 12 +- .../pipeline/camss/camss_isp_ope.cpp | 229 ++++++++++++++++++ src/libcamera/pipeline/camss/camss_isp_ope.h | 59 +++++ src/libcamera/pipeline/camss/meson.build | 1 + 5 files changed, 298 insertions(+), 4 deletions(-) create mode 100644 src/libcamera/pipeline/camss/camss_isp_ope.cpp create mode 100644 src/libcamera/pipeline/camss/camss_isp_ope.h