[{"id":29924,"web_url":"https://patchwork.libcamera.org/comment/29924/","msgid":"<45ffacf9-1639-4da7-b250-067ce547442a@ideasonboard.com>","date":"2024-06-13T14:09:21","subject":"Re: [PATCH 05/10] mali-c55: Plumb the IPA module in","submitter":{"id":156,"url":"https://patchwork.libcamera.org/api/people/156/","name":"Dan Scally","email":"dan.scally@ideasonboard.com"},"content":"Hi Jacopo\n\nOn 13/06/2024 14:25, Daniel Scally wrote:\n> From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n>\n> Plumb the Pipeline-IPA loop in.\n>\n> Load the IPA module at camera creation time and create the loop between\n> the pipeline and the IPA.\n>\n> When a new Request is queued the IPA is asked to prepare the parameters\n> buffer, once ready it notifies the pipeline which queues the parameters\n> to the ISP along with a buffer for statistics and frames,\n>\n> Once statistics are ready they get passed to the IPA which upates its\n> settings for the next frame.\n>\n> Acked-by: Nayden Kanchev  <nayden.kanchev@arm.com>\n> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> ---\nI just realised I should have squashed the patch that skips the IPA if the camera in use is the TPG \ninto this one when I collected everything; sorry about that. I've done that now in my tree  ready \nfor the v2.\n>   src/libcamera/pipeline/mali-c55/mali-c55.cpp | 353 +++++++++++++++++--\n>   1 file changed, 314 insertions(+), 39 deletions(-)\n>\n> diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp\n> index b9be1207..871674cb 100644\n> --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp\n> +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp\n> @@ -23,13 +23,19 @@\n>   #include <libcamera/geometry.h>\n>   #include <libcamera/stream.h>\n>   \n> +#include <libcamera/ipa/mali-c55_ipa_interface.h>\n> +#include <libcamera/ipa/mali-c55_ipa_proxy.h>\n> +\n>   #include \"libcamera/internal/bayer_format.h\"\n>   #include \"libcamera/internal/camera.h\"\n>   #include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/delayed_controls.h\"\n>   #include \"libcamera/internal/device_enumerator.h\"\n>   #include \"libcamera/internal/framebuffer.h\"\n> +#include \"libcamera/internal/ipa_manager.h\"\n>   #include \"libcamera/internal/media_device.h\"\n>   #include \"libcamera/internal/pipeline_handler.h\"\n> +#include \"libcamera/internal/request.h\"\n>   #include \"libcamera/internal/v4l2_subdevice.h\"\n>   #include \"libcamera/internal/v4l2_videodevice.h\"\n>   \n> @@ -85,6 +91,16 @@ constexpr Size kMaliC55MinSize = { 128, 128 };\n>   constexpr Size kMaliC55MaxSize = { 8192, 8192 };\n>   constexpr unsigned int kMaliC55ISPInternalFormat = MEDIA_BUS_FMT_RGB121212_1X36;\n>   \n> +struct MaliC55FrameInfo {\n> +\tRequest *request;\n> +\n> +\tFrameBuffer *paramBuffer;\n> +\tFrameBuffer *statBuffer;\n> +\n> +\tbool paramsDone;\n> +\tbool statsDone;\n> +};\n> +\n>   class MaliC55CameraData : public Camera::Private\n>   {\n>   public:\n> @@ -94,6 +110,7 @@ public:\n>   \t}\n>   \n>   \tint init();\n> +\tint loadIPA();\n>   \n>   \t/* Deflect these functionalities to either TPG or CameraSensor. */\n>   \tconst std::vector<unsigned int> mbusCodes() const;\n> @@ -102,7 +119,7 @@ public:\n>   \n>   \tPixelFormat bestRawFormat() const;\n>   \n> -\tvoid updateControls();\n> +\tvoid updateControls(const ControlInfoMap &ipaControls);\n>   \n>   \tPixelFormat adjustRawFormat(const PixelFormat &pixFmt) const;\n>   \tSize adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const;\n> @@ -115,8 +132,15 @@ public:\n>   \tStream frStream_;\n>   \tStream dsStream_;\n>   \n> +\tstd::unique_ptr<ipa::mali_c55::IPAProxyMaliC55> ipa_;\n> +\tstd::vector<IPABuffer> ipaStatBuffers_;\n> +\tstd::vector<IPABuffer> ipaParamBuffers_;\n> +\n> +\tstd::unique_ptr<DelayedControls> delayedCtrls_;\n> +\n>   private:\n>   \tvoid initTPGData();\n> +\tvoid setSensorControls(const ControlList &sensorControls);\n>   \n>   \tstd::string id_;\n>   \tstd::vector<unsigned int> tpgCodes_;\n> @@ -181,6 +205,11 @@ void MaliC55CameraData::initTPGData()\n>   \ttpgResolution_ = tpgSizes_.back();\n>   }\n>   \n> +void MaliC55CameraData::setSensorControls(const ControlList &sensorControls)\n> +{\n> +\tdelayedCtrls_->push(sensorControls);\n> +}\n> +\n>   const std::vector<unsigned int> MaliC55CameraData::mbusCodes() const\n>   {\n>   \tif (sensor_)\n> @@ -249,7 +278,7 @@ PixelFormat MaliC55CameraData::bestRawFormat() const\n>   \treturn rawFormat;\n>   }\n>   \n> -void MaliC55CameraData::updateControls()\n> +void MaliC55CameraData::updateControls(const ControlInfoMap &ipaControls)\n>   {\n>   \tif (!sensor_)\n>   \t\treturn;\n> @@ -267,6 +296,9 @@ void MaliC55CameraData::updateControls()\n>   \t\tControlInfo(ispMinCrop, sensorInfo.analogCrop,\n>   \t\t\t    sensorInfo.analogCrop);\n>   \n> +\tfor (auto const &c : ipaControls)\n> +\t\tcontrols.emplace(c.first, c.second);\n> +\n>   \tcontrolInfo_ = ControlInfoMap(std::move(controls), controls::controls);\n>   }\n>   \n> @@ -321,6 +353,46 @@ Size MaliC55CameraData::adjustRawSizes(const PixelFormat &rawFmt, const Size &ra\n>   \treturn bestSize;\n>   }\n>   \n> +int MaliC55CameraData::loadIPA()\n> +{\n> +\tint ret;\n> +\n> +\tipa_ = IPAManager::createIPA<ipa::mali_c55::IPAProxyMaliC55>(pipe(), 1, 1);\n> +\tif (!ipa_)\n> +\t\treturn -ENOENT;\n> +\n> +\tipa_->setSensorControls.connect(this, &MaliC55CameraData::setSensorControls);\n> +\n> +\t/* Do not initialize IPA for TPG. */\n> +\tif (!sensor_)\n> +\t\treturn 0;\n> +\n> +\tstd::string ipaTuningFile = ipa_->configurationFile(sensor_->model() + \".yaml\");\n> +\tif (ipaTuningFile.empty())\n> +\t\tipaTuningFile = ipa_->configurationFile(\"uncalibrated.yaml\");\n> +\n> +\t/* We need to inform the IPA of the sensor configuration */\n> +\tipa::mali_c55::IPAConfigInfo ipaConfig{};\n> +\n> +\tret = sensor_->sensorInfo(&ipaConfig.sensorInfo);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tipaConfig.sensorControls = sensor_->controls();\n> +\n> +\tControlInfoMap ipaControls;\n> +\tret = ipa_->init({ ipaTuningFile, sensor_->model() }, ipaConfig,\n> +\t\t\t &ipaControls);\n> +\tif (ret) {\n> +\t\tLOG(MaliC55, Error) << \"Failed to initialise the Mali-C55 IPA\";\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tupdateControls(ipaControls);\n> +\n> +\treturn 0;\n> +}\n> +\n>   class MaliC55CameraConfiguration : public CameraConfiguration\n>   {\n>   public:\n> @@ -330,6 +402,7 @@ public:\n>   \t}\n>   \n>   \tStatus validate() override;\n> +\tconst Transform &combinedTransform() { return combinedTransform_; }\n>   \n>   \tV4L2SubdeviceFormat sensorFormat_;\n>   \n> @@ -337,6 +410,7 @@ private:\n>   \tstatic constexpr unsigned int kMaxStreams = 2;\n>   \n>   \tconst MaliC55CameraData *data_;\n> +\tTransform combinedTransform_;\n>   };\n>   \n>   CameraConfiguration::Status MaliC55CameraConfiguration::validate()\n> @@ -346,6 +420,11 @@ CameraConfiguration::Status MaliC55CameraConfiguration::validate()\n>   \tif (config_.empty())\n>   \t\treturn Invalid;\n>   \n> +\tOrientation requestedOrientation = orientation;\n> +\tcombinedTransform_ = data_->sensor_->computeTransform(&orientation);\n> +\tif (orientation != requestedOrientation)\n> +\t\tstatus = Adjusted;\n> +\n>   \t/* Only 2 streams available. */\n>   \tif (config_.size() > kMaxStreams) {\n>   \t\tconfig_.resize(kMaxStreams);\n> @@ -492,7 +571,10 @@ public:\n>   \tint queueRequestDevice(Camera *camera, Request *request) override;\n>   \n>   \tvoid bufferReady(FrameBuffer *buffer);\n> +\tvoid paramsBufferReady(FrameBuffer *buffer);\n>   \tvoid statsBufferReady(FrameBuffer *buffer);\n> +\tvoid paramsComputed(unsigned int requestId);\n> +\tvoid statsProcessed(unsigned int requestId, const ControlList &metadata);\n>   \n>   \tbool match(DeviceEnumerator *enumerator) override;\n>   \n> @@ -536,6 +618,10 @@ private:\n>   \t\t\tpipe.stream = nullptr;\n>   \t}\n>   \n> +\tMaliC55FrameInfo *findFrameInfo(FrameBuffer *buffer);\n> +\tMaliC55FrameInfo *findFrameInfo(Request *request);\n> +\tvoid tryComplete(MaliC55FrameInfo *info);\n> +\n>   \tint configureRawStream(MaliC55CameraData *data,\n>   \t\t\t       const StreamConfiguration &config,\n>   \t\t\t       V4L2SubdeviceFormat &subdevFormat);\n> @@ -545,7 +631,7 @@ private:\n>   \n>   \tvoid applyScalerCrop(Camera *camera, const ControlList &controls);\n>   \n> -\tvoid registerMaliCamera(std::unique_ptr<MaliC55CameraData> data,\n> +\tbool registerMaliCamera(std::unique_ptr<MaliC55CameraData> data,\n>   \t\t\t\tconst std::string &name);\n>   \tbool registerTPGCamera(MediaLink *link);\n>   \tbool registerSensorCamera(MediaLink *link);\n> @@ -561,6 +647,8 @@ private:\n>   \tstd::vector<std::unique_ptr<FrameBuffer>> paramsBuffers_;\n>   \tstd::queue<FrameBuffer *> availableParamsBuffers_;\n>   \n> +\tstd::map<unsigned int, MaliC55FrameInfo> frameInfoMap_;\n> +\n>   \tstd::array<MaliC55Pipe, MaliC55NumPipes> pipes_;\n>   \n>   \tbool dsFitted_;\n> @@ -806,6 +894,11 @@ int PipelineHandlerMaliC55::configure(Camera *camera,\n>   \tif (ret)\n>   \t\treturn ret;\n>   \n> +\tret = data->sensor_->setFormat(&subdevFormat,\n> +\t\t\t\t       maliConfig->combinedTransform());\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n>   \tif (data->csi_) {\n>   \t\tret = data->csi_->setFormat(0, &subdevFormat);\n>   \t\tif (ret)\n> @@ -876,7 +969,31 @@ int PipelineHandlerMaliC55::configure(Camera *camera,\n>   \t\tpipe->stream = stream;\n>   \t}\n>   \n> -\tdata->updateControls();\n> +\t/* We need to inform the IPA of the sensor configuration */\n> +\tipa::mali_c55::IPAConfigInfo ipaConfig{};\n> +\n> +\tret = data->sensor_->sensorInfo(&ipaConfig.sensorInfo);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tipaConfig.sensorControls = data->sensor_->controls();\n> +\n> +\t/*\n> +\t * And we also need to tell the IPA the bayerOrder of the data (as\n> +\t * affected by any flips that we've configured)\n> +\t */\n> +\tconst Transform &combinedTransform = maliConfig->combinedTransform();\n> +\tBayerFormat::Order bayerOrder = data->sensor_->bayerOrder(combinedTransform);\n> +\n> +\tControlInfoMap ipaControls;\n> +\tret = data->ipa_->configure(ipaConfig, utils::to_underlying(bayerOrder),\n> +\t\t\t\t    &ipaControls);\n> +\tif (ret) {\n> +\t\tLOG(MaliC55, Error) << \"Failed to configure IPA\";\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tdata->updateControls(ipaControls);\n>   \n>   \treturn 0;\n>   }\n> @@ -890,8 +1007,10 @@ int PipelineHandlerMaliC55::exportFrameBuffers(Camera *camera, Stream *stream,\n>   \treturn pipe->cap->exportBuffers(count, buffers);\n>   }\n>   \n> -void PipelineHandlerMaliC55::freeBuffers([[maybe_unused]] Camera *camera)\n> +void PipelineHandlerMaliC55::freeBuffers(Camera *camera)\n>   {\n> +\tMaliC55CameraData *data = cameraData(camera);\n> +\n>   \twhile (!availableStatsBuffers_.empty())\n>   \t\tavailableStatsBuffers_.pop();\n>   \twhile (!availableParamsBuffers_.empty())\n> @@ -900,11 +1019,16 @@ void PipelineHandlerMaliC55::freeBuffers([[maybe_unused]] Camera *camera)\n>   \tstatsBuffers_.clear();\n>   \tparamsBuffers_.clear();\n>   \n> +\tdata->ipa_->unmapBuffers(data->ipaStatBuffers_);\n> +\tdata->ipaStatBuffers_.clear();\n> +\tdata->ipa_->unmapBuffers(data->ipaParamBuffers_);\n> +\tdata->ipaParamBuffers_.clear();\n> +\n>   \tif (stats_->releaseBuffers())\n>   \t\tLOG(MaliC55, Error) << \"Failed to release stats buffers\";\n>   \n>   \tif (params_->releaseBuffers())\n> -\t\tLOG(MaliC55, Error) << \"Failed to release stats buffers\";\n> +\t\tLOG(MaliC55, Error) << \"Failed to release params buffers\";\n>   \n>   \treturn;\n>   }\n> @@ -912,6 +1036,7 @@ void PipelineHandlerMaliC55::freeBuffers([[maybe_unused]] Camera *camera)\n>   int PipelineHandlerMaliC55::allocateBuffers(Camera *camera)\n>   {\n>   \tMaliC55CameraData *data = cameraData(camera);\n> +\tunsigned int ipaBufferId = 1;\n>   \tunsigned int bufferCount;\n>   \tint ret;\n>   \n> @@ -924,27 +1049,47 @@ int PipelineHandlerMaliC55::allocateBuffers(Camera *camera)\n>   \tif (ret < 0)\n>   \t\treturn ret;\n>   \n> -\tfor (std::unique_ptr<FrameBuffer> &buffer : statsBuffers_)\n> +\tfor (std::unique_ptr<FrameBuffer> &buffer : statsBuffers_) {\n> +\t\tbuffer->setCookie(ipaBufferId++);\n> +\t\tdata->ipaStatBuffers_.emplace_back(buffer->cookie(),\n> +\t\t\t\t\t\t   buffer->planes());\n>   \t\tavailableStatsBuffers_.push(buffer.get());\n> +\t}\n> +\n> +\tdata->ipa_->mapBuffers(data->ipaStatBuffers_, true);\n>   \n>   \tret = params_->allocateBuffers(bufferCount, &paramsBuffers_);\n>   \tif (ret < 0)\n>   \t\treturn ret;\n>   \n> -\tfor (std::unique_ptr<FrameBuffer> &buffer : paramsBuffers_)\n> +\tfor (std::unique_ptr<FrameBuffer> &buffer : paramsBuffers_) {\n> +\t\tbuffer->setCookie(ipaBufferId++);\n> +\t\tdata->ipaParamBuffers_.emplace_back(buffer->cookie(),\n> +\t\t\t\t\t\t    buffer->planes());\n>   \t\tavailableParamsBuffers_.push(buffer.get());\n> +\t}\n> +\n> +\tdata->ipa_->mapBuffers(data->ipaParamBuffers_, false);\n>   \n>   \treturn 0;\n>   }\n>   \n>   int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const ControlList *controls)\n>   {\n> +\tMaliC55CameraData *data = cameraData(camera);\n>   \tint ret;\n>   \n>   \tret = allocateBuffers(camera);\n>   \tif (ret)\n>   \t\treturn ret;\n>   \n> +\tret = data->ipa_->start();\n> +\tif (ret) {\n> +\t\tLOG(MaliC55, Error) << \"Failed to start IPA\" << camera->id();\n> +\t\tfreeBuffers(camera);\n> +\t\treturn ret;\n> +\t}\n> +\n>   \tfor (MaliC55Pipe &pipe : pipes_) {\n>   \t\tif (!pipe.stream)\n>   \t\t\tcontinue;\n> @@ -954,6 +1099,7 @@ int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const Control\n>   \t\tret = pipe.cap->importBuffers(stream->configuration().bufferCount);\n>   \t\tif (ret) {\n>   \t\t\tLOG(MaliC55, Error) << \"Failed to import buffers\";\n> +\t\t\tdata->ipa_->stop();\n>   \t\t\tfreeBuffers(camera);\n>   \t\t\treturn ret;\n>   \t\t}\n> @@ -961,6 +1107,7 @@ int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const Control\n>   \t\tret = pipe.cap->streamOn();\n>   \t\tif (ret) {\n>   \t\t\tLOG(MaliC55, Error) << \"Failed to start stream\";\n> +\t\t\tdata->ipa_->stop();\n>   \t\t\tfreeBuffers(camera);\n>   \t\t\treturn ret;\n>   \t\t}\n> @@ -970,6 +1117,8 @@ int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const Control\n>   \tif (ret) {\n>   \t\tLOG(MaliC55, Error) << \"Failed to start stats stream\";\n>   \n> +\t\tdata->ipa_->stop();\n> +\n>   \t\tfor (MaliC55Pipe &pipe : pipes_) {\n>   \t\t\tif (pipe.stream)\n>   \t\t\t\tpipe.cap->streamOff();\n> @@ -984,6 +1133,7 @@ int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const Control\n>   \t\tLOG(MaliC55, Error) << \"Failed to start params stream\";\n>   \n>   \t\tstats_->streamOff();\n> +\t\tdata->ipa_->stop();\n>   \n>   \t\tfor (MaliC55Pipe &pipe : pipes_) {\n>   \t\t\tif (pipe.stream)\n> @@ -994,11 +1144,19 @@ int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const Control\n>   \t\treturn ret;\n>   \t}\n>   \n> +\tret = isp_->setFrameStartEnabled(true);\n> +\tif (ret)\n> +\t\tLOG(MaliC55, Error) << \"Failed to enable frame start events\";\n> +\n>   \treturn 0;\n>   }\n>   \n> -void PipelineHandlerMaliC55::stopDevice([[maybe_unused]] Camera *camera)\n> +void PipelineHandlerMaliC55::stopDevice(Camera *camera)\n>   {\n> +\tMaliC55CameraData *data = cameraData(camera);\n> +\n> +\tisp_->setFrameStartEnabled(false);\n> +\n>   \tfor (MaliC55Pipe &pipe : pipes_) {\n>   \t\tif (!pipe.stream)\n>   \t\t\tcontinue;\n> @@ -1009,6 +1167,7 @@ void PipelineHandlerMaliC55::stopDevice([[maybe_unused]] Camera *camera)\n>   \n>   \tstats_->streamOff();\n>   \tparams_->streamOff();\n> +\tdata->ipa_->stop();\n>   \tfreeBuffers(camera);\n>   }\n>   \n> @@ -1112,30 +1271,16 @@ void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera,\n>   \n>   int PipelineHandlerMaliC55::queueRequestDevice(Camera *camera, Request *request)\n>   {\n> -\tFrameBuffer *statsBuffer;\n> -\tint ret;\n> +\tMaliC55CameraData *data = cameraData(camera);\n>   \n>   \tif (availableStatsBuffers_.empty()) {\n>   \t\tLOG(MaliC55, Error) << \"Stats buffer underrun\";\n>   \t\treturn -ENOENT;\n>   \t}\n>   \n> -\tstatsBuffer = availableStatsBuffers_.front();\n> -\tavailableStatsBuffers_.pop();\n> -\n> -\t/*\n> -\t * We need to associate the Request to this buffer even though it's a\n> -\t * purely internal one because we will need to use request->sequence()\n> -\t * later.\n> -\t */\n> -\tstatsBuffer->_d()->setRequest(request);\n> -\n> -\tfor (auto &[stream, buffer] : request->buffers()) {\n> -\t\tMaliC55Pipe *pipe = pipeFromStream(cameraData(camera), stream);\n> -\n> -\t\tret = pipe->cap->queueBuffer(buffer);\n> -\t\tif (ret)\n> -\t\t\treturn ret;\n> +\tif (availableParamsBuffers_.empty()) {\n> +\t\tLOG(MaliC55, Error) << \"Params buffer underrun\";\n> +\t\treturn -ENOENT;\n>   \t}\n>   \n>   \t/*\n> @@ -1147,29 +1292,143 @@ int PipelineHandlerMaliC55::queueRequestDevice(Camera *camera, Request *request)\n>   \t */\n>   \tapplyScalerCrop(camera, request->controls());\n>   \n> -\tret = stats_->queueBuffer(statsBuffer);\n> -\tif (ret)\n> -\t\treturn ret;\n> +\tMaliC55FrameInfo frameInfo;\n> +\tframeInfo.request = request;\n> +\n> +\tframeInfo.statBuffer = availableStatsBuffers_.front();\n> +\tavailableStatsBuffers_.pop();\n> +\tframeInfo.paramBuffer = availableParamsBuffers_.front();\n> +\tavailableParamsBuffers_.pop();\n> +\n> +\tframeInfo.paramsDone = false;\n> +\tframeInfo.statsDone = false;\n> +\n> +\tframeInfoMap_[request->sequence()] = frameInfo;\n> +\n> +\tdata->ipa_->queueRequest(request->sequence(), request->controls());\n> +\tdata->ipa_->fillParams(request->sequence(),\n> +\t\t\t       frameInfo.paramBuffer->cookie());\n>   \n>   \treturn 0;\n>   }\n>   \n> +MaliC55FrameInfo *PipelineHandlerMaliC55::findFrameInfo(Request *request)\n> +{\n> +\tfor (auto &[sequence, info] : frameInfoMap_) {\n> +\t\tif (info.request == request)\n> +\t\t\treturn &info;\n> +\t}\n> +\n> +\treturn nullptr;\n> +}\n> +\n> +MaliC55FrameInfo *PipelineHandlerMaliC55::findFrameInfo(FrameBuffer *buffer)\n> +{\n> +\tfor (auto &[sequence, info] : frameInfoMap_) {\n> +\t\tif (info.paramBuffer == buffer ||\n> +\t\t    info.statBuffer == buffer)\n> +\t\t\treturn &info;\n> +\t}\n> +\n> +\treturn nullptr;\n> +}\n> +\n> +void PipelineHandlerMaliC55::tryComplete(MaliC55FrameInfo *info)\n> +{\n> +\tif (!info->paramsDone)\n> +\t\treturn;\n> +\tif (!info->statsDone)\n> +\t\treturn;\n> +\n> +\tRequest *request = info->request;\n> +\tif (request->hasPendingBuffers())\n> +\t\treturn;\n> +\n> +\tavailableStatsBuffers_.push(info->statBuffer);\n> +\tavailableParamsBuffers_.push(info->paramBuffer);\n> +\n> +\tframeInfoMap_.erase(request->sequence());\n> +\n> +\tcompleteRequest(request);\n> +}\n> +\n>   void PipelineHandlerMaliC55::bufferReady(FrameBuffer *buffer)\n>   {\n>   \tRequest *request = buffer->request();\n> +\tMaliC55FrameInfo *info = findFrameInfo(request);\n> +\tASSERT(info);\n>   \n>   \tif (completeBuffer(request, buffer))\n> -\t\tcompleteRequest(request);\n> +\t\ttryComplete(info);\n> +}\n> +\n> +void PipelineHandlerMaliC55::paramsBufferReady(FrameBuffer *buffer)\n> +{\n> +\tMaliC55FrameInfo *info = findFrameInfo(buffer);\n> +\tASSERT(info);\n> +\n> +\tinfo->paramsDone = true;\n> +\n> +\ttryComplete(info);\n>   }\n>   \n>   void PipelineHandlerMaliC55::statsBufferReady(FrameBuffer *buffer)\n>   {\n> -\tavailableStatsBuffers_.push(buffer);\n> +\tMaliC55FrameInfo *info = findFrameInfo(buffer);\n> +\tASSERT(info);\n> +\n> +\tRequest *request = info->request;\n> +\tMaliC55CameraData *data = cameraData(request->_d()->camera());\n> +\n> +\tControlList sensorControls = data->delayedCtrls_->get(buffer->metadata().sequence);\n> +\n> +\tdata->ipa_->processStats(request->sequence(), buffer->cookie(),\n> +\t\t\t\t sensorControls);\n> +}\n> +\n> +void PipelineHandlerMaliC55::paramsComputed(unsigned int requestId)\n> +{\n> +\tMaliC55FrameInfo &frameInfo = frameInfoMap_[requestId];\n> +\tRequest *request = frameInfo.request;\n> +\tMaliC55CameraData *data = cameraData(request->_d()->camera());\n> +\n> +\t/*\n> +\t * Queue buffers for stats and params, then queue buffers to the capture\n> +\t * video devices.\n> +\t */\n> +\n> +\tframeInfo.paramBuffer->_d()->metadata().planes()[0].bytesused =\n> +\t\tsizeof(struct mali_c55_params_buffer);\n> +\tparams_->queueBuffer(frameInfo.paramBuffer);\n> +\tstats_->queueBuffer(frameInfo.statBuffer);\n> +\n> +\tfor (auto &[stream, buffer] : request->buffers()) {\n> +\t\tMaliC55Pipe *pipe = pipeFromStream(data, stream);\n> +\n> +\t\tpipe->cap->queueBuffer(buffer);\n> +\t}\n>   }\n>   \n> -void PipelineHandlerMaliC55::registerMaliCamera(std::unique_ptr<MaliC55CameraData> data,\n> +void PipelineHandlerMaliC55::statsProcessed(unsigned int requestId,\n> +\t\t\t\t\t    const ControlList &metadata)\n> +{\n> +\tMaliC55FrameInfo &frameInfo = frameInfoMap_[requestId];\n> +\n> +\tframeInfo.statsDone = true;\n> +\tframeInfo.request->metadata().merge(metadata);\n> +\n> +\ttryComplete(&frameInfo);\n> +}\n> +\n> +bool PipelineHandlerMaliC55::registerMaliCamera(std::unique_ptr<MaliC55CameraData> data,\n>   \t\t\t\t\t\tconst std::string &name)\n>   {\n> +\tif (data->loadIPA())\n> +\t\treturn false;\n> +\n> +\tdata->ipa_->statsProcessed.connect(this, &PipelineHandlerMaliC55::statsProcessed);\n> +\tdata->ipa_->paramsComputed.connect(this, &PipelineHandlerMaliC55::paramsComputed);\n> +\n>   \tstd::set<Stream *> streams{ &data->frStream_ };\n>   \tif (dsFitted_)\n>   \t\tstreams.insert(&data->dsStream_);\n> @@ -1177,6 +1436,8 @@ void PipelineHandlerMaliC55::registerMaliCamera(std::unique_ptr<MaliC55CameraDat\n>   \tstd::shared_ptr<Camera> camera = Camera::create(std::move(data),\n>   \t\t\t\t\t\t\tname, streams);\n>   \tregisterCamera(std::move(camera));\n> +\n> +\treturn true;\n>   }\n>   \n>   /*\n> @@ -1202,9 +1463,7 @@ bool PipelineHandlerMaliC55::registerTPGCamera(MediaLink *link)\n>   \tif (data->init())\n>   \t\treturn false;\n>   \n> -\tregisterMaliCamera(std::move(data), name);\n> -\n> -\treturn true;\n> +\treturn registerMaliCamera(std::move(data), name);\n>   }\n>   \n>   /*\n> @@ -1230,11 +1489,26 @@ bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink)\n>   \t\tif (data->init())\n>   \t\t\treturn false;\n>   \n> -\t\t/* \\todo: Init properties. */\n> +\t\t/*\n> +\t\t * \\todo Read delay values from the sensor itself or from a\n> +\t\t * a sensor database. For now use generic values taken from\n> +\t\t * the Raspberry Pi and listed as 'generic values'.\n> +\t\t */\n> +\t\tstd::unordered_map<uint32_t, DelayedControls::ControlParams> params = {\n> +\t\t\t{ V4L2_CID_ANALOGUE_GAIN, { 1, false } },\n> +\t\t\t{ V4L2_CID_EXPOSURE, { 2, false } },\n> +\t\t};\n> +\n> +\t\tdata->delayedCtrls_ =\n> +\t\t\tstd::make_unique<DelayedControls>(data->sensor_->device(),\n> +\t\t\t\t\t\t\t  params);\n> +\t\tisp_->frameStart.connect(data->delayedCtrls_.get(),\n> +\t\t\t\t\t &DelayedControls::applyControls);\n>   \n> -\t\tdata->updateControls();\n> +\t\t/* \\todo: Init properties. */\n>   \n> -\t\tregisterMaliCamera(std::move(data), sensor->name());\n> +\t\tif (!registerMaliCamera(std::move(data), sensor->name()))\n> +\t\t\treturn false;\n>   \t}\n>   \n>   \treturn true;\n> @@ -1301,6 +1575,7 @@ bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)\n>   \t}\n>   \n>   \tstats_->bufferReady.connect(this, &PipelineHandlerMaliC55::statsBufferReady);\n> +\tparams_->bufferReady.connect(this, &PipelineHandlerMaliC55::paramsBufferReady);\n>   \n>   \tispSink = isp_->entity()->getPadByIndex(0);\n>   \tif (!ispSink || ispSink->links().empty()) {","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id E3990BD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 13 Jun 2024 14:09:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B161465496;\n\tThu, 13 Jun 2024 16:09:26 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7AA836548F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 13 Jun 2024 16:09:24 +0200 (CEST)","from [192.168.0.43]\n\t(cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 1B188D77;\n\tThu, 13 Jun 2024 16:09:10 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"qILAR5Z5\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1718287750;\n\tbh=Rij/t7c2C3dnvCE3imA2i/sse1mxXsw+gjN3Tm0zoeo=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=qILAR5Z5j3jqjA3J3+Bv/ob+pt1DyTIzVYZo6gkkXXG/aAkVVoeBMP21RhsZS4IXw\n\tt0sHQ8Ua9UEH3RivR7XIG/BAPMCrm++er8CP0ChW66cfCYWJzZJDfFi1HejUB+nyTi\n\tGKIukx5Mx51beLfI2OfqZDUcYvBP/dMNqAGpj9bs=","Message-ID":"<45ffacf9-1639-4da7-b250-067ce547442a@ideasonboard.com>","Date":"Thu, 13 Jun 2024 15:09:21 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 05/10] mali-c55: Plumb the IPA module in","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Cc":"nayden.kanchev@arm.com, libcamera-devel@lists.libcamera.org","References":"<20240613132602.1021721-1-dan.scally@ideasonboard.com>\n\t<20240613132602.1021721-6-dan.scally@ideasonboard.com>","Content-Language":"en-US","From":"Dan Scally <dan.scally@ideasonboard.com>","Autocrypt":"addr=dan.scally@ideasonboard.com; keydata=\n\txsFNBGLydlEBEADa5O2s0AbUguprfvXOQun/0a8y2Vk6BqkQALgeD6KnXSWwaoCULp18etYW\n\tB31bfgrdphXQ5kUQibB0ADK8DERB4wrzrUb5CMxLBFE7mQty+v5NsP0OFNK9XTaAOcmD+Ove\n\teIjYvqurAaro91jrRVrS1gBRxIFqyPgNvwwL+alMZhn3/2jU2uvBmuRrgnc/e9cHKiuT3Dtq\n\tMHGPKL2m+plk+7tjMoQFfexoQ1JKugHAjxAhJfrkXh6uS6rc01bYCyo7ybzg53m1HLFJdNGX\n\tsUKR+dQpBs3SY4s66tc1sREJqdYyTsSZf80HjIeJjU/hRunRo4NjRIJwhvnK1GyjOvvuCKVU\n\tRWpY8dNjNu5OeAfdrlvFJOxIE9M8JuYCQTMULqd1NuzbpFMjc9524U3Cngs589T7qUMPb1H1\n\tNTA81LmtJ6Y+IV5/kiTUANflpzBwhu18Ok7kGyCq2a2jsOcVmk8gZNs04gyjuj8JziYwwLbf\n\tvzABwpFVcS8aR+nHIZV1HtOzyw8CsL8OySc3K9y+Y0NRpziMRvutrppzgyMb9V+N31mK9Mxl\n\t1YkgaTl4ciNWpdfUe0yxH03OCuHi3922qhPLF4XX5LN+NaVw5Xz2o3eeWklXdouxwV7QlN33\n\tu4+u2FWzKxDqO6WLQGjxPE0mVB4Gh5Pa1Vb0ct9Ctg0qElvtGQARAQABzShEYW4gU2NhbGx5\n\tIDxkYW4uc2NhbGx5QGlkZWFzb25ib2FyZC5jb20+wsGNBBMBCAA3FiEEsdtt8OWP7+8SNfQe\n\tkiQuh/L+GMQFAmLydlIFCQWjmoACGwMECwkIBwUVCAkKCwUWAgMBAAAKCRCSJC6H8v4YxDI2\n\tEAC2Gz0iyaXJkPInyshrREEWbo0CA6v5KKf3I/HlMPqkZ48bmGoYm4mEQGFWZJAT3K4ir8bg\n\tcEfs9V54gpbrZvdwS4abXbUK4WjKwEs8HK3XJv1WXUN2bsz5oEJWZUImh9gD3naiLLI9QMMm\n\tw/aZkT+NbN5/2KvChRWhdcha7+2Te4foOY66nIM+pw2FZM6zIkInLLUik2zXOhaZtqdeJZQi\n\tHSPU9xu7TRYN4cvdZAnSpG7gQqmLm5/uGZN1/sB3kHTustQtSXKMaIcD/DMNI3JN/t+RJVS7\n\tc0Jh/ThzTmhHyhxx3DRnDIy7kwMI4CFvmhkVC2uNs9kWsj1DuX5kt8513mvfw2OcX9UnNKmZ\n\tnhNCuF6DxVrL8wjOPuIpiEj3V+K7DFF1Cxw1/yrLs8dYdYh8T8vCY2CHBMsqpESROnTazboh\n\tAiQ2xMN1cyXtX11Qwqm5U3sykpLbx2BcmUUUEAKNsM//Zn81QXKG8vOx0ZdMfnzsCaCzt8f6\n\t9dcDBBI3tJ0BI9ByiocqUoL6759LM8qm18x3FYlxvuOs4wSGPfRVaA4yh0pgI+ModVC2Pu3y\n\tejE/IxeatGqJHh6Y+iJzskdi27uFkRixl7YJZvPJAbEn7kzSi98u/5ReEA8Qhc8KO/B7wprj\n\txjNMZNYd0Eth8+WkixHYj752NT5qshKJXcyUU87BTQRi8nZSARAAx0BJayh1Fhwbf4zoY56x\n\txHEpT6DwdTAYAetd3yiKClLVJadYxOpuqyWa1bdfQWPb+h4MeXbWw/53PBgn7gI2EA7ebIRC\n\tPJJhAIkeym7hHZoxqDQTGDJjxFEL11qF+U3rhWiL2Zt0Pl+zFq0eWYYVNiXjsIS4FI2+4m16\n\ttPbDWZFJnSZ828VGtRDQdhXfx3zyVX21lVx1bX4/OZvIET7sVUufkE4hrbqrrufre7wsjD1t\n\t8MQKSapVrr1RltpzPpScdoxknOSBRwOvpp57pJJe5A0L7+WxJ+vQoQXj0j+5tmIWOAV1qBQp\n\thyoyUk9JpPfntk2EKnZHWaApFp5TcL6c5LhUvV7F6XwOjGPuGlZQCWXee9dr7zym8iR3irWT\n\t+49bIh5PMlqSLXJDYbuyFQHFxoiNdVvvf7etvGfqFYVMPVjipqfEQ38ST2nkzx+KBICz7uwj\n\tJwLBdTXzGFKHQNckGMl7F5QdO/35An/QcxBnHVMXqaSd12tkJmoRVWduwuuoFfkTY5mUV3uX\n\txGj3iVCK4V+ezOYA7c2YolfRCNMTza6vcK/P4tDjjsyBBZrCCzhBvd4VVsnnlZhVaIxoky4K\n\taL+AP+zcQrUZmXmgZjXOLryGnsaeoVrIFyrU6ly90s1y3KLoPsDaTBMtnOdwxPmo1xisH8oL\n\ta/VRgpFBfojLPxMAEQEAAcLBfAQYAQgAJhYhBLHbbfDlj+/vEjX0HpIkLofy/hjEBQJi8nZT\n\tBQkFo5qAAhsMAAoJEJIkLofy/hjEXPcQAMIPNqiWiz/HKu9W4QIf1OMUpKn3YkVIj3p3gvfM\n\tRes4fGX94Ji599uLNrPoxKyaytC4R6BTxVriTJjWK8mbo9jZIRM4vkwkZZ2bu98EweSucxbp\n\tvjESsvMXGgxniqV/RQ/3T7LABYRoIUutARYq58p5HwSP0frF0fdFHYdTa2g7MYZl1ur2JzOC\n\tFHRpGadlNzKDE3fEdoMobxHB3Lm6FDml5GyBAA8+dQYVI0oDwJ3gpZPZ0J5Vx9RbqXe8RDuR\n\tdu90hvCJkq7/tzSQ0GeD3BwXb9/R/A4dVXhaDd91Q1qQXidI+2jwhx8iqiYxbT+DoAUkQRQy\n\txBtoCM1CxH7u45URUgD//fxYr3D4B1SlonA6vdaEdHZOGwECnDpTxecENMbz/Bx7qfrmd901\n\tD+N9SjIwrbVhhSyUXYnSUb8F+9g2RDY42Sk7GcYxIeON4VzKqWM7hpkXZ47pkK0YodO+dRKM\n\tyMcoUWrTK0Uz6UzUGKoJVbxmSW/EJLEGoI5p3NWxWtScEVv8mO49gqQdrRIOheZycDmHnItt\n\t9Qjv00uFhEwv2YfiyGk6iGF2W40s2pH2t6oeuGgmiZ7g6d0MEK8Ql/4zPItvr1c1rpwpXUC1\n\tu1kQWgtnNjFHX3KiYdqjcZeRBiry1X0zY+4Y24wUU0KsEewJwjhmCKAsju1RpdlPg2kC","In-Reply-To":"<20240613132602.1021721-6-dan.scally@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]