Message ID | 20201229160318.77536-7-niklas.soderlund@ragnatech.se |
---|---|
State | Superseded |
Delegated to: | Niklas Söderlund |
Headers | show |
Series |
|
Related | show |
Hi Niklas, Thank you for the patch. On Tue, Dec 29, 2020 at 05:03:13PM +0100, Niklas Söderlund wrote: > Add an empty IPA skeleton for the IPU3 pipeline. The skeleton IPA > handles the flow of parameter and statistic buffers but does not read or > write anything in the buffers. It also allows the IPA to set sensor > controls but does not implement any logic to set optimal values and > instead sets the V4L2 exposure and gain controls to max and keeps them > at that setting. > > This IPA is meant as a base to allow the pipeline handler to be wired up > to an IPA. The image algorithms can then later be added to the IPA > independently from also having to add plumbing to the pipeline handler. > > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> > Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > --- > * Changes since v1 > - Updated commit message. > - Rename IPU3_IPA_EVENT_PARSE_STAT to IPU3_IPA_EVENT_PARSE_STAT. > - Sort and drop unused headers. > - Fix style issue in meson.build and add dependency on libatomic. > - Switch to MappedFrameBuffer interface. > - Add result of IPA configuration status. > --- > include/libcamera/ipa/ipu3.h | 23 ++++ > src/ipa/ipu3/ipu3.cpp | 236 +++++++++++++++++++++++++++++++++++ > src/ipa/ipu3/meson.build | 21 ++++ > src/ipa/meson.build | 2 +- > 4 files changed, 281 insertions(+), 1 deletion(-) > create mode 100644 include/libcamera/ipa/ipu3.h > create mode 100644 src/ipa/ipu3/ipu3.cpp > create mode 100644 src/ipa/ipu3/meson.build > > diff --git a/include/libcamera/ipa/ipu3.h b/include/libcamera/ipa/ipu3.h > new file mode 100644 > index 0000000000000000..1bcf3591f9fe36a9 > --- /dev/null > +++ b/include/libcamera/ipa/ipu3.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Google Inc. > + * > + * ipu3.h - Image Processing Algorithm interface for IPU3 > + */ > +#ifndef __LIBCAMERA_IPA_INTERFACE_IPU3_H__ > +#define __LIBCAMERA_IPA_INTERFACE_IPU3_H__ > + > +#ifndef __DOXYGEN__ > + > +enum IPU3Operations { > + IPU3_IPA_STATUS_CONFIGURATION = 1, > + IPU3_IPA_ACTION_SET_SENSOR_CONTROLS = 2, > + IPU3_IPA_ACTION_PARAM_FILLED = 3, > + IPU3_IPA_ACTION_METADATA_READY = 4, > + IPU3_IPA_EVENT_STAT_READY = 5, > + IPU3_IPA_EVENT_FILL_PARAMS = 6, > +}; > + > +#endif /* __DOXYGEN__ */ > + > +#endif /* __LIBCAMERA_IPA_INTERFACE_IPU3_H__ */ > diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp > new file mode 100644 > index 0000000000000000..809a3eec398a8e57 > --- /dev/null > +++ b/src/ipa/ipu3/ipu3.cpp > @@ -0,0 +1,236 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Google Inc. > + * > + * ipu3.cpp - IPU3 Image Processing Algorithms > + */ > + > +#include <libcamera/ipa/ipu3.h> > + I think you need stdint.h for uint32_t. > +#include <sys/mman.h> > + > +#include <linux/intel-ipu3.h> > +#include <linux/v4l2-controls.h> > + > +#include <libcamera/buffer.h> > +#include <libcamera/control_ids.h> > +#include <libcamera/ipa/ipa_interface.h> > +#include <libcamera/ipa/ipa_module_info.h> > +#include <libcamera/request.h> > + > +#include <libipa/ipa_interface_wrapper.h> > + > +#include "libcamera/internal/buffer.h" > +#include "libcamera/internal/log.h" > + > +namespace libcamera { > + > +LOG_DEFINE_CATEGORY(IPAIPU3) > + > +class IPAIPU3 : public IPAInterface > +{ > +public: > + int init([[maybe_unused]] const IPASettings &settings) override > + { > + return 0; > + } > + int start([[maybe_unused]] const IPAOperationData &data, > + [[maybe_unused]] IPAOperationData *result) override { return 0; } > + void stop() override {} > + > + void configure(const CameraSensorInfo &info, > + const std::map<unsigned int, IPAStream> &streamConfig, > + const std::map<unsigned int, const ControlInfoMap &> &entityControls, > + const IPAOperationData &ipaConfig, > + IPAOperationData *response) override; > + void mapBuffers(const std::vector<IPABuffer> &buffers) override; > + void unmapBuffers(const std::vector<unsigned int> &ids) override; > + void processEvent(const IPAOperationData &event) override; > + > +private: > + void fillParams(unsigned int frame, ipu3_uapi_params *params, > + const ControlList &controls); > + > + void parseStatistics(unsigned int frame, > + const ipu3_uapi_stats_3a *stats); > + > + void setControls(unsigned int frame); > + > + std::map<unsigned int, MappedFrameBuffer> buffers_; > + > + ControlInfoMap ctrls_; > + > + /* Camera sensor controls. */ > + uint32_t exposure_; > + uint32_t minExposure_; > + uint32_t maxExposure_; > + uint32_t gain_; > + uint32_t minGain_; > + uint32_t maxGain_; > +}; > + > +void IPAIPU3::configure([[maybe_unused]] const CameraSensorInfo &info, > + [[maybe_unused]] const std::map<unsigned int, IPAStream> &streamConfig, > + const std::map<unsigned int, const ControlInfoMap &> &entityControls, > + [[maybe_unused]] const IPAOperationData &ipaConfig, > + [[maybe_unused]] IPAOperationData *result) > +{ > + result->operation = IPU3_IPA_STATUS_CONFIGURATION; > + > + if (entityControls.empty()) > + return; > + > + ctrls_ = entityControls.at(0); > + > + const auto itExp = ctrls_.find(V4L2_CID_EXPOSURE); > + if (itExp == ctrls_.end()) { > + LOG(IPAIPU3, Error) << "Can't find exposure control"; > + return; > + } > + > + const auto itGain = ctrls_.find(V4L2_CID_ANALOGUE_GAIN); > + if (itGain == ctrls_.end()) { > + LOG(IPAIPU3, Error) << "Can't find gain control"; > + return; > + } > + > + minExposure_ = std::max<uint32_t>(itExp->second.min().get<int32_t>(), 1); Won't this convert both arguments to uint32_t, having the effect of turning a negative value into a large positive value for the first argument ? I think you want minExposure_ = std::max(itExp->second.min().get<int32_t>(), 1); instead. > + maxExposure_ = itExp->second.max().get<int32_t>(); > + exposure_ = maxExposure_; > + > + minGain_ = std::max<uint32_t>(itGain->second.min().get<int32_t>(), 1); Same here. > + maxGain_ = itGain->second.max().get<int32_t>(); > + gain_ = maxGain_; > + > + setControls(0); > + > + result->data.push_back(1); > +} > + > +void IPAIPU3::mapBuffers(const std::vector<IPABuffer> &buffers) > +{ > + for (const IPABuffer &buffer : buffers) { > + const FrameBuffer fb(buffer.planes); > + buffers_.emplace(buffer.id, > + MappedFrameBuffer(&fb, PROT_READ | PROT_WRITE)); > + } > +} > + > +void IPAIPU3::unmapBuffers(const std::vector<unsigned int> &ids) > +{ > + for (unsigned int id : ids) { > + auto it = buffers_.find(id); > + if (it == buffers_.end()) > + continue; > + > + buffers_.erase(id); s/id/it/ ? Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > + } > +} > + > +void IPAIPU3::processEvent(const IPAOperationData &event) > +{ > + switch (event.operation) { > + case IPU3_IPA_EVENT_STAT_READY: { > + unsigned int frame = event.data[0]; > + unsigned int bufferId = event.data[1]; > + > + auto it = buffers_.find(bufferId); > + if (it == buffers_.end()) { > + LOG(IPAIPU3, Error) << "Could not find stats buffer!"; > + return; > + } > + > + Span<uint8_t> mem = it->second.maps()[0]; > + const ipu3_uapi_stats_3a *stats = > + reinterpret_cast<ipu3_uapi_stats_3a *>(mem.data()); > + > + parseStatistics(frame, stats); > + break; > + } > + case IPU3_IPA_EVENT_FILL_PARAMS: { > + unsigned int frame = event.data[0]; > + unsigned int bufferId = event.data[1]; > + > + auto it = buffers_.find(bufferId); > + if (it == buffers_.end()) { > + LOG(IPAIPU3, Error) << "Could not find param buffer!"; > + return; > + } > + > + Span<uint8_t> mem = it->second.maps()[0]; > + ipu3_uapi_params *params = > + reinterpret_cast<ipu3_uapi_params *>(mem.data()); > + > + fillParams(frame, params, event.controls[0]); > + break; > + } > + default: > + LOG(IPAIPU3, Error) << "Unknown event " << event.operation; > + break; > + } > +} > + > +void IPAIPU3::fillParams(unsigned int frame, ipu3_uapi_params *params, > + [[maybe_unused]] const ControlList &controls) > +{ > + /* Prepare parameters buffer. */ > + memset(params, 0, sizeof(*params)); > + > + /* \todo Fill in parameters buffer. */ > + > + IPAOperationData op; > + op.operation = IPU3_IPA_ACTION_PARAM_FILLED; > + > + queueFrameAction.emit(frame, op); > + > + /* \todo Calculate new values for exposure_ and gain_. */ > + setControls(frame); > +} > + > +void IPAIPU3::parseStatistics(unsigned int frame, > + [[maybe_unused]] const ipu3_uapi_stats_3a *stats) > +{ > + ControlList ctrls(controls::controls); > + > + /* \todo React to statistics and update internal state machine. */ > + /* \todo Add meta-data information to ctrls. */ > + > + IPAOperationData op; > + op.operation = IPU3_IPA_ACTION_METADATA_READY; > + op.controls.push_back(ctrls); > + > + queueFrameAction.emit(frame, op); > +} > + > +void IPAIPU3::setControls(unsigned int frame) > +{ > + IPAOperationData op; > + op.operation = IPU3_IPA_ACTION_SET_SENSOR_CONTROLS; > + > + ControlList ctrls(ctrls_); > + ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure_)); > + ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain_)); > + op.controls.push_back(ctrls); > + > + queueFrameAction.emit(frame, op); > +} > + > +/* > + * External IPA module interface > + */ > + > +extern "C" { > +const struct IPAModuleInfo ipaModuleInfo = { > + IPA_MODULE_API_VERSION, > + 1, > + "PipelineHandlerIPU3", > + "ipu3", > +}; > + > +struct ipa_context *ipaCreate() > +{ > + return new IPAInterfaceWrapper(std::make_unique<IPAIPU3>()); > +} > +} > + > +} /* namespace libcamera */ > diff --git a/src/ipa/ipu3/meson.build b/src/ipa/ipu3/meson.build > new file mode 100644 > index 0000000000000000..444c82453eac42ff > --- /dev/null > +++ b/src/ipa/ipu3/meson.build > @@ -0,0 +1,21 @@ > +# SPDX-License-Identifier: CC0-1.0 > + > +ipa_name = 'ipa_ipu3' > + > +mod = shared_module(ipa_name, > + 'ipu3.cpp', > + name_prefix : '', > + include_directories : [ ipa_includes, libipa_includes ], > + dependencies : [ libatomic, libcamera_dep ], > + link_with : libipa, > + install : true, > + install_dir : ipa_install_dir) > + > +if ipa_sign_module > + custom_target(ipa_name + '.so.sign', > + input : mod, > + output : ipa_name + '.so.sign', > + command : [ ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@' ], > + install : false, > + build_by_default : true) > +endif > diff --git a/src/ipa/meson.build b/src/ipa/meson.build > index 5a5de267c1477d24..9d623f227a1f9feb 100644 > --- a/src/ipa/meson.build > +++ b/src/ipa/meson.build > @@ -19,7 +19,7 @@ subdir('libipa') > > ipa_sign = files('ipa-sign.sh') > > -ipas = ['raspberrypi', 'rkisp1', 'vimc'] > +ipas = ['ipu3', 'raspberrypi', 'rkisp1', 'vimc'] > ipa_names = [] > > foreach pipeline : get_option('pipelines')
Hi Niklas, On Tue, Dec 29, 2020 at 05:03:13PM +0100, Niklas Söderlund wrote: > Add an empty IPA skeleton for the IPU3 pipeline. The skeleton IPA > handles the flow of parameter and statistic buffers but does not read or > write anything in the buffers. It also allows the IPA to set sensor > controls but does not implement any logic to set optimal values and > instead sets the V4L2 exposure and gain controls to max and keeps them > at that setting. > > This IPA is meant as a base to allow the pipeline handler to be wired up > to an IPA. The image algorithms can then later be added to the IPA > independently from also having to add plumbing to the pipeline handler. > > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> > Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> Thanks j > --- > * Changes since v1 > - Updated commit message. > - Rename IPU3_IPA_EVENT_PARSE_STAT to IPU3_IPA_EVENT_PARSE_STAT. > - Sort and drop unused headers. > - Fix style issue in meson.build and add dependency on libatomic. > - Switch to MappedFrameBuffer interface. > - Add result of IPA configuration status. > --- > include/libcamera/ipa/ipu3.h | 23 ++++ > src/ipa/ipu3/ipu3.cpp | 236 +++++++++++++++++++++++++++++++++++ > src/ipa/ipu3/meson.build | 21 ++++ > src/ipa/meson.build | 2 +- > 4 files changed, 281 insertions(+), 1 deletion(-) > create mode 100644 include/libcamera/ipa/ipu3.h > create mode 100644 src/ipa/ipu3/ipu3.cpp > create mode 100644 src/ipa/ipu3/meson.build > > diff --git a/include/libcamera/ipa/ipu3.h b/include/libcamera/ipa/ipu3.h > new file mode 100644 > index 0000000000000000..1bcf3591f9fe36a9 > --- /dev/null > +++ b/include/libcamera/ipa/ipu3.h > @@ -0,0 +1,23 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Google Inc. > + * > + * ipu3.h - Image Processing Algorithm interface for IPU3 > + */ > +#ifndef __LIBCAMERA_IPA_INTERFACE_IPU3_H__ > +#define __LIBCAMERA_IPA_INTERFACE_IPU3_H__ > + > +#ifndef __DOXYGEN__ > + > +enum IPU3Operations { > + IPU3_IPA_STATUS_CONFIGURATION = 1, > + IPU3_IPA_ACTION_SET_SENSOR_CONTROLS = 2, > + IPU3_IPA_ACTION_PARAM_FILLED = 3, > + IPU3_IPA_ACTION_METADATA_READY = 4, > + IPU3_IPA_EVENT_STAT_READY = 5, > + IPU3_IPA_EVENT_FILL_PARAMS = 6, > +}; > + > +#endif /* __DOXYGEN__ */ > + > +#endif /* __LIBCAMERA_IPA_INTERFACE_IPU3_H__ */ > diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp > new file mode 100644 > index 0000000000000000..809a3eec398a8e57 > --- /dev/null > +++ b/src/ipa/ipu3/ipu3.cpp > @@ -0,0 +1,236 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Google Inc. > + * > + * ipu3.cpp - IPU3 Image Processing Algorithms > + */ > + > +#include <libcamera/ipa/ipu3.h> > + > +#include <sys/mman.h> > + > +#include <linux/intel-ipu3.h> > +#include <linux/v4l2-controls.h> > + > +#include <libcamera/buffer.h> > +#include <libcamera/control_ids.h> > +#include <libcamera/ipa/ipa_interface.h> > +#include <libcamera/ipa/ipa_module_info.h> > +#include <libcamera/request.h> > + > +#include <libipa/ipa_interface_wrapper.h> > + > +#include "libcamera/internal/buffer.h" > +#include "libcamera/internal/log.h" > + > +namespace libcamera { > + > +LOG_DEFINE_CATEGORY(IPAIPU3) > + > +class IPAIPU3 : public IPAInterface > +{ > +public: > + int init([[maybe_unused]] const IPASettings &settings) override > + { > + return 0; > + } > + int start([[maybe_unused]] const IPAOperationData &data, > + [[maybe_unused]] IPAOperationData *result) override { return 0; } > + void stop() override {} > + > + void configure(const CameraSensorInfo &info, > + const std::map<unsigned int, IPAStream> &streamConfig, > + const std::map<unsigned int, const ControlInfoMap &> &entityControls, > + const IPAOperationData &ipaConfig, > + IPAOperationData *response) override; > + void mapBuffers(const std::vector<IPABuffer> &buffers) override; > + void unmapBuffers(const std::vector<unsigned int> &ids) override; > + void processEvent(const IPAOperationData &event) override; > + > +private: > + void fillParams(unsigned int frame, ipu3_uapi_params *params, > + const ControlList &controls); > + > + void parseStatistics(unsigned int frame, > + const ipu3_uapi_stats_3a *stats); > + > + void setControls(unsigned int frame); > + > + std::map<unsigned int, MappedFrameBuffer> buffers_; > + > + ControlInfoMap ctrls_; > + > + /* Camera sensor controls. */ > + uint32_t exposure_; > + uint32_t minExposure_; > + uint32_t maxExposure_; > + uint32_t gain_; > + uint32_t minGain_; > + uint32_t maxGain_; > +}; > + > +void IPAIPU3::configure([[maybe_unused]] const CameraSensorInfo &info, > + [[maybe_unused]] const std::map<unsigned int, IPAStream> &streamConfig, > + const std::map<unsigned int, const ControlInfoMap &> &entityControls, > + [[maybe_unused]] const IPAOperationData &ipaConfig, > + [[maybe_unused]] IPAOperationData *result) > +{ > + result->operation = IPU3_IPA_STATUS_CONFIGURATION; > + > + if (entityControls.empty()) > + return; > + > + ctrls_ = entityControls.at(0); > + > + const auto itExp = ctrls_.find(V4L2_CID_EXPOSURE); > + if (itExp == ctrls_.end()) { > + LOG(IPAIPU3, Error) << "Can't find exposure control"; > + return; > + } > + > + const auto itGain = ctrls_.find(V4L2_CID_ANALOGUE_GAIN); > + if (itGain == ctrls_.end()) { > + LOG(IPAIPU3, Error) << "Can't find gain control"; > + return; > + } > + > + minExposure_ = std::max<uint32_t>(itExp->second.min().get<int32_t>(), 1); > + maxExposure_ = itExp->second.max().get<int32_t>(); > + exposure_ = maxExposure_; > + > + minGain_ = std::max<uint32_t>(itGain->second.min().get<int32_t>(), 1); > + maxGain_ = itGain->second.max().get<int32_t>(); > + gain_ = maxGain_; > + > + setControls(0); > + > + result->data.push_back(1); > +} > + > +void IPAIPU3::mapBuffers(const std::vector<IPABuffer> &buffers) > +{ > + for (const IPABuffer &buffer : buffers) { > + const FrameBuffer fb(buffer.planes); > + buffers_.emplace(buffer.id, > + MappedFrameBuffer(&fb, PROT_READ | PROT_WRITE)); > + } > +} > + > +void IPAIPU3::unmapBuffers(const std::vector<unsigned int> &ids) > +{ > + for (unsigned int id : ids) { > + auto it = buffers_.find(id); > + if (it == buffers_.end()) > + continue; > + > + buffers_.erase(id); > + } > +} > + > +void IPAIPU3::processEvent(const IPAOperationData &event) > +{ > + switch (event.operation) { > + case IPU3_IPA_EVENT_STAT_READY: { > + unsigned int frame = event.data[0]; > + unsigned int bufferId = event.data[1]; > + > + auto it = buffers_.find(bufferId); > + if (it == buffers_.end()) { > + LOG(IPAIPU3, Error) << "Could not find stats buffer!"; > + return; > + } > + > + Span<uint8_t> mem = it->second.maps()[0]; > + const ipu3_uapi_stats_3a *stats = > + reinterpret_cast<ipu3_uapi_stats_3a *>(mem.data()); > + > + parseStatistics(frame, stats); > + break; > + } > + case IPU3_IPA_EVENT_FILL_PARAMS: { > + unsigned int frame = event.data[0]; > + unsigned int bufferId = event.data[1]; > + > + auto it = buffers_.find(bufferId); > + if (it == buffers_.end()) { > + LOG(IPAIPU3, Error) << "Could not find param buffer!"; > + return; > + } > + > + Span<uint8_t> mem = it->second.maps()[0]; > + ipu3_uapi_params *params = > + reinterpret_cast<ipu3_uapi_params *>(mem.data()); > + > + fillParams(frame, params, event.controls[0]); > + break; > + } > + default: > + LOG(IPAIPU3, Error) << "Unknown event " << event.operation; > + break; > + } > +} > + > +void IPAIPU3::fillParams(unsigned int frame, ipu3_uapi_params *params, > + [[maybe_unused]] const ControlList &controls) > +{ > + /* Prepare parameters buffer. */ > + memset(params, 0, sizeof(*params)); > + > + /* \todo Fill in parameters buffer. */ > + > + IPAOperationData op; > + op.operation = IPU3_IPA_ACTION_PARAM_FILLED; > + > + queueFrameAction.emit(frame, op); > + > + /* \todo Calculate new values for exposure_ and gain_. */ > + setControls(frame); > +} > + > +void IPAIPU3::parseStatistics(unsigned int frame, > + [[maybe_unused]] const ipu3_uapi_stats_3a *stats) > +{ > + ControlList ctrls(controls::controls); > + > + /* \todo React to statistics and update internal state machine. */ > + /* \todo Add meta-data information to ctrls. */ > + > + IPAOperationData op; > + op.operation = IPU3_IPA_ACTION_METADATA_READY; > + op.controls.push_back(ctrls); > + > + queueFrameAction.emit(frame, op); > +} > + > +void IPAIPU3::setControls(unsigned int frame) > +{ > + IPAOperationData op; > + op.operation = IPU3_IPA_ACTION_SET_SENSOR_CONTROLS; > + > + ControlList ctrls(ctrls_); > + ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure_)); > + ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain_)); > + op.controls.push_back(ctrls); > + > + queueFrameAction.emit(frame, op); > +} > + > +/* > + * External IPA module interface > + */ > + > +extern "C" { > +const struct IPAModuleInfo ipaModuleInfo = { > + IPA_MODULE_API_VERSION, > + 1, > + "PipelineHandlerIPU3", > + "ipu3", > +}; > + > +struct ipa_context *ipaCreate() > +{ > + return new IPAInterfaceWrapper(std::make_unique<IPAIPU3>()); > +} > +} > + > +} /* namespace libcamera */ > diff --git a/src/ipa/ipu3/meson.build b/src/ipa/ipu3/meson.build > new file mode 100644 > index 0000000000000000..444c82453eac42ff > --- /dev/null > +++ b/src/ipa/ipu3/meson.build > @@ -0,0 +1,21 @@ > +# SPDX-License-Identifier: CC0-1.0 > + > +ipa_name = 'ipa_ipu3' > + > +mod = shared_module(ipa_name, > + 'ipu3.cpp', > + name_prefix : '', > + include_directories : [ ipa_includes, libipa_includes ], > + dependencies : [ libatomic, libcamera_dep ], > + link_with : libipa, > + install : true, > + install_dir : ipa_install_dir) > + > +if ipa_sign_module > + custom_target(ipa_name + '.so.sign', > + input : mod, > + output : ipa_name + '.so.sign', > + command : [ ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@' ], > + install : false, > + build_by_default : true) > +endif > diff --git a/src/ipa/meson.build b/src/ipa/meson.build > index 5a5de267c1477d24..9d623f227a1f9feb 100644 > --- a/src/ipa/meson.build > +++ b/src/ipa/meson.build > @@ -19,7 +19,7 @@ subdir('libipa') > > ipa_sign = files('ipa-sign.sh') > > -ipas = ['raspberrypi', 'rkisp1', 'vimc'] > +ipas = ['ipu3', 'raspberrypi', 'rkisp1', 'vimc'] > ipa_names = [] > > foreach pipeline : get_option('pipelines') > -- > 2.29.2 > > _______________________________________________ > libcamera-devel mailing list > libcamera-devel@lists.libcamera.org > https://lists.libcamera.org/listinfo/libcamera-devel
diff --git a/include/libcamera/ipa/ipu3.h b/include/libcamera/ipa/ipu3.h new file mode 100644 index 0000000000000000..1bcf3591f9fe36a9 --- /dev/null +++ b/include/libcamera/ipa/ipu3.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipu3.h - Image Processing Algorithm interface for IPU3 + */ +#ifndef __LIBCAMERA_IPA_INTERFACE_IPU3_H__ +#define __LIBCAMERA_IPA_INTERFACE_IPU3_H__ + +#ifndef __DOXYGEN__ + +enum IPU3Operations { + IPU3_IPA_STATUS_CONFIGURATION = 1, + IPU3_IPA_ACTION_SET_SENSOR_CONTROLS = 2, + IPU3_IPA_ACTION_PARAM_FILLED = 3, + IPU3_IPA_ACTION_METADATA_READY = 4, + IPU3_IPA_EVENT_STAT_READY = 5, + IPU3_IPA_EVENT_FILL_PARAMS = 6, +}; + +#endif /* __DOXYGEN__ */ + +#endif /* __LIBCAMERA_IPA_INTERFACE_IPU3_H__ */ diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp new file mode 100644 index 0000000000000000..809a3eec398a8e57 --- /dev/null +++ b/src/ipa/ipu3/ipu3.cpp @@ -0,0 +1,236 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipu3.cpp - IPU3 Image Processing Algorithms + */ + +#include <libcamera/ipa/ipu3.h> + +#include <sys/mman.h> + +#include <linux/intel-ipu3.h> +#include <linux/v4l2-controls.h> + +#include <libcamera/buffer.h> +#include <libcamera/control_ids.h> +#include <libcamera/ipa/ipa_interface.h> +#include <libcamera/ipa/ipa_module_info.h> +#include <libcamera/request.h> + +#include <libipa/ipa_interface_wrapper.h> + +#include "libcamera/internal/buffer.h" +#include "libcamera/internal/log.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(IPAIPU3) + +class IPAIPU3 : public IPAInterface +{ +public: + int init([[maybe_unused]] const IPASettings &settings) override + { + return 0; + } + int start([[maybe_unused]] const IPAOperationData &data, + [[maybe_unused]] IPAOperationData *result) override { return 0; } + void stop() override {} + + void configure(const CameraSensorInfo &info, + const std::map<unsigned int, IPAStream> &streamConfig, + const std::map<unsigned int, const ControlInfoMap &> &entityControls, + const IPAOperationData &ipaConfig, + IPAOperationData *response) override; + void mapBuffers(const std::vector<IPABuffer> &buffers) override; + void unmapBuffers(const std::vector<unsigned int> &ids) override; + void processEvent(const IPAOperationData &event) override; + +private: + void fillParams(unsigned int frame, ipu3_uapi_params *params, + const ControlList &controls); + + void parseStatistics(unsigned int frame, + const ipu3_uapi_stats_3a *stats); + + void setControls(unsigned int frame); + + std::map<unsigned int, MappedFrameBuffer> buffers_; + + ControlInfoMap ctrls_; + + /* Camera sensor controls. */ + uint32_t exposure_; + uint32_t minExposure_; + uint32_t maxExposure_; + uint32_t gain_; + uint32_t minGain_; + uint32_t maxGain_; +}; + +void IPAIPU3::configure([[maybe_unused]] const CameraSensorInfo &info, + [[maybe_unused]] const std::map<unsigned int, IPAStream> &streamConfig, + const std::map<unsigned int, const ControlInfoMap &> &entityControls, + [[maybe_unused]] const IPAOperationData &ipaConfig, + [[maybe_unused]] IPAOperationData *result) +{ + result->operation = IPU3_IPA_STATUS_CONFIGURATION; + + if (entityControls.empty()) + return; + + ctrls_ = entityControls.at(0); + + const auto itExp = ctrls_.find(V4L2_CID_EXPOSURE); + if (itExp == ctrls_.end()) { + LOG(IPAIPU3, Error) << "Can't find exposure control"; + return; + } + + const auto itGain = ctrls_.find(V4L2_CID_ANALOGUE_GAIN); + if (itGain == ctrls_.end()) { + LOG(IPAIPU3, Error) << "Can't find gain control"; + return; + } + + minExposure_ = std::max<uint32_t>(itExp->second.min().get<int32_t>(), 1); + maxExposure_ = itExp->second.max().get<int32_t>(); + exposure_ = maxExposure_; + + minGain_ = std::max<uint32_t>(itGain->second.min().get<int32_t>(), 1); + maxGain_ = itGain->second.max().get<int32_t>(); + gain_ = maxGain_; + + setControls(0); + + result->data.push_back(1); +} + +void IPAIPU3::mapBuffers(const std::vector<IPABuffer> &buffers) +{ + for (const IPABuffer &buffer : buffers) { + const FrameBuffer fb(buffer.planes); + buffers_.emplace(buffer.id, + MappedFrameBuffer(&fb, PROT_READ | PROT_WRITE)); + } +} + +void IPAIPU3::unmapBuffers(const std::vector<unsigned int> &ids) +{ + for (unsigned int id : ids) { + auto it = buffers_.find(id); + if (it == buffers_.end()) + continue; + + buffers_.erase(id); + } +} + +void IPAIPU3::processEvent(const IPAOperationData &event) +{ + switch (event.operation) { + case IPU3_IPA_EVENT_STAT_READY: { + unsigned int frame = event.data[0]; + unsigned int bufferId = event.data[1]; + + auto it = buffers_.find(bufferId); + if (it == buffers_.end()) { + LOG(IPAIPU3, Error) << "Could not find stats buffer!"; + return; + } + + Span<uint8_t> mem = it->second.maps()[0]; + const ipu3_uapi_stats_3a *stats = + reinterpret_cast<ipu3_uapi_stats_3a *>(mem.data()); + + parseStatistics(frame, stats); + break; + } + case IPU3_IPA_EVENT_FILL_PARAMS: { + unsigned int frame = event.data[0]; + unsigned int bufferId = event.data[1]; + + auto it = buffers_.find(bufferId); + if (it == buffers_.end()) { + LOG(IPAIPU3, Error) << "Could not find param buffer!"; + return; + } + + Span<uint8_t> mem = it->second.maps()[0]; + ipu3_uapi_params *params = + reinterpret_cast<ipu3_uapi_params *>(mem.data()); + + fillParams(frame, params, event.controls[0]); + break; + } + default: + LOG(IPAIPU3, Error) << "Unknown event " << event.operation; + break; + } +} + +void IPAIPU3::fillParams(unsigned int frame, ipu3_uapi_params *params, + [[maybe_unused]] const ControlList &controls) +{ + /* Prepare parameters buffer. */ + memset(params, 0, sizeof(*params)); + + /* \todo Fill in parameters buffer. */ + + IPAOperationData op; + op.operation = IPU3_IPA_ACTION_PARAM_FILLED; + + queueFrameAction.emit(frame, op); + + /* \todo Calculate new values for exposure_ and gain_. */ + setControls(frame); +} + +void IPAIPU3::parseStatistics(unsigned int frame, + [[maybe_unused]] const ipu3_uapi_stats_3a *stats) +{ + ControlList ctrls(controls::controls); + + /* \todo React to statistics and update internal state machine. */ + /* \todo Add meta-data information to ctrls. */ + + IPAOperationData op; + op.operation = IPU3_IPA_ACTION_METADATA_READY; + op.controls.push_back(ctrls); + + queueFrameAction.emit(frame, op); +} + +void IPAIPU3::setControls(unsigned int frame) +{ + IPAOperationData op; + op.operation = IPU3_IPA_ACTION_SET_SENSOR_CONTROLS; + + ControlList ctrls(ctrls_); + ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure_)); + ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain_)); + op.controls.push_back(ctrls); + + queueFrameAction.emit(frame, op); +} + +/* + * External IPA module interface + */ + +extern "C" { +const struct IPAModuleInfo ipaModuleInfo = { + IPA_MODULE_API_VERSION, + 1, + "PipelineHandlerIPU3", + "ipu3", +}; + +struct ipa_context *ipaCreate() +{ + return new IPAInterfaceWrapper(std::make_unique<IPAIPU3>()); +} +} + +} /* namespace libcamera */ diff --git a/src/ipa/ipu3/meson.build b/src/ipa/ipu3/meson.build new file mode 100644 index 0000000000000000..444c82453eac42ff --- /dev/null +++ b/src/ipa/ipu3/meson.build @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: CC0-1.0 + +ipa_name = 'ipa_ipu3' + +mod = shared_module(ipa_name, + 'ipu3.cpp', + name_prefix : '', + include_directories : [ ipa_includes, libipa_includes ], + dependencies : [ libatomic, libcamera_dep ], + link_with : libipa, + install : true, + install_dir : ipa_install_dir) + +if ipa_sign_module + custom_target(ipa_name + '.so.sign', + input : mod, + output : ipa_name + '.so.sign', + command : [ ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@' ], + install : false, + build_by_default : true) +endif diff --git a/src/ipa/meson.build b/src/ipa/meson.build index 5a5de267c1477d24..9d623f227a1f9feb 100644 --- a/src/ipa/meson.build +++ b/src/ipa/meson.build @@ -19,7 +19,7 @@ subdir('libipa') ipa_sign = files('ipa-sign.sh') -ipas = ['raspberrypi', 'rkisp1', 'vimc'] +ipas = ['ipu3', 'raspberrypi', 'rkisp1', 'vimc'] ipa_names = [] foreach pipeline : get_option('pipelines')