blob: c756663912227c31d45c68e706cf93bef8d0ac84 [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* rkisp1.cpp - Pipeline handler for Rockchip ISP1
*/
#include <algorithm>
#include <array>
#include <iomanip>
#include <memory>
#include <numeric>
#include <queue>
#include <linux/media-bus-format.h>
#include <libcamera/buffer.h>
#include <libcamera/camera.h>
#include <libcamera/control_ids.h>
#include <libcamera/formats.h>
#include <libcamera/ipa/core_ipa_interface.h>
#include <libcamera/ipa/rkisp1_ipa_interface.h>
#include <libcamera/ipa/rkisp1_ipa_proxy.h>
#include <libcamera/request.h>
#include <libcamera/stream.h>
#include "libcamera/internal/camera_sensor.h"
#include "libcamera/internal/delayed_controls.h"
#include "libcamera/internal/device_enumerator.h"
#include "libcamera/internal/ipa_manager.h"
#include "libcamera/internal/log.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/pipeline_handler.h"
#include "libcamera/internal/utils.h"
#include "libcamera/internal/v4l2_subdevice.h"
#include "libcamera/internal/v4l2_videodevice.h"
#include "rkisp1_path.h"
namespace libcamera {
LOG_DEFINE_CATEGORY(RkISP1)
class PipelineHandlerRkISP1;
class RkISP1CameraData;
struct RkISP1FrameInfo {
unsigned int frame;
Request *request;
FrameBuffer *paramBuffer;
FrameBuffer *statBuffer;
FrameBuffer *mainPathBuffer;
FrameBuffer *selfPathBuffer;
bool paramDequeued;
bool metadataProcessed;
};
class RkISP1Frames
{
public:
RkISP1Frames(PipelineHandler *pipe);
RkISP1FrameInfo *create(const RkISP1CameraData *data, Request *request);
int destroy(unsigned int frame);
void clear();
RkISP1FrameInfo *find(unsigned int frame);
RkISP1FrameInfo *find(FrameBuffer *buffer);
RkISP1FrameInfo *find(Request *request);
private:
PipelineHandlerRkISP1 *pipe_;
std::map<unsigned int, RkISP1FrameInfo *> frameInfo_;
};
class RkISP1CameraData : public CameraData
{
public:
RkISP1CameraData(PipelineHandler *pipe, RkISP1MainPath *mainPath,
RkISP1SelfPath *selfPath)
: CameraData(pipe), frame_(0), frameInfo_(pipe),
mainPath_(mainPath), selfPath_(selfPath)
{
}
int loadIPA(unsigned int hwRevision);
Stream mainPathStream_;
Stream selfPathStream_;
std::unique_ptr<CameraSensor> sensor_;
std::unique_ptr<DelayedControls> delayedCtrls_;
unsigned int frame_;
std::vector<IPABuffer> ipaBuffers_;
RkISP1Frames frameInfo_;
RkISP1MainPath *mainPath_;
RkISP1SelfPath *selfPath_;
std::unique_ptr<ipa::rkisp1::IPAProxyRkISP1> ipa_;
private:
void queueFrameAction(unsigned int frame,
const ipa::rkisp1::RkISP1Action &action);
void metadataReady(unsigned int frame, const ControlList &metadata);
};
class RkISP1CameraConfiguration : public CameraConfiguration
{
public:
RkISP1CameraConfiguration(Camera *camera, RkISP1CameraData *data);
Status validate() override;
const V4L2SubdeviceFormat &sensorFormat() { return sensorFormat_; }
private:
bool fitsAllPaths(const StreamConfiguration &cfg);
/*
* The RkISP1CameraData 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> camera_;
const RkISP1CameraData *data_;
V4L2SubdeviceFormat sensorFormat_;
};
class PipelineHandlerRkISP1 : public PipelineHandler
{
public:
PipelineHandlerRkISP1(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<std::unique_ptr<FrameBuffer>> *buffers) override;
int start(Camera *camera, const ControlList *controls) override;
void stop(Camera *camera) override;
int queueRequestDevice(Camera *camera, Request *request) override;
bool match(DeviceEnumerator *enumerator) override;
private:
RkISP1CameraData *cameraData(const Camera *camera)
{
return static_cast<RkISP1CameraData *>(
PipelineHandler::cameraData(camera));
}
friend RkISP1CameraData;
friend RkISP1Frames;
int initLinks(const Camera *camera, const CameraSensor *sensor,
const RkISP1CameraConfiguration &config);
int createCamera(MediaEntity *sensor);
void tryCompleteRequest(Request *request);
void bufferReady(FrameBuffer *buffer);
void paramReady(FrameBuffer *buffer);
void statReady(FrameBuffer *buffer);
void frameStart(uint32_t sequence);
int allocateBuffers(Camera *camera);
int freeBuffers(Camera *camera);
MediaDevice *media_;
std::unique_ptr<V4L2Subdevice> isp_;
std::unique_ptr<V4L2VideoDevice> param_;
std::unique_ptr<V4L2VideoDevice> stat_;
RkISP1MainPath mainPath_;
RkISP1SelfPath selfPath_;
std::vector<std::unique_ptr<FrameBuffer>> paramBuffers_;
std::vector<std::unique_ptr<FrameBuffer>> statBuffers_;
std::queue<FrameBuffer *> availableParamBuffers_;
std::queue<FrameBuffer *> availableStatBuffers_;
Camera *activeCamera_;
};
RkISP1Frames::RkISP1Frames(PipelineHandler *pipe)
: pipe_(static_cast<PipelineHandlerRkISP1 *>(pipe))
{
}
RkISP1FrameInfo *RkISP1Frames::create(const RkISP1CameraData *data, Request *request)
{
unsigned int frame = data->frame_;
if (pipe_->availableParamBuffers_.empty()) {
LOG(RkISP1, Error) << "Parameters buffer underrun";
return nullptr;
}
FrameBuffer *paramBuffer = pipe_->availableParamBuffers_.front();
if (pipe_->availableStatBuffers_.empty()) {
LOG(RkISP1, Error) << "Statisitc buffer underrun";
return nullptr;
}
FrameBuffer *statBuffer = pipe_->availableStatBuffers_.front();
FrameBuffer *mainPathBuffer = request->findBuffer(&data->mainPathStream_);
FrameBuffer *selfPathBuffer = request->findBuffer(&data->selfPathStream_);
pipe_->availableParamBuffers_.pop();
pipe_->availableStatBuffers_.pop();
RkISP1FrameInfo *info = new RkISP1FrameInfo;
info->frame = frame;
info->request = request;
info->paramBuffer = paramBuffer;
info->mainPathBuffer = mainPathBuffer;
info->selfPathBuffer = selfPathBuffer;
info->statBuffer = statBuffer;
info->paramDequeued = false;
info->metadataProcessed = false;
frameInfo_[frame] = info;
return info;
}
int RkISP1Frames::destroy(unsigned int frame)
{
RkISP1FrameInfo *info = find(frame);
if (!info)
return -ENOENT;
pipe_->availableParamBuffers_.push(info->paramBuffer);
pipe_->availableStatBuffers_.push(info->statBuffer);
frameInfo_.erase(info->frame);
delete info;
return 0;
}
void RkISP1Frames::clear()
{
for (const auto &entry : frameInfo_) {
RkISP1FrameInfo *info = entry.second;
pipe_->availableParamBuffers_.push(info->paramBuffer);
pipe_->availableStatBuffers_.push(info->statBuffer);
delete info;
}
frameInfo_.clear();
}
RkISP1FrameInfo *RkISP1Frames::find(unsigned int frame)
{
auto itInfo = frameInfo_.find(frame);
if (itInfo != frameInfo_.end())
return itInfo->second;
LOG(RkISP1, Fatal) << "Can't locate info from frame";
return nullptr;
}
RkISP1FrameInfo *RkISP1Frames::find(FrameBuffer *buffer)
{
for (auto &itInfo : frameInfo_) {
RkISP1FrameInfo *info = itInfo.second;
if (info->paramBuffer == buffer ||
info->statBuffer == buffer ||
info->mainPathBuffer == buffer ||
info->selfPathBuffer == buffer)
return info;
}
LOG(RkISP1, Fatal) << "Can't locate info from buffer";
return nullptr;
}
RkISP1FrameInfo *RkISP1Frames::find(Request *request)
{
for (auto &itInfo : frameInfo_) {
RkISP1FrameInfo *info = itInfo.second;
if (info->request == request)
return info;
}
LOG(RkISP1, Fatal) << "Can't locate info from request";
return nullptr;
}
int RkISP1CameraData::loadIPA(unsigned int hwRevision)
{
ipa_ = IPAManager::createIPA<ipa::rkisp1::IPAProxyRkISP1>(pipe_, 1, 1);
if (!ipa_)
return -ENOENT;
ipa_->queueFrameAction.connect(this,
&RkISP1CameraData::queueFrameAction);
int ret = ipa_->init(hwRevision);
if (ret < 0) {
LOG(RkISP1, Error) << "IPA initialization failure";
return ret;
}
return 0;
}
void RkISP1CameraData::queueFrameAction(unsigned int frame,
const ipa::rkisp1::RkISP1Action &action)
{
switch (action.op) {
case ipa::rkisp1::ActionV4L2Set: {
const ControlList &controls = action.controls;
delayedCtrls_->push(controls);
break;
}
case ipa::rkisp1::ActionParamFilled: {
PipelineHandlerRkISP1 *pipe = static_cast<PipelineHandlerRkISP1 *>(pipe_);
RkISP1FrameInfo *info = frameInfo_.find(frame);
if (!info)
break;
pipe->param_->queueBuffer(info->paramBuffer);
pipe->stat_->queueBuffer(info->statBuffer);
if (info->mainPathBuffer)
mainPath_->queueBuffer(info->mainPathBuffer);
if (info->selfPathBuffer)
selfPath_->queueBuffer(info->selfPathBuffer);
break;
}
case ipa::rkisp1::ActionMetadata:
metadataReady(frame, action.controls);
break;
default:
LOG(RkISP1, Error) << "Unknown action " << action.op;
break;
}
}
void RkISP1CameraData::metadataReady(unsigned int frame, const ControlList &metadata)
{
PipelineHandlerRkISP1 *pipe =
static_cast<PipelineHandlerRkISP1 *>(pipe_);
RkISP1FrameInfo *info = frameInfo_.find(frame);
if (!info)
return;
info->request->metadata() = metadata;
info->metadataProcessed = true;
pipe->tryCompleteRequest(info->request);
}
RkISP1CameraConfiguration::RkISP1CameraConfiguration(Camera *camera,
RkISP1CameraData *data)
: CameraConfiguration()
{
camera_ = camera->shared_from_this();
data_ = data;
}
bool RkISP1CameraConfiguration::fitsAllPaths(const StreamConfiguration &cfg)
{
StreamConfiguration config;
config = cfg;
if (data_->mainPath_->validate(&config) != Valid)
return false;
config = cfg;
if (data_->selfPath_->validate(&config) != Valid)
return false;
return true;
}
CameraConfiguration::Status RkISP1CameraConfiguration::validate()
{
const CameraSensor *sensor = data_->sensor_.get();
Status status = Valid;
if (config_.empty())
return Invalid;
if (transform != Transform::Identity) {
transform = Transform::Identity;
status = Adjusted;
}
/* Cap the number of entries to the available streams. */
if (config_.size() > 2) {
config_.resize(2);
status = Adjusted;
}
/*
* If there are more than one stream in the configuration figure out the
* order to evaluate the streams. The first stream has the highest
* priority but if both main path and self path can satisfy it evaluate
* the second stream first as the first stream is guaranteed to work
* with whichever path is not used by the second one.
*/
std::vector<unsigned int> order(config_.size());
std::iota(order.begin(), order.end(), 0);
if (config_.size() == 2 && fitsAllPaths(config_[0]))
std::reverse(order.begin(), order.end());
bool mainPathAvailable = true;
bool selfPathAvailable = true;
for (unsigned int index : order) {
StreamConfiguration &cfg = config_[index];
/* Try to match stream without adjusting configuration. */
if (mainPathAvailable) {
StreamConfiguration tryCfg = cfg;
if (data_->mainPath_->validate(&tryCfg) == Valid) {
mainPathAvailable = false;
cfg = tryCfg;
cfg.setStream(const_cast<Stream *>(&data_->mainPathStream_));
continue;
}
}
if (selfPathAvailable) {
StreamConfiguration tryCfg = cfg;
if (data_->selfPath_->validate(&tryCfg) == Valid) {
selfPathAvailable = false;
cfg = tryCfg;
cfg.setStream(const_cast<Stream *>(&data_->selfPathStream_));
continue;
}
}
/* Try to match stream allowing adjusting configuration. */
if (mainPathAvailable) {
StreamConfiguration tryCfg = cfg;
if (data_->mainPath_->validate(&tryCfg) == Adjusted) {
mainPathAvailable = false;
cfg = tryCfg;
cfg.setStream(const_cast<Stream *>(&data_->mainPathStream_));
status = Adjusted;
continue;
}
}
if (selfPathAvailable) {
StreamConfiguration tryCfg = cfg;
if (data_->selfPath_->validate(&tryCfg) == Adjusted) {
selfPathAvailable = false;
cfg = tryCfg;
cfg.setStream(const_cast<Stream *>(&data_->selfPathStream_));
status = Adjusted;
continue;
}
}
/* All paths rejected configuraiton. */
LOG(RkISP1, Debug) << "Camera configuration not supported "
<< cfg.toString();
return Invalid;
}
/* Select the sensor format. */
Size maxSize;
for (const StreamConfiguration &cfg : config_)
maxSize = std::max(maxSize, cfg.size);
sensorFormat_ = 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 },
maxSize);
if (sensorFormat_.size.isNull())
sensorFormat_.size = sensor->resolution();
return status;
}
PipelineHandlerRkISP1::PipelineHandlerRkISP1(CameraManager *manager)
: PipelineHandler(manager)
{
}
/* -----------------------------------------------------------------------------
* Pipeline Operations
*/
CameraConfiguration *PipelineHandlerRkISP1::generateConfiguration(Camera *camera,
const StreamRoles &roles)
{
RkISP1CameraData *data = cameraData(camera);
CameraConfiguration *config = new RkISP1CameraConfiguration(camera, data);
if (roles.empty())
return config;
bool mainPathAvailable = true;
bool selfPathAvailable = true;
for (const StreamRole role : roles) {
bool useMainPath;
switch (role) {
case StreamRole::StillCapture: {
useMainPath = mainPathAvailable;
break;
}
case StreamRole::Viewfinder:
case StreamRole::VideoRecording: {
useMainPath = !selfPathAvailable;
break;
}
default:
LOG(RkISP1, Warning)
<< "Requested stream role not supported: " << role;
delete config;
return nullptr;
}
StreamConfiguration cfg;
if (useMainPath) {
cfg = data->mainPath_->generateConfiguration(
data->sensor_->resolution());
mainPathAvailable = false;
} else {
cfg = data->selfPath_->generateConfiguration(
data->sensor_->resolution());
selfPathAvailable = false;
}
config->addConfiguration(cfg);
}
config->validate();
return config;
}
int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
{
RkISP1CameraConfiguration *config =
static_cast<RkISP1CameraConfiguration *>(c);
RkISP1CameraData *data = cameraData(camera);
CameraSensor *sensor = data->sensor_.get();
int ret;
ret = initLinks(camera, sensor, *config);
if (ret)
return ret;
/*
* Configure the format on the sensor output and propagate it through
* the pipeline.
*/
V4L2SubdeviceFormat format = config->sensorFormat();
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 = isp_->setFormat(0, &format);
if (ret < 0)
return ret;
Rectangle rect(0, 0, format.size);
ret = isp_->setSelection(0, V4L2_SEL_TGT_CROP, &rect);
if (ret < 0)
return ret;
LOG(RkISP1, Debug)
<< "ISP input pad configured with " << format.toString()
<< " crop " << rect.toString();
/* YUYV8_2X8 is required on the ISP source path pad for YUV output. */
format.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8;
LOG(RkISP1, Debug)
<< "Configuring ISP output pad with " << format.toString()
<< " crop " << rect.toString();
ret = isp_->setSelection(2, V4L2_SEL_TGT_CROP, &rect);
if (ret < 0)
return ret;
ret = isp_->setFormat(2, &format);
if (ret < 0)
return ret;
LOG(RkISP1, Debug)
<< "ISP output pad configured with " << format.toString()
<< " crop " << rect.toString();
std::map<unsigned int, IPAStream> streamConfig;
for (const StreamConfiguration &cfg : *config) {
if (cfg.stream() == &data->mainPathStream_) {
ret = mainPath_.configure(cfg, format);
streamConfig[0] = IPAStream(cfg.pixelFormat,
cfg.size);
} else {
ret = selfPath_.configure(cfg, format);
streamConfig[1] = IPAStream(cfg.pixelFormat,
cfg.size);
}
if (ret)
return ret;
}
V4L2DeviceFormat paramFormat;
paramFormat.fourcc = V4L2PixelFormat(V4L2_META_FMT_RK_ISP1_PARAMS);
ret = param_->setFormat(&paramFormat);
if (ret)
return ret;
V4L2DeviceFormat statFormat;
statFormat.fourcc = V4L2PixelFormat(V4L2_META_FMT_RK_ISP1_STAT_3A);
ret = stat_->setFormat(&statFormat);
if (ret)
return ret;
/* Inform IPA of stream configuration and sensor controls. */
CameraSensorInfo sensorInfo = {};
ret = data->sensor_->sensorInfo(&sensorInfo);
if (ret) {
/* \todo Turn this into a hard failure. */
LOG(RkISP1, Warning) << "Camera sensor information not available";
sensorInfo = {};
ret = 0;
}
std::map<uint32_t, ControlInfoMap> entityControls;
entityControls.emplace(0, data->sensor_->controls());
ret = data->ipa_->configure(sensorInfo, streamConfig, entityControls);
if (ret) {
LOG(RkISP1, Error) << "failed configuring IPA (" << ret << ")";
return ret;
}
return 0;
}
int PipelineHandlerRkISP1::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream,
std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
RkISP1CameraData *data = cameraData(camera);
unsigned int count = stream->configuration().bufferCount;
if (stream == &data->mainPathStream_)
return mainPath_.exportBuffers(count, buffers);
else if (stream == &data->selfPathStream_)
return selfPath_.exportBuffers(count, buffers);
return -EINVAL;
}
int PipelineHandlerRkISP1::allocateBuffers(Camera *camera)
{
RkISP1CameraData *data = cameraData(camera);
unsigned int ipaBufferId = 1;
int ret;
unsigned int maxCount = std::max({
data->mainPathStream_.configuration().bufferCount,
data->selfPathStream_.configuration().bufferCount,
});
ret = param_->allocateBuffers(maxCount, &paramBuffers_);
if (ret < 0)
goto error;
ret = stat_->allocateBuffers(maxCount, &statBuffers_);
if (ret < 0)
goto error;
for (std::unique_ptr<FrameBuffer> &buffer : paramBuffers_) {
buffer->setCookie(ipaBufferId++);
data->ipaBuffers_.emplace_back(buffer->cookie(),
buffer->planes());
availableParamBuffers_.push(buffer.get());
}
for (std::unique_ptr<FrameBuffer> &buffer : statBuffers_) {
buffer->setCookie(ipaBufferId++);
data->ipaBuffers_.emplace_back(buffer->cookie(),
buffer->planes());
availableStatBuffers_.push(buffer.get());
}
data->ipa_->mapBuffers(data->ipaBuffers_);
return 0;
error:
paramBuffers_.clear();
statBuffers_.clear();
return ret;
}
int PipelineHandlerRkISP1::freeBuffers(Camera *camera)
{
RkISP1CameraData *data = cameraData(camera);
while (!availableStatBuffers_.empty())
availableStatBuffers_.pop();
while (!availableParamBuffers_.empty())
availableParamBuffers_.pop();
paramBuffers_.clear();
statBuffers_.clear();
std::vector<unsigned int> ids;
for (IPABuffer &ipabuf : data->ipaBuffers_)
ids.push_back(ipabuf.id);
data->ipa_->unmapBuffers(ids);
data->ipaBuffers_.clear();
if (param_->releaseBuffers())
LOG(RkISP1, Error) << "Failed to release parameters buffers";
if (stat_->releaseBuffers())
LOG(RkISP1, Error) << "Failed to release stat buffers";
return 0;
}
int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlList *controls)
{
RkISP1CameraData *data = cameraData(camera);
int ret;
/* Allocate buffers for internal pipeline usage. */
ret = allocateBuffers(camera);
if (ret)
return ret;
ret = data->ipa_->start();
if (ret) {
freeBuffers(camera);
LOG(RkISP1, Error)
<< "Failed to start IPA " << camera->id();
return ret;
}
data->frame_ = 0;
ret = param_->streamOn();
if (ret) {
data->ipa_->stop();
freeBuffers(camera);
LOG(RkISP1, Error)
<< "Failed to start parameters " << camera->id();
return ret;
}
ret = stat_->streamOn();
if (ret) {
param_->streamOff();
data->ipa_->stop();
freeBuffers(camera);
LOG(RkISP1, Error)
<< "Failed to start statistics " << camera->id();
return ret;
}
if (data->mainPath_->isEnabled()) {
ret = mainPath_.start();
if (ret) {
param_->streamOff();
stat_->streamOff();
data->ipa_->stop();
freeBuffers(camera);
return ret;
}
}
if (data->selfPath_->isEnabled()) {
ret = selfPath_.start();
if (ret) {
mainPath_.stop();
param_->streamOff();
stat_->streamOff();
data->ipa_->stop();
freeBuffers(camera);
return ret;
}
}
isp_->setFrameStartEnabled(true);
activeCamera_ = camera;
return ret;
}
void PipelineHandlerRkISP1::stop(Camera *camera)
{
RkISP1CameraData *data = cameraData(camera);
int ret;
isp_->setFrameStartEnabled(false);
data->ipa_->stop();
selfPath_.stop();
mainPath_.stop();
ret = stat_->streamOff();
if (ret)
LOG(RkISP1, Warning)
<< "Failed to stop statistics for " << camera->id();
ret = param_->streamOff();
if (ret)
LOG(RkISP1, Warning)
<< "Failed to stop parameters for " << camera->id();
ASSERT(data->queuedRequests_.empty());
data->frameInfo_.clear();
freeBuffers(camera);
activeCamera_ = nullptr;
}
int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request)
{
RkISP1CameraData *data = cameraData(camera);
RkISP1FrameInfo *info = data->frameInfo_.create(data, request);
if (!info)
return -ENOENT;
ipa::rkisp1::RkISP1Event ev;
ev.op = ipa::rkisp1::EventQueueRequest;
ev.frame = data->frame_;
ev.bufferId = info->paramBuffer->cookie();
ev.controls = request->controls();
data->ipa_->processEvent(ev);
data->frame_++;
return 0;
}
/* -----------------------------------------------------------------------------
* Match and Setup
*/
int PipelineHandlerRkISP1::initLinks(const Camera *camera,
const CameraSensor *sensor,
const RkISP1CameraConfiguration &config)
{
RkISP1CameraData *data = cameraData(camera);
int ret;
ret = media_->disableLinks();
if (ret < 0)
return ret;
/*
* Configure the sensor links: enable the link corresponding to this
* camera.
*/
const MediaPad *pad = isp_->entity()->getPadByIndex(0);
for (MediaLink *link : pad->links()) {
if (link->source()->entity() != sensor->entity())
continue;
LOG(RkISP1, Debug)
<< "Enabling link from sensor '"
<< link->source()->entity()->name()
<< "' to ISP";
ret = link->setEnabled(true);
if (ret < 0)
return ret;
}
for (const StreamConfiguration &cfg : config) {
if (cfg.stream() == &data->mainPathStream_)
ret = data->mainPath_->setEnabled(true);
else if (cfg.stream() == &data->selfPathStream_)
ret = data->selfPath_->setEnabled(true);
else
return -EINVAL;
if (ret < 0)
return ret;
}
return 0;
}
int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
{
int ret;
std::unique_ptr<RkISP1CameraData> data =
std::make_unique<RkISP1CameraData>(this, &mainPath_, &selfPath_);
ControlInfoMap::Map ctrls;
ctrls.emplace(std::piecewise_construct,
std::forward_as_tuple(&controls::AeEnable),
std::forward_as_tuple(false, true));
data->controlInfo_ = std::move(ctrls);
data->sensor_ = std::make_unique<CameraSensor>(sensor);
ret = data->sensor_->init();
if (ret)
return ret;
/* Initialize the camera properties. */
data->properties_ = data->sensor_->properties();
/*
* \todo Read dealy values from the sensor itself or from a
* a sensor database. For now use generic values taken from
* the Raspberry Pi and listed as generic values.
*/
std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
{ V4L2_CID_ANALOGUE_GAIN, { 1, false } },
{ V4L2_CID_EXPOSURE, { 2, false } },
};
data->delayedCtrls_ =
std::make_unique<DelayedControls>(data->sensor_->device(),
params);
isp_->frameStart.connect(data->delayedCtrls_.get(),
&DelayedControls::applyControls);
ret = data->loadIPA(media_->hwRevision());
if (ret)
return ret;
std::set<Stream *> streams{
&data->mainPathStream_,
&data->selfPathStream_,
};
std::shared_ptr<Camera> camera =
Camera::create(this, data->sensor_->id(), streams);
registerCamera(std::move(camera), std::move(data));
return 0;
}
bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
{
const MediaPad *pad;
DeviceMatch dm("rkisp1");
dm.add("rkisp1_isp");
dm.add("rkisp1_resizer_selfpath");
dm.add("rkisp1_resizer_mainpath");
dm.add("rkisp1_selfpath");
dm.add("rkisp1_mainpath");
dm.add("rkisp1_stats");
dm.add("rkisp1_params");
media_ = acquireMediaDevice(enumerator, dm);
if (!media_)
return false;
if (!media_->hwRevision()) {
LOG(RkISP1, Error)
<< "The rkisp1 driver is too old, v5.11 or newer is required";
return false;
}
/* Create the V4L2 subdevices we will need. */
isp_ = V4L2Subdevice::fromEntityName(media_, "rkisp1_isp");
if (isp_->open() < 0)
return false;
/* Locate and open the stats and params video nodes. */
stat_ = V4L2VideoDevice::fromEntityName(media_, "rkisp1_stats");
if (stat_->open() < 0)
return false;
param_ = V4L2VideoDevice::fromEntityName(media_, "rkisp1_params");
if (param_->open() < 0)
return false;
/* Locate and open the ISP main and self paths. */
if (!mainPath_.init(media_))
return false;
if (!selfPath_.init(media_))
return false;
mainPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
selfPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statReady);
param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramReady);
/*
* Enumerate all sensors connected to the ISP and create one
* camera instance for each of them.
*/
pad = isp_->entity()->getPadByIndex(0);
if (!pad)
return false;
bool registered = false;
for (MediaLink *link : pad->links()) {
if (!createCamera(link->source()->entity()))
registered = true;
}
return registered;
}
/* -----------------------------------------------------------------------------
* Buffer Handling
*/
void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)
{
RkISP1CameraData *data = cameraData(activeCamera_);
RkISP1FrameInfo *info = data->frameInfo_.find(request);
if (!info)
return;
if (request->hasPendingBuffers())
return;
if (!info->metadataProcessed)
return;
if (!info->paramDequeued)
return;
data->frameInfo_.destroy(info->frame);
completeRequest(request);
}
void PipelineHandlerRkISP1::bufferReady(FrameBuffer *buffer)
{
Request *request = buffer->request();
completeBuffer(request, buffer);
tryCompleteRequest(request);
}
void PipelineHandlerRkISP1::paramReady(FrameBuffer *buffer)
{
ASSERT(activeCamera_);
RkISP1CameraData *data = cameraData(activeCamera_);
RkISP1FrameInfo *info = data->frameInfo_.find(buffer);
info->paramDequeued = true;
tryCompleteRequest(info->request);
}
void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer)
{
ASSERT(activeCamera_);
RkISP1CameraData *data = cameraData(activeCamera_);
RkISP1FrameInfo *info = data->frameInfo_.find(buffer);
if (!info)
return;
if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
info->metadataProcessed = true;
tryCompleteRequest(info->request);
return;
}
if (data->frame_ <= buffer->metadata().sequence)
data->frame_ = buffer->metadata().sequence + 1;
ipa::rkisp1::RkISP1Event ev;
ev.op = ipa::rkisp1::EventSignalStatBuffer;
ev.frame = info->frame;
ev.bufferId = info->statBuffer->cookie();
data->ipa_->processEvent(ev);
}
REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1)
} /* namespace libcamera */