libcamera: pipeline: Add RKISP1 pipeline

The pipeline handler for the Rockchip ISP creates one camera instance
per detected raw Bayer CSI-2 sensor. Parallel sensors and YUV sensors
are not supported yet.

As the ISP has a single CSI-2 receiver, only one camera can be used at a
time. Mutual exclusion isn't implemented yet.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Tested-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
This commit is contained in:
Laurent Pinchart
2019-04-06 19:17:15 +03:00
parent 2619480146
commit 0d1b60adaf
3 changed files with 454 additions and 0 deletions

View File

@@ -4,3 +4,4 @@ libcamera_sources += files([
])
subdir('ipu3')
subdir('rkisp1')

View File

@@ -0,0 +1,3 @@
libcamera_sources += files([
'rkisp1.cpp',
])

View File

@@ -0,0 +1,450 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* rkisp1.cpp - Pipeline handler for Rockchip ISP1
*/
#include <iomanip>
#include <memory>
#include <vector>
#include <linux/media-bus-format.h>
#include <libcamera/camera.h>
#include <libcamera/request.h>
#include <libcamera/stream.h>
#include "camera_sensor.h"
#include "device_enumerator.h"
#include "log.h"
#include "media_device.h"
#include "pipeline_handler.h"
#include "utils.h"
#include "v4l2_device.h"
#include "v4l2_subdevice.h"
namespace libcamera {
LOG_DEFINE_CATEGORY(RkISP1)
class PipelineHandlerRkISP1 : public PipelineHandler
{
public:
PipelineHandlerRkISP1(CameraManager *manager);
~PipelineHandlerRkISP1();
CameraConfiguration streamConfiguration(Camera *camera,
const std::vector<StreamUsage> &usages) override;
int configureStreams(Camera *camera,
const CameraConfiguration &config) override;
int allocateBuffers(Camera *camera,
const std::set<Stream *> &streams) override;
int freeBuffers(Camera *camera,
const std::set<Stream *> &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:
class RkISP1CameraData : public CameraData
{
public:
RkISP1CameraData(PipelineHandler *pipe)
: CameraData(pipe), sensor_(nullptr)
{
}
~RkISP1CameraData()
{
delete sensor_;
}
Stream stream_;
CameraSensor *sensor_;
};
static constexpr unsigned int RKISP1_BUFFER_COUNT = 4;
RkISP1CameraData *cameraData(const Camera *camera)
{
return static_cast<RkISP1CameraData *>(
PipelineHandler::cameraData(camera));
}
int initLinks();
int createCamera(MediaEntity *sensor);
void bufferReady(Buffer *buffer);
std::shared_ptr<MediaDevice> media_;
V4L2Subdevice *dphy_;
V4L2Subdevice *isp_;
V4L2Device *video_;
Camera *activeCamera_;
};
PipelineHandlerRkISP1::PipelineHandlerRkISP1(CameraManager *manager)
: PipelineHandler(manager), dphy_(nullptr), isp_(nullptr),
video_(nullptr)
{
}
PipelineHandlerRkISP1::~PipelineHandlerRkISP1()
{
delete video_;
delete isp_;
delete dphy_;
if (media_)
media_->release();
}
/* -----------------------------------------------------------------------------
* Pipeline Operations
*/
CameraConfiguration PipelineHandlerRkISP1::streamConfiguration(Camera *camera,
const std::vector<StreamUsage> &usages)
{
RkISP1CameraData *data = cameraData(camera);
CameraConfiguration configs;
StreamConfiguration config{};
const Size &resolution = data->sensor_->resolution();
config.width = resolution.width;
config.height = resolution.height;
config.pixelFormat = V4L2_PIX_FMT_NV12;
config.bufferCount = RKISP1_BUFFER_COUNT;
configs[&data->stream_] = config;
LOG(RkISP1, Debug)
<< "Stream format set to " << config.width << "x"
<< config.height << "-0x" << std::hex << std::setfill('0')
<< std::setw(8) << config.pixelFormat;
return configs;
}
int PipelineHandlerRkISP1::configureStreams(Camera *camera,
const CameraConfiguration &config)
{
RkISP1CameraData *data = cameraData(camera);
const StreamConfiguration &cfg = config[&data->stream_];
CameraSensor *sensor = data->sensor_;
int ret;
/* Verify the configuration. */
const Size &resolution = sensor->resolution();
if (cfg.width > resolution.width ||
cfg.height > resolution.height) {
LOG(RkISP1, Error)
<< "Invalid stream size: larger than sensor resolution";
return -EINVAL;
}
/*
* 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);
ret = media_->open();
if (ret < 0)
return ret;
for (MediaLink *link : pad->links()) {
bool enable = link->source()->entity() == sensor->entity();
if (!!(link->flags() & MEDIA_LNK_FL_ENABLED) == enable)
continue;
LOG(RkISP1, Debug)
<< (enable ? "Enabling" : "Disabling")
<< " link from sensor '"
<< link->source()->entity()->name()
<< "' to CSI-2 receiver";
ret = link->setEnabled(enable);
if (ret < 0)
break;
}
media_->close();
if (ret < 0)
return ret;
/*
* Configure the format on the sensor output and propagate it through
* the pipeline.
*/
V4L2SubdeviceFormat format;
format = sensor->getFormat({ MEDIA_BUS_FMT_SBGGR12_1X12,
MEDIA_BUS_FMT_SGBRG12_1X12,
MEDIA_BUS_FMT_SGRBG12_1X12,
MEDIA_BUS_FMT_SRGGB12_1X12,
MEDIA_BUS_FMT_SBGGR10_1X10,
MEDIA_BUS_FMT_SGBRG10_1X10,
MEDIA_BUS_FMT_SGRBG10_1X10,
MEDIA_BUS_FMT_SRGGB10_1X10,
MEDIA_BUS_FMT_SBGGR8_1X8,
MEDIA_BUS_FMT_SGBRG8_1X8,
MEDIA_BUS_FMT_SGRBG8_1X8,
MEDIA_BUS_FMT_SRGGB8_1X8 },
Size(cfg.width, cfg.height));
LOG(RkISP1, Debug) << "Configuring sensor with " << format.toString();
ret = sensor->setFormat(&format);
if (ret < 0)
return ret;
LOG(RkISP1, Debug) << "Sensor configured with " << format.toString();
ret = dphy_->setFormat(0, &format);
if (ret < 0)
return ret;
ret = dphy_->getFormat(1, &format);
if (ret < 0)
return ret;
ret = isp_->setFormat(0, &format);
if (ret < 0)
return ret;
V4L2DeviceFormat outputFormat = {};
outputFormat.width = cfg.width;
outputFormat.height = cfg.height;
outputFormat.fourcc = V4L2_PIX_FMT_NV12;
outputFormat.planesCount = 2;
ret = video_->setFormat(&outputFormat);
if (ret)
return ret;
if (outputFormat.width != cfg.width ||
outputFormat.height != cfg.height ||
outputFormat.fourcc != V4L2_PIX_FMT_NV12) {
LOG(RkISP1, Error)
<< "Unable to configure capture in " << cfg.width
<< "x" << cfg.height << "-0x"
<< std::hex << std::setfill('0') << std::setw(8)
<< V4L2_PIX_FMT_NV12;
return -EINVAL;
}
return 0;
}
int PipelineHandlerRkISP1::allocateBuffers(Camera *camera,
const std::set<Stream *> &streams)
{
Stream *stream = *streams.begin();
return video_->exportBuffers(&stream->bufferPool());
}
int PipelineHandlerRkISP1::freeBuffers(Camera *camera,
const std::set<Stream *> &streams)
{
if (video_->releaseBuffers())
LOG(RkISP1, Error) << "Failed to release buffers";
return 0;
}
int PipelineHandlerRkISP1::start(Camera *camera)
{
int ret;
ret = video_->streamOn();
if (ret)
LOG(RkISP1, Error)
<< "Failed to start camera " << camera->name();
activeCamera_ = camera;
return ret;
}
void PipelineHandlerRkISP1::stop(Camera *camera)
{
int ret;
ret = video_->streamOff();
if (ret)
LOG(RkISP1, Warning)
<< "Failed to stop camera " << camera->name();
PipelineHandler::stop(camera);
activeCamera_ = nullptr;
}
int PipelineHandlerRkISP1::queueRequest(Camera *camera, Request *request)
{
RkISP1CameraData *data = cameraData(camera);
Stream *stream = &data->stream_;
Buffer *buffer = request->findBuffer(stream);
if (!buffer) {
LOG(RkISP1, 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 PipelineHandlerRkISP1::initLinks()
{
MediaLink *link;
int ret;
ret = media_->disableLinks();
if (ret < 0)
return ret;
link = media_->link("rockchip-sy-mipi-dphy", 1, "rkisp1-isp-subdev", 0);
if (!link)
return -ENODEV;
ret = link->setEnabled(true);
if (ret < 0)
return ret;
link = media_->link("rkisp1-isp-subdev", 2, "rkisp1_mainpath", 0);
if (!link)
return -ENODEV;
ret = link->setEnabled(true);
if (ret < 0)
return ret;
return 0;
}
int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
{
int ret;
std::unique_ptr<RkISP1CameraData> data =
utils::make_unique<RkISP1CameraData>(this);
data->sensor_ = new CameraSensor(sensor);
ret = data->sensor_->init();
if (ret)
return ret;
std::set<Stream *> streams{ &data->stream_ };
std::shared_ptr<Camera> camera =
Camera::create(this, sensor->name(), streams);
registerCamera(std::move(camera), std::move(data));
return 0;
}
bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
{
const MediaPad *pad;
int ret;
DeviceMatch dm("rkisp1");
dm.add("rkisp1-isp-subdev");
dm.add("rkisp1_selfpath");
dm.add("rkisp1_mainpath");
dm.add("rkisp1-statistics");
dm.add("rkisp1-input-params");
dm.add("rockchip-sy-mipi-dphy");
media_ = enumerator->search(dm);
if (!media_)
return false;
media_->acquire();
ret = media_->open();
if (ret < 0)
return ret;
/* Create the V4L2 subdevices we will need. */
dphy_ = V4L2Subdevice::fromEntityName(media_.get(),
"rockchip-sy-mipi-dphy");
ret = dphy_->open();
if (ret < 0)
goto done;
isp_ = V4L2Subdevice::fromEntityName(media_.get(), "rkisp1-isp-subdev");
ret = isp_->open();
if (ret < 0)
goto done;
/* Locate and open the capture video node. */
video_ = V4L2Device::fromEntityName(media_.get(), "rkisp1_mainpath");
ret = video_->open();
if (ret < 0)
goto done;
video_->bufferReady.connect(this, &PipelineHandlerRkISP1::bufferReady);
/* Configure default links. */
ret = initLinks();
if (ret < 0) {
LOG(RkISP1, Error) << "Failed to setup links";
goto done;
}
/*
* 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) {
ret = -EINVAL;
goto done;
}
for (MediaLink *link : pad->links())
createCamera(link->source()->entity());
done:
media_->close();
return ret == 0;
}
/* -----------------------------------------------------------------------------
* Buffer Handling
*/
void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)
{
ASSERT(activeCamera_);
RkISP1CameraData *data = cameraData(activeCamera_);
Request *request = data->queuedRequests_.front();
completeBuffer(activeCamera_, request, buffer);
completeRequest(activeCamera_, request);
}
REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1);
} /* namespace libcamera */