From 5e8933eeebcedda55d4c4e84db9b4983eecefe1b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 25 Mar 2020 09:36:35 +0200 Subject: libcamera: pipeline: Move uvcvideo and vimc to subdirectories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give a subdirectory to all pipeline handlers to make the structure of the source tree more consistent. This will also simplify the implementation of pipeline handlers selection at build time. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Niklas Söderlund --- src/libcamera/pipeline/vimc/meson.build | 3 + src/libcamera/pipeline/vimc/vimc.cpp | 467 ++++++++++++++++++++++++++++++++ 2 files changed, 470 insertions(+) create mode 100644 src/libcamera/pipeline/vimc/meson.build create mode 100644 src/libcamera/pipeline/vimc/vimc.cpp (limited to 'src/libcamera/pipeline/vimc') diff --git a/src/libcamera/pipeline/vimc/meson.build b/src/libcamera/pipeline/vimc/meson.build new file mode 100644 index 00000000..615ecd20 --- /dev/null +++ b/src/libcamera/pipeline/vimc/meson.build @@ -0,0 +1,3 @@ +libcamera_sources += files([ + 'vimc.cpp', +]) diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp new file mode 100644 index 00000000..b04a9726 --- /dev/null +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -0,0 +1,467 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * vimc.cpp - Pipeline handler for the vimc device + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "camera_sensor.h" +#include "device_enumerator.h" +#include "ipa_manager.h" +#include "log.h" +#include "media_device.h" +#include "pipeline_handler.h" +#include "utils.h" +#include "v4l2_controls.h" +#include "v4l2_subdevice.h" +#include "v4l2_videodevice.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(VIMC) + +class VimcCameraData : public CameraData +{ +public: + VimcCameraData(PipelineHandler *pipe) + : CameraData(pipe), sensor_(nullptr), debayer_(nullptr), + scaler_(nullptr), video_(nullptr), raw_(nullptr) + { + } + + ~VimcCameraData() + { + delete sensor_; + delete debayer_; + delete scaler_; + delete video_; + delete raw_; + } + + int init(MediaDevice *media); + void bufferReady(FrameBuffer *buffer); + + CameraSensor *sensor_; + V4L2Subdevice *debayer_; + V4L2Subdevice *scaler_; + V4L2VideoDevice *video_; + V4L2VideoDevice *raw_; + Stream stream_; +}; + +class VimcCameraConfiguration : public CameraConfiguration +{ +public: + VimcCameraConfiguration(); + + Status validate() override; +}; + +class PipelineHandlerVimc : public PipelineHandler +{ +public: + PipelineHandlerVimc(CameraManager *manager); + + CameraConfiguration *generateConfiguration(Camera *camera, + const StreamRoles &roles) override; + int configure(Camera *camera, CameraConfiguration *config) override; + + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + + int start(Camera *camera) override; + void stop(Camera *camera) override; + + int queueRequestDevice(Camera *camera, Request *request) override; + + bool match(DeviceEnumerator *enumerator) override; + +private: + int processControls(VimcCameraData *data, Request *request); + + VimcCameraData *cameraData(const Camera *camera) + { + return static_cast( + PipelineHandler::cameraData(camera)); + } +}; + +namespace { + +static const std::array pixelformats{ + PixelFormat(DRM_FORMAT_RGB888), + PixelFormat(DRM_FORMAT_BGR888), + PixelFormat(DRM_FORMAT_BGRA8888), +}; + +} /* namespace */ + +VimcCameraConfiguration::VimcCameraConfiguration() + : CameraConfiguration() +{ +} + +CameraConfiguration::Status VimcCameraConfiguration::validate() +{ + Status status = Valid; + + if (config_.empty()) + return Invalid; + + /* Cap the number of entries to the available streams. */ + if (config_.size() > 1) { + config_.resize(1); + status = Adjusted; + } + + StreamConfiguration &cfg = config_[0]; + + /* Adjust the pixel format. */ + if (std::find(pixelformats.begin(), pixelformats.end(), cfg.pixelFormat) == + pixelformats.end()) { + LOG(VIMC, Debug) << "Adjusting format to RGB24"; + cfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888); + status = Adjusted; + } + + /* Clamp the size based on the device limits. */ + const Size size = cfg.size; + + /* The scaler hardcodes a x3 scale-up ratio. */ + cfg.size.width = std::max(48U, std::min(4096U, cfg.size.width)); + cfg.size.height = std::max(48U, std::min(2160U, cfg.size.height)); + cfg.size.width -= cfg.size.width % 3; + cfg.size.height -= cfg.size.height % 3; + + if (cfg.size != size) { + LOG(VIMC, Debug) + << "Adjusting size to " << cfg.size.toString(); + status = Adjusted; + } + + cfg.bufferCount = 4; + + return status; +} + +PipelineHandlerVimc::PipelineHandlerVimc(CameraManager *manager) + : PipelineHandler(manager) +{ +} + +CameraConfiguration *PipelineHandlerVimc::generateConfiguration(Camera *camera, + const StreamRoles &roles) +{ + CameraConfiguration *config = new VimcCameraConfiguration(); + + if (roles.empty()) + return config; + + std::map> formats; + + for (PixelFormat pixelformat : pixelformats) { + /* The scaler hardcodes a x3 scale-up ratio. */ + std::vector sizes{ + SizeRange{ { 48, 48 }, { 4096, 2160 } } + }; + formats[pixelformat] = sizes; + } + + StreamConfiguration cfg(formats); + + cfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888); + cfg.size = { 1920, 1080 }; + cfg.bufferCount = 4; + + config->addConfiguration(cfg); + + config->validate(); + + return config; +} + +int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config) +{ + VimcCameraData *data = cameraData(camera); + StreamConfiguration &cfg = config->at(0); + int ret; + + /* The scaler hardcodes a x3 scale-up ratio. */ + V4L2SubdeviceFormat subformat = {}; + subformat.mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8; + subformat.size = { cfg.size.width / 3, cfg.size.height / 3 }; + + ret = data->sensor_->setFormat(&subformat); + if (ret) + return ret; + + ret = data->debayer_->setFormat(0, &subformat); + if (ret) + return ret; + + subformat.mbus_code = MEDIA_BUS_FMT_RGB888_1X24; + ret = data->debayer_->setFormat(1, &subformat); + if (ret) + return ret; + + ret = data->scaler_->setFormat(0, &subformat); + if (ret) + return ret; + + subformat.size = cfg.size; + ret = data->scaler_->setFormat(1, &subformat); + if (ret) + return ret; + + V4L2DeviceFormat format = {}; + format.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat); + format.size = cfg.size; + + ret = data->video_->setFormat(&format); + if (ret) + return ret; + + if (format.size != cfg.size || + format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat)) + return -EINVAL; + + /* + * Format has to be set on the raw capture video node, otherwise the + * vimc driver will fail pipeline validation. + */ + format.fourcc = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8); + format.size = { cfg.size.width / 3, cfg.size.height / 3 }; + + ret = data->raw_->setFormat(&format); + if (ret) + return ret; + + cfg.setStream(&data->stream_); + + return 0; +} + +int PipelineHandlerVimc::exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) +{ + VimcCameraData *data = cameraData(camera); + unsigned int count = stream->configuration().bufferCount; + + return data->video_->exportBuffers(count, buffers); +} + +int PipelineHandlerVimc::start(Camera *camera) +{ + VimcCameraData *data = cameraData(camera); + unsigned int count = data->stream_.configuration().bufferCount; + + int ret = data->video_->importBuffers(count); + if (ret < 0) + return ret; + + ret = data->video_->streamOn(); + if (ret < 0) { + data->video_->releaseBuffers(); + return ret; + } + + return 0; +} + +void PipelineHandlerVimc::stop(Camera *camera) +{ + VimcCameraData *data = cameraData(camera); + data->video_->streamOff(); + data->video_->releaseBuffers(); +} + +int PipelineHandlerVimc::processControls(VimcCameraData *data, Request *request) +{ + ControlList controls(data->sensor_->controls()); + + for (auto it : request->controls()) { + unsigned int id = it.first; + ControlValue &value = it.second; + + if (id == controls::Brightness) + controls.set(V4L2_CID_BRIGHTNESS, value); + else if (id == controls::Contrast) + controls.set(V4L2_CID_CONTRAST, value); + else if (id == controls::Saturation) + controls.set(V4L2_CID_SATURATION, value); + } + + for (const auto &ctrl : controls) + LOG(VIMC, Debug) + << "Setting control " << utils::hex(ctrl.first) + << " to " << ctrl.second.toString(); + + int ret = data->sensor_->setControls(&controls); + if (ret) { + LOG(VIMC, Error) << "Failed to set controls: " << ret; + return ret < 0 ? ret : -EINVAL; + } + + return ret; +} + +int PipelineHandlerVimc::queueRequestDevice(Camera *camera, Request *request) +{ + VimcCameraData *data = cameraData(camera); + FrameBuffer *buffer = request->findBuffer(&data->stream_); + if (!buffer) { + LOG(VIMC, Error) + << "Attempt to queue request with invalid stream"; + + return -ENOENT; + } + + int ret = processControls(data, request); + if (ret < 0) + return ret; + + ret = data->video_->queueBuffer(buffer); + if (ret < 0) + return ret; + + return 0; +} + +bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator) +{ + DeviceMatch dm("vimc"); + + dm.add("Raw Capture 0"); + dm.add("Raw Capture 1"); + dm.add("RGB/YUV Capture"); + dm.add("Sensor A"); + dm.add("Sensor B"); + dm.add("Debayer A"); + dm.add("Debayer B"); + dm.add("RGB/YUV Input"); + dm.add("Scaler"); + + MediaDevice *media = acquireMediaDevice(enumerator, dm); + if (!media) + return false; + + std::unique_ptr data = std::make_unique(this); + + data->ipa_ = IPAManager::instance()->createIPA(this, 0, 0); + if (data->ipa_ == nullptr) + LOG(VIMC, Warning) << "no matching IPA found"; + else + data->ipa_->init(); + + /* Locate and open the capture video node. */ + if (data->init(media)) + return false; + + /* Create and register the camera. */ + std::set streams{ &data->stream_ }; + std::shared_ptr camera = Camera::create(this, "VIMC Sensor B", + streams); + registerCamera(std::move(camera), std::move(data)); + + return true; +} + +int VimcCameraData::init(MediaDevice *media) +{ + int ret; + + ret = media->disableLinks(); + if (ret < 0) + return ret; + + MediaLink *link = media->link("Debayer B", 1, "Scaler", 0); + if (!link) + return -ENODEV; + + ret = link->setEnabled(true); + if (ret < 0) + return ret; + + /* Create and open the camera sensor, debayer, scaler and video device. */ + sensor_ = new CameraSensor(media->getEntityByName("Sensor B")); + ret = sensor_->init(); + if (ret) + return ret; + + debayer_ = new V4L2Subdevice(media->getEntityByName("Debayer B")); + if (debayer_->open()) + return -ENODEV; + + scaler_ = new V4L2Subdevice(media->getEntityByName("Scaler")); + if (scaler_->open()) + return -ENODEV; + + video_ = new V4L2VideoDevice(media->getEntityByName("RGB/YUV Capture")); + if (video_->open()) + return -ENODEV; + + video_->bufferReady.connect(this, &VimcCameraData::bufferReady); + + raw_ = new V4L2VideoDevice(media->getEntityByName("Raw Capture 1")); + if (raw_->open()) + return -ENODEV; + + /* Initialise the supported controls. */ + const ControlInfoMap &controls = sensor_->controls(); + ControlInfoMap::Map ctrls; + + for (const auto &ctrl : controls) { + const ControlInfo &info = ctrl.second; + const ControlId *id; + + switch (ctrl.first->id()) { + case V4L2_CID_BRIGHTNESS: + id = &controls::Brightness; + break; + case V4L2_CID_CONTRAST: + id = &controls::Contrast; + break; + case V4L2_CID_SATURATION: + id = &controls::Saturation; + break; + default: + continue; + } + + ctrls.emplace(id, info); + } + + controlInfo_ = std::move(ctrls); + + /* Initialize the camera properties. */ + properties_ = sensor_->properties(); + + return 0; +} + +void VimcCameraData::bufferReady(FrameBuffer *buffer) +{ + Request *request = buffer->request(); + + pipe_->completeBuffer(camera_, request, buffer); + pipe_->completeRequest(camera_, request); +} + +REGISTER_PIPELINE_HANDLER(PipelineHandlerVimc); + +} /* namespace libcamera */ -- cgit v1.2.1