From patchwork Mon Jan 21 10:57:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 290 Return-Path: Received: from relay3-d.mail.gandi.net (relay3-d.mail.gandi.net [217.70.183.195]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E44CD60B21 for ; Mon, 21 Jan 2019 11:57:21 +0100 (CET) X-Originating-IP: 2.224.242.101 Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay3-d.mail.gandi.net (Postfix) with ESMTPSA id 7F4C860004; Mon, 21 Jan 2019 10:57:21 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Mon, 21 Jan 2019 11:57:24 +0100 Message-Id: <20190121105725.8351-2-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190121105725.8351-1-jacopo@jmondi.org> References: <20190121105725.8351-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 1/2] libcamera: pipeline: Add Intel IPU3 pipeline X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Jan 2019 10:57:22 -0000 Add a pipeline handler for the Intel IPU3 device. The pipeline handler creates a Camera for each image sensor it finds to be connected to an IPU3 CSI-2 receiver, and enables the link between the two. Tested on Soraka, listing detected cameras on the system, verifying the pipeline handler gets matched and links properly enabled. Signed-off-by: Jacopo Mondi --- src/libcamera/pipeline/ipu3/ipu3.cpp | 216 ++++++++++++++++++++++++ src/libcamera/pipeline/ipu3/meson.build | 3 + src/libcamera/pipeline/meson.build | 2 + 3 files changed, 221 insertions(+) create mode 100644 src/libcamera/pipeline/ipu3/ipu3.cpp create mode 100644 src/libcamera/pipeline/ipu3/meson.build diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp new file mode 100644 index 0000000..2081743 --- /dev/null +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * ipu3.cpp - Pipeline handler for Intel IPU3 + */ + +#include +#include + +#include +#include + +#include "device_enumerator.h" +#include "log.h" +#include "media_device.h" +#include "pipeline_handler.h" + +namespace libcamera { + +class PipelineHandlerIPU3 : public PipelineHandler +{ +public: + PipelineHandlerIPU3(); + ~PipelineHandlerIPU3(); + + bool match(CameraManager *manager, DeviceEnumerator *enumerator); + +private: + MediaDevice *cio2_; + MediaDevice *imgu_; + + void registerCameras(CameraManager *manager); +}; + +PipelineHandlerIPU3::PipelineHandlerIPU3() + : cio2_(nullptr), imgu_(nullptr) +{ +} + +PipelineHandlerIPU3::~PipelineHandlerIPU3() +{ + if (cio2_) + cio2_->release(); + + if (imgu_) + imgu_->release(); + + cio2_ = nullptr; + imgu_ = nullptr; +} + +bool PipelineHandlerIPU3::match(CameraManager *manager, DeviceEnumerator *enumerator) +{ + DeviceMatch cio2_dm("ipu3-cio2"); + cio2_dm.add("ipu3-csi2 0"); + cio2_dm.add("ipu3-cio2 0"); + cio2_dm.add("ipu3-csi2 1"); + cio2_dm.add("ipu3-cio2 1"); + cio2_dm.add("ipu3-csi2 2"); + cio2_dm.add("ipu3-cio2 2"); + cio2_dm.add("ipu3-csi2 3"); + cio2_dm.add("ipu3-cio2 3"); + + DeviceMatch imgu_dm("ipu3-imgu"); + imgu_dm.add("ipu3-imgu 0"); + imgu_dm.add("ipu3-imgu 0 input"); + imgu_dm.add("ipu3-imgu 0 parameters"); + imgu_dm.add("ipu3-imgu 0 output"); + imgu_dm.add("ipu3-imgu 0 viewfinder"); + imgu_dm.add("ipu3-imgu 0 3a stat"); + imgu_dm.add("ipu3-imgu 1"); + imgu_dm.add("ipu3-imgu 1 input"); + imgu_dm.add("ipu3-imgu 1 parameters"); + imgu_dm.add("ipu3-imgu 1 output"); + imgu_dm.add("ipu3-imgu 1 viewfinder"); + imgu_dm.add("ipu3-imgu 1 3a stat"); + + cio2_ = enumerator->search(cio2_dm); + if (!cio2_) + return false; + + imgu_ = enumerator->search(imgu_dm); + if (!imgu_) + return false; + + /* + * It is safe to acquire both media devices at this point as + * DeviceEnumerator::search() skips the busy ones for us. + */ + cio2_->acquire(); + imgu_->acquire(); + + /* + * Disable all links that are enabled by default on CIO2, as camera + * creation enables all valid links it finds. + * + * Close the CIO2 media device after, as links are enabled and should + * not need to be changed after. + */ + if (cio2_->open()) + goto error_release_mdev; + + if (cio2_->disableLinks()) + goto error_close_cio2; + + registerCameras(manager); + LOG(Debug) << "\"Intel IPU3\" pipeline handler initialized"; + + cio2_->close(); + + return true; + +error_close_cio2: + cio2_->close(); + +error_release_mdev: + cio2_->release(); + imgu_->release(); + + return false; +} + +/* + * Cameras are created associating an image sensor (represented by a + * media entity with function MEDIA_ENT_F_CAM_SENSOR) to one of the four + * CIO2 CSI-2 receivers. + */ +void PipelineHandlerIPU3::registerCameras(CameraManager *manager) +{ + const std::vector entities = cio2_->entities(); + struct { + unsigned int id; + MediaEntity *csi2; + } csi2Receivers[] = { + { 0, cio2_->getEntityByName("ipu3-csi2 0") }, + { 1, cio2_->getEntityByName("ipu3-csi2 1") }, + { 2, cio2_->getEntityByName("ipu3-csi2 2") }, + { 3, cio2_->getEntityByName("ipu3-csi2 3") }, + }; + + /* + * For each CSI-2 receiver on the IPU3, create a Camera if an + * image sensor is connected to it. + */ + unsigned int numCameras = 0; + for (auto csi2Receiver : csi2Receivers) { + MediaEntity *csi2 = csi2Receiver.csi2; + unsigned int id = csi2Receiver.id; + + /* + * This shall not happen, as the device enumerator matched + * all entities described in the cio2_dm DeviceMatch. + * + * As this check is basically free, better stay safe than sorry. + */ + if (!csi2) + continue; + + std::vector pads = csi2->pads(); + MediaPad *sink; + for (MediaPad *pad : pads) { + if (!(pad->flags() & MEDIA_PAD_FL_SINK)) + continue; + + /* IPU3 CSI-2 receivers have a single sink pad. */ + sink = pad; + break; + } + + std::vector links = sink->links(); + if (links.empty()) + continue; + + /* + * Verify that the receiver is connected to a sensor, enable + * the media link between the two, and create a Camera with + * a unique name. + * + * FIXME: This supports creating a single camera per CSI-2 receiver. + */ + for (MediaLink *link : links) { + /* Again, this shall not happen, but better stay safe. */ + if (!link->source()) + continue; + + MediaEntity *sensor = link->source()->entity(); + if (!sensor) + continue; + + if (sensor->function() != MEDIA_ENT_F_CAM_SENSOR) + continue; + + if (link->setEnabled(true)) + continue; + + std::size_t pos = sensor->name().find(" "); + std::string cameraName = sensor->name().substr(0, pos); + cameraName += " " + std::to_string(id); + + std::shared_ptr camera = Camera::create(cameraName); + manager->addCamera(std::move(camera)); + + LOG(Info) << "Registered Camera[" << numCameras + << "] \"" << cameraName << "\"" + << " connected to CSI-2 receiver " << id; + + numCameras++; + break; + } + } +} + +REGISTER_PIPELINE_HANDLER(PipelineHandlerIPU3); + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/ipu3/meson.build b/src/libcamera/pipeline/ipu3/meson.build new file mode 100644 index 0000000..0ab766a --- /dev/null +++ b/src/libcamera/pipeline/ipu3/meson.build @@ -0,0 +1,3 @@ +libcamera_sources += files([ + 'ipu3.cpp', +]) diff --git a/src/libcamera/pipeline/meson.build b/src/libcamera/pipeline/meson.build index 615ecd2..811c075 100644 --- a/src/libcamera/pipeline/meson.build +++ b/src/libcamera/pipeline/meson.build @@ -1,3 +1,5 @@ libcamera_sources += files([ 'vimc.cpp', ]) + +subdir('ipu3') From patchwork Mon Jan 21 10:57:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 291 Return-Path: Received: from relay3-d.mail.gandi.net (relay3-d.mail.gandi.net [217.70.183.195]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7EA0B60C97 for ; Mon, 21 Jan 2019 11:57:22 +0100 (CET) X-Originating-IP: 2.224.242.101 Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay3-d.mail.gandi.net (Postfix) with ESMTPSA id 13E8D60005; Mon, 21 Jan 2019 10:57:21 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Mon, 21 Jan 2019 11:57:25 +0100 Message-Id: <20190121105725.8351-3-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190121105725.8351-1-jacopo@jmondi.org> References: <20190121105725.8351-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 2/2] test: pipeline: IPU3: Add IPU3 pipeline test X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Jan 2019 10:57:22 -0000 Add test for the Intel IPU3 pipeline that lists all the cameras registered in the system and verifies the result matches the expected. This test is meant to be run on IPU3 platforms, it gets skipped otherwise. Signed-off-by: Jacopo Mondi Acked-by: Kieran Bingham --- test/meson.build | 3 + test/pipeline/ipu3/ipu3_pipeline_test.cpp | 140 ++++++++++++++++++++++ test/pipeline/ipu3/meson.build | 11 ++ test/pipeline/meson.build | 1 + 4 files changed, 155 insertions(+) create mode 100644 test/pipeline/ipu3/ipu3_pipeline_test.cpp create mode 100644 test/pipeline/ipu3/meson.build create mode 100644 test/pipeline/meson.build diff --git a/test/meson.build b/test/meson.build index fb6b115..594082a 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,6 +1,9 @@ subdir('libtest') subdir('media_device') + +subdir('pipeline') + subdir('v4l2_device') public_tests = [ diff --git a/test/pipeline/ipu3/ipu3_pipeline_test.cpp b/test/pipeline/ipu3/ipu3_pipeline_test.cpp new file mode 100644 index 0000000..deaee40 --- /dev/null +++ b/test/pipeline/ipu3/ipu3_pipeline_test.cpp @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * ipu3_pipeline_test.cpp - Intel IPU3 pipeline test + */ + +#include + +#include +#include +#include + +#include +#include + +#include "media_device.h" +#include "media_object.h" +#include "test.h" + +using namespace std; +using namespace libcamera; + +/* + * Verify that the Intel IPU3 pipeline handler gets matched and cameras + * are enumerated correctly. + * + * The test is supposed to be run on an IPU3 platform, otherwise it gets + * skipped. + * + * The test lists all cameras registered in the system, if any camera is + * available at all. + */ +class IPU3PipelineTest : public Test +{ +protected: + int init(); + int run(); + void cleanup(); + +private: + CameraManager *cameraManager_; + unsigned int sensors_; +}; + +int IPU3PipelineTest::init() +{ + const string devnode("/dev/media"); + bool cio2 = false; + bool imgu = false; + unsigned int i; + int ret; + + sensors_ = 0; + + /* + * Test all media devices from /dev/media0 to /dev/media256. + * Media device numbering might not be continue, and we cannot stop + * as soon as we hit a non accessible media device. + */ + for (i = 0; i < 256; i++) { + string mediadev = devnode + to_string(i); + struct stat pstat = { }; + + if (stat(mediadev.c_str(), &pstat)) + continue; + + MediaDevice dev(mediadev); + if (dev.open()) { + cerr << "Failed to open media device " << mediadev << endl; + return TestFail; + } + + if (dev.driver() == "ipu3-imgu") { + imgu = true; + } else if (dev.driver() == "ipu3-cio2") { + /* + * Camera sensor are connected to the CIO2 unit. + * Count how many sensors are connected in the system + * and later verify this matches the number of registered + * cameras. + */ + ret = dev.populate(); + if (ret) { + cerr << "Failed to populate media device " << dev.devnode() << endl; + return TestFail; + } + + vector entities = dev.entities(); + for (MediaEntity *entity : entities) { + if (entity->function() == MEDIA_ENT_F_CAM_SENSOR) + sensors_++; + } + + cio2 = true; + } + + dev.close(); + + if (imgu && cio2) + break; + } + + /* Not running on an IPU3 system: skip test. */ + if (!(imgu && cio2)) + return TestSkip; + + cameraManager_ = CameraManager::instance(); + ret = cameraManager_->start(); + if (ret) { + cerr << "Failed to start the CameraManager" << endl; + return TestFail; + } + + return 0; +} + +int IPU3PipelineTest::run() +{ + unsigned int cameras = 0; + for (const shared_ptr &cam : cameraManager_->cameras()) { + cout << "Found camera '" << cam->name() << "'" << endl; + cameras++; + } + + if (cameras != sensors_) { + cerr << cameras << " cameras registered, but " << sensors_ + << " were expected" << endl; + return TestFail; + } + + return TestPass; +} + +void IPU3PipelineTest::cleanup() +{ + cameraManager_->stop(); +} + +TEST_REGISTER(IPU3PipelineTest) diff --git a/test/pipeline/ipu3/meson.build b/test/pipeline/ipu3/meson.build new file mode 100644 index 0000000..caba5c7 --- /dev/null +++ b/test/pipeline/ipu3/meson.build @@ -0,0 +1,11 @@ +ipu3_test = [ + ['ipu3_pipeline_test', 'ipu3_pipeline_test.cpp'], +] + +foreach t : ipu3_test + exe = executable(t[0], t[1], + link_with : test_libraries, + include_directories : test_includes_internal) + + test(t[0], exe, suite: 'ipu3', is_parallel: false) +endforeach diff --git a/test/pipeline/meson.build b/test/pipeline/meson.build new file mode 100644 index 0000000..f434c79 --- /dev/null +++ b/test/pipeline/meson.build @@ -0,0 +1 @@ +subdir('ipu3')