mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-25 01:25:08 +03:00
libcamera: ipu3: Create CIO2Device class
Group CIO2 components (cio2, csi2 and image sensor) in a class associated with the CameraData, to ease management and initialization of CIO2 unit at camera registration time. A CIO2 unit will always be associated with a single Camera only. Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
This commit is contained in:
parent
289343a24a
commit
cfd2fff2cd
1 changed files with 138 additions and 109 deletions
|
@ -24,6 +24,28 @@ namespace libcamera {
|
||||||
|
|
||||||
LOG_DEFINE_CATEGORY(IPU3)
|
LOG_DEFINE_CATEGORY(IPU3)
|
||||||
|
|
||||||
|
class CIO2Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CIO2Device()
|
||||||
|
: output_(nullptr), csi2_(nullptr), sensor_(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~CIO2Device()
|
||||||
|
{
|
||||||
|
delete output_;
|
||||||
|
delete csi2_;
|
||||||
|
delete sensor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init(const MediaDevice *media, unsigned int index);
|
||||||
|
|
||||||
|
V4L2Device *output_;
|
||||||
|
V4L2Subdevice *csi2_;
|
||||||
|
V4L2Subdevice *sensor_;
|
||||||
|
};
|
||||||
|
|
||||||
class PipelineHandlerIPU3 : public PipelineHandler
|
class PipelineHandlerIPU3 : public PipelineHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -51,23 +73,13 @@ private:
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IPU3CameraData(PipelineHandler *pipe)
|
IPU3CameraData(PipelineHandler *pipe)
|
||||||
: CameraData(pipe), cio2_(nullptr), csi2_(nullptr),
|
: CameraData(pipe)
|
||||||
sensor_(nullptr)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~IPU3CameraData()
|
|
||||||
{
|
|
||||||
delete cio2_;
|
|
||||||
delete csi2_;
|
|
||||||
delete sensor_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bufferReady(Buffer *buffer);
|
void bufferReady(Buffer *buffer);
|
||||||
|
|
||||||
V4L2Device *cio2_;
|
CIO2Device cio2_;
|
||||||
V4L2Subdevice *csi2_;
|
|
||||||
V4L2Subdevice *sensor_;
|
|
||||||
|
|
||||||
Stream stream_;
|
Stream stream_;
|
||||||
};
|
};
|
||||||
|
@ -80,22 +92,22 @@ private:
|
||||||
|
|
||||||
void registerCameras();
|
void registerCameras();
|
||||||
|
|
||||||
std::shared_ptr<MediaDevice> cio2_;
|
std::shared_ptr<MediaDevice> cio2MediaDev_;
|
||||||
std::shared_ptr<MediaDevice> imgu_;
|
std::shared_ptr<MediaDevice> imguMediaDev_;
|
||||||
};
|
};
|
||||||
|
|
||||||
PipelineHandlerIPU3::PipelineHandlerIPU3(CameraManager *manager)
|
PipelineHandlerIPU3::PipelineHandlerIPU3(CameraManager *manager)
|
||||||
: PipelineHandler(manager), cio2_(nullptr), imgu_(nullptr)
|
: PipelineHandler(manager), cio2MediaDev_(nullptr), imguMediaDev_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PipelineHandlerIPU3::~PipelineHandlerIPU3()
|
PipelineHandlerIPU3::~PipelineHandlerIPU3()
|
||||||
{
|
{
|
||||||
if (cio2_)
|
if (cio2MediaDev_)
|
||||||
cio2_->release();
|
cio2MediaDev_->release();
|
||||||
|
|
||||||
if (imgu_)
|
if (imguMediaDev_)
|
||||||
imgu_->release();
|
imguMediaDev_->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<Stream *, StreamConfiguration>
|
std::map<Stream *, StreamConfiguration>
|
||||||
|
@ -110,7 +122,7 @@ PipelineHandlerIPU3::streamConfiguration(Camera *camera,
|
||||||
* FIXME: As of now, return the image format reported by the sensor.
|
* FIXME: As of now, return the image format reported by the sensor.
|
||||||
* In future good defaults should be provided for each stream.
|
* In future good defaults should be provided for each stream.
|
||||||
*/
|
*/
|
||||||
if (data->sensor_->getFormat(0, &format)) {
|
if (data->cio2_.sensor_->getFormat(0, &format)) {
|
||||||
LOG(IPU3, Error) << "Failed to create stream configurations";
|
LOG(IPU3, Error) << "Failed to create stream configurations";
|
||||||
return configs;
|
return configs;
|
||||||
}
|
}
|
||||||
|
@ -131,9 +143,9 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera,
|
||||||
{
|
{
|
||||||
IPU3CameraData *data = cameraData(camera);
|
IPU3CameraData *data = cameraData(camera);
|
||||||
StreamConfiguration *cfg = &config[&data->stream_];
|
StreamConfiguration *cfg = &config[&data->stream_];
|
||||||
V4L2Subdevice *sensor = data->sensor_;
|
V4L2Subdevice *sensor = data->cio2_.sensor_;
|
||||||
V4L2Subdevice *csi2 = data->csi2_;
|
V4L2Subdevice *csi2 = data->cio2_.csi2_;
|
||||||
V4L2Device *cio2 = data->cio2_;
|
V4L2Device *cio2 = data->cio2_.output_;
|
||||||
V4L2SubdeviceFormat subdevFormat = {};
|
V4L2SubdeviceFormat subdevFormat = {};
|
||||||
V4L2DeviceFormat devFormat = {};
|
V4L2DeviceFormat devFormat = {};
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -190,13 +202,14 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera,
|
||||||
|
|
||||||
int PipelineHandlerIPU3::allocateBuffers(Camera *camera, Stream *stream)
|
int PipelineHandlerIPU3::allocateBuffers(Camera *camera, Stream *stream)
|
||||||
{
|
{
|
||||||
IPU3CameraData *data = cameraData(camera);
|
|
||||||
const StreamConfiguration &cfg = stream->configuration();
|
const StreamConfiguration &cfg = stream->configuration();
|
||||||
|
IPU3CameraData *data = cameraData(camera);
|
||||||
|
V4L2Device *cio2 = data->cio2_.output_;
|
||||||
|
|
||||||
if (!cfg.bufferCount)
|
if (!cfg.bufferCount)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
int ret = data->cio2_->exportBuffers(&stream->bufferPool());
|
int ret = cio2->exportBuffers(&stream->bufferPool());
|
||||||
if (ret) {
|
if (ret) {
|
||||||
LOG(IPU3, Error) << "Failed to request memory";
|
LOG(IPU3, Error) << "Failed to request memory";
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -208,8 +221,9 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera, Stream *stream)
|
||||||
int PipelineHandlerIPU3::freeBuffers(Camera *camera, Stream *stream)
|
int PipelineHandlerIPU3::freeBuffers(Camera *camera, Stream *stream)
|
||||||
{
|
{
|
||||||
IPU3CameraData *data = cameraData(camera);
|
IPU3CameraData *data = cameraData(camera);
|
||||||
|
V4L2Device *cio2 = data->cio2_.output_;
|
||||||
|
|
||||||
int ret = data->cio2_->releaseBuffers();
|
int ret = cio2->releaseBuffers();
|
||||||
if (ret) {
|
if (ret) {
|
||||||
LOG(IPU3, Error) << "Failed to release memory";
|
LOG(IPU3, Error) << "Failed to release memory";
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -221,9 +235,10 @@ int PipelineHandlerIPU3::freeBuffers(Camera *camera, Stream *stream)
|
||||||
int PipelineHandlerIPU3::start(Camera *camera)
|
int PipelineHandlerIPU3::start(Camera *camera)
|
||||||
{
|
{
|
||||||
IPU3CameraData *data = cameraData(camera);
|
IPU3CameraData *data = cameraData(camera);
|
||||||
|
V4L2Device *cio2 = data->cio2_.output_;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = data->cio2_->streamOn();
|
ret = cio2->streamOn();
|
||||||
if (ret) {
|
if (ret) {
|
||||||
LOG(IPU3, Info) << "Failed to start camera " << camera->name();
|
LOG(IPU3, Info) << "Failed to start camera " << camera->name();
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -235,8 +250,9 @@ int PipelineHandlerIPU3::start(Camera *camera)
|
||||||
void PipelineHandlerIPU3::stop(Camera *camera)
|
void PipelineHandlerIPU3::stop(Camera *camera)
|
||||||
{
|
{
|
||||||
IPU3CameraData *data = cameraData(camera);
|
IPU3CameraData *data = cameraData(camera);
|
||||||
|
V4L2Device *cio2 = data->cio2_.output_;
|
||||||
|
|
||||||
if (data->cio2_->streamOff())
|
if (cio2->streamOff())
|
||||||
LOG(IPU3, Info) << "Failed to stop camera " << camera->name();
|
LOG(IPU3, Info) << "Failed to stop camera " << camera->name();
|
||||||
|
|
||||||
PipelineHandler::stop(camera);
|
PipelineHandler::stop(camera);
|
||||||
|
@ -245,6 +261,7 @@ void PipelineHandlerIPU3::stop(Camera *camera)
|
||||||
int PipelineHandlerIPU3::queueRequest(Camera *camera, Request *request)
|
int PipelineHandlerIPU3::queueRequest(Camera *camera, Request *request)
|
||||||
{
|
{
|
||||||
IPU3CameraData *data = cameraData(camera);
|
IPU3CameraData *data = cameraData(camera);
|
||||||
|
V4L2Device *cio2 = data->cio2_.output_;
|
||||||
Stream *stream = &data->stream_;
|
Stream *stream = &data->stream_;
|
||||||
|
|
||||||
Buffer *buffer = request->findBuffer(stream);
|
Buffer *buffer = request->findBuffer(stream);
|
||||||
|
@ -254,7 +271,7 @@ int PipelineHandlerIPU3::queueRequest(Camera *camera, Request *request)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = data->cio2_->queueBuffer(buffer);
|
int ret = cio2->queueBuffer(buffer);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -293,17 +310,17 @@ bool PipelineHandlerIPU3::match(DeviceEnumerator *enumerator)
|
||||||
* It is safe to acquire both media devices at this point as
|
* It is safe to acquire both media devices at this point as
|
||||||
* DeviceEnumerator::search() skips the busy ones for us.
|
* DeviceEnumerator::search() skips the busy ones for us.
|
||||||
*/
|
*/
|
||||||
cio2_ = enumerator->search(cio2_dm);
|
cio2MediaDev_ = enumerator->search(cio2_dm);
|
||||||
if (!cio2_)
|
if (!cio2MediaDev_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
cio2_->acquire();
|
cio2MediaDev_->acquire();
|
||||||
|
|
||||||
imgu_ = enumerator->search(imgu_dm);
|
imguMediaDev_ = enumerator->search(imgu_dm);
|
||||||
if (!imgu_)
|
if (!imguMediaDev_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
imgu_->acquire();
|
imguMediaDev_->acquire();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disable all links that are enabled by default on CIO2, as camera
|
* Disable all links that are enabled by default on CIO2, as camera
|
||||||
|
@ -312,17 +329,17 @@ bool PipelineHandlerIPU3::match(DeviceEnumerator *enumerator)
|
||||||
* Close the CIO2 media device after, as links are enabled and should
|
* Close the CIO2 media device after, as links are enabled and should
|
||||||
* not need to be changed after.
|
* not need to be changed after.
|
||||||
*/
|
*/
|
||||||
if (cio2_->open())
|
if (cio2MediaDev_->open())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (cio2_->disableLinks()) {
|
if (cio2MediaDev_->disableLinks()) {
|
||||||
cio2_->close();
|
cio2MediaDev_->close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCameras();
|
registerCameras();
|
||||||
|
|
||||||
cio2_->close();
|
cio2MediaDev_->close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -336,85 +353,28 @@ void PipelineHandlerIPU3::registerCameras()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* For each CSI-2 receiver on the IPU3, create a Camera if an
|
* For each CSI-2 receiver on the IPU3, create a Camera if an
|
||||||
* image sensor is connected to it.
|
* image sensor is connected to it and the sensor can produce images
|
||||||
|
* in a compatible format.
|
||||||
*/
|
*/
|
||||||
unsigned int numCameras = 0;
|
unsigned int numCameras = 0;
|
||||||
for (unsigned int id = 0; id < 4; ++id) {
|
for (unsigned int id = 0; id < 4; ++id) {
|
||||||
std::string csi2Name = "ipu3-csi2 " + std::to_string(id);
|
std::unique_ptr<IPU3CameraData> data =
|
||||||
MediaEntity *csi2 = cio2_->getEntityByName(csi2Name);
|
utils::make_unique<IPU3CameraData>(this);
|
||||||
int ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
const std::vector<MediaPad *> &pads = csi2->pads();
|
|
||||||
if (pads.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* 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())
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
MediaLink *link = links[0];
|
|
||||||
MediaEntity *sensor = link->source()->entity();
|
|
||||||
if (sensor->function() != MEDIA_ENT_F_CAM_SENSOR)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (link->setEnabled(true))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::unique_ptr<IPU3CameraData> data = utils::make_unique<IPU3CameraData>(this);
|
|
||||||
|
|
||||||
std::string cameraName = sensor->name() + " " + std::to_string(id);
|
|
||||||
std::set<Stream *> streams{ &data->stream_ };
|
std::set<Stream *> streams{ &data->stream_ };
|
||||||
std::shared_ptr<Camera> camera = Camera::create(this, cameraName, streams);
|
CIO2Device *cio2 = &data->cio2_;
|
||||||
|
|
||||||
/*
|
int ret = cio2->init(cio2MediaDev_.get(), id);
|
||||||
* Create and open video devices and subdevices associated with
|
|
||||||
* the camera.
|
|
||||||
*
|
|
||||||
* If any of these operations fails, the Camera instance won't
|
|
||||||
* be registered. The 'camera' shared pointer and the 'data'
|
|
||||||
* unique pointers go out of scope and delete the objects they
|
|
||||||
* manage.
|
|
||||||
*/
|
|
||||||
std::string cio2Name = "ipu3-cio2 " + std::to_string(id);
|
|
||||||
MediaEntity *cio2 = cio2_->getEntityByName(cio2Name);
|
|
||||||
if (!cio2) {
|
|
||||||
LOG(IPU3, Error)
|
|
||||||
<< "Failed to get entity '" << cio2Name << "'";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->cio2_ = new V4L2Device(cio2);
|
|
||||||
ret = data->cio2_->open();
|
|
||||||
if (ret)
|
if (ret)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
data->cio2_->bufferReady.connect(data.get(), &IPU3CameraData::bufferReady);
|
std::string cameraName = cio2->sensor_->entityName() + " "
|
||||||
|
+ std::to_string(id);
|
||||||
|
std::shared_ptr<Camera> camera = Camera::create(this,
|
||||||
|
cameraName,
|
||||||
|
streams);
|
||||||
|
|
||||||
data->sensor_ = new V4L2Subdevice(sensor);
|
cio2->output_->bufferReady.connect(data.get(),
|
||||||
ret = data->sensor_->open();
|
&IPU3CameraData::bufferReady);
|
||||||
if (ret)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
data->csi2_ = new V4L2Subdevice(csi2);
|
|
||||||
ret = data->csi2_->open();
|
|
||||||
if (ret)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
registerCamera(std::move(camera), std::move(data));
|
registerCamera(std::move(camera), std::move(data));
|
||||||
|
|
||||||
|
@ -435,6 +395,75 @@ void PipelineHandlerIPU3::IPU3CameraData::bufferReady(Buffer *buffer)
|
||||||
pipe_->completeRequest(camera_, request);
|
pipe_->completeRequest(camera_, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* CIO2 Device
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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.
|
||||||
|
*
|
||||||
|
* \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();
|
||||||
|
if (sensorEntity->function() != MEDIA_ENT_F_CAM_SENSOR)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
ret = link->setEnabled(true);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* \todo Define when to open and close video device nodes, as they
|
||||||
|
* might impact on power consumption.
|
||||||
|
*/
|
||||||
|
sensor_ = new V4L2Subdevice(sensorEntity);
|
||||||
|
ret = sensor_->open();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
csi2_ = new V4L2Subdevice(csi2Entity);
|
||||||
|
ret = csi2_->open();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
std::string cio2Name = "ipu3-cio2 " + std::to_string(index);
|
||||||
|
output_ = V4L2Device::fromEntityName(media, cio2Name);
|
||||||
|
ret = output_->open();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
REGISTER_PIPELINE_HANDLER(PipelineHandlerIPU3);
|
REGISTER_PIPELINE_HANDLER(PipelineHandlerIPU3);
|
||||||
|
|
||||||
} /* namespace libcamera */
|
} /* namespace libcamera */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue