Files
external_libcamera/src/libcamera/pipeline/ipu3/cio2.cpp
T
Niklas Söderlund 40148cfcaf libcamera: ipu3: Allow zero-copy RAW stream capture
With the refactored CIO2 interface it's now easy to add zero-copy for
buffers in the RAW stream. Use the internally allocated buffers inside
the CIO2Device if no buffer for the RAW stream is provided by the
application, or use the application-provided buffer if any.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-06-26 13:32:29 +02:00

298 lines
7.5 KiB
C++

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* cio2.cpp - Intel IPU3 CIO2
*/
#include "cio2.h"
#include <linux/media-bus-format.h>
#include <libcamera/formats.h>
#include <libcamera/stream.h>
#include "libcamera/internal/camera_sensor.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/v4l2_subdevice.h"
#include "libcamera/internal/v4l2_videodevice.h"
namespace libcamera {
LOG_DECLARE_CATEGORY(IPU3)
namespace {
static const std::map<uint32_t, PixelFormat> mbusCodesToInfo = {
{ MEDIA_BUS_FMT_SBGGR10_1X10, formats::SBGGR10_IPU3 },
{ MEDIA_BUS_FMT_SGBRG10_1X10, formats::SGBRG10_IPU3 },
{ MEDIA_BUS_FMT_SGRBG10_1X10, formats::SGRBG10_IPU3 },
{ MEDIA_BUS_FMT_SRGGB10_1X10, formats::SRGGB10_IPU3 },
};
} /* namespace */
CIO2Device::CIO2Device()
: sensor_(nullptr), csi2_(nullptr), output_(nullptr)
{
}
CIO2Device::~CIO2Device()
{
delete output_;
delete csi2_;
delete sensor_;
}
/**
* \brief Initialize components of the CIO2 device with \a index
* \param[in] media The CIO2 media device
* \param[in] index The CIO2 device index
*
* Create and open the video device and subdevices in the CIO2 instance at \a
* index, if a supported image sensor is connected to the CSI-2 receiver of
* this CIO2 instance. Enable the media links connecting the CIO2 components
* to prepare for capture operations and cached the sensor maximum size.
*
* \return 0 on success or a negative error code otherwise
* \retval -ENODEV No supported image sensor is connected to this CIO2 instance
*/
int CIO2Device::init(const MediaDevice *media, unsigned int index)
{
int ret;
/*
* Verify that a sensor subdevice is connected to this CIO2 instance
* and enable the media link between the two.
*/
std::string csi2Name = "ipu3-csi2 " + std::to_string(index);
MediaEntity *csi2Entity = media->getEntityByName(csi2Name);
const std::vector<MediaPad *> &pads = csi2Entity->pads();
if (pads.empty())
return -ENODEV;
/* IPU3 CSI-2 receivers have a single sink pad at index 0. */
MediaPad *sink = pads[0];
const std::vector<MediaLink *> &links = sink->links();
if (links.empty())
return -ENODEV;
MediaLink *link = links[0];
MediaEntity *sensorEntity = link->source()->entity();
sensor_ = new CameraSensor(sensorEntity);
ret = sensor_->init();
if (ret)
return ret;
ret = link->setEnabled(true);
if (ret)
return ret;
/*
* Make sure the sensor produces at least one format compatible with
* the CIO2 requirements.
*
* utils::set_overlap requires the ranges to be sorted, keep the
* cio2Codes vector sorted in ascending order.
*/
std::vector<unsigned int> cio2Codes;
cio2Codes.reserve(mbusCodesToInfo.size());
std::transform(mbusCodesToInfo.begin(), mbusCodesToInfo.end(),
std::back_inserter(cio2Codes),
[](auto &pair) { return pair.first; });
const std::vector<unsigned int> &sensorCodes = sensor_->mbusCodes();
if (!utils::set_overlap(sensorCodes.begin(), sensorCodes.end(),
cio2Codes.begin(), cio2Codes.end())) {
LOG(IPU3, Error)
<< "Sensor " << sensor_->entity()->name()
<< " has not format compatible with the IPU3";
return -EINVAL;
}
/*
* \todo Define when to open and close video device nodes, as they
* might impact on power consumption.
*/
csi2_ = new V4L2Subdevice(csi2Entity);
ret = csi2_->open();
if (ret)
return ret;
std::string cio2Name = "ipu3-cio2 " + std::to_string(index);
output_ = V4L2VideoDevice::fromEntityName(media, cio2Name);
ret = output_->open();
if (ret)
return ret;
output_->bufferReady.connect(this, &CIO2Device::cio2BufferReady);
return 0;
}
/**
* \brief Configure the CIO2 unit
* \param[in] size The requested CIO2 output frame size
* \param[out] outputFormat The CIO2 unit output image format
* \return 0 on success or a negative error code otherwise
*/
int CIO2Device::configure(const Size &size, V4L2DeviceFormat *outputFormat)
{
V4L2SubdeviceFormat sensorFormat;
int ret;
/*
* Apply the selected format to the sensor, the CSI-2 receiver and
* the CIO2 output device.
*/
std::vector<unsigned int> mbusCodes;
mbusCodes.reserve(mbusCodesToInfo.size());
std::transform(mbusCodesToInfo.begin(), mbusCodesToInfo.end(),
std::back_inserter(mbusCodes),
[](auto &pair) { return pair.first; });
sensorFormat = sensor_->getFormat(mbusCodes, size);
ret = sensor_->setFormat(&sensorFormat);
if (ret)
return ret;
ret = csi2_->setFormat(0, &sensorFormat);
if (ret)
return ret;
const auto &itInfo = mbusCodesToInfo.find(sensorFormat.mbus_code);
if (itInfo == mbusCodesToInfo.end())
return -EINVAL;
const PixelFormatInfo &info = PixelFormatInfo::info(itInfo->second);
outputFormat->fourcc = info.v4l2Format;
outputFormat->size = sensorFormat.size;
outputFormat->planesCount = 1;
ret = output_->setFormat(outputFormat);
if (ret)
return ret;
LOG(IPU3, Debug) << "CIO2 output format " << outputFormat->toString();
return 0;
}
StreamConfiguration
CIO2Device::generateConfiguration(Size size) const
{
StreamConfiguration cfg;
/* If no desired size use the sensor resolution. */
if (!size.width && !size.height)
size = sensor_->resolution();
/* Query the sensor static information for closest match. */
std::vector<unsigned int> mbusCodes;
std::transform(mbusCodesToInfo.begin(), mbusCodesToInfo.end(),
std::back_inserter(mbusCodes),
[](auto &pair) { return pair.first; });
V4L2SubdeviceFormat sensorFormat = sensor_->getFormat(mbusCodes, size);
if (!sensorFormat.mbus_code) {
LOG(IPU3, Error) << "Sensor does not support mbus code";
return {};
}
cfg.size = sensorFormat.size;
cfg.pixelFormat = mbusCodesToInfo.at(sensorFormat.mbus_code);
cfg.bufferCount = CIO2_BUFFER_COUNT;
return cfg;
}
int CIO2Device::exportBuffers(unsigned int count,
std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
return output_->exportBuffers(count, buffers);
}
int CIO2Device::start()
{
int ret = output_->exportBuffers(CIO2_BUFFER_COUNT, &buffers_);
if (ret < 0)
return ret;
ret = output_->importBuffers(CIO2_BUFFER_COUNT);
if (ret)
LOG(IPU3, Error) << "Failed to import CIO2 buffers";
for (std::unique_ptr<FrameBuffer> &buffer : buffers_)
availableBuffers_.push(buffer.get());
ret = output_->streamOn();
if (ret)
freeBuffers();
return ret;
}
int CIO2Device::stop()
{
int ret = output_->streamOff();
freeBuffers();
return ret;
}
int CIO2Device::queueBuffer(Request *request, FrameBuffer *rawBuffer)
{
FrameBuffer *buffer = rawBuffer;
/* If no buffer is provided in the request, use an internal one. */
if (!buffer) {
if (availableBuffers_.empty()) {
LOG(IPU3, Error) << "CIO2 buffer underrun";
return -EINVAL;
}
buffer = availableBuffers_.front();
availableBuffers_.pop();
}
buffer->setRequest(request);
return output_->queueBuffer(buffer);
}
void CIO2Device::tryReturnBuffer(FrameBuffer *buffer)
{
/*
* \todo Once more pipelines deal with buffers that may be allocated
* internally or externally this pattern might become a common need. At
* that point this check should be moved to something clever in
* FrameBuffer.
*/
for (const std::unique_ptr<FrameBuffer> &buf : buffers_) {
if (buf.get() == buffer) {
availableBuffers_.push(buffer);
break;
}
}
}
void CIO2Device::freeBuffers()
{
/* The default std::queue constructor is explicit with gcc 5 and 6. */
availableBuffers_ = std::queue<FrameBuffer *>{};
buffers_.clear();
if (output_->releaseBuffers())
LOG(IPU3, Error) << "Failed to release CIO2 buffers";
}
void CIO2Device::cio2BufferReady(FrameBuffer *buffer)
{
bufferReady.emit(buffer);
}
} /* namespace libcamera */