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:
Jacopo Mondi 2019-02-27 13:25:30 +01:00
parent 289343a24a
commit cfd2fff2cd

View file

@ -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 */