libcamera: mali-c55: Add RZG2LCRU class
Add a class allowing us to control the RZ/G2L Camera Receiver Unit. The class is named after the media device it handles, which is called "rzg2l_cru" but the CRU is actually found in other SoCs than the RZ/G2L one, such as the RZ/V2H(P). The RZG2LCRU class models the CSI-2 receiver found on those SoCs and allows to add support for memory-to-memory operations on the RZ/V2H(P) SoC which integrates a Mali-C55 ISP and a specialized DMA engine named IVC that feeds images from memory, where the CRU has saved them. Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
committed by
Jacopo Mondi
parent
9bd7dc3a69
commit
157f03e2df
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
libcamera_internal_sources += files([
|
||||
'mali-c55.cpp'
|
||||
'mali-c55.cpp',
|
||||
'rzg2l-cru.cpp',
|
||||
])
|
||||
|
||||
253
src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp
Normal file
253
src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2025, Ideas on Board Oy
|
||||
*
|
||||
* Pipine handler element for the Renesas RZ/G2L Camera Receiver Unit
|
||||
*/
|
||||
|
||||
#include "rzg2l-cru.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <libcamera/base/log.h>
|
||||
#include <libcamera/base/regex.h>
|
||||
#include <libcamera/base/utils.h>
|
||||
|
||||
#include <libcamera/formats.h>
|
||||
#include <libcamera/pixel_format.h>
|
||||
|
||||
#include "libcamera/internal/bayer_format.h"
|
||||
#include "libcamera/internal/camera_sensor.h"
|
||||
#include "libcamera/internal/framebuffer.h"
|
||||
#include "libcamera/internal/media_device.h"
|
||||
#include "libcamera/internal/request.h"
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
static const std::map<uint8_t, V4L2PixelFormat> bitDepthToFmt{
|
||||
{ 10, V4L2PixelFormat(V4L2_PIX_FMT_RAW_CRU10) },
|
||||
{ 12, V4L2PixelFormat(V4L2_PIX_FMT_RAW_CRU12) },
|
||||
{ 14, V4L2PixelFormat(V4L2_PIX_FMT_RAW_CRU14) },
|
||||
};
|
||||
|
||||
LOG_DEFINE_CATEGORY(RZG2LCRU)
|
||||
|
||||
FrameBuffer *RZG2LCRU::queueBuffer(Request *request)
|
||||
{
|
||||
FrameBuffer *buffer;
|
||||
|
||||
if (availableBuffers_.empty()) {
|
||||
LOG(RZG2LCRU, Debug) << "CRU buffer underrun";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buffer = availableBuffers_.back();
|
||||
|
||||
int ret = output_->queueBuffer(buffer);
|
||||
if (ret) {
|
||||
LOG(RZG2LCRU, Error) << "Failed to queue buffer to CRU";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
availableBuffers_.pop_back();
|
||||
buffer->_d()->setRequest(request);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void RZG2LCRU::returnBuffer(FrameBuffer *buffer)
|
||||
{
|
||||
auto it = std::find_if(buffers_.begin(), buffers_.end(),
|
||||
[&](const auto &b) { return b.get() == buffer; });
|
||||
ASSERT(it != buffers_.end());
|
||||
|
||||
availableBuffers_.push_back(buffer);
|
||||
}
|
||||
|
||||
int RZG2LCRU::start(unsigned int bufferCount)
|
||||
{
|
||||
int ret = output_->exportBuffers(bufferCount, &buffers_);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
utils::scope_exit bufferGuard([&] { freeBuffers(); });
|
||||
|
||||
ret = output_->importBuffers(bufferCount);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (std::unique_ptr<FrameBuffer> &buffer : buffers_)
|
||||
availableBuffers_.push_back(buffer.get());
|
||||
|
||||
ret = output_->streamOn();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bufferGuard.release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RZG2LCRU::stop()
|
||||
{
|
||||
return output_->streamOff();
|
||||
}
|
||||
|
||||
int RZG2LCRU::freeBuffers()
|
||||
{
|
||||
availableBuffers_.clear();
|
||||
buffers_.clear();
|
||||
|
||||
return output_->releaseBuffers();
|
||||
}
|
||||
|
||||
int RZG2LCRU::configure(V4L2SubdeviceFormat *subdevFormat, V4L2DeviceFormat *inputFormat)
|
||||
{
|
||||
/*
|
||||
* Set format on the sensor and propagate it up to the CRU video
|
||||
* device.
|
||||
*/
|
||||
|
||||
int ret = sensor_->setFormat(subdevFormat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = csi2_->setFormat(0, subdevFormat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = csi2_->getFormat(1, subdevFormat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cru_->setFormat(0, subdevFormat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cru_->getFormat(1, subdevFormat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The capture device needs to be set with a format that can be produced
|
||||
* from the mbus code of the subdevFormat. The CRU and IVC use bayer
|
||||
* order agnostic pixel formats, so all we need to do is find the right
|
||||
* bitdepth and select the appropriate format.
|
||||
*/
|
||||
BayerFormat bayerFormat = BayerFormat::fromMbusCode(subdevFormat->code);
|
||||
if (!bayerFormat.isValid())
|
||||
return -EINVAL;
|
||||
|
||||
V4L2DeviceFormat captureFormat;
|
||||
captureFormat.fourcc = bitDepthToFmt.at(bayerFormat.bitDepth);
|
||||
captureFormat.size = subdevFormat->size;
|
||||
|
||||
ret = output_->setFormat(&captureFormat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* We return the format that we set against the output device, as the
|
||||
* same format will also need to be set against the Input Video Control
|
||||
* Block device.
|
||||
*/
|
||||
*inputFormat = captureFormat;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RZG2LCRU::initCRUSizes()
|
||||
{
|
||||
Size maxCSI2Size;
|
||||
|
||||
/*
|
||||
* Get the maximum supported size on the CSI-2 receiver. We need to
|
||||
* query the kernel interface as the size limits differ from RZ/G2L
|
||||
* (2800x4095) and RZ/V2H (4096x4096).
|
||||
*/
|
||||
V4L2Subdevice::Formats csi2Formats = csi2_->formats(0);
|
||||
if (csi2Formats.empty())
|
||||
return;
|
||||
|
||||
for (const auto &format : csi2Formats) {
|
||||
for (const auto &range : format.second) {
|
||||
if (range.max > maxCSI2Size)
|
||||
maxCSI2Size = range.max;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enumerate the sensor supported resolutions and filter out the ones
|
||||
* larger than the maximum supported CSI-2 receiver input size.
|
||||
*/
|
||||
V4L2Subdevice::Formats formats = sensor_->device()->formats(0);
|
||||
if (formats.empty())
|
||||
return;
|
||||
|
||||
for (const auto &format : formats) {
|
||||
for (const auto &range : format.second) {
|
||||
const Size &max = range.max;
|
||||
|
||||
if (max.width > maxCSI2Size.width ||
|
||||
max.height > maxCSI2Size.height)
|
||||
continue;
|
||||
|
||||
csi2Sizes_.push_back(max);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort in increasing order and remove duplicates. */
|
||||
std::sort(csi2Sizes_.begin(), csi2Sizes_.end());
|
||||
auto last = std::unique(csi2Sizes_.begin(), csi2Sizes_.end());
|
||||
csi2Sizes_.erase(last, csi2Sizes_.end());
|
||||
|
||||
csi2Resolution_ = csi2Sizes_.back();
|
||||
}
|
||||
|
||||
int RZG2LCRU::init(const MediaDevice *media)
|
||||
{
|
||||
static const std::regex cruRegex("cru-ip-[0-9a-f]{8}.cru[0-9]");
|
||||
static const std::regex csi2Regex("csi-[0-9a-f]{8}.csi2");
|
||||
|
||||
csi2_ = V4L2Subdevice::fromEntityName(media, csi2Regex);
|
||||
if (!csi2_)
|
||||
return -ENODEV;
|
||||
|
||||
int ret = csi2_->open();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
const std::vector<MediaPad *> &pads = csi2_->entity()->pads();
|
||||
if (pads.empty())
|
||||
return -ENODEV;
|
||||
|
||||
/* The receiver has 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];
|
||||
sensor_ = CameraSensorFactoryBase::create(link->source()->entity());
|
||||
if (!sensor_)
|
||||
return -ENODEV;
|
||||
|
||||
cru_ = V4L2Subdevice::fromEntityName(media, cruRegex);
|
||||
ret = cru_->open();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
output_ = V4L2VideoDevice::fromEntityName(media, "CRU output");
|
||||
ret = output_->open();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
initCRUSizes();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* namespace libcamera */
|
||||
70
src/libcamera/pipeline/mali-c55/rzg2l-cru.h
Normal file
70
src/libcamera/pipeline/mali-c55/rzg2l-cru.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2025, Ideas on Board Oy
|
||||
*
|
||||
* Pipine handler element for the Renesas RZ/G2L Camera Receiver Unit
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <libcamera/base/signal.h>
|
||||
|
||||
#include "libcamera/internal/v4l2_subdevice.h"
|
||||
#include "libcamera/internal/v4l2_videodevice.h"
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
class CameraSensor;
|
||||
class FrameBuffer;
|
||||
class MediaDevice;
|
||||
class Request;
|
||||
class Size;
|
||||
|
||||
class RZG2LCRU
|
||||
{
|
||||
public:
|
||||
RZG2LCRU() = default;
|
||||
|
||||
const std::vector<Size> &sizes() const
|
||||
{
|
||||
return csi2Sizes_;
|
||||
}
|
||||
|
||||
int init(const MediaDevice *media);
|
||||
const Size &resolution() const
|
||||
{
|
||||
return csi2Resolution_;
|
||||
}
|
||||
|
||||
CameraSensor *sensor() const { return sensor_.get(); }
|
||||
V4L2Subdevice *csi2() const { return csi2_.get(); }
|
||||
V4L2Subdevice *cru() const { return cru_.get(); }
|
||||
V4L2VideoDevice *output() { return output_.get(); }
|
||||
|
||||
int configure(V4L2SubdeviceFormat *subdevFormat, V4L2DeviceFormat *inputFormat);
|
||||
FrameBuffer *queueBuffer(Request *request);
|
||||
void returnBuffer(FrameBuffer *buffer);
|
||||
int freeBuffers();
|
||||
|
||||
int start(unsigned int bufferCount);
|
||||
int stop();
|
||||
|
||||
private:
|
||||
void initCRUSizes();
|
||||
|
||||
std::unique_ptr<CameraSensor> sensor_;
|
||||
std::unique_ptr<V4L2Subdevice> csi2_;
|
||||
std::unique_ptr<V4L2Subdevice> cru_;
|
||||
std::unique_ptr<V4L2VideoDevice> output_;
|
||||
|
||||
std::vector<std::unique_ptr<FrameBuffer>> buffers_;
|
||||
std::vector<FrameBuffer *> availableBuffers_;
|
||||
|
||||
std::vector<Size> csi2Sizes_;
|
||||
Size csi2Resolution_;
|
||||
};
|
||||
|
||||
} /* namespace libcamera */
|
||||
Reference in New Issue
Block a user