From patchwork Fri Oct 4 19:45:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martijn Braam X-Patchwork-Id: 2103 Return-Path: Received: from mail-ed1-x533.google.com (mail-ed1-x533.google.com [IPv6:2a00:1450:4864:20::533]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1912360BC6 for ; Fri, 4 Oct 2019 21:45:56 +0200 (CEST) Received: by mail-ed1-x533.google.com with SMTP id v38so7061515edm.7 for ; Fri, 04 Oct 2019 12:45:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=brixit-nl.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=8+i5+V3+uILTMa+7lZoHP9RsCRvvzbBNIFJus+dUpV0=; b=Dasu6z7hearJwBgnBeOTnpOls9R5ca61r1Nz8eLJ0ATfK7jCaw5k62anPJJV1dYbyS 7xuu2dfxbDMT9enyrepc/DkWKBKrvmCV4Nl0Sl75fq9BiUXFgsWaRgCtJH5ajkdq1akA /4AQsA6fkUe6LjT38XBSP/ppJChAwmEyXfRYtAuKHFX0aKyi9T4/HRRT7pg0BW5iJ28R 6oL3WcU4wQdO7jKb4iqYKLCL5aOSLyeWR3sft2gnal6utIpn6fP1tWAIvG45JzjWGlFz Np0Opv98E3Gc0NHR0KB59wMbNu7vg4QE4KsCJ/dc0Unb4+7m5HJhcx/tD9Y1ZSzBhVh6 e8OA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=8+i5+V3+uILTMa+7lZoHP9RsCRvvzbBNIFJus+dUpV0=; b=ibCEhdG6Vq8as8Wb8tuhapmes44TdKw8yhZImVN6IdnwTckllgYkt4kCKqrJlsPnJ/ E1RYixbXBAtyDfEPY3xoDUif+M6RyzTXo3bMNXeop2ymSUMQDqQWw9XC+MkpUTsVHEtF XCDyfY4Vv9CunlJafuFSu+qac64PwtgCGdLS2htV5OTWXBI06ORz69EG/meOCoKA8p6p k/K3HKmtKAZnKdVtPoX76+LSf5qAf3uyA6x2zCyxJSbQvAwdiJAxpE+f2mMDFqydOHgH MoDz5ojCXuK0uvU3RWnze1MIzOIkJabas+C7iBMuJcJk5d0kuEe8k+0vFMqRZbmB0Bmg WhNA== X-Gm-Message-State: APjAAAXhU+6jZDSEknOwgA/vqRpR5L+t8DZcO16s3TR7pq/wasB76mOk WVC4oxn06G7ps1IYgfRxghKffsdLT0Y= X-Google-Smtp-Source: APXvYqyqB9Pap7DfBPhCDxKgZqsuTwo2H1XdGKiZRXDNjN+GqmOprrpBzx9D4tgLNsuaWv7nug/6rw== X-Received: by 2002:a17:906:1991:: with SMTP id g17mr13684952ejd.220.1570218353846; Fri, 04 Oct 2019 12:45:53 -0700 (PDT) Received: from msi.localhost ([185.54.207.135]) by smtp.gmail.com with ESMTPSA id u27sm1322780edb.48.2019.10.04.12.45.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Oct 2019 12:45:52 -0700 (PDT) From: Martijn Braam To: libcamera-devel@lists.libcamera.org Cc: Martijn Braam Date: Fri, 4 Oct 2019 21:45:34 +0200 Message-Id: <20191004194534.25287-1-martijn@brixit.nl> X-Mailer: git-send-email 2.23.0 MIME-Version: 1.0 X-Mailman-Approved-At: Sat, 05 Oct 2019 18:37:34 +0200 Subject: [libcamera-devel] [PATCH] libcamera: pipeline: Add Simple pipeline X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 04 Oct 2019 19:45:56 -0000 This add a generic pipeline handler for simple pipelines. It currently handles pipelines where there's only a PHY node and a sensor node and the kernel sets up the graph. The current implementation can deal with sunxi sun6i-csi pipelines but other simple pipelines can be added to the infos array. Signed-off-by: Martijn Braam --- src/libcamera/pipeline/meson.build | 1 + src/libcamera/pipeline/simple.cpp | 457 +++++++++++++++++++++++++++++ 2 files changed, 458 insertions(+) create mode 100644 src/libcamera/pipeline/simple.cpp diff --git a/src/libcamera/pipeline/meson.build b/src/libcamera/pipeline/meson.build index 0d46622..df95a43 100644 --- a/src/libcamera/pipeline/meson.build +++ b/src/libcamera/pipeline/meson.build @@ -1,6 +1,7 @@ libcamera_sources += files([ 'uvcvideo.cpp', 'vimc.cpp', + 'simple.cpp' ]) subdir('ipu3') diff --git a/src/libcamera/pipeline/simple.cpp b/src/libcamera/pipeline/simple.cpp new file mode 100644 index 0000000..f150d7e --- /dev/null +++ b/src/libcamera/pipeline/simple.cpp @@ -0,0 +1,457 @@ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "camera_sensor.h" +#include "device_enumerator.h" +#include "log.h" +#include "media_device.h" +#include "pipeline_handler.h" +#include "utils.h" +#include "v4l2_subdevice.h" +#include "v4l2_videodevice.h" + +namespace libcamera { +LOG_DEFINE_CATEGORY(Simple) + +struct SimplePipelineInfo { + std::string driverName; + std::string phyName; + std::string v4l2Name; + unsigned int v4l2PixFmt; + unsigned int mediaBusFmt; + unsigned int maxWidth; + unsigned int maxHeight; +}; + +class SimpleCameraData : public CameraData +{ +public: + SimpleCameraData(PipelineHandler *pipe) + : CameraData(pipe), sensor_(nullptr) + { + } + + ~SimpleCameraData() + { + delete sensor_; + } + + Stream stream_; + CameraSensor *sensor_; +}; + +class SimpleCameraConfiguration : public CameraConfiguration +{ +public: + SimpleCameraConfiguration(Camera *camera, SimpleCameraData *data, const SimplePipelineInfo *pipelineInfo); + + Status validate() override; + + const V4L2SubdeviceFormat &sensorFormat() { return sensorFormat_; } + +private: + /* + * The SimpleCameraData instance is guaranteed to be valid as long as the + * corresponding Camera instance is valid. In order to borrow a + * reference to the camera data, store a new reference to the camera. + */ + std::shared_ptr camera_; + const SimpleCameraData *data_; + + V4L2SubdeviceFormat sensorFormat_; + + const SimplePipelineInfo *pipelineInfo_; +}; + +class PipelineHandlerSimple : public PipelineHandler +{ +public: + PipelineHandlerSimple(CameraManager *manager); + + ~PipelineHandlerSimple(); + + CameraConfiguration *generateConfiguration(Camera *camera, + const StreamRoles &roles) override; + + int configure(Camera *camera, CameraConfiguration *config) override; + + int allocateBuffers(Camera *camera, + const std::set &streams) override; + + int freeBuffers(Camera *camera, + const std::set &streams) override; + + int start(Camera *camera) override; + + void stop(Camera *camera) override; + + int queueRequest(Camera *camera, Request *request) override; + + bool match(DeviceEnumerator *enumerator) override; + +private: + SimpleCameraData *cameraData(const Camera *camera) + { + return static_cast( + PipelineHandler::cameraData(camera)); + } + + int initLinks(); + + int createCamera(MediaEntity *sensor); + + void bufferReady(Buffer *buffer); + + MediaDevice *media_; + V4L2Subdevice *dphy_; + V4L2VideoDevice *video_; + + Camera *activeCamera_; + + const SimplePipelineInfo *pipelineInfo_; +}; + +SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera, + SimpleCameraData *data, + const SimplePipelineInfo *pipelineInfo) + : CameraConfiguration() +{ + camera_ = camera->shared_from_this(); + data_ = data; + pipelineInfo_ = pipelineInfo; +} + +CameraConfiguration::Status SimpleCameraConfiguration::validate() +{ + const CameraSensor *sensor = data_->sensor_; + 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 (cfg.pixelFormat != pipelineInfo_->v4l2PixFmt) { + LOG(Simple, Debug) << "Adjusting pixel format"; + cfg.pixelFormat = pipelineInfo_->v4l2PixFmt; + status = Adjusted; + } + + /* Select the sensor format. */ + sensorFormat_ = sensor->getFormat({ pipelineInfo_->mediaBusFmt }, + cfg.size); + if (!sensorFormat_.size.width || !sensorFormat_.size.height) + sensorFormat_.size = sensor->resolution(); + + /* + * Provide a suitable default that matches the sensor aspect + * ratio and clamp the size to the hardware bounds. + * + * \todo: Check the hardware alignment constraints. + */ + const Size size = cfg.size; + + unsigned int pipelineMaxWidth = std::min(sensorFormat_.size.width, pipelineInfo_->maxWidth); + unsigned int pipelineMaxHeight = std::min(sensorFormat_.size.height, pipelineInfo_->maxHeight); + + if (!cfg.size.width || !cfg.size.height) { + cfg.size.width = pipelineMaxWidth; + cfg.size.height = pipelineMaxWidth * sensorFormat_.size.height / sensorFormat_.size.width; + } + + cfg.size.width = std::min(pipelineMaxWidth, cfg.size.width); + cfg.size.height = std::min(pipelineMaxHeight, cfg.size.height); + + cfg.size.width = std::max(32U, std::min(4416U, cfg.size.width)); + cfg.size.height = std::max(16U, std::min(3312U, cfg.size.height)); + + if (cfg.size != size) { + LOG(Simple, Debug) + << "Adjusting size from " << size.toString() + << " to " << cfg.size.toString(); + status = Adjusted; + } + + cfg.bufferCount = 3; + + return status; +} + +PipelineHandlerSimple::PipelineHandlerSimple(CameraManager *manager) + : PipelineHandler(manager), dphy_(nullptr), video_(nullptr) +{ +} + +PipelineHandlerSimple::~PipelineHandlerSimple() +{ + delete video_; + delete dphy_; +} + +/* ----------------------------------------------------------------------------- + * Pipeline Operations + */ + +CameraConfiguration *PipelineHandlerSimple::generateConfiguration(Camera *camera, + const StreamRoles &roles) +{ + SimpleCameraData *data = cameraData(camera); + CameraConfiguration *config = new SimpleCameraConfiguration(camera, data, PipelineHandlerSimple::pipelineInfo_); + + if (roles.empty()) + return config; + + StreamConfiguration cfg{}; + cfg.pixelFormat = pipelineInfo_->v4l2PixFmt; + cfg.size = data->sensor_->resolution(); + + config->addConfiguration(cfg); + + config->validate(); + + return config; +} + +int PipelineHandlerSimple::configure(Camera *camera, CameraConfiguration *c) +{ + SimpleCameraConfiguration *config = + static_cast(c); + SimpleCameraData *data = cameraData(camera); + StreamConfiguration &cfg = config->at(0); + CameraSensor *sensor = data->sensor_; + int ret; + + /* + * Configure the sensor links: enable the link corresponding to this + * camera and disable all the other sensor links. + */ + const MediaPad *pad = dphy_->entity()->getPadByIndex(0); + + for (MediaLink *link : pad->links()) { + bool enable = link->source()->entity() == sensor->entity(); + + if (!!(link->flags() & MEDIA_LNK_FL_ENABLED) == enable) + continue; + + LOG(Simple, Debug) + << (enable ? "Enabling" : "Disabling") + << " link from sensor '" + << link->source()->entity()->name() + << "' to CSI-2 receiver"; + + ret = link->setEnabled(enable); + if (ret < 0) + return ret; + } + + /* + * Configure the format on the sensor output and propagate it through + * the pipeline. + */ + V4L2SubdeviceFormat format = config->sensorFormat(); + LOG(Simple, Debug) << "Configuring sensor with " << format.toString(); + + ret = sensor->setFormat(&format); + if (ret < 0) + return ret; + + LOG(Simple, Debug) << "Sensor configured with " << format.toString(); + + V4L2DeviceFormat outputFormat = {}; + outputFormat.fourcc = cfg.pixelFormat; + outputFormat.size = cfg.size; + outputFormat.planesCount = 2; + + ret = video_->setFormat(&outputFormat); + if (ret) + return ret; + + if (outputFormat.size != cfg.size || + outputFormat.fourcc != cfg.pixelFormat) { + LOG(Simple, Error) + << "Unable to configure capture in " << cfg.toString(); + return -EINVAL; + } + + cfg.setStream(&data->stream_); + + return 0; +} + +int PipelineHandlerSimple::allocateBuffers(Camera *camera, + const std::set &streams) +{ + Stream *stream = *streams.begin(); + + if (stream->memoryType() == InternalMemory) + return video_->exportBuffers(&stream->bufferPool()); + else + return video_->importBuffers(&stream->bufferPool()); +} + +int PipelineHandlerSimple::freeBuffers(Camera *camera, + const std::set &streams) +{ + if (video_->releaseBuffers()) + LOG(Simple, Error) << "Failed to release buffers"; + + return 0; +} + +int PipelineHandlerSimple::start(Camera *camera) +{ + int ret; + + ret = video_->streamOn(); + if (ret) + LOG(Simple, Error) + << "Failed to start camera " << camera->name(); + + activeCamera_ = camera; + + return ret; +} + +void PipelineHandlerSimple::stop(Camera *camera) +{ + int ret; + + ret = video_->streamOff(); + if (ret) + LOG(Simple, Warning) + << "Failed to stop camera " << camera->name(); + + activeCamera_ = nullptr; +} + +int PipelineHandlerSimple::queueRequest(Camera *camera, Request *request) +{ + SimpleCameraData *data = cameraData(camera); + Stream *stream = &data->stream_; + + Buffer *buffer = request->findBuffer(stream); + if (!buffer) { + LOG(Simple, Error) + << "Attempt to queue request with invalid stream"; + return -ENOENT; + } + + int ret = video_->queueBuffer(buffer); + if (ret < 0) + return ret; + + PipelineHandler::queueRequest(camera, request); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Match and Setup + */ + +int PipelineHandlerSimple::createCamera(MediaEntity *sensor) +{ + int ret; + + std::unique_ptr data = + utils::make_unique(this); + + data->sensor_ = new CameraSensor(sensor); + ret = data->sensor_->init(); + if (ret) + return ret; + + std::set streams{ &data->stream_ }; + std::shared_ptr camera = + Camera::create(this, sensor->name(), streams); + registerCamera(std::move(camera), std::move(data)); + + return 0; +} + +bool PipelineHandlerSimple::match(DeviceEnumerator *enumerator) +{ + const MediaPad *pad; + + static const SimplePipelineInfo infos[1] = { + { .driverName = "sun6i-csi", + .phyName = "sun6i-csi", + .v4l2Name = "sun6i-csi", + .v4l2PixFmt = V4L2_PIX_FMT_UYVY, + .mediaBusFmt = MEDIA_BUS_FMT_UYVY8_2X8, + .maxWidth = 1280, + .maxHeight = 720 } + }; + + const SimplePipelineInfo *ptr = infos; + for (int i = 0; i < 1; i++, ptr++) { + DeviceMatch dm(ptr->driverName); + dm.add(ptr->phyName); + + media_ = acquireMediaDevice(enumerator, dm); + if (!media_) + continue; + + PipelineHandlerSimple::pipelineInfo_ = ptr; + + /* Create the V4L2 subdevices we will need. */ + dphy_ = V4L2Subdevice::fromEntityName(media_, ptr->phyName); + if (dphy_->open() < 0) + return false; + + /* Locate and open the capture video node. */ + video_ = V4L2VideoDevice::fromEntityName(media_, ptr->v4l2Name); + if (video_->open() < 0) + return false; + + video_->bufferReady.connect(this, &PipelineHandlerSimple::bufferReady); + + /* + * Enumerate all sensors connected to the CSI-2 receiver and create one + * camera instance for each of them. + */ + pad = dphy_->entity()->getPadByIndex(0); + if (!pad) + return false; + + for (MediaLink *link : pad->links()) + createCamera(link->source()->entity()); + + return true; + } + return false; +} + +/* ----------------------------------------------------------------------------- + * Buffer Handling + */ + +void PipelineHandlerSimple::bufferReady(Buffer *buffer) +{ + ASSERT(activeCamera_); + LOG(Simple, Debug) << "bufferReady"; + Request *request = buffer->request(); + completeBuffer(activeCamera_, request, buffer); + completeRequest(activeCamera_, request); +} + +REGISTER_PIPELINE_HANDLER(PipelineHandlerSimple); + +} // namespace libcamera