[{"id":14942,"web_url":"https://patchwork.libcamera.org/comment/14942/","msgid":"<YBtg4ctE33HcbD8l@pendragon.ideasonboard.com>","date":"2021-02-04T02:50:09","subject":"Re: [libcamera-devel] [PATCH v6 09/11] libcamera: pipeline,\n\tipa: raspberrypi: Use new data definition","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nOn Thu, Dec 24, 2020 at 05:16:11PM +0900, Paul Elder wrote:\n> Now that we can generate custom functions and data structures with mojo,\n> switch the raspberrypi pipeline handler and IPA to use the custom data\n> structures as defined in the mojom data definition file.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> Changes in v6:\n> - rebase on \"pipeline: ipa: raspberrypi: Handle failures during IPA\n>   configuration\"\n> - move the enums and consts to the mojom file\n>   - use them with the namespace in the IPA and pipeline handler\n> - no longer need to initialize the ControlInfoMap\n> - fill in the LS table handle in the pipeline handler instead of passing\n>   it from the IPA (which was passed to the IPA from the pipeline handler\n>   in the first place)\n> - use custom start()\n> - no more postfix _ in generated struct fields\n> \n> Changes in v5:\n> - minor fixes\n> - use new struct names\n> \n> Changes in v4:\n> - rename Controls to controls\n> - use the renamed header (raspberrypi_ipa_interface.h)\n> \n> Changes in v3:\n> - use ipa::rpi namespace\n> - rebase on the RPi namespace patch series newly merged into master\n> \n> Changes in v2:\n> - rebased on \"libcamera: pipeline: ipa: raspberrypi: Rework drop frame\n>   signalling\"\n> - use newly customized RPi IPA interface\n> ---\n>  include/libcamera/ipa/raspberrypi.h           |  11 -\n>  src/ipa/raspberrypi/raspberrypi.cpp           | 181 ++++++-------\n>  .../pipeline/raspberrypi/raspberrypi.cpp      | 244 +++++++++---------\n>  .../pipeline/raspberrypi/rpi_stream.cpp       |   6 +-\n>  .../pipeline/raspberrypi/rpi_stream.h         |   5 +-\n>  5 files changed, 208 insertions(+), 239 deletions(-)\n> \n> diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h\n> index df30b4a2..70ecc5a8 100644\n> --- a/include/libcamera/ipa/raspberrypi.h\n> +++ b/include/libcamera/ipa/raspberrypi.h\n> @@ -16,17 +16,6 @@ namespace libcamera {\n>  \n>  namespace RPi {\n>  \n> -enum BufferMask {\n> -\tID\t\t= 0x00ffff,\n> -\tSTATS\t\t= 0x010000,\n> -\tEMBEDDED_DATA\t= 0x020000,\n> -\tBAYER_DATA\t= 0x040000,\n> -\tEXTERNAL_BUFFER\t= 0x100000,\n> -};\n> -\n> -/* Size of the LS grid allocation. */\n> -static constexpr unsigned int MaxLsGridSize = 32 << 10;\n> -\n>  /* List of controls handled by the Raspberry Pi IPA */\n>  static const ControlInfoMap Controls = {\n>  \t{ &controls::AeEnable, ControlInfo(false, true) },\n> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp\n> index 67f47c11..5acc25a0 100644\n> --- a/src/ipa/raspberrypi/raspberrypi.cpp\n> +++ b/src/ipa/raspberrypi/raspberrypi.cpp\n> @@ -20,6 +20,7 @@\n>  #include <libcamera/ipa/ipa_interface.h>\n>  #include <libcamera/ipa/ipa_module_info.h>\n>  #include <libcamera/ipa/raspberrypi.h>\n> +#include <libcamera/ipa/raspberrypi_ipa_interface.h>\n>  #include <libcamera/request.h>\n>  #include <libcamera/span.h>\n>  \n> @@ -59,7 +60,7 @@ constexpr unsigned int DefaultExposureTime = 20000;\n>  \n>  LOG_DEFINE_CATEGORY(IPARPI)\n>  \n> -class IPARPi : public IPAInterface\n> +class IPARPi : public ipa::rpi::IPARPiInterface\n>  {\n>  public:\n>  \tIPARPi()\n> @@ -72,21 +73,24 @@ public:\n>  \t~IPARPi()\n>  \t{\n>  \t\tif (lsTable_)\n> -\t\t\tmunmap(lsTable_, RPi::MaxLsGridSize);\n> +\t\t\tmunmap(lsTable_, ipa::rpi::MaxLsGridSize);\n>  \t}\n>  \n>  \tint init(const IPASettings &settings) override;\n> -\tint start(const IPAOperationData &data, IPAOperationData *result) override;\n> +\tvoid start(const ipa::rpi::StartControls &data,\n> +\t\t   ipa::rpi::StartControls *result, int *ret) override;\n>  \tvoid stop() override {}\n>  \n>  \tvoid configure(const CameraSensorInfo &sensorInfo,\n>  \t\t       const std::map<unsigned int, IPAStream> &streamConfig,\n> -\t\t       const std::map<unsigned int, const ControlInfoMap &> &entityControls,\n> -\t\t       const IPAOperationData &ipaConfig,\n> -\t\t       IPAOperationData *response) override;\n> +\t\t       const std::map<unsigned int, ControlInfoMap> &entityControls,\n> +\t\t       const ipa::rpi::ConfigInput &data,\n> +\t\t       ipa::rpi::ConfigOutput *response) override;\n>  \tvoid mapBuffers(const std::vector<IPABuffer> &buffers) override;\n>  \tvoid unmapBuffers(const std::vector<unsigned int> &ids) override;\n> -\tvoid processEvent(const IPAOperationData &event) override;\n> +\tvoid signalStatReady(const uint32_t bufferId) override;\n> +\tvoid signalQueueRequest(const ControlList &controls) override;\n> +\tvoid signalIspPrepare(const ipa::rpi::ISPConfig &data) override;\n>  \n>  private:\n>  \tvoid setMode(const CameraSensorInfo &sensorInfo);\n> @@ -156,15 +160,17 @@ int IPARPi::init(const IPASettings &settings)\n>  \treturn 0;\n>  }\n>  \n> -int IPARPi::start(const IPAOperationData &data, IPAOperationData *result)\n> +void IPARPi::start(const ipa::rpi::StartControls &data,\n> +\t\t   ipa::rpi::StartControls *result, int *ret)\n>  {\n>  \tRPiController::Metadata metadata;\n>  \n> +\tASSERT(ret);\n>  \tASSERT(result);\n> -\tresult->operation = 0;\n> -\tif (data.operation & RPi::IPA_CONFIG_STARTUP) {\n> +\tresult->hasControls = false;\n> +\tif (data.hasControls) {\n>  \t\t/* We have been given some controls to action before start. */\n> -\t\tqueueRequest(data.controls[0]);\n> +\t\tqueueRequest(data.controls);\n>  \t}\n>  \n>  \tcontroller_.SwitchMode(mode_, &metadata);\n> @@ -179,8 +185,8 @@ int IPARPi::start(const IPAOperationData &data, IPAOperationData *result)\n>  \tif (agcStatus.shutter_time != 0.0 && agcStatus.analogue_gain != 0.0) {\n>  \t\tControlList ctrls(sensorCtrls_);\n>  \t\tapplyAGC(&agcStatus, ctrls);\n> -\t\tresult->controls.emplace_back(ctrls);\n> -\t\tresult->operation |= RPi::IPA_CONFIG_SENSOR;\n> +\t\tresult->controls = std::move(ctrls);\n> +\t\tresult->hasControls = true;\n>  \t}\n>  \n>  \t/*\n> @@ -227,12 +233,11 @@ int IPARPi::start(const IPAOperationData &data, IPAOperationData *result)\n>  \t\tmistrustCount_ = helper_->MistrustFramesModeSwitch();\n>  \t}\n>  \n> -\tresult->data.push_back(dropFrame);\n> -\tresult->operation |= RPi::IPA_CONFIG_DROP_FRAMES;\n> +\tresult->dropFrameCount = dropFrame;\n>  \n>  \tfirstStart_ = false;\n>  \n> -\treturn 0;\n> +\t*ret = 0;\n\nAs start() seems to never fail, you could drop the ret parameter.\n\n>  }\n>  \n>  void IPARPi::setMode(const CameraSensorInfo &sensorInfo)\n> @@ -275,30 +280,30 @@ void IPARPi::setMode(const CameraSensorInfo &sensorInfo)\n>  \n>  void IPARPi::configure(const CameraSensorInfo &sensorInfo,\n>  \t\t       [[maybe_unused]] const std::map<unsigned int, IPAStream> &streamConfig,\n> -\t\t       const std::map<unsigned int, const ControlInfoMap &> &entityControls,\n> -\t\t       const IPAOperationData &ipaConfig,\n> -\t\t       IPAOperationData *result)\n> +\t\t       const std::map<unsigned int, ControlInfoMap> &entityControls,\n> +\t\t       const ipa::rpi::ConfigInput &ipaConfig,\n> +\t\t       ipa::rpi::ConfigOutput *result)\n>  {\n>  \tif (entityControls.size() != 2) {\n>  \t\tLOG(IPARPI, Error) << \"No ISP or sensor controls found.\";\n> -\t\tresult->operation = RPi::IPA_CONFIG_FAILED;\n> +\t\tresult->op = ipa::rpi::ConfigFailed;\n>  \t\treturn;\n>  \t}\n>  \n> -\tresult->operation = 0;\n> +\tresult->op = 0;\n>  \n>  \tsensorCtrls_ = entityControls.at(0);\n>  \tispCtrls_ = entityControls.at(1);\n>  \n>  \tif (!validateSensorControls()) {\n>  \t\tLOG(IPARPI, Error) << \"Sensor control validation failed.\";\n> -\t\tresult->operation = RPi::IPA_CONFIG_FAILED;\n> +\t\tresult->op = ipa::rpi::ConfigFailed;\n>  \t\treturn;\n>  \t}\n>  \n>  \tif (!validateIspControls()) {\n>  \t\tLOG(IPARPI, Error) << \"ISP control validation failed.\";\n> -\t\tresult->operation = RPi::IPA_CONFIG_FAILED;\n> +\t\tresult->op = ipa::rpi::ConfigFailed;\n>  \t\treturn;\n>  \t}\n>  \n> @@ -317,7 +322,7 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo,\n>  \t\tif (!helper_) {\n>  \t\t\tLOG(IPARPI, Error) << \"Could not create camera helper for \"\n>  \t\t\t\t\t   << cameraName;\n> -\t\t\tresult->operation = RPi::IPA_CONFIG_FAILED;\n> +\t\t\tresult->op = ipa::rpi::ConfigFailed;\n>  \t\t\treturn;\n>  \t\t}\n>  \n> @@ -329,34 +334,29 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo,\n>  \t\thelper_->GetDelays(exposureDelay, gainDelay);\n>  \t\tsensorMetadata = helper_->SensorEmbeddedDataPresent();\n>  \n> -\t\tresult->data.push_back(gainDelay);\n> -\t\tresult->data.push_back(exposureDelay);\n> -\t\tresult->data.push_back(sensorMetadata);\n> -\n> -\t\tresult->operation |= RPi::IPA_CONFIG_STAGGERED_WRITE;\n> +\t\tresult->op |= ipa::rpi::ConfigStaggeredWrite;\n> +\t\tresult->sensorConfig.gainDelay = gainDelay;\n> +\t\tresult->sensorConfig.exposureDelay = exposureDelay;\n> +\t\tresult->sensorConfig.sensorMetadata = sensorMetadata;\n>  \t}\n>  \n>  \t/* Re-assemble camera mode using the sensor info. */\n>  \tsetMode(sensorInfo);\n>  \n> -\t/*\n> -\t * The ipaConfig.data always gives us the user transform first. Note that\n> -\t * this will always make the LS table pointer (if present) element 1.\n> -\t */\n> -\tmode_.transform = static_cast<libcamera::Transform>(ipaConfig.data[0]);\n> +\tmode_.transform = static_cast<libcamera::Transform>(ipaConfig.transform);\n>  \n>  \t/* Store the lens shading table pointer and handle if available. */\n> -\tif (ipaConfig.operation & RPi::IPA_CONFIG_LS_TABLE) {\n> +\tif (ipaConfig.op & ipa::rpi::ConfigLsTable) {\n>  \t\t/* Remove any previous table, if there was one. */\n>  \t\tif (lsTable_) {\n> -\t\t\tmunmap(lsTable_, RPi::MaxLsGridSize);\n> +\t\t\tmunmap(lsTable_, ipa::rpi::MaxLsGridSize);\n>  \t\t\tlsTable_ = nullptr;\n>  \t\t}\n>  \n> -\t\t/* Map the LS table buffer into user space (now element 1). */\n> -\t\tlsTableHandle_ = FileDescriptor(ipaConfig.data[1]);\n> +\t\t/* Map the LS table buffer into user space. */\n> +\t\tlsTableHandle_ = FileDescriptor(ipaConfig.lsTableHandle);\n\nIsn't ipaConfig.lsTableHandle already a FileDescriptor ?\n\n>  \t\tif (lsTableHandle_.isValid()) {\n> -\t\t\tlsTable_ = mmap(nullptr, RPi::MaxLsGridSize, PROT_READ | PROT_WRITE,\n> +\t\t\tlsTable_ = mmap(nullptr, ipa::rpi::MaxLsGridSize, PROT_READ | PROT_WRITE,\n>  \t\t\t\t\tMAP_SHARED, lsTableHandle_.fd(), 0);\n>  \n>  \t\t\tif (lsTable_ == MAP_FAILED) {\n> @@ -382,8 +382,8 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo,\n>  \t\tagcStatus.analogue_gain = DefaultAnalogueGain;\n>  \t\tapplyAGC(&agcStatus, ctrls);\n>  \n> -\t\tresult->controls.emplace_back(ctrls);\n> -\t\tresult->operation |= RPi::IPA_CONFIG_SENSOR;\n> +\t\tresult->op |= ipa::rpi::ConfigSensor;\n> +\t\tresult->controls = std::move(ctrls);\n>  \t}\n>  \n>  \tlastMode_ = mode_;\n> @@ -408,56 +408,38 @@ void IPARPi::unmapBuffers(const std::vector<unsigned int> &ids)\n>  \t}\n>  }\n>  \n> -void IPARPi::processEvent(const IPAOperationData &event)\n> +void IPARPi::signalStatReady(const uint32_t bufferId)\n\nFor arithemtic arguments we could drop the const in the template. If\nit's too difficult, it doesn't matter much.\n\n>  {\n> -\tswitch (event.operation) {\n> -\tcase RPi::IPA_EVENT_SIGNAL_STAT_READY: {\n> -\t\tunsigned int bufferId = event.data[0];\n> -\n> -\t\tif (++checkCount_ != frameCount_) /* assert here? */\n> -\t\t\tLOG(IPARPI, Error) << \"WARNING: Prepare/Process mismatch!!!\";\n> -\t\tif (frameCount_ > mistrustCount_)\n> -\t\t\tprocessStats(bufferId);\n> -\n> -\t\treportMetadata();\n> -\n> -\t\tIPAOperationData op;\n> -\t\top.operation = RPi::IPA_ACTION_STATS_METADATA_COMPLETE;\n> -\t\top.data = { bufferId & RPi::BufferMask::ID };\n> -\t\top.controls = { libcameraMetadata_ };\n> -\t\tqueueFrameAction.emit(0, op);\n> -\t\tbreak;\n> -\t}\n> +\tif (++checkCount_ != frameCount_) /* assert here? */\n> +\t\tLOG(IPARPI, Error) << \"WARNING: Prepare/Process mismatch!!!\";\n> +\tif (frameCount_ > mistrustCount_)\n> +\t\tprocessStats(bufferId);\n>  \n> -\tcase RPi::IPA_EVENT_SIGNAL_ISP_PREPARE: {\n> -\t\tunsigned int embeddedbufferId = event.data[0];\n> -\t\tunsigned int bayerbufferId = event.data[1];\n> +\treportMetadata();\n>  \n> -\t\t/*\n> -\t\t * At start-up, or after a mode-switch, we may want to\n> -\t\t * avoid running the control algos for a few frames in case\n> -\t\t * they are \"unreliable\".\n> -\t\t */\n> -\t\tprepareISP(embeddedbufferId);\n> -\t\tframeCount_++;\n> -\n> -\t\t/* Ready to push the input buffer into the ISP. */\n> -\t\tIPAOperationData op;\n> -\t\top.operation = RPi::IPA_ACTION_RUN_ISP;\n> -\t\top.data = { bayerbufferId & RPi::BufferMask::ID };\n> -\t\tqueueFrameAction.emit(0, op);\n> -\t\tbreak;\n> -\t}\n> +\tstatsMetadataComplete.emit(bufferId & ipa::rpi::MaskID, libcameraMetadata_);\n> +}\n>  \n> -\tcase RPi::IPA_EVENT_QUEUE_REQUEST: {\n> -\t\tqueueRequest(event.controls[0]);\n> -\t\tbreak;\n> -\t}\n> +void IPARPi::signalQueueRequest(const ControlList &controls)\n> +{\n> +\tqueueRequest(controls);\n> +}\n>  \n> -\tdefault:\n> -\t\tLOG(IPARPI, Error) << \"Unknown event \" << event.operation;\n> -\t\tbreak;\n> -\t}\n> +void IPARPi::signalIspPrepare(const ipa::rpi::ISPConfig &data)\n> +{\n> +\tunsigned int embeddedbufferId = data.embeddedbufferId;\n> +\tunsigned int bayerbufferId = data.bayerbufferId;\n> +\n> +\t/*\n> +\t * At start-up, or after a mode-switch, we may want to\n> +\t * avoid running the control algos for a few frames in case\n> +\t * they are \"unreliable\".\n> +\t */\n> +\tprepareISP(embeddedbufferId);\n\nYou could write\n\n\tprepareISP(data.embeddedbufferId);\n\nand drop the local variable.\n\n> +\tframeCount_++;\n> +\n> +\t/* Ready to push the input buffer into the ISP. */\n> +\trunIsp.emit(bayerbufferId & ipa::rpi::MaskID);\n\nSame here.\n\n>  }\n>  \n>  void IPARPi::reportMetadata()\n> @@ -815,10 +797,7 @@ void IPARPi::queueRequest(const ControlList &controls)\n>  \n>  void IPARPi::returnEmbeddedBuffer(unsigned int bufferId)\n>  {\n> -\tIPAOperationData op;\n> -\top.operation = RPi::IPA_ACTION_EMBEDDED_COMPLETE;\n> -\top.data = { bufferId & RPi::BufferMask::ID };\n> -\tqueueFrameAction.emit(0, op);\n> +\tembeddedComplete.emit(bufferId & ipa::rpi::MaskID);\n>  }\n>  \n>  void IPARPi::prepareISP(unsigned int bufferId)\n> @@ -879,12 +858,8 @@ void IPARPi::prepareISP(unsigned int bufferId)\n>  \t\tif (dpcStatus)\n>  \t\t\tapplyDPC(dpcStatus, ctrls);\n>  \n> -\t\tif (!ctrls.empty()) {\n> -\t\t\tIPAOperationData op;\n> -\t\t\top.operation = RPi::IPA_ACTION_V4L2_SET_ISP;\n> -\t\t\top.controls.push_back(ctrls);\n> -\t\t\tqueueFrameAction.emit(0, op);\n> -\t\t}\n> +\t\tif (!ctrls.empty())\n> +\t\t\tsetIsp.emit(ctrls);\n>  \t}\n>  }\n>  \n> @@ -941,10 +916,7 @@ void IPARPi::processStats(unsigned int bufferId)\n>  \t\tControlList ctrls(sensorCtrls_);\n>  \t\tapplyAGC(&agcStatus, ctrls);\n>  \n> -\t\tIPAOperationData op;\n> -\t\top.operation = RPi::IPA_ACTION_V4L2_SET_STAGGERED;\n> -\t\top.controls.emplace_back(ctrls);\n> -\t\tqueueFrameAction.emit(0, op);\n> +\t\tsetStaggered.emit(ctrls);\n>  \t}\n>  }\n>  \n> @@ -1114,13 +1086,14 @@ void IPARPi::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)\n>  \t\t.grid_width = w,\n>  \t\t.grid_stride = w,\n>  \t\t.grid_height = h,\n> -\t\t.dmabuf = lsTableHandle_.fd(),\n> +\t\t/* .dmabuf will be filled in by pipeline handler. */\n> +\t\t.dmabuf = 0,\n>  \t\t.ref_transform = 0,\n>  \t\t.corner_sampled = 1,\n>  \t\t.gain_format = GAIN_FORMAT_U4P10\n>  \t};\n>  \n> -\tif (!lsTable_ || w * h * 4 * sizeof(uint16_t) > RPi::MaxLsGridSize) {\n> +\tif (!lsTable_ || w * h * 4 * sizeof(uint16_t) > ipa::rpi::MaxLsGridSize) {\n>  \t\tLOG(IPARPI, Error) << \"Do not have a correctly allocate lens shading table!\";\n>  \t\treturn;\n>  \t}\n> @@ -1191,9 +1164,9 @@ const struct IPAModuleInfo ipaModuleInfo = {\n>  \t\"raspberrypi\",\n>  };\n>  \n> -struct ipa_context *ipaCreate()\n> +IPAInterface *ipaCreate()\n>  {\n> -\treturn new IPAInterfaceWrapper(std::make_unique<IPARPi>());\n\nIs there a corresponding header that could be dropped ?\n\n> +\treturn new IPARPi();\n>  }\n>  \n>  } /* extern \"C\" */\n> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> index 7a5f5881..4152ab69 100644\n> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> @@ -17,11 +17,15 @@\n>  #include <libcamera/control_ids.h>\n>  #include <libcamera/file_descriptor.h>\n>  #include <libcamera/formats.h>\n> +#include <libcamera/ipa/core_ipa_interface.h>\n\nThis is included by libcamera/ipa/raspberrypi_ipa_interface.h, do we\nneed to include it manually ?\n\n>  #include <libcamera/ipa/raspberrypi.h>\n> +#include <libcamera/ipa/raspberrypi_ipa_interface.h>\n> +#include <libcamera/ipa/raspberrypi_ipa_proxy.h>\n>  #include <libcamera/logging.h>\n>  #include <libcamera/property_ids.h>\n>  #include <libcamera/request.h>\n>  \n> +#include <linux/bcm2835-isp.h>\n>  #include <linux/videodev2.h>\n>  \n>  #include \"libcamera/internal/bayer_format.h\"\n> @@ -146,7 +150,11 @@ public:\n>  \tint loadIPA();\n>  \tint configureIPA(const CameraConfiguration *config);\n>  \n> -\tvoid queueFrameAction(unsigned int frame, const IPAOperationData &action);\n> +\tvoid statsMetadataComplete(uint32_t bufferId, const ControlList &controls);\n> +\tvoid runIsp(uint32_t bufferId);\n> +\tvoid embeddedComplete(uint32_t bufferId);\n> +\tvoid setIsp(const ControlList &controls);\n> +\tvoid setStaggered(const ControlList &controls);\n>  \n>  \t/* bufferComplete signal handlers. */\n>  \tvoid unicamBufferDequeue(FrameBuffer *buffer);\n> @@ -159,6 +167,8 @@ public:\n>  \tvoid handleState();\n>  \tvoid applyScalerCrop(const ControlList &controls);\n>  \n> +\tstd::unique_ptr<ipa::rpi::IPAProxyRPi> ipa_;\n> +\n>  \tstd::unique_ptr<CameraSensor> sensor_;\n>  \t/* Array of Unicam and ISP device streams and associated buffers/streams. */\n>  \tRPi::Device<Unicam, 2> unicam_;\n> @@ -733,7 +743,7 @@ int PipelineHandlerRPi::exportFrameBuffers([[maybe_unused]] Camera *camera, Stre\n>  \treturn ret;\n>  }\n>  \n> -int PipelineHandlerRPi::start(Camera *camera, [[maybe_unused]] ControlList *controls)\n> +int PipelineHandlerRPi::start(Camera *camera, ControlList *controls)\n>  {\n>  \tRPiCameraData *data = cameraData(camera);\n>  \tint ret;\n> @@ -751,13 +761,13 @@ int PipelineHandlerRPi::start(Camera *camera, [[maybe_unused]] ControlList *cont\n>  \t\tdata->applyScalerCrop(*controls);\n>  \n>  \t/* Start the IPA. */\n> -\tIPAOperationData ipaData = {};\n> -\tIPAOperationData result = {};\n> +\tipa::rpi::StartControls ipaData = {};\n> +\tipa::rpi::StartControls result = {};\n\nNo need for = {}, there's a default constructor.\n\n>  \tif (controls) {\n> -\t\tipaData.operation = RPi::IPA_CONFIG_STARTUP;\n> -\t\tipaData.controls.emplace_back(*controls);\n> +\t\tipaData.hasControls = true;\n> +\t\tipaData.controls = std::move(*controls);\n\nMaybe we could drop the hasControls field and rely on\nipaData.controls.empty() ?\n\n>  \t}\n> -\tret = data->ipa_->start(ipaData, &result);\n> +\tdata->ipa_->start(ipaData, &result, &ret);\n>  \tif (ret) {\n>  \t\tLOG(RPI, Error)\n>  \t\t\t<< \"Failed to start IPA for \" << camera->id();\n> @@ -767,15 +777,15 @@ int PipelineHandlerRPi::start(Camera *camera, [[maybe_unused]] ControlList *cont\n>  \n>  \t/* Apply any gain/exposure settings that the IPA may have passed back. */\n>  \tASSERT(data->staggeredCtrl_);\n> -\tif (result.operation & RPi::IPA_CONFIG_SENSOR) {\n> -\t\tconst ControlList &ctrls = result.controls[0];\n> +\tif (result.hasControls) {\n> +\t\tconst ControlList &ctrls = result.controls;\n>  \t\tif (!data->staggeredCtrl_.set(ctrls))\n>  \t\t\tLOG(RPI, Error) << \"V4L2 staggered set failed\";\n>  \t}\n>  \n> -\tif (result.operation & RPi::IPA_CONFIG_DROP_FRAMES) {\n> +\tif (result.dropFrameCount > 0) {\n\nI think you can drop the condition, RPi::IPA_CONFIG_DROP_FRAMES was set\nunconditionally.\n\n>  \t\t/* Configure the number of dropped frames required on startup. */\n> -\t\tdata->dropFrameCount_ = result.data[0];\n> +\t\tdata->dropFrameCount_ = result.dropFrameCount;\n>  \t}\n>  \n>  \t/* We need to set the dropFrameCount_ before queueing buffers. */\n> @@ -1102,8 +1112,8 @@ int PipelineHandlerRPi::prepareBuffers(Camera *camera)\n>  \t * Pass the stats and embedded data buffers to the IPA. No other\n>  \t * buffers need to be passed.\n>  \t */\n> -\tmapBuffers(camera, data->isp_[Isp::Stats].getBuffers(), RPi::BufferMask::STATS);\n> -\tmapBuffers(camera, data->unicam_[Unicam::Embedded].getBuffers(), RPi::BufferMask::EMBEDDED_DATA);\n> +\tmapBuffers(camera, data->isp_[Isp::Stats].getBuffers(), ipa::rpi::MaskStats);\n> +\tmapBuffers(camera, data->unicam_[Unicam::Embedded].getBuffers(), ipa::rpi::MaskEmbeddedData);\n>  \n>  \treturn 0;\n>  }\n> @@ -1120,8 +1130,8 @@ void PipelineHandlerRPi::mapBuffers(Camera *camera, const RPi::BufferMap &buffer\n>  \t * handler and the IPA.\n>  \t */\n>  \tfor (auto const &it : buffers) {\n> -\t\tipaBuffers.push_back({ .id = mask | it.first,\n> -\t\t\t\t       .planes = it.second->planes() });\n> +\t\tipaBuffers.push_back(IPABuffer(mask | it.first,\n> +\t\t\t\t\t       it.second->planes()));\n>  \t\tdata->ipaBuffers_.insert(mask | it.first);\n>  \t}\n>  \n> @@ -1152,15 +1162,18 @@ void RPiCameraData::frameStarted(uint32_t sequence)\n>  \n>  int RPiCameraData::loadIPA()\n>  {\n> -\tipa_ = IPAManager::createIPA(pipe_, 1, 1);\n> +\tipa_ = IPAManager::createIPA<ipa::rpi::IPAProxyRPi>(pipe_, 1, 1);\n> +\n>  \tif (!ipa_)\n>  \t\treturn -ENOENT;\n>  \n> -\tipa_->queueFrameAction.connect(this, &RPiCameraData::queueFrameAction);\n> +\tipa_->statsMetadataComplete.connect(this, &RPiCameraData::statsMetadataComplete);\n> +\tipa_->runIsp.connect(this, &RPiCameraData::runIsp);\n> +\tipa_->embeddedComplete.connect(this, &RPiCameraData::embeddedComplete);\n> +\tipa_->setIsp.connect(this, &RPiCameraData::setIsp);\n> +\tipa_->setStaggered.connect(this, &RPiCameraData::setStaggered);\n>  \n> -\tIPASettings settings{\n> -\t\t.configurationFile = ipa_->configurationFile(sensor_->model() + \".json\")\n> -\t};\n> +\tIPASettings settings(ipa_->configurationFile(sensor_->model() + \".json\"));\n>  \n>  \treturn ipa_->init(settings);\n>  }\n> @@ -1172,8 +1185,8 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n>  \t\tstatic_cast<const RPiCameraConfiguration *>(config);\n>  \n>  \tstd::map<unsigned int, IPAStream> streamConfig;\n> -\tstd::map<unsigned int, const ControlInfoMap &> entityControls;\n> -\tIPAOperationData ipaConfig = {};\n> +\tstd::map<unsigned int, ControlInfoMap> entityControls;\n\nIt's not nice to have to make a copy of the ControlInfoMap just to pass\nthis to the serializer :-5 Could you add a \\todo in an appropriate place\n(I suppose int he patch to the IPC infrastructure that introduces this\nneed, not in the RPi pipeline handler) ?\n\n> +\tipa::rpi::ConfigInput ipaConfig;\n>  \n>  \t/* Get the device format to pass to the IPA. */\n>  \tV4L2DeviceFormat sensorFormat;\n> @@ -1182,10 +1195,9 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n>  \tunsigned int i = 0;\n>  \tfor (auto const &stream : isp_) {\n>  \t\tif (stream.isExternal()) {\n> -\t\t\tstreamConfig[i++] = {\n> -\t\t\t\t.pixelFormat = stream.configuration().pixelFormat,\n> -\t\t\t\t.size = stream.configuration().size\n> -\t\t\t};\n> +\t\t\tstreamConfig[i++] = IPAStream(\n> +\t\t\t\tstream.configuration().pixelFormat,\n> +\t\t\t\tstream.configuration().size);\n>  \t\t}\n>  \t}\n>  \n> @@ -1193,17 +1205,17 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n>  \tentityControls.emplace(1, isp_[Isp::Input].dev()->controls());\n>  \n>  \t/* Always send the user transform to the IPA. */\n> -\tipaConfig.data = { static_cast<unsigned int>(config->transform) };\n> +\tipaConfig.transform = static_cast<unsigned int>(config->transform);\n>  \n>  \t/* Allocate the lens shading table via dmaHeap and pass to the IPA. */\n>  \tif (!lsTable_.isValid()) {\n> -\t\tlsTable_ = dmaHeap_.alloc(\"ls_grid\", RPi::MaxLsGridSize);\n> +\t\tlsTable_ = dmaHeap_.alloc(\"ls_grid\", ipa::rpi::MaxLsGridSize);\n>  \t\tif (!lsTable_.isValid())\n>  \t\t\treturn -ENOMEM;\n>  \n>  \t\t/* Allow the IPA to mmap the LS table via the file descriptor. */\n\nIt just occurred to me that this seems to duplicate the mapBuffers()\ninfrastructure. Could you add a\n\n\t\t/*\n\t\t * \\todo Investigate if mapping the lens shading table buffer\n\t\t * could be handled with mapBuffers().\n\t\t */\n\n> -\t\tipaConfig.operation = RPi::IPA_CONFIG_LS_TABLE;\n> -\t\tipaConfig.data.push_back(static_cast<unsigned int>(lsTable_.fd()));\n> +\t\tipaConfig.op |= ipa::rpi::ConfigLsTable;\n> +\t\tipaConfig.lsTableHandle = lsTable_;\n>  \t}\n>  \n>  \t/* We store the CameraSensorInfo for digital zoom calculations. */\n> @@ -1214,32 +1226,33 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n>  \t}\n>  \n>  \t/* Ready the IPA - it must know about the sensor resolution. */\n> -\tIPAOperationData result = {};\n> +\tipa::rpi::ConfigOutput result = {};\n\nNo need for = {} here either. There could be other places below.\n\n>  \n>  \tipa_->configure(sensorInfo_, streamConfig, entityControls, ipaConfig,\n>  \t\t\t&result);\n>  \n> -\tif (result.operation & RPi::IPA_CONFIG_FAILED) {\n> +\tif (result.op & ipa::rpi::ConfigFailed) {\n>  \t\tLOG(RPI, Error) << \"IPA configuration failed!\";\n>  \t\treturn -EPIPE;\n>  \t}\n>  \n> -\tunsigned int resultIdx = 0;\n> -\tif (result.operation & RPi::IPA_CONFIG_STAGGERED_WRITE) {\n> +\tif (result.op & ipa::rpi::ConfigStaggeredWrite) {\n>  \t\t/*\n>  \t\t * Setup our staggered control writer with the sensor default\n>  \t\t * gain and exposure delays.\n>  \t\t */\n>  \t\tif (!staggeredCtrl_) {\n>  \t\t\tstaggeredCtrl_.init(unicam_[Unicam::Image].dev(),\n> -\t\t\t\t\t    { { V4L2_CID_ANALOGUE_GAIN, result.data[resultIdx++] },\n> -\t\t\t\t\t      { V4L2_CID_EXPOSURE, result.data[resultIdx++] } });\n> -\t\t\tsensorMetadata_ = result.data[resultIdx++];\n> +\t\t\t\t\t    { { V4L2_CID_ANALOGUE_GAIN,\n> +\t\t\t\t\t\tresult.sensorConfig.gainDelay },\n> +\t\t\t\t\t      { V4L2_CID_EXPOSURE,\n> +\t\t\t\t\t\tresult.sensorConfig.exposureDelay } });\n> +\t\t\tsensorMetadata_ = result.sensorConfig.sensorMetadata;\n>  \t\t}\n>  \t}\n>  \n> -\tif (result.operation & RPi::IPA_CONFIG_SENSOR) {\n> -\t\tconst ControlList &ctrls = result.controls[0];\n> +\tif (result.op & ipa::rpi::ConfigSensor) {\n\nMaybe ipa::rpi::ConfigSensor could be dropped, replaced with a\n!result.controls.empty() check ?\n\n> +\t\tconst ControlList &ctrls = result.controls;\n>  \t\tif (!staggeredCtrl_.set(ctrls))\n>  \t\t\tLOG(RPI, Error) << \"V4L2 staggered set failed\";\n>  \t}\n> @@ -1260,90 +1273,87 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n>  \treturn 0;\n>  }\n>  \n> -void RPiCameraData::queueFrameAction([[maybe_unused]] unsigned int frame,\n> -\t\t\t\t     const IPAOperationData &action)\n> +void RPiCameraData::statsMetadataComplete(uint32_t bufferId, const ControlList &controls)\n>  {\n> +\tif (state_ == State::Stopped)\n> +\t\thandleState();\n> +\n> +\tFrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(bufferId);\n> +\n> +\thandleStreamBuffer(buffer, &isp_[Isp::Stats]);\n> +\n> +\t/* Fill the Request metadata buffer with what the IPA has provided */\n> +\tRequest *request = requestQueue_.front();\n> +\trequest->metadata() = std::move(controls);\n\nThat's interesting, controls being a const reference, I wouldn't have\nexpected a move to be possible.\n\n> +\n>  \t/*\n> -\t * The following actions can be handled when the pipeline handler is in\n> -\t * a stopped state.\n> +\t * Also update the ScalerCrop in the metadata with what we actually\n> +\t * used. But we must first rescale that from ISP (camera mode) pixels\n> +\t * back into sensor native pixels.\n> +\t *\n> +\t * Sending this information on every frame may be helpful.\n>  \t */\n> -\tswitch (action.operation) {\n> -\tcase RPi::IPA_ACTION_V4L2_SET_STAGGERED: {\n> -\t\tconst ControlList &controls = action.controls[0];\n> -\t\tif (!staggeredCtrl_.set(controls))\n> -\t\t\tLOG(RPI, Error) << \"V4L2 staggered set failed\";\n> -\t\tgoto done;\n> +\tif (updateScalerCrop_) {\n> +\t\tupdateScalerCrop_ = false;\n> +\t\tscalerCrop_ = ispCrop_.scaledBy(sensorInfo_.analogCrop.size(),\n> +\t\t\t\t\t\tsensorInfo_.outputSize);\n> +\t\tscalerCrop_.translateBy(sensorInfo_.analogCrop.topLeft());\n>  \t}\n> +\trequest->metadata().set(controls::ScalerCrop, scalerCrop_);\n>  \n> -\tcase RPi::IPA_ACTION_V4L2_SET_ISP: {\n> -\t\tControlList controls = action.controls[0];\n> -\t\tisp_[Isp::Input].dev()->setControls(&controls);\n> -\t\tgoto done;\n> -\t}\n> -\t}\n> +\tstate_ = State::IpaComplete;\n>  \n> -\tif (state_ == State::Stopped)\n> -\t\tgoto done;\n> +\thandleState();\n> +}\n>  \n> -\t/*\n> -\t * The following actions must not be handled when the pipeline handler\n> -\t * is in a stopped state.\n> -\t */\n> -\tswitch (action.operation) {\n> -\tcase RPi::IPA_ACTION_STATS_METADATA_COMPLETE: {\n> -\t\tunsigned int bufferId = action.data[0];\n> -\t\tFrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(bufferId);\n> +void RPiCameraData::runIsp(uint32_t bufferId)\n> +{\n> +\tif (state_ == State::Stopped)\n> +\t\thandleState();\n>  \n> -\t\thandleStreamBuffer(buffer, &isp_[Isp::Stats]);\n> +\tFrameBuffer *buffer = unicam_[Unicam::Image].getBuffers().at(bufferId);\n>  \n> -\t\t/* Fill the Request metadata buffer with what the IPA has provided */\n> -\t\tRequest *request = requestQueue_.front();\n> -\t\trequest->metadata() = std::move(action.controls[0]);\n> +\tLOG(RPI, Debug) << \"Input re-queue to ISP, buffer id \" << bufferId\n> +\t\t\t<< \", timestamp: \" << buffer->metadata().timestamp;\n>  \n> -\t\t/*\n> -\t\t * Also update the ScalerCrop in the metadata with what we actually\n> -\t\t * used. But we must first rescale that from ISP (camera mode) pixels\n> -\t\t * back into sensor native pixels.\n> -\t\t *\n> -\t\t * Sending this information on every frame may be helpful.\n> -\t\t */\n> -\t\tif (updateScalerCrop_) {\n> -\t\t\tupdateScalerCrop_ = false;\n> -\t\t\tscalerCrop_ = ispCrop_.scaledBy(sensorInfo_.analogCrop.size(),\n> -\t\t\t\t\t\t\tsensorInfo_.outputSize);\n> -\t\t\tscalerCrop_.translateBy(sensorInfo_.analogCrop.topLeft());\n> -\t\t}\n> -\t\trequest->metadata().set(controls::ScalerCrop, scalerCrop_);\n> +\tisp_[Isp::Input].queueBuffer(buffer);\n> +\tispOutputCount_ = 0;\n> +\thandleState();\n> +}\n>  \n> -\t\tstate_ = State::IpaComplete;\n> -\t\tbreak;\n> -\t}\n> +void RPiCameraData::embeddedComplete(uint32_t bufferId)\n> +{\n> +\tif (state_ == State::Stopped)\n> +\t\thandleState();\n>  \n> -\tcase RPi::IPA_ACTION_EMBEDDED_COMPLETE: {\n> -\t\tunsigned int bufferId = action.data[0];\n> -\t\tFrameBuffer *buffer = unicam_[Unicam::Embedded].getBuffers().at(bufferId);\n> -\t\thandleStreamBuffer(buffer, &unicam_[Unicam::Embedded]);\n> -\t\tbreak;\n> -\t}\n> +\tFrameBuffer *buffer = unicam_[Unicam::Embedded].getBuffers().at(bufferId);\n> +\thandleStreamBuffer(buffer, &unicam_[Unicam::Embedded]);\n> +\thandleState();\n> +}\n>  \n> -\tcase RPi::IPA_ACTION_RUN_ISP: {\n> -\t\tunsigned int bufferId = action.data[0];\n> -\t\tFrameBuffer *buffer = unicam_[Unicam::Image].getBuffers().at(bufferId);\n> +void RPiCameraData::setIsp(const ControlList &controls)\n> +{\n> +\tControlList ctrls = controls;\n>  \n> -\t\tLOG(RPI, Debug) << \"Input re-queue to ISP, buffer id \" << bufferId\n> -\t\t\t\t<< \", timestamp: \" << buffer->metadata().timestamp;\n> +\tSpan<const uint8_t> s =\n> +\t\tctrls.get(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING).data();\n> +\tconst bcm2835_isp_lens_shading *lsp =\n> +\t\treinterpret_cast<const bcm2835_isp_lens_shading *>(s.data());\n> +\tbcm2835_isp_lens_shading ls = *lsp;\n\n\tbcm2835_isp_lens_shading ls =\n\t\t*reinterpret_cast<const bcm2835_isp_lens_shading *>(s.data());\n\nThe double-copy is a bit of a shame, maybe we should extend the\nControlList and/or ControlValue API to make it possible to modify\ncontrols in place. That's out of scope for this series though.\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> +\tls.dmabuf = lsTable_.fd();\n>  \n> -\t\tisp_[Isp::Input].queueBuffer(buffer);\n> -\t\tispOutputCount_ = 0;\n> -\t\tbreak;\n> -\t}\n> +\tControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ls),\n> +\t\t\t\t\t    sizeof(ls) });\n> +\tctrls.set(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, c);\n>  \n> -\tdefault:\n> -\t\tLOG(RPI, Error) << \"Unknown action \" << action.operation;\n> -\t\tbreak;\n> -\t}\n> +\tisp_[Isp::Input].dev()->setControls(&ctrls);\n> +\thandleState();\n> +}\n>  \n> -done:\n> +void RPiCameraData::setStaggered(const ControlList &controls)\n> +{\n> +\tif (!staggeredCtrl_.set(controls))\n> +\t\tLOG(RPI, Error) << \"V4L2 staggered set failed\";\n>  \thandleState();\n>  }\n>  \n> @@ -1445,10 +1455,7 @@ void RPiCameraData::ispOutputDequeue(FrameBuffer *buffer)\n>  \t * application until after the IPA signals so.\n>  \t */\n>  \tif (stream == &isp_[Isp::Stats]) {\n> -\t\tIPAOperationData op;\n> -\t\top.operation = RPi::IPA_EVENT_SIGNAL_STAT_READY;\n> -\t\top.data = { RPi::BufferMask::STATS | static_cast<unsigned int>(index) };\n> -\t\tipa_->processEvent(op);\n> +\t\tipa_->signalStatReady(ipa::rpi::MaskStats | static_cast<unsigned int>(index));\n>  \t} else {\n>  \t\t/* Any other ISP output can be handed back to the application now. */\n>  \t\thandleStreamBuffer(buffer, stream);\n> @@ -1552,7 +1559,7 @@ void RPiCameraData::handleExternalBuffer(FrameBuffer *buffer, RPi::Stream *strea\n>  {\n>  \tunsigned int id = stream->getBufferId(buffer);\n>  \n> -\tif (!(id & RPi::BufferMask::EXTERNAL_BUFFER))\n> +\tif (!(id & ipa::rpi::MaskExternalBuffer))\n>  \t\treturn;\n>  \n>  \t/* Stop the Stream object from tracking the buffer. */\n> @@ -1652,7 +1659,6 @@ void RPiCameraData::applyScalerCrop(const ControlList &controls)\n>  void RPiCameraData::tryRunPipeline()\n>  {\n>  \tFrameBuffer *bayerBuffer, *embeddedBuffer;\n> -\tIPAOperationData op;\n>  \n>  \t/* If any of our request or buffer queues are empty, we cannot proceed. */\n>  \tif (state_ != State::Idle || requestQueue_.empty() ||\n> @@ -1673,9 +1679,7 @@ void RPiCameraData::tryRunPipeline()\n>  \t * queue the ISP output buffer listed in the request to start the HW\n>  \t * pipeline.\n>  \t */\n> -\top.operation = RPi::IPA_EVENT_QUEUE_REQUEST;\n> -\top.controls = { request->controls() };\n> -\tipa_->processEvent(op);\n> +\tipa_->signalQueueRequest(request->controls());\n>  \n>  \t/* Set our state to say the pipeline is active. */\n>  \tstate_ = State::Busy;\n> @@ -1683,14 +1687,14 @@ void RPiCameraData::tryRunPipeline()\n>  \tunsigned int bayerId = unicam_[Unicam::Image].getBufferId(bayerBuffer);\n>  \tunsigned int embeddedId = unicam_[Unicam::Embedded].getBufferId(embeddedBuffer);\n>  \n> -\tLOG(RPI, Debug) << \"Signalling RPi::IPA_EVENT_SIGNAL_ISP_PREPARE:\"\n> +\tLOG(RPI, Debug) << \"Signalling signalIspPrepare:\"\n>  \t\t\t<< \" Bayer buffer id: \" << bayerId\n>  \t\t\t<< \" Embedded buffer id: \" << embeddedId;\n>  \n> -\top.operation = RPi::IPA_EVENT_SIGNAL_ISP_PREPARE;\n> -\top.data = { RPi::BufferMask::EMBEDDED_DATA | embeddedId,\n> -\t\t    RPi::BufferMask::BAYER_DATA | bayerId };\n> -\tipa_->processEvent(op);\n> +\tipa::rpi::ISPConfig ispPrepare;\n> +\tispPrepare.embeddedbufferId = ipa::rpi::MaskEmbeddedData | embeddedId;\n> +\tispPrepare.bayerbufferId = ipa::rpi::MaskBayerData | bayerId;\n> +\tipa_->signalIspPrepare(ispPrepare);\n>  }\n>  \n>  bool RPiCameraData::findMatchingBuffers(FrameBuffer *&bayerBuffer, FrameBuffer *&embeddedBuffer)\n> diff --git a/src/libcamera/pipeline/raspberrypi/rpi_stream.cpp b/src/libcamera/pipeline/raspberrypi/rpi_stream.cpp\n> index 3a5dadab..496dd36f 100644\n> --- a/src/libcamera/pipeline/raspberrypi/rpi_stream.cpp\n> +++ b/src/libcamera/pipeline/raspberrypi/rpi_stream.cpp\n> @@ -6,6 +6,8 @@\n>   */\n>  #include \"rpi_stream.h\"\n>  \n> +#include <libcamera/ipa/raspberrypi_ipa_interface.h>\n> +\n>  #include \"libcamera/internal/log.h\"\n>  \n>  namespace libcamera {\n> @@ -70,7 +72,7 @@ int Stream::getBufferId(FrameBuffer *buffer) const\n>  \n>  void Stream::setExternalBuffer(FrameBuffer *buffer)\n>  {\n> -\tbufferMap_.emplace(RPi::BufferMask::EXTERNAL_BUFFER | id_.get(), buffer);\n> +\tbufferMap_.emplace(ipa::rpi::MaskExternalBuffer | id_.get(), buffer);\n>  }\n>  \n>  void Stream::removeExternalBuffer(FrameBuffer *buffer)\n> @@ -78,7 +80,7 @@ void Stream::removeExternalBuffer(FrameBuffer *buffer)\n>  \tint id = getBufferId(buffer);\n>  \n>  \t/* Ensure we have this buffer in the stream, and it is marked external. */\n> -\tASSERT(id != -1 && (id & RPi::BufferMask::EXTERNAL_BUFFER));\n> +\tASSERT(id != -1 && (id & ipa::rpi::MaskExternalBuffer));\n>  \tbufferMap_.erase(id);\n>  }\n>  \n> diff --git a/src/libcamera/pipeline/raspberrypi/rpi_stream.h b/src/libcamera/pipeline/raspberrypi/rpi_stream.h\n> index 0b502f64..701110d0 100644\n> --- a/src/libcamera/pipeline/raspberrypi/rpi_stream.h\n> +++ b/src/libcamera/pipeline/raspberrypi/rpi_stream.h\n> @@ -13,6 +13,7 @@\n>  #include <vector>\n>  \n>  #include <libcamera/ipa/raspberrypi.h>\n> +#include <libcamera/ipa/raspberrypi_ipa_interface.h>\n>  #include <libcamera/stream.h>\n>  \n>  #include \"libcamera/internal/v4l2_videodevice.h\"\n> @@ -31,13 +32,13 @@ class Stream : public libcamera::Stream\n>  {\n>  public:\n>  \tStream()\n> -\t\t: id_(RPi::BufferMask::ID)\n> +\t\t: id_(ipa::rpi::MaskID)\n>  \t{\n>  \t}\n>  \n>  \tStream(const char *name, MediaEntity *dev, bool importOnly = false)\n>  \t\t: external_(false), importOnly_(importOnly), name_(name),\n> -\t\t  dev_(std::make_unique<V4L2VideoDevice>(dev)), id_(RPi::BufferMask::ID)\n> +\t\t  dev_(std::make_unique<V4L2VideoDevice>(dev)), id_(ipa::rpi::MaskID)\n>  \t{\n>  \t}\n>","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 C49B7BD162\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  4 Feb 2021 02:50:33 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 51D90613F1;\n\tThu,  4 Feb 2021 03:50:33 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8AA6C613DA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  4 Feb 2021 03:50:32 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id CDEC15A5;\n\tThu,  4 Feb 2021 03:50:31 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"eokSeJ97\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1612407032;\n\tbh=zNH/AXYrMz16Lp/kaoSz5IwTaGPGF7uAMK8eND08YnE=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=eokSeJ97LJb+4aJ3PWIaXgrbas76L+gXOOwcNAWmll1bKrz67RCjemDUpLvEcOflr\n\tyozSrqyEIzXiqvkkvkUgK5vEdWyEqWWxyRUPd1xBgPWN8DSm9DSpMPJCbh7Az+95OS\n\tYXtgr01p4dC2MP2yaOv3N7H6VJ0t1uuSJqOcz2vE=","Date":"Thu, 4 Feb 2021 04:50:09 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<YBtg4ctE33HcbD8l@pendragon.ideasonboard.com>","References":"<20201224081613.41662-1-paul.elder@ideasonboard.com>\n\t<20201224081613.41662-10-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201224081613.41662-10-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v6 09/11] libcamera: pipeline,\n\tipa: raspberrypi: Use new data definition","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]